Merge "Check if CPU time tracking is expected to work" into sc-dev
diff --git a/Android.bp b/Android.bp
index 202e548..9655daf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -536,7 +536,7 @@
"android.hardware.vibrator-V1.3-java",
"android.security.apc-java",
"android.security.authorization-java",
- "android.system.keystore2-java",
+ "android.system.keystore2-V1-java",
"android.system.suspend.control.internal-java",
"cameraprotosnano",
"devicepolicyprotosnano",
@@ -760,6 +760,7 @@
srcs: [
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
+ ":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
":service-permission-protos",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 49433f1..86364af 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -82,12 +82,13 @@
"android.hardware.vibrator-V1.3-java",
"framework-protos",
"stable.core.platform.api.stubs",
- // There are a few classes from modules used as type arguments that
- // need to be resolved by metalava. For now, we can use a previously
- // finalized stub library to resolve them. If a new class gets added,
- // this may be need to be revisited to use a manually maintained stub
- // library with empty classes in order to resolve those references.
- "sdk_system_30_android",
+ // There are a few classes from modules used by the core that
+ // need to be resolved by metalava. We use a prebuilt stub of the
+ // full sdk to ensure we can resolve them. If a new class gets added,
+ // the prebuilts/sdk/current needs to be updated.
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
],
high_mem: true, // Lots of sources => high memory use, see b/170701554
installable: false,
@@ -404,7 +405,11 @@
"android_defaults_stubs_current",
"android_stubs_dists_default",
],
- libs: ["sdk_system_29_android"],
+ libs: [
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
+ ],
static_libs: ["art.module.public.api.stubs"],
dist: {
dir: "apistubs/android/module-lib",
diff --git a/apct-tests/perftests/core/OWNERS b/apct-tests/perftests/core/OWNERS
new file mode 100644
index 0000000..18486af
--- /dev/null
+++ b/apct-tests/perftests/core/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index c37f6d9..a2dc1c2 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -19,14 +19,12 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
import android.perftests.utils.ManualBenchmarkState;
import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
import android.perftests.utils.PerfManualStatusReporter;
import android.view.Display;
-import android.view.DisplayCutout;
import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InsetsSourceControl;
@@ -85,9 +83,6 @@
private static class TestWindow extends BaseIWindow {
final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
final InsetsState mRequestedVisibility = new InsetsState();
- final Rect mOutFrame = new Rect();
- final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
final InsetsState mOutInsetsState = new InsetsState();
final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
@@ -107,7 +102,7 @@
long startTime = SystemClock.elapsedRealtimeNanos();
session.addToDisplay(this, mLayoutParams, View.VISIBLE,
- Display.DEFAULT_DISPLAY, mRequestedVisibility, mOutFrame, inputChannel,
+ Display.DEFAULT_DISPLAY, mRequestedVisibility, inputChannel,
mOutInsetsState, mOutControls);
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
state.addExtraResult("add", elapsedTimeNsOfAdd);
diff --git a/apex/Android.bp b/apex/Android.bp
index 1876110..8310ba7 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -15,189 +15,3 @@
package {
default_visibility: [":__subpackages__"],
}
-
-mainline_stubs_args =
- "--error UnhiddenSystemApi " +
- "--hide BroadcastBehavior " +
- "--hide CallbackInterface " +
- "--hide DeprecationMismatch " +
- "--hide HiddenSuperclass " +
- "--hide HiddenTypedefConstant " +
- "--hide HiddenTypeParameter " +
- "--hide MissingPermission " +
- "--hide RequiresPermission " +
- "--hide SdkConstant " +
- "--hide Todo " +
- "--hide Typo " +
- "--hide UnavailableSymbol "
-
-// TODO: modularize this so not every module has the same whitelist
-framework_packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
-// TODO: remove the hiding when server classes are cleaned up.
-mainline_framework_stubs_args =
- mainline_stubs_args +
- "--hide-package com.android.server "
-
-priv_apps = " " +
- "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\) "
-
-module_libs = " " +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
- "\\)" +
- " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\) "
-
-mainline_service_stubs_args =
- mainline_stubs_args +
- "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.SYSTEM_SERVER" +
- "\\) " +
- "--hide-annotation android.annotation.Hide " +
- "--hide InternalClasses " // com.android.* classes are okay in this interface
-
-// Defaults common to all mainline module java_sdk_library instances.
-java_defaults {
- name: "framework-module-common-defaults",
-
- // Additional annotations used for compiling both the implementation and the
- // stubs libraries.
- libs: ["framework-annotations-lib"],
-
- // Framework modules are not generally shared libraries, i.e. they are not
- // intended, and must not be allowed, to be used in a <uses-library> manifest
- // entry.
- shared_library: false,
-
- // Prevent dependencies that do not specify an sdk_version from accessing the
- // implementation library by default and force them to use stubs instead.
- default_to_stubs: true,
-
- // Enable api lint. This will eventually become the default for java_sdk_library
- // but it cannot yet be turned on because some usages have not been cleaned up.
- // TODO(b/156126315) - Remove when no longer needed.
- api_lint: {
- enabled: true,
- },
-
- // The API scope specific properties.
- public: {
- enabled: true,
- sdk_version: "module_current",
- },
-
- // installable implies we'll create a non-apex (platform) variant, which
- // we shouldn't ordinarily need (and it can create issues), so disable that.
- installable: false,
-
- // Configure framework module specific metalava options.
- droiddoc_options: [mainline_stubs_args],
-
- annotations_enabled: true,
-
- // Allow access to the stubs from anywhere
- visibility: ["//visibility:public"],
- stubs_library_visibility: ["//visibility:public"],
-
- // Hide impl library and stub sources
- impl_library_visibility: [
- ":__pkg__",
- "//frameworks/base", // For framework-all
- ],
- stubs_source_visibility: ["//visibility:private"],
-
- defaults_visibility: ["//visibility:private"],
-
- // Collates API usages from each module for further analysis.
- plugins: ["java_api_finder"],
-
- // Mainline modules should only rely on 'module_lib' APIs provided by other modules
- // and the non updatable parts of the platform.
- sdk_version: "module_current",
-}
-
-// Defaults for mainline module provided java_sdk_library instances.
-java_defaults {
- name: "framework-module-defaults",
- defaults: ["framework-module-common-defaults"],
-
- system: {
- enabled: true,
- sdk_version: "module_current",
- },
- module_lib: {
- enabled: true,
- sdk_version: "module_current",
- },
- defaults_visibility: [
- ":__subpackages__",
- "//frameworks/base/libs/hwui",
- "//frameworks/base/wifi",
- "//packages/modules:__subpackages__",
- "//packages/providers/MediaProvider:__subpackages__",
- ],
-}
-
-// Defaults for mainline module system server provided java_sdk_library instances.
-java_defaults {
- name: "framework-system-server-module-defaults",
- defaults: ["framework-module-common-defaults"],
-
- system_server: {
- enabled: true,
- sdk_version: "module_current",
- },
- defaults_visibility: [
- ":__subpackages__",
- "//packages/modules:__subpackages__",
- ],
-}
-
-stubs_defaults {
- name: "service-module-stubs-srcs-defaults",
- args: mainline_service_stubs_args,
- installable: false,
- annotations_enabled: true,
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- filter_packages: ["com.android."],
- check_api: {
- current: {
- api_file: "api/current.txt",
- removed_api_file: "api/removed.txt",
- },
- api_lint: {
- enabled: true,
- },
- },
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system-server/api",
- },
-}
-
-// Empty for now, but a convenient place to add rules for all
-// module java_library system_server stub libs.
-java_defaults {
- name: "service-module-stubs-defaults",
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system-server",
- },
-}
diff --git a/apex/OWNERS b/apex/OWNERS
index bde2bec..b3e81b9 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,8 +1 @@
-# Mainline modularization team
-
-andreionea@google.com
-dariofreni@google.com
-hansson@google.com
-mathewi@google.com
-pedroql@google.com
-satayev@google.com
+file:platform/packages/modules/common:/OWNERS
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 6eb44a7..930415f 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -593,15 +593,6 @@
}
/**
- * @see JobInfo.Builder#setExpedited(boolean)
- * @deprecated Use {@link #isExpedited()} instead
- */
- @Deprecated
- public boolean isForegroundJob() {
- return (flags & FLAG_EXPEDITED) != 0;
- }
-
- /**
* @see JobInfo.Builder#setImportantWhileForeground(boolean)
*/
public boolean isImportantWhileForeground() {
@@ -1503,20 +1494,6 @@
}
/**
- * @deprecated Use {@link #setExpedited(boolean)} instead.
- */
- @Deprecated
- @NonNull
- public Builder setForeground(boolean foreground) {
- if (foreground) {
- mFlags |= FLAG_EXPEDITED;
- } else {
- mFlags &= (~FLAG_EXPEDITED);
- }
- return this;
- }
-
- /**
* Setting this to true indicates that this job is important while the scheduling app
* is in the foreground or on the temporary whitelist for background restrictions.
* This means that the system will relax doze restrictions on this job during this time.
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index ab87222..283e933 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -74,6 +74,27 @@
}
/**
+ * Allow the temp allowlist behavior, plus allow foreground service start from background.
+ */
+ public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+ /**
+ * Only allow the temp allowlist behavior, not allow foreground service start from
+ * background.
+ */
+ public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+
+ /**
+ * The list of temp allowlist types.
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "TEMPORARY_ALLOW_TYPE_" }, value = {
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TempAllowListType {}
+
+ /**
* @hide
*/
public PowerWhitelistManager(@NonNull Context context) {
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 4441643..e045b0f 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -16,7 +16,7 @@
package com.android.server;
-import android.app.BroadcastOptions;
+import android.os.PowerWhitelistManager.TempAllowListType;
import com.android.server.deviceidle.IDeviceIdleConstraint;
@@ -39,12 +39,12 @@
* allowlist.
* @param uid
* @param duration duration in milliseconds
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param type temp allowlist type defined at {@link TempAllowListType}
* @param sync
* @param reason
*/
void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
- @BroadcastOptions.TempAllowListType int type, boolean sync,
+ @TempAllowListType int type, boolean sync,
String reason);
// duration in milliseconds
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index c9427e9..8f7f705 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -22,7 +22,6 @@
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.BroadcastOptions;
-import android.app.BroadcastOptions.TempAllowListType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -57,6 +56,7 @@
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
+import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -3879,7 +3879,7 @@
* @param adding true to add to temp allowlist, false to remove from temp allowlist.
* @param durationMs duration in milliseconds to add to temp allowlist, only valid when
* param adding is true.
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param type temp allowlist type defined at {@link TempAllowListType}
*/
private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs,
@TempAllowListType int type) {
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 6ab1051..18856f7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -62,6 +62,7 @@
CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000;
+ // Try to give higher priority types lower values.
static final int WORK_TYPE_NONE = 0;
static final int WORK_TYPE_TOP = 1 << 0;
static final int WORK_TYPE_BG = 1 << 1;
@@ -520,6 +521,120 @@
}
}
+ void onJobCompletedLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
+ @WorkType final int workType) {
+ mWorkCountTracker.onJobFinished(workType);
+ mRunningJobs.remove(jobStatus);
+ final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
+ updateCounterConfigLocked();
+ // Preemption case needs special care.
+ updateNonRunningPriorities(pendingJobs, false);
+
+ JobStatus highestPriorityJob = null;
+ int highPriWorkType = workType;
+ JobStatus backupJob = null;
+ int backupWorkType = WORK_TYPE_NONE;
+ for (int i = 0; i < pendingJobs.size(); i++) {
+ final JobStatus nextPending = pendingJobs.get(i);
+
+ if (mRunningJobs.contains(nextPending)) {
+ continue;
+ }
+
+ if (worker.getPreferredUid() != nextPending.getUid()) {
+ if (backupJob == null) {
+ int workAsType =
+ mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ if (workAsType != WORK_TYPE_NONE) {
+ backupJob = nextPending;
+ backupWorkType = workAsType;
+ }
+ }
+ continue;
+ }
+
+ if (highestPriorityJob == null
+ || highestPriorityJob.lastEvaluatedPriority
+ < nextPending.lastEvaluatedPriority) {
+ highestPriorityJob = nextPending;
+ } else {
+ continue;
+ }
+
+ // In this path, we pre-empted an existing job. We don't fully care about the
+ // reserved slots. We should just run the highest priority job we can find,
+ // though it would be ideal to use an available WorkType slot instead of
+ // overloading slots.
+ final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ if (workAsType == WORK_TYPE_NONE) {
+ // Just use the preempted job's work type since this new one is technically
+ // replacing it anyway.
+ highPriWorkType = workType;
+ } else {
+ highPriWorkType = workAsType;
+ }
+ }
+ if (highestPriorityJob != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Running job " + jobStatus + " as preemption");
+ }
+ mWorkCountTracker.stageJob(highPriWorkType);
+ startJobLocked(worker, highestPriorityJob, highPriWorkType);
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Couldn't find preemption job for uid " + worker.getPreferredUid());
+ }
+ worker.clearPreferredUid();
+ if (backupJob != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Running job " + jobStatus + " instead");
+ }
+ mWorkCountTracker.stageJob(backupWorkType);
+ startJobLocked(worker, backupJob, backupWorkType);
+ }
+ }
+ } else if (pendingJobs.size() > 0) {
+ updateCounterConfigLocked();
+ updateNonRunningPriorities(pendingJobs, false);
+
+ // This slot is now free and we have pending jobs. Start the highest priority job we
+ // find.
+ JobStatus highestPriorityJob = null;
+ int highPriWorkType = workType;
+ for (int i = 0; i < pendingJobs.size(); i++) {
+ final JobStatus nextPending = pendingJobs.get(i);
+
+ if (mRunningJobs.contains(nextPending)) {
+ continue;
+ }
+
+ final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ if (workAsType == WORK_TYPE_NONE) {
+ continue;
+ }
+ if (highestPriorityJob == null
+ || highestPriorityJob.lastEvaluatedPriority
+ < nextPending.lastEvaluatedPriority) {
+ highestPriorityJob = nextPending;
+ highPriWorkType = workAsType;
+ }
+ }
+
+ if (highestPriorityJob != null) {
+ // This slot is free, and we haven't yet hit the limit on
+ // concurrent jobs... we can just throw the job in to here.
+ if (DEBUG) {
+ Slog.d(TAG, "About to run job: " + jobStatus);
+ }
+ mWorkCountTracker.stageJob(highPriWorkType);
+ startJobLocked(worker, highestPriorityJob, highPriWorkType);
+ }
+ }
+
+ noteConcurrency();
+ }
+
@GuardedBy("mLock")
private String printPendingQueueLocked() {
StringBuilder s = new StringBuilder("Pending queue: ");
@@ -855,7 +970,25 @@
if (numRemainingForType < mNumActuallyReservedSlots.get(workType)) {
// We've run all jobs for this type. Let another type use it now.
mNumActuallyReservedSlots.put(workType, numRemainingForType);
- mNumUnspecializedRemaining++;
+ int assignWorkType = WORK_TYPE_NONE;
+ for (int i = 0; i < mNumActuallyReservedSlots.size(); ++i) {
+ int wt = mNumActuallyReservedSlots.keyAt(i);
+ if (assignWorkType == WORK_TYPE_NONE || wt < assignWorkType) {
+ // Try to give this slot to the highest priority one within its limits.
+ int total = mNumRunningJobs.get(wt) + mNumStartingJobs.get(wt)
+ + mNumPendingJobs.get(wt);
+ if (mNumActuallyReservedSlots.valueAt(i) < mConfigAbsoluteMaxSlots.get(wt)
+ && total > mNumActuallyReservedSlots.valueAt(i)) {
+ assignWorkType = wt;
+ }
+ }
+ }
+ if (assignWorkType != WORK_TYPE_NONE) {
+ mNumActuallyReservedSlots.put(assignWorkType,
+ mNumActuallyReservedSlots.get(assignWorkType) + 1);
+ } else {
+ mNumUnspecializedRemaining++;
+ }
}
}
@@ -871,6 +1004,18 @@
}
}
+ void onJobFinished(@WorkType int workType) {
+ final int newNumRunningJobs = mNumRunningJobs.get(workType) - 1;
+ if (newNumRunningJobs < 0) {
+ // We are in a bad state. We will eventually recover when the pending list is
+ // regenerated.
+ Slog.e(TAG, "# running jobs for " + workType + " went negative.");
+ return;
+ }
+ mNumRunningJobs.put(workType, newNumRunningJobs);
+ maybeAdjustReservations(workType);
+ }
+
void onCountDone() {
// Calculate how many slots to reserve for each work type. "Unspecialized" slots will
// be reserved for higher importance types first (ie. top before bg).
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 ba78bda..7ce867c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1426,8 +1426,8 @@
// Create the "runners".
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
mActiveServices.add(
- new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
- getContext().getMainLooper()));
+ new JobServiceContext(this, mConcurrencyManager, mBatteryStats,
+ mJobPackageTracker, getContext().getMainLooper()));
}
// Attach jobs to their controllers.
mJobs.forEachJob((job) -> {
@@ -1710,9 +1710,6 @@
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
- // We still want to check for jobs to execute, because this job may have
- // scheduled a new job under the same job id, and now we can run it.
- mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
return;
}
@@ -1734,7 +1731,6 @@
}
jobStatus.unprepareLocked();
reportActiveLocked();
- mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
}
// StateChangedListener implementations.
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 247b421..d15bae0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -107,6 +107,7 @@
private final Handler mCallbackHandler;
/** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
private final JobCompletedListener mCompletedListener;
+ private final JobConcurrencyManager mJobConcurrencyManager;
/** Used for service binding, etc. */
private final Context mContext;
private final Object mLock;
@@ -183,13 +184,14 @@
}
}
- JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
- JobPackageTracker tracker, Looper looper) {
+ JobServiceContext(JobSchedulerService service, JobConcurrencyManager concurrencyManager,
+ IBatteryStats batteryStats, JobPackageTracker tracker, Looper looper) {
mContext = service.getContext();
mLock = service.getLock();
mBatteryStats = batteryStats;
mJobPackageTracker = tracker;
mCallbackHandler = new JobServiceHandler(looper);
+ mJobConcurrencyManager = concurrencyManager;
mCompletedListener = service;
mAvailable = true;
mVerb = VERB_FINISHED;
@@ -835,6 +837,7 @@
if (mWakeLock != null) {
mWakeLock.release();
}
+ final int workType = mRunningJobWorkType;
mContext.unbindService(JobServiceContext.this);
mWakeLock = null;
mRunningJob = null;
@@ -847,6 +850,7 @@
mAvailable = true;
removeOpTimeOutLocked();
mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
+ mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType);
}
private void applyStoppedReasonLocked(String reason) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index e8a2817..d249f2a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -16,7 +16,6 @@
package com.android.server.job.controllers;
-import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -332,7 +331,7 @@
if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps();
// If we don't know the bandwidth, all we can do is hope the job finishes in time.
- if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+ if (bandwidth > 0) {
// Divide by 8 to convert bits to bytes.
final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS)
/ (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -350,7 +349,7 @@
if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
final long bandwidth = capabilities.getLinkUpstreamBandwidthKbps();
// If we don't know the bandwidth, all we can do is hope the job finishes in time.
- if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+ if (bandwidth > 0) {
// Divide by 8 to convert bits to bytes.
final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS)
/ (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -380,18 +379,16 @@
private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
- final NetworkCapabilities required;
// A restricted job that's out of quota MUST use an unmetered network.
if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
&& !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
- required = new NetworkCapabilities(
+ final NetworkCapabilities required = new NetworkCapabilities.Builder(
jobStatus.getJob().getRequiredNetwork().networkCapabilities)
- .addCapability(NET_CAPABILITY_NOT_METERED);
+ .addCapability(NET_CAPABILITY_NOT_METERED).build();
+ return required.satisfiedByNetworkCapabilities(capabilities);
} else {
- required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
+ return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities);
}
-
- return required.satisfiedByNetworkCapabilities(capabilities);
}
private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
@@ -402,9 +399,9 @@
}
// See if we match after relaxing any unmetered request
- final NetworkCapabilities relaxed = new NetworkCapabilities(
+ final NetworkCapabilities relaxed = new NetworkCapabilities.Builder(
jobStatus.getJob().getRequiredNetwork().networkCapabilities)
- .removeCapability(NET_CAPABILITY_NOT_METERED);
+ .removeCapability(NET_CAPABILITY_NOT_METERED).build();
if (relaxed.satisfiedByNetworkCapabilities(capabilities)) {
// TODO: treat this as "maybe" response; need to check quotas
return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index e1a8596..aefeab6 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -36,30 +36,46 @@
import java.util.Set;
/**
- * ApplicationMediaCapabilities is an immutable class that encapsulates an application's
- * capabilities for handling newer video codec format and media features.
- *
- * The ApplicationMediaCapabilities class is used by the platform to represent an application's
- * media capabilities as defined in their manifest(TODO: Add link) in order to determine
- * whether modern media files need to be transcoded for that application (TODO: Add link).
- *
- * ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
- * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
- * control over the transcoding that is built into the platform. ApplicationMediaCapabilities
- * provided by applications at runtime like this override the default manifest capabilities for that
- * media access.
- *
- * <h3> Video Codec Support</h3>
- * Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
- * for newer format with this class as they are assumed to support older format like h.264.
- *
- * <h4>Capability of handling HDR(high dynamic range) video</h4>
- * There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
- * application will only need to specify individual types they supported.
+ ApplicationMediaCapabilities is an immutable class that encapsulates an application's capabilities
+ for handling newer video codec format and media features.
+
+ <p>
+ Android 12 introduces seamless media transcoding feature. By default, Android assumes apps can
+ support playback of all media formats. Apps that would like to request that media be transcoded
+ into a more compatible format should declare their media capabilities in a media_capabilities
+ .xml resource file and add it as a property tag in the AndroidManifest.xml file. Here is a example:
+ <pre>
+ {@code
+ <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="true"/>
+ <format android:name="HDR10" supported="false"/>
+ <format android:name="HDR10Plus" supported="false"/>
+ </media-capabilities>
+ }
+ </pre>
+ The ApplicationMediaCapabilities class is generated from this xml and used by the platform to
+ represent an application's media capabilities in order to determine whether modern media files need
+ to be transcoded for that application.
+ </p>
+
+ <p>
+ ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
+ {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
+ control over the transcoding that is built into the platform. ApplicationMediaCapabilities
+ provided by applications at runtime like this override the default manifest capabilities for that
+ media access.The object could be build either through {@link #createFromXml(XmlPullParser)} or
+ through the builder class {@link ApplicationMediaCapabilities.Builder}
+
+ <h3> Video Codec Support</h3>
+ <p>
+ Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
+ for newer format with this class as they are assumed to support older format like h.264.
+
+ <h3>Capability of handling HDR(high dynamic range) video</h3>
+ <p>
+ There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
+ application will only need to specify individual types they supported.
*/
-// TODO(huang): Correct openTypedAssetFileDescriptor with the new API after it is added.
-// TODO(hkuang): Add a link to seamless transcoding detail when it is published
-// TODO(hkuang): Add code sample on how to build a capability object with MediaCodecList
public final class ApplicationMediaCapabilities implements Parcelable {
private static final String TAG = "ApplicationMediaCapabilities";
@@ -105,9 +121,9 @@
*/
public boolean isVideoMimeTypeSupported(
@NonNull String videoMime) throws FormatNotFoundException {
- if (mUnsupportedVideoMimeTypes.contains(videoMime)) {
+ if (mUnsupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
return false;
- } else if (mSupportedVideoMimeTypes.contains(videoMime)) {
+ } else if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
return true;
} else {
throw new FormatNotFoundException(videoMime);
@@ -262,11 +278,27 @@
/**
* Creates {@link ApplicationMediaCapabilities} from an xml.
+ *
+ * The xml's syntax is the same as the media_capabilities.xml used by the AndroidManifest.xml.
+ * <p> Here is an example:
+ *
+ * <pre>
+ * {@code
+ * <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ * <format android:name="HEVC" supported="true"/>
+ * <format android:name="HDR10" supported="false"/>
+ * <format android:name="HDR10Plus" supported="false"/>
+ * </media-capabilities>
+ * }
+ * </pre>
+ * <p>
+ *
* @param xmlParser The underlying {@link XmlPullParser} that will read the xml.
* @return An ApplicationMediaCapabilities object.
* @throws UnsupportedOperationException if the capabilities in xml config are invalid or
* incompatible.
*/
+ // TODO: Add developer.android.com link for the format of the xml.
@NonNull
public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) {
ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder();
@@ -430,7 +462,7 @@
mIsSlowMotionSupported = isSupported;
break;
default:
- throw new UnsupportedOperationException("Invalid format name " + name);
+ Log.w(TAG, "Invalid format name " + name);
}
// Save the name and isSupported into the map for validate later.
mFormatSupportedMap.put(name, isSupported);
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 5f13a5c..f85e30d 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -70,6 +70,7 @@
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression";
private static final String COMMAND_CLEANUP_STUCK_CALLS = "cleanup-stuck-calls";
+ private static final String COMMAND_RESET_CAR_MODE = "reset-car-mode";
/**
* Change the system dialer package name if a package name was specified,
@@ -220,6 +221,9 @@
case COMMAND_CLEANUP_STUCK_CALLS:
runCleanupStuckCalls();
break;
+ case COMMAND_RESET_CAR_MODE:
+ runResetCarMode();
+ break;
case COMMAND_SET_DEFAULT_DIALER:
runSetDefaultDialer();
break;
@@ -345,6 +349,10 @@
mTelecomService.cleanupStuckCalls();
}
+ private void runResetCarMode() throws RemoteException {
+ mTelecomService.resetCarMode();
+ }
+
private void runSetDefaultDialer() throws RemoteException {
String packageName = nextArg();
if ("default".equals(packageName)) packageName = null;
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index 43a8a87..da4b255 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -4,7 +4,6 @@
android.os.NullVibrator
android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
android.widget.Magnifier
-com.android.server.BootReceiver$2
gov.nist.core.net.DefaultNetworkLayer
android.net.rtp.AudioGroup
android.net.rtp.AudioStream
diff --git a/core/api/current.txt b/core/api/current.txt
index 9414453..f5c67c1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -657,7 +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 = 16844321; // 0x1010621
+ 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
@@ -722,7 +722,7 @@
field public static final int gwpAsanMode = 16844310; // 0x1010616
field public static final int hand_hour = 16843011; // 0x1010103
field public static final int hand_minute = 16843012; // 0x1010104
- field public static final int hand_second = 16844322; // 0x1010622
+ field public static final int hand_second = 16844323; // 0x1010623
field public static final int handle = 16843354; // 0x101025a
field public static final int handleProfiling = 16842786; // 0x1010022
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
@@ -964,6 +964,7 @@
field public static final int measureWithLargestChild = 16843476; // 0x10102d4
field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad
field public static final int mediaRouteTypes = 16843694; // 0x10103ae
+ field public static final int memtagMode = 16844324; // 0x1010624
field public static final int menuCategory = 16843230; // 0x10101de
field public static final int mimeGroup = 16844309; // 0x1010615
field public static final int mimeType = 16842790; // 0x1010026
@@ -987,6 +988,7 @@
field public static final int multiArch = 16843918; // 0x101048e
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
+ field public static final int nativeHeapZeroInit = 16844325; // 0x1010625
field public static final int navigationBarColor = 16843858; // 0x1010452
field public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
@@ -1057,11 +1059,11 @@
field public static final int parentActivityName = 16843687; // 0x10103a7
field @Deprecated public static final int password = 16843100; // 0x101015c
field public static final int path = 16842794; // 0x101002a
- field public static final int pathAdvancedPattern = 16844319; // 0x101061f
+ field public static final int pathAdvancedPattern = 16844320; // 0x1010620
field public static final int pathData = 16843781; // 0x1010405
field public static final int pathPattern = 16842796; // 0x101002c
field public static final int pathPrefix = 16842795; // 0x101002b
- field public static final int pathSuffix = 16844317; // 0x101061d
+ field public static final int pathSuffix = 16844318; // 0x101061e
field public static final int patternPathData = 16843978; // 0x10104ca
field public static final int permission = 16842758; // 0x1010006
field public static final int permissionFlags = 16843719; // 0x10103c7
@@ -1154,7 +1156,7 @@
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
- field public static final int requireDeviceScreenOn = 16844316; // 0x101061c
+ field public static final int requireDeviceScreenOn = 16844317; // 0x101061d
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
@@ -1295,10 +1297,10 @@
field public static final int spotShadowAlpha = 16843967; // 0x10104bf
field public static final int src = 16843033; // 0x1010119
field public static final int ssp = 16843747; // 0x10103e3
- field public static final int sspAdvancedPattern = 16844320; // 0x1010620
+ field public static final int sspAdvancedPattern = 16844321; // 0x1010621
field public static final int sspPattern = 16843749; // 0x10103e5
field public static final int sspPrefix = 16843748; // 0x10103e4
- field public static final int sspSuffix = 16844318; // 0x101061e
+ field public static final int sspSuffix = 16844319; // 0x101061f
field public static final int stackFromBottom = 16843005; // 0x10100fd
field public static final int stackViewStyle = 16843838; // 0x101043e
field public static final int starStyle = 16842882; // 0x1010082
@@ -1609,6 +1611,8 @@
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
field public static final int windowBackgroundFallback = 16844035; // 0x1010503
+ field public static final int windowBlurBehindEnabled = 16844316; // 0x101061c
+ field public static final int windowBlurBehindRadius = 16844315; // 0x101061b
field public static final int windowClipToOutline = 16843947; // 0x10104ab
field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
field public static final int windowContentOverlay = 16842841; // 0x1010059
@@ -6926,6 +6930,7 @@
method public void addPersistentPreferredActivity(@NonNull android.content.ComponentName, android.content.IntentFilter, @NonNull android.content.ComponentName);
method public void addUserRestriction(@NonNull android.content.ComponentName, String);
method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
+ method public boolean canAdminGrantSensorsPermissions();
method public void clearApplicationUserData(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener);
method public void clearCrossProfileIntentFilters(@NonNull android.content.ComponentName);
method @Deprecated public void clearDeviceOwnerApp(String);
@@ -7223,6 +7228,7 @@
field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
field public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE";
+ field public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER";
field public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS = "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS";
field public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
@@ -7312,6 +7318,7 @@
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+ field public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1; // 0x1
field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -7465,6 +7472,7 @@
public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
method public int describeContents();
+ method public int getReason();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnsafeStateException> CREATOR;
}
@@ -7743,7 +7751,6 @@
method public long getTriggerContentUpdateDelay();
method @Nullable public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
method public boolean isExpedited();
- method @Deprecated public boolean isForegroundJob();
method public boolean isImportantWhileForeground();
method public boolean isPeriodic();
method public boolean isPersisted();
@@ -7776,7 +7783,6 @@
method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
method @NonNull public android.app.job.JobInfo.Builder setExpedited(boolean);
method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
- method @Deprecated @NonNull public android.app.job.JobInfo.Builder setForeground(boolean);
method @Deprecated public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
@@ -9280,7 +9286,7 @@
method public boolean getIncludeTxPowerLevel();
method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData();
- method @Nullable public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
+ method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
method public java.util.List<android.os.ParcelUuid> getServiceUuids();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR;
@@ -11639,6 +11645,8 @@
method public void dump(android.util.Printer, String);
method public static CharSequence getCategoryTitle(android.content.Context, int);
method public int getGwpAsanMode();
+ method public int getMemtagMode();
+ method @Nullable public Boolean isNativeHeapZeroInit();
method public boolean isProfileableByShell();
method public boolean isResourceOverlay();
method public boolean isVirtualPreload();
@@ -11688,6 +11696,10 @@
field public static final int GWP_ASAN_ALWAYS = 1; // 0x1
field public static final int GWP_ASAN_DEFAULT = -1; // 0xffffffff
field public static final int GWP_ASAN_NEVER = 0; // 0x0
+ field public static final int MEMTAG_ASYNC = 1; // 0x1
+ field public static final int MEMTAG_DEFAULT = -1; // 0xffffffff
+ field public static final int MEMTAG_OFF = 0; // 0x0
+ field public static final int MEMTAG_SYNC = 2; // 0x2
field public String appComponentFactory;
field public String backupAgentName;
field public int category;
@@ -12288,7 +12300,7 @@
method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
method public abstract void removePermission(@NonNull String);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
- method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
+ method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12308,7 +12320,6 @@
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field public static final int DONT_KILL_APP = 1; // 0x1
- field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12494,6 +12505,10 @@
ctor public PackageManager.NameNotFoundException(String);
}
+ @java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener {
+ method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
+ }
+
public static final class PackageManager.Property implements android.os.Parcelable {
method public int describeContents();
method public boolean getBoolean();
@@ -15015,6 +15030,7 @@
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
field public static final int Y8 = 538982489; // 0x20203859
+ field public static final int YCBCR_P010 = 54; // 0x36
field public static final int YUV_420_888 = 35; // 0x23
field public static final int YUV_422_888 = 39; // 0x27
field public static final int YUV_444_888 = 40; // 0x28
@@ -19453,7 +19469,7 @@
public interface LocationListener {
method public default void onFlushComplete(int);
method public void onLocationChanged(@NonNull android.location.Location);
- method public default void onLocationChanged(@NonNull android.location.LocationResult);
+ method public default void onLocationChanged(@NonNull java.util.List<android.location.Location>);
method public default void onProviderDisabled(@NonNull String);
method public default void onProviderEnabled(@NonNull String);
method @Deprecated public default void onStatusChanged(String, int, android.os.Bundle);
@@ -19539,8 +19555,8 @@
field public static final String FUSED_PROVIDER = "fused";
field public static final String GPS_PROVIDER = "gps";
field public static final String KEY_FLUSH_COMPLETE = "flushComplete";
+ field public static final String KEY_LOCATIONS = "locations";
field public static final String KEY_LOCATION_CHANGED = "location";
- field public static final String KEY_LOCATION_RESULT = "locationResult";
field public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
field public static final String KEY_PROXIMITY_ENTERING = "entering";
field @Deprecated public static final String KEY_STATUS_CHANGED = "status";
@@ -19598,18 +19614,6 @@
method @NonNull public android.location.LocationRequest.Builder setQuality(int);
}
- public final class LocationResult implements android.os.Parcelable {
- method @NonNull public java.util.List<android.location.Location> asList();
- method @NonNull public static android.location.LocationResult create(@NonNull android.location.Location);
- method @NonNull public static android.location.LocationResult create(@NonNull java.util.List<android.location.Location>);
- method public int describeContents();
- method @NonNull public android.location.Location get(@IntRange(from=0) int);
- method @NonNull public android.location.Location getLastLocation();
- method @IntRange(from=1) public int size();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationResult> CREATOR;
- }
-
public interface OnNmeaMessageListener {
method public void onNmeaMessage(String, long);
}
@@ -19869,6 +19873,10 @@
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
+ field public static final int ENCODING_MPEGH_BL_L3 = 23; // 0x17
+ field public static final int ENCODING_MPEGH_BL_L4 = 24; // 0x18
+ field public static final int ENCODING_MPEGH_LC_L3 = 25; // 0x19
+ field public static final int ENCODING_MPEGH_LC_L4 = 26; // 0x1a
field public static final int ENCODING_OPUS = 20; // 0x14
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_24BIT_PACKED = 21; // 0x15
@@ -20179,7 +20187,7 @@
}
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
- ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+ ctor @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
method @Deprecated public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
method protected void finalize();
@@ -20240,7 +20248,7 @@
public static class AudioRecord.Builder {
ctor public AudioRecord.Builder();
- method public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException;
+ method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException;
method public android.media.AudioRecord.Builder setAudioFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException;
method @NonNull public android.media.AudioRecord.Builder setAudioPlaybackCaptureConfig(@NonNull android.media.AudioPlaybackCaptureConfiguration);
method public android.media.AudioRecord.Builder setAudioSource(int) throws java.lang.IllegalArgumentException;
@@ -31442,6 +31450,7 @@
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(String);
method public boolean isDemoUser();
+ method public static boolean isHeadlessSystemUserMode();
method public boolean isManagedProfile();
method public boolean isQuietModeEnabled(android.os.UserHandle);
method public boolean isSystemUser();
@@ -34973,6 +34982,55 @@
field public static final String PATH_SETTING_INTENT = "intent";
}
+ public final class SimPhonebookContract {
+ field public static final String AUTHORITY = "com.android.simphonebook";
+ field @NonNull public static final android.net.Uri AUTHORITY_URI;
+ }
+
+ public static final class SimPhonebookContract.ElementaryFiles {
+ method @NonNull public static android.net.Uri getItemUri(int, int);
+ field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-elementary-file";
+ field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+ field @NonNull public static final android.net.Uri CONTENT_URI;
+ field public static final int EF_ADN = 1; // 0x1
+ field public static final int EF_FDN = 2; // 0x2
+ field public static final int EF_SDN = 3; // 0x3
+ field public static final String EF_TYPE = "ef_type";
+ field public static final int EF_UNKNOWN = 0; // 0x0
+ field public static final String MAX_RECORDS = "max_records";
+ field public static final String NAME_MAX_LENGTH = "name_max_length";
+ field public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+ field public static final String RECORD_COUNT = "record_count";
+ field public static final String SLOT_INDEX = "slot_index";
+ field public static final String SUBSCRIPTION_ID = "subscription_id";
+ }
+
+ public static final class SimPhonebookContract.SimRecords {
+ method @NonNull public static android.net.Uri getContentUri(int, int);
+ method @NonNull public static android.net.Uri getItemUri(int, int, int);
+ method @NonNull @WorkerThread public static android.provider.SimPhonebookContract.SimRecords.NameValidationResult validateName(@NonNull android.content.ContentResolver, int, int, @NonNull String);
+ field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+ field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+ field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+ field public static final String NAME = "name";
+ field public static final String PHONE_NUMBER = "phone_number";
+ field public static final String RECORD_NUMBER = "record_number";
+ field public static final String SUBSCRIPTION_ID = "subscription_id";
+ }
+
+ public static final class SimPhonebookContract.SimRecords.NameValidationResult implements android.os.Parcelable {
+ ctor public SimPhonebookContract.SimRecords.NameValidationResult(@NonNull String, @NonNull String, int, int);
+ method public int describeContents();
+ method public int getEncodedLength();
+ method public int getMaxEncodedLength();
+ method @NonNull public String getName();
+ method @NonNull public String getSanitizedName();
+ method public boolean isSupportedCharacter(int);
+ method public boolean isValid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.provider.SimPhonebookContract.SimRecords.NameValidationResult> CREATOR;
+ }
+
public class SyncStateContract {
ctor public SyncStateContract();
}
@@ -40339,6 +40397,7 @@
field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
+ field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
field public static final String KEY_PREFIX = "ims.";
field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
@@ -45738,6 +45797,8 @@
method public void clear();
method public android.util.SparseArray<E> clone();
method public boolean contains(int);
+ method public boolean contentEquals(@Nullable android.util.SparseArray<E>);
+ method public int contentHashCode();
method public void delete(int);
method public E get(int);
method public E get(int, E);
@@ -46148,6 +46209,7 @@
method @Deprecated public void getRectSize(android.graphics.Rect);
method public float getRefreshRate();
method public int getRotation();
+ method @Nullable public android.view.RoundedCorner getRoundedCorner(int);
method @Deprecated public void getSize(android.graphics.Point);
method public int getState();
method public android.view.Display.Mode[] getSupportedModes();
@@ -47402,6 +47464,20 @@
field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
}
+ public final class RoundedCorner implements android.os.Parcelable {
+ ctor public RoundedCorner(int, int, int, int);
+ method public int describeContents();
+ method @NonNull public android.graphics.Point getCenter();
+ method public int getPosition();
+ method public int getRadius();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.RoundedCorner> CREATOR;
+ field public static final int POSITION_BOTTOM_LEFT = 3; // 0x3
+ field public static final int POSITION_BOTTOM_RIGHT = 2; // 0x2
+ field public static final int POSITION_TOP_LEFT = 0; // 0x0
+ field public static final int POSITION_TOP_RIGHT = 1; // 0x1
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -49411,6 +49487,7 @@
method @NonNull public android.graphics.Insets getInsets(int);
method @NonNull public android.graphics.Insets getInsetsIgnoringVisibility(int);
method @Deprecated @NonNull public android.graphics.Insets getMandatorySystemGestureInsets();
+ method @Nullable public android.view.RoundedCorner getRoundedCorner(int);
method @Deprecated public int getStableInsetBottom();
method @Deprecated public int getStableInsetLeft();
method @Deprecated public int getStableInsetRight();
@@ -49444,6 +49521,7 @@
method @NonNull public android.view.WindowInsets.Builder setInsets(int, @NonNull android.graphics.Insets);
method @NonNull public android.view.WindowInsets.Builder setInsetsIgnoringVisibility(int, @NonNull android.graphics.Insets) throws java.lang.IllegalArgumentException;
method @Deprecated @NonNull public android.view.WindowInsets.Builder setMandatorySystemGestureInsets(@NonNull android.graphics.Insets);
+ method @NonNull public android.view.WindowInsets.Builder setRoundedCorner(int, @Nullable android.view.RoundedCorner);
method @Deprecated @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets);
method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemGestureInsets(@NonNull android.graphics.Insets);
method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemWindowInsets(@NonNull android.graphics.Insets);
@@ -49596,7 +49674,7 @@
field public static final int FLAGS_CHANGED = 4; // 0x4
field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1
field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000
- field @Deprecated public static final int FLAG_BLUR_BEHIND = 4; // 0x4
+ field public static final int FLAG_BLUR_BEHIND = 4; // 0x4
field public static final int FLAG_DIM_BEHIND = 2; // 0x2
field @Deprecated public static final int FLAG_DISMISS_KEYGUARD = 4194304; // 0x400000
field @Deprecated public static final int FLAG_DITHER = 4096; // 0x1000
@@ -49687,6 +49765,7 @@
field @Deprecated public static final int TYPE_TOAST = 2005; // 0x7d5
field public static final int TYPE_WALLPAPER = 2013; // 0x7dd
field public float alpha;
+ field public int blurBehindRadius;
field public float buttonBrightness;
field public float dimAmount;
field public int flags;
@@ -50996,7 +51075,7 @@
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
method public boolean setComposingText(CharSequence, int);
- method public default boolean setImeTemporarilyConsumesInput(boolean);
+ method public default boolean setImeConsumesInput(boolean);
method public boolean setSelection(int, int);
field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1
field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
@@ -53089,10 +53168,12 @@
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet);
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int);
ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int, int);
+ method @Deprecated @Nullable public String getTimeZone();
method @Deprecated public void setDial(@NonNull android.graphics.drawable.Icon);
method @Deprecated public void setHourHand(@NonNull android.graphics.drawable.Icon);
method @Deprecated public void setMinuteHand(@NonNull android.graphics.drawable.Icon);
method @Deprecated public void setSecondHand(@Nullable android.graphics.drawable.Icon);
+ method @Deprecated public void setTimeZone(@Nullable String);
}
public class ArrayAdapter<T> extends android.widget.BaseAdapter implements android.widget.Filterable android.widget.ThemedSpinnerAdapter {
@@ -53270,7 +53351,7 @@
method public void onSelectedDayChange(@NonNull android.widget.CalendarView, int, int, int);
}
- public class CheckBox extends android.widget.CompoundButton {
+ @android.widget.RemoteViews.RemoteView public class CheckBox extends android.widget.CompoundButton {
ctor public CheckBox(android.content.Context);
ctor public CheckBox(android.content.Context, android.util.AttributeSet);
ctor public CheckBox(android.content.Context, android.util.AttributeSet, int);
@@ -53336,6 +53417,7 @@
method public boolean isChecked();
method public void setButtonDrawable(@DrawableRes int);
method public void setButtonDrawable(@Nullable android.graphics.drawable.Drawable);
+ method public void setButtonIcon(@Nullable android.graphics.drawable.Icon);
method public void setButtonTintBlendMode(@Nullable android.graphics.BlendMode);
method public void setButtonTintList(@Nullable android.content.res.ColorStateList);
method public void setButtonTintMode(@Nullable android.graphics.PorterDuff.Mode);
@@ -54376,14 +54458,14 @@
field protected String[] mExcludeMimes;
}
- public class RadioButton extends android.widget.CompoundButton {
+ @android.widget.RemoteViews.RemoteView public class RadioButton extends android.widget.CompoundButton {
ctor public RadioButton(android.content.Context);
ctor public RadioButton(android.content.Context, android.util.AttributeSet);
ctor public RadioButton(android.content.Context, android.util.AttributeSet, int);
ctor public RadioButton(android.content.Context, android.util.AttributeSet, int, int);
}
- public class RadioGroup extends android.widget.LinearLayout {
+ @android.widget.RemoteViews.RemoteView public class RadioGroup extends android.widget.LinearLayout {
ctor public RadioGroup(android.content.Context);
ctor public RadioGroup(android.content.Context, android.util.AttributeSet);
method public void check(@IdRes int);
@@ -54500,19 +54582,27 @@
method public void setByte(@IdRes int, String, byte);
method public void setChar(@IdRes int, String, char);
method public void setCharSequence(@IdRes int, String, CharSequence);
+ method public void setCharSequence(@IdRes int, @NonNull String, @StringRes int);
method public void setChronometer(@IdRes int, long, String, boolean);
method public void setChronometerCountDown(@IdRes int, boolean);
+ method public void setColor(@IdRes int, @NonNull String, @ColorRes int);
+ method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int);
+ method public void setCompoundButtonChecked(@IdRes int, boolean);
method public void setContentDescription(@IdRes int, CharSequence);
method public void setDisplayedChild(@IdRes int, int);
method public void setDouble(@IdRes int, String, double);
method public void setEmptyView(@IdRes int, @IdRes int);
method public void setFloat(@IdRes int, String, float);
+ method public void setFloatDimen(@IdRes int, @NonNull String, @DimenRes int);
+ method public void setFloatDimen(@IdRes int, @NonNull String, float, int);
method public void setIcon(@IdRes int, String, android.graphics.drawable.Icon);
method public void setImageViewBitmap(@IdRes int, android.graphics.Bitmap);
method public void setImageViewIcon(@IdRes int, android.graphics.drawable.Icon);
method public void setImageViewResource(@IdRes int, @DrawableRes int);
method public void setImageViewUri(@IdRes int, android.net.Uri);
method public void setInt(@IdRes int, String, int);
+ method public void setIntDimen(@IdRes int, @NonNull String, @DimenRes int);
+ method public void setIntDimen(@IdRes int, @NonNull String, float, int);
method public void setIntent(@IdRes int, String, android.content.Intent);
method public void setLabelFor(@IdRes int, @IdRes int);
method public void setLightBackgroundLayoutId(@LayoutRes int);
@@ -54522,6 +54612,7 @@
method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse);
method public void setPendingIntentTemplate(@IdRes int, android.app.PendingIntent);
method public void setProgressBar(@IdRes int, int, int, boolean);
+ method public void setRadioGroupChecked(@IdRes int, @IdRes int);
method public void setRelativeScrollPosition(@IdRes int, int);
method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent);
method public void setRemoteAdapter(@IdRes int, android.content.Intent);
@@ -54888,7 +54979,7 @@
ctor public StackView(android.content.Context, android.util.AttributeSet, int, int);
}
- public class Switch extends android.widget.CompoundButton {
+ @android.widget.RemoteViews.RemoteView public class Switch extends android.widget.CompoundButton {
ctor public Switch(android.content.Context);
ctor public Switch(android.content.Context, android.util.AttributeSet);
ctor public Switch(android.content.Context, android.util.AttributeSet, int);
@@ -54919,12 +55010,14 @@
method public void setTextOff(CharSequence);
method public void setTextOn(CharSequence);
method public void setThumbDrawable(android.graphics.drawable.Drawable);
+ method public void setThumbIcon(@Nullable android.graphics.drawable.Icon);
method public void setThumbResource(@DrawableRes int);
method public void setThumbTextPadding(int);
method public void setThumbTintBlendMode(@Nullable android.graphics.BlendMode);
method public void setThumbTintList(@Nullable android.content.res.ColorStateList);
method public void setThumbTintMode(@Nullable android.graphics.PorterDuff.Mode);
method public void setTrackDrawable(android.graphics.drawable.Drawable);
+ method public void setTrackIcon(@Nullable android.graphics.drawable.Icon);
method public void setTrackResource(@DrawableRes int);
method public void setTrackTintBlendMode(@Nullable android.graphics.BlendMode);
method public void setTrackTintList(@Nullable android.content.res.ColorStateList);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index be3c246..bf70803 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -182,6 +182,10 @@
field public static final int TRANSPORT_TEST = 7; // 0x7
}
+ public final class Proxy {
+ method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
+ }
+
public final class TcpRepairWindow {
ctor public TcpRepairWindow(int, int, int, int, int, int);
field public final int maxWindow;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4359495..4d24cad 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -34,6 +34,7 @@
field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
+ field public static final String BIND_DOMAIN_VERIFICATION_AGENT = "android.permission.BIND_DOMAIN_VERIFICATION_AGENT";
field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE";
@@ -86,6 +87,7 @@
field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER";
field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
+ field public static final String DOMAIN_VERIFICATION_AGENT = "android.permission.DOMAIN_VERIFICATION_AGENT";
field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS";
field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
@@ -258,6 +260,7 @@
field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
field public static final String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS";
+ field public static final String UPDATE_DOMAIN_VERIFICATION_USER_SELECTION = "android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION";
field public static final String UPDATE_FONTS = "android.permission.UPDATE_FONTS";
field public static final String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
@@ -287,6 +290,7 @@
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+ field public static final int hotwordDetectionService = 16844326; // 0x1010626
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844305; // 0x1010611
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -629,8 +633,6 @@
method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long);
method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(int, long);
method public android.os.Bundle toBundle();
- field public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
- field public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
}
public class DownloadManager {
@@ -868,6 +870,7 @@
}
public class DevicePolicyManager {
+ method public boolean canAdminGrantSensorsPermissionsForUser(int);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
@@ -2102,6 +2105,7 @@
field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
+ field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
field public static final String ETHERNET_SERVICE = "ethernet";
field public static final String EUICC_CARD_SERVICE = "euicc_card";
field public static final String FONT_SERVICE = "font";
@@ -2145,12 +2149,13 @@
field public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
field public static final String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY";
field public static final String ACTION_DIAL_EMERGENCY = "android.intent.action.DIAL_EMERGENCY";
+ field public static final String ACTION_DOMAINS_NEED_VERIFICATION = "android.intent.action.DOMAINS_NEED_VERIFICATION";
field public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
field public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
field public static final String ACTION_INCIDENT_REPORT_READY = "android.intent.action.INCIDENT_REPORT_READY";
field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
- field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+ field @Deprecated public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
field public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
@@ -2483,8 +2488,8 @@
method @Nullable public abstract android.content.ComponentName getInstantAppInstallerComponent();
method @Nullable public abstract android.content.ComponentName getInstantAppResolverSettingsComponent();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
- method @NonNull public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
+ method @Deprecated @NonNull public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(@NonNull String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
@@ -2508,9 +2513,9 @@
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
- method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int);
method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List<java.lang.String>);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List<java.lang.String>);
field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
@@ -2520,6 +2525,7 @@
field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
+ field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
field public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = -268435456; // 0xf0000000
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000
@@ -2577,13 +2583,13 @@
field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99
field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a
field public static final int INSTALL_SUCCEEDED = 1; // 0x1
- field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; // 0x2
- field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; // 0x4
- field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1; // 0x1
- field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3; // 0x3
- field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0; // 0x0
- field public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff
- field public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
+ field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; // 0x2
+ field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; // 0x4
+ field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1; // 0x1
+ field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3; // 0x3
+ field @Deprecated public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0; // 0x0
+ field @Deprecated public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff
+ field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
@@ -2711,6 +2717,52 @@
}
+package android.content.pm.verify.domain {
+
+ public final class DomainVerificationInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap();
+ method @NonNull public java.util.UUID getIdentifier();
+ method @NonNull public String getPackageName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationInfo> CREATOR;
+ }
+
+ public interface DomainVerificationManager {
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.verify.domain.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> getValidVerificationPackageNames();
+ method public static boolean isStateModifiable(int);
+ method public static boolean isStateVerified(int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+ field public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
+ field public static final int STATE_FIRST_VERIFIER_DEFINED = 1024; // 0x400
+ field public static final int STATE_NO_RESPONSE = 0; // 0x0
+ field public static final int STATE_SUCCESS = 1; // 0x1
+ }
+
+ public final class DomainVerificationRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Set<java.lang.String> getPackageNames();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationRequest> CREATOR;
+ }
+
+ public final class DomainVerificationUserSelection implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Map<java.lang.String,java.lang.Boolean> getHostToUserSelectionMap();
+ method @NonNull public java.util.UUID getIdentifier();
+ method @NonNull public String getPackageName();
+ method @NonNull public android.os.UserHandle getUser();
+ method @NonNull public boolean isLinkHandlingAllowed();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationUserSelection> CREATOR;
+ }
+
+}
+
package android.content.rollback {
public final class PackageRollbackInfo implements android.os.Parcelable {
@@ -2860,6 +2912,9 @@
field public final boolean nightMode;
field public final String packageName;
field public final float powerBrightnessFactor;
+ field public final boolean reduceBrightColors;
+ field public final float reduceBrightColorsOffset;
+ field public final int reduceBrightColorsStrength;
field public final long timeStamp;
}
@@ -4665,10 +4720,6 @@
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
- public final class LocationResult implements android.os.Parcelable {
- method @NonNull public static android.location.LocationResult wrap(@NonNull android.location.Location);
- }
-
public final class SatellitePvt implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.location.SatellitePvt.ClockInfo getClockInfo();
@@ -4735,7 +4786,7 @@
method public abstract void onSendExtraCommand(@NonNull String, @Nullable android.os.Bundle);
method public abstract void onSetRequest(@NonNull android.location.provider.ProviderRequest);
method public void reportLocation(@NonNull android.location.Location);
- method public void reportLocation(@NonNull android.location.LocationResult);
+ method public void reportLocations(@NonNull java.util.List<android.location.Location>);
method public void setAllowed(boolean);
method public void setProperties(@NonNull android.location.provider.ProviderProperties);
field public static final String ACTION_FUSED_PROVIDER = "com.android.location.service.FusedLocationProvider";
@@ -4921,6 +4972,7 @@
method public android.media.PlayerProxy getPlayerProxy();
method public int getPlayerState();
method public int getPlayerType();
+ method @IntRange(from=0) public int getSessionId();
method public boolean isActive();
field public static final int PLAYER_STATE_IDLE = 1; // 0x1
field public static final int PLAYER_STATE_PAUSED = 3; // 0x3
@@ -4938,7 +4990,7 @@
}
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
- ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
+ ctor @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
}
public static class AudioRecord.Builder {
@@ -8462,6 +8514,8 @@
field public static final int EVENT_MMS = 2; // 0x2
field public static final int EVENT_SMS = 1; // 0x1
field public static final int EVENT_UNSPECIFIED = 0; // 0x0
+ field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
+ field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
}
public class RecoverySystem {
@@ -8591,6 +8645,7 @@
method @NonNull public static String formatUid(int);
method public static int getAppId(int);
method public int getIdentifier();
+ method public static int getUid(@NonNull android.os.UserHandle, int);
method @Deprecated public boolean isOwner();
method public boolean isSystem();
method public static int myUserId();
@@ -9312,6 +9367,24 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
}
+ public final class SimPhonebookContract {
+ method @NonNull public static String getEfUriPath(int);
+ field public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+ }
+
+ public static final class SimPhonebookContract.ElementaryFiles {
+ field public static final String EF_ADN_PATH_SEGMENT = "adn";
+ field public static final String EF_FDN_PATH_SEGMENT = "fdn";
+ field public static final String EF_SDN_PATH_SEGMENT = "sdn";
+ field public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+ }
+
+ public static final class SimPhonebookContract.SimRecords {
+ field public static final String EXTRA_NAME_VALIDATION_RESULT = "android.provider.extra.NAME_VALIDATION_RESULT";
+ field public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+ field public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+ }
+
public static final class Telephony.Carriers implements android.provider.BaseColumns {
field public static final String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
@@ -9440,6 +9513,11 @@
method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
}
+ public abstract class KeyProperties {
+ field public static final int NAMESPACE_APPLICATION = -1; // 0xffffffff
+ field public static final int NAMESPACE_WIFI = 102; // 0x66
+ }
+
}
package android.security.keystore.recovery {
@@ -9604,29 +9682,6 @@
}
-package android.service.attestation {
-
- public abstract class ImpressionAttestationService extends android.app.Service {
- ctor public ImpressionAttestationService();
- method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
- method public abstract boolean onVerifyImpressionToken(@NonNull byte[], @NonNull android.service.attestation.ImpressionToken);
- }
-
- public final class ImpressionToken implements android.os.Parcelable {
- ctor public ImpressionToken(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
- method public int describeContents();
- method @NonNull public android.graphics.Rect getBoundsInWindow();
- method @NonNull public String getHashingAlgorithm();
- method @NonNull public byte[] getHmac();
- method @NonNull public byte[] getImageHash();
- method public long getScreenshotTimeMillis();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.attestation.ImpressionToken> CREATOR;
- }
-
-}
-
package android.service.autofill {
public abstract class AutofillFieldClassificationService extends android.app.Service {
@@ -10192,6 +10247,30 @@
}
+package android.service.screenshot {
+
+ public final class ScreenshotHash implements android.os.Parcelable {
+ ctor public ScreenshotHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
+ method public int describeContents();
+ method @NonNull public android.graphics.Rect getBoundsInWindow();
+ method @NonNull public String getHashingAlgorithm();
+ method @NonNull public byte[] getHmac();
+ method @NonNull public byte[] getImageHash();
+ method public long getScreenshotTimeMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.screenshot.ScreenshotHash> CREATOR;
+ }
+
+ public abstract class ScreenshotHasherService extends android.app.Service {
+ ctor public ScreenshotHasherService();
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @Nullable public abstract android.service.screenshot.ScreenshotHash onGenerateScreenshotHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
+ method public abstract boolean onVerifyScreenshotHash(@NonNull byte[], @NonNull android.service.screenshot.ScreenshotHash);
+ field public static final String SERVICE_INTERFACE = "android.service.screenshot.ScreenshotHasherService";
+ }
+
+}
+
package android.service.search {
public abstract class SearchUiService extends android.app.Service {
@@ -10261,7 +10340,7 @@
ctor public ExternalStorageService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
- method public void onFreeCacheRequested(@NonNull java.util.UUID, long);
+ method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException;
method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
@@ -11634,6 +11713,7 @@
method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup();
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
@@ -11641,6 +11721,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
method public void requestEmbeddedSubscriptionInfoListRefresh(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
@@ -11806,7 +11887,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method public int setNrDualConnectivityState(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
@@ -13726,11 +13807,13 @@
public static interface RcsCapabilityExchangeImplBase.PublishResponseCallback {
method public void onCommandError(int) throws android.telephony.ims.ImsException;
method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
}
public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback {
method public void onCommandError(int) throws android.telephony.ims.ImsException;
method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException;
method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException;
method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException;
@@ -14358,6 +14441,7 @@
method public String getDataDirectorySuffix();
method public String getErrorString(android.content.Context, int);
method public int getPackageId(android.content.res.Resources, String);
+ method @NonNull public long[] getTimestamps();
method @Deprecated public void invokeDrawGlFunctor(android.view.View, long, boolean);
method public boolean isMultiProcessEnabled();
method public boolean isTraceTagEnabled();
@@ -14373,6 +14457,12 @@
method public static android.content.pm.PackageInfo getLoadedPackageInfo();
method public static int loadWebViewNativeLibraryFromPackage(String, ClassLoader);
method public static void prepareWebViewInZygote();
+ field public static final int ADD_ASSETS_END = 4; // 0x4
+ field public static final int ADD_ASSETS_START = 3; // 0x3
+ field public static final int CREATE_CONTEXT_END = 2; // 0x2
+ field public static final int CREATE_CONTEXT_START = 1; // 0x1
+ field public static final int GET_CLASS_LOADER_END = 6; // 0x6
+ field public static final int GET_CLASS_LOADER_START = 5; // 0x5
field public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; // 0x2
field public static final int LIBLOAD_FAILED_JNI_CALL = 7; // 0x7
field public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; // 0x4
@@ -14383,6 +14473,11 @@
field public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8; // 0x8
field public static final int LIBLOAD_SUCCESS = 0; // 0x0
field public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; // 0x1
+ field public static final int NATIVE_LOAD_END = 8; // 0x8
+ field public static final int NATIVE_LOAD_START = 7; // 0x7
+ field public static final int PROVIDER_CLASS_FOR_NAME_END = 10; // 0xa
+ field public static final int PROVIDER_CLASS_FOR_NAME_START = 9; // 0x9
+ field public static final int WEBVIEW_LOAD_START = 0; // 0x0
}
public interface WebViewFactoryProvider {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e0391ee..e39b2b8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -12,6 +12,7 @@
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
+ field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE";
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS";
@@ -23,10 +24,12 @@
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
+ field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
+ field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
@@ -118,6 +121,7 @@
public class ActivityOptions {
method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
+ method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method public static void setExitTransitionTimeout(long);
method public void setLaunchActivityType(int);
method public void setLaunchTaskId(int);
@@ -389,10 +393,10 @@
method public boolean isFactoryResetProtectionPolicySupported();
method @NonNull public static String operationToString(int);
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
- method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, boolean);
+ method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
+ method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
+ method @NonNull public static String unsafeOperationReasonToString(int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
- field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED";
- field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE";
field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
@@ -456,9 +460,11 @@
field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
+ field public static final int UNSAFE_OPERATION_REASON_NONE = -1; // 0xffffffff
}
public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
+ method public boolean canDeviceOwnerGrantSensorsPermissions();
method public int describeContents();
method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
method public long getLocalTime();
@@ -473,6 +479,7 @@
public static final class FullyManagedDeviceProvisioningParams.Builder {
ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setDeviceOwnerCanGrantSensorsPermissions(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
@@ -512,6 +519,7 @@
}
public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
+ ctor public UnsafeStateException(int, int);
method public int getOperation();
}
@@ -895,6 +903,40 @@
}
+package android.hardware.devicestate {
+
+ public final class DeviceStateManager {
+ method public void addDeviceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ method @NonNull public int[] getSupportedStates();
+ method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
+ }
+
+ public static interface DeviceStateManager.DeviceStateListener {
+ method public void onDeviceStateChanged(int);
+ }
+
+ public final class DeviceStateRequest {
+ method public int getFlags();
+ method public int getState();
+ method @NonNull public static android.hardware.devicestate.DeviceStateRequest.Builder newBuilder(int);
+ field public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1; // 0x1
+ }
+
+ public static final class DeviceStateRequest.Builder {
+ method @NonNull public android.hardware.devicestate.DeviceStateRequest build();
+ method @NonNull public android.hardware.devicestate.DeviceStateRequest.Builder setFlags(int);
+ }
+
+ public static interface DeviceStateRequest.Callback {
+ method public default void onRequestActivated(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ method public default void onRequestCanceled(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ method public default void onRequestSuspended(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ }
+
+}
+
package android.hardware.display {
public class AmbientDisplayConfiguration {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bdd541a..4728f11 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5090,6 +5090,13 @@
mTaskDescription.setBackgroundColor(colorBackground);
}
+ int colorBackgroundFloating = a.getColor(
+ com.android.internal.R.styleable.ActivityTaskDescription_colorBackgroundFloating,
+ 0);
+ if (colorBackgroundFloating != 0 && Color.alpha(colorBackgroundFloating) == 0xFF) {
+ mTaskDescription.setBackgroundColorFloating(colorBackgroundFloating);
+ }
+
final int statusBarColor = a.getColor(
com.android.internal.R.styleable.ActivityTaskDescription_statusBarColor, 0);
if (statusBarColor != 0) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 520959c..43d0269 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1072,13 +1072,15 @@
private static final String ATTR_TASKDESCRIPTIONCOLOR_PRIMARY =
ATTR_TASKDESCRIPTION_PREFIX + "color";
private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND =
- ATTR_TASKDESCRIPTION_PREFIX + "colorBackground";
+ ATTR_TASKDESCRIPTION_PREFIX + "color_background";
private static final String ATTR_TASKDESCRIPTIONICON_FILENAME =
ATTR_TASKDESCRIPTION_PREFIX + "icon_filename";
private static final String ATTR_TASKDESCRIPTIONICON_RESOURCE =
ATTR_TASKDESCRIPTION_PREFIX + "icon_resource";
private static final String ATTR_TASKDESCRIPTIONICON_RESOURCE_PACKAGE =
ATTR_TASKDESCRIPTION_PREFIX + "icon_package";
+ private static final String ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND_FLOATING =
+ ATTR_TASKDESCRIPTION_PREFIX + "color_background_floating";
private String mLabel;
@Nullable
@@ -1086,6 +1088,7 @@
private String mIconFilename;
private int mColorPrimary;
private int mColorBackground;
+ private int mColorBackgroundFloating;
private int mStatusBarColor;
private int mNavigationBarColor;
private boolean mEnsureStatusBarContrastWhenTransparent;
@@ -1106,7 +1109,7 @@
*/
public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) {
this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes),
- colorPrimary, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ colorPrimary, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1121,7 +1124,7 @@
*/
public TaskDescription(String label, @DrawableRes int iconRes) {
this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes),
- 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/**
@@ -1130,14 +1133,14 @@
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
- this(label, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ this(label, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
- this(null, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ this(null, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/**
@@ -1152,7 +1155,7 @@
@Deprecated
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
this(label, icon != null ? Icon.createWithBitmap(icon) : null, colorPrimary, 0, 0, 0,
- false, false, RESIZE_MODE_RESIZEABLE, -1, -1);
+ false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1168,7 +1171,7 @@
@Deprecated
public TaskDescription(String label, Bitmap icon) {
this(label, icon != null ? Icon.createWithBitmap(icon) : null, 0, 0, 0, 0, false, false,
- RESIZE_MODE_RESIZEABLE, -1, -1);
+ RESIZE_MODE_RESIZEABLE, -1, -1, 0);
}
/** @hide */
@@ -1177,7 +1180,7 @@
int statusBarColor, int navigationBarColor,
boolean ensureStatusBarContrastWhenTransparent,
boolean ensureNavigationBarContrastWhenTransparent, int resizeMode, int minWidth,
- int minHeight) {
+ int minHeight, int colorBackgroundFloating) {
mLabel = label;
mIcon = icon;
mColorPrimary = colorPrimary;
@@ -1190,6 +1193,7 @@
mResizeMode = resizeMode;
mMinWidth = minWidth;
mMinHeight = minHeight;
+ mColorBackgroundFloating = colorBackgroundFloating;
}
/**
@@ -1217,6 +1221,7 @@
mResizeMode = other.mResizeMode;
mMinWidth = other.mMinWidth;
mMinHeight = other.mMinHeight;
+ mColorBackgroundFloating = other.mColorBackgroundFloating;
}
/**
@@ -1253,6 +1258,9 @@
if (other.mMinHeight != -1) {
mMinHeight = other.mMinHeight;
}
+ if (other.mColorBackgroundFloating != 0) {
+ mColorBackgroundFloating = other.mColorBackgroundFloating;
+ }
}
private TaskDescription(Parcel source) {
@@ -1292,6 +1300,19 @@
}
/**
+ * Sets the background color floating for this task description.
+ * @hide
+ */
+ public void setBackgroundColorFloating(int backgroundColor) {
+ // Ensure that the given color is valid
+ if ((backgroundColor != 0) && (Color.alpha(backgroundColor) != 255)) {
+ throw new RuntimeException(
+ "A TaskDescription's background color floating should be opaque");
+ }
+ mColorBackgroundFloating = backgroundColor;
+ }
+
+ /**
* @hide
*/
public void setStatusBarColor(int statusBarColor) {
@@ -1461,6 +1482,14 @@
}
/**
+ * @return The background color floating.
+ * @hide
+ */
+ public int getBackgroundColorFloating() {
+ return mColorBackgroundFloating;
+ }
+
+ /**
* @hide
*/
public int getStatusBarColor() {
@@ -1537,6 +1566,10 @@
if (mColorBackground != 0) {
out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, mColorBackground);
}
+ if (mColorBackgroundFloating != 0) {
+ out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND_FLOATING,
+ mColorBackgroundFloating);
+ }
if (mIconFilename != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONICON_FILENAME, mIconFilename);
}
@@ -1563,6 +1596,11 @@
if (colorBackground != 0) {
setBackgroundColor(colorBackground);
}
+ final int colorBackgroundFloating = in.getAttributeIntHex(null,
+ ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND_FLOATING, 0);
+ if (colorBackgroundFloating != 0) {
+ setBackgroundColorFloating(colorBackgroundFloating);
+ }
final String iconFilename = in.getAttributeValue(null,
ATTR_TASKDESCRIPTIONICON_FILENAME);
if (iconFilename != null) {
@@ -1615,6 +1653,7 @@
dest.writeInt(1);
dest.writeString(mIconFilename);
}
+ dest.writeInt(mColorBackgroundFloating);
}
public void readFromParcel(Parcel source) {
@@ -1632,6 +1671,7 @@
mMinWidth = source.readInt();
mMinHeight = source.readInt();
mIconFilename = source.readInt() > 0 ? source.readString() : null;
+ mColorBackgroundFloating = source.readInt();
}
public static final @android.annotation.NonNull Creator<TaskDescription> CREATOR
@@ -1655,7 +1695,8 @@
+ (mEnsureNavigationBarContrastWhenTransparent
? " (contrast when transparent)" : "")
+ " resizeMode: " + ActivityInfo.resizeModeToString(mResizeMode)
- + " minWidth: " + mMinWidth + " minHeight: " + mMinHeight;
+ + " minWidth: " + mMinWidth + " minHeight: " + mMinHeight
+ + " colorBackgrounFloating: " + mColorBackgroundFloating;
}
@Override
@@ -1678,7 +1719,8 @@
== other.mEnsureNavigationBarContrastWhenTransparent
&& mResizeMode == other.mResizeMode
&& mMinWidth == other.mMinWidth
- && mMinHeight == other.mMinHeight;
+ && mMinHeight == other.mMinHeight
+ && mColorBackgroundFloating == other.mColorBackgroundFloating;
}
/** @hide */
@@ -1826,6 +1868,8 @@
pw.print(ActivityInfo.resizeModeToString(td.getResizeMode()));
pw.print(" minWidth="); pw.print(td.getMinWidth());
pw.print(" minHeight="); pw.print(td.getMinHeight());
+ pw.print(" colorBackgroundFloating=#");
+ pw.print(Integer.toHexString(td.getBackgroundColorFloating()));
pw.println(" }");
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7402690..c31c22c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -30,6 +30,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
import android.util.ArraySet;
@@ -103,7 +104,7 @@
* @param target
* @param whitelistToken
* @param duration temp allowlist duration in milliseconds.
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param type temp allowlist type defined at {@link TempAllowListType}
*/
public abstract void setPendingIntentWhitelistDuration(IIntentSender target,
IBinder whitelistToken, long duration, int type);
@@ -136,10 +137,10 @@
* @param changingUid uid to add or remove to temp allowlist.
* @param adding true to add to temp allowlist, false to remove from temp allowlist.
* @param durationMs when adding is true, the duration to be in temp allowlist.
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}.
+ * @param type temp allowlist type defined at {@link TempAllowListType}.
*/
public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid,
- boolean adding, long durationMs, @BroadcastOptions.TempAllowListType int type);
+ boolean adding, long durationMs, @TempAllowListType int type);
/**
* Get the procstate for the UID. The return value will be between
@@ -333,7 +334,7 @@
* @param callerUid the UID that sent the PendingIntent.
* @param targetUid the UID that is been temp allowlisted.
* @param duration temp allowlist duration in milliseconds.
- * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param type temp allowlist type defined at {@link TempAllowListType}
* @param tag
*/
public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
@@ -537,4 +538,10 @@
* @return mBootTimeTempAllowlistDuration of ActivityManagerConstants.
*/
public abstract long getBootTimeTempAllowListDuration();
+
+ /** Register an {@link AnrController} to control the ANR dialog behavior */
+ public abstract void registerAnrController(AnrController controller);
+
+ /** Unregister an {@link AnrController} */
+ public abstract void unregisterAnrController(AnrController controller);
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 2b5e18d..28da1c3 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,6 +17,7 @@
package android.app;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
@@ -310,6 +311,9 @@
private static final String KEY_REMOTE_TRANSITION =
"android:activity.remoteTransition";
+ private static final String KEY_OVERRIDE_TASK_TRANSITION =
+ "android:activity.overrideTaskTransition";
+
/**
* @see #setLaunchCookie
* @hide
@@ -393,6 +397,7 @@
private RemoteAnimationAdapter mRemoteAnimationAdapter;
private IBinder mLaunchCookie;
private IRemoteTransition mRemoteTransition;
+ private boolean mOverrideTaskTransition;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -476,6 +481,40 @@
}
/**
+ * Create an ActivityOptions specifying a custom animation to run when the activity in the
+ * different task is displayed.
+ *
+ * @param context Who is defining this. This is the application that the
+ * animation resources will be loaded from.
+ * @param enterResId A resource ID of the animation resource to use for
+ * the incoming activity. Use 0 for no animation.
+ * @param exitResId A resource ID of the animation resource to use for
+ * the outgoing activity. Use 0 for no animation.
+ * @param handler If <var>listener</var> is non-null this must be a valid
+ * Handler on which to dispatch the callback; otherwise it should be null.
+ * @param startedListener Optional OnAnimationStartedListener to find out when the
+ * requested animation has started running. If for some reason the animation
+ * is not executed, the callback will happen immediately.
+ * @param finishedListener Optional OnAnimationFinishedListener when the animation
+ * has finished running.
+ *
+ * @return Returns a new ActivityOptions object that you can use to
+ * supply these options as the options Bundle when starting an activity.
+ * @hide
+ */
+ @RequiresPermission(START_TASKS_FROM_RECENTS)
+ @TestApi
+ public static @NonNull ActivityOptions makeCustomTaskAnimation(@NonNull Context context,
+ int enterResId, int exitResId, @Nullable Handler handler,
+ @Nullable OnAnimationStartedListener startedListener,
+ @Nullable OnAnimationFinishedListener finishedListener) {
+ ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
+ startedListener, finishedListener);
+ opts.mOverrideTaskTransition = true;
+ return opts;
+ }
+
+ /**
* Creates an ActivityOptions specifying a custom animation to run in place on an existing
* activity.
*
@@ -1107,6 +1146,7 @@
mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE);
mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder(
KEY_REMOTE_TRANSITION));
+ mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
}
/**
@@ -1561,6 +1601,12 @@
return mLaunchCookie;
}
+
+ /** @hide */
+ public boolean getOverrideTaskTransition() {
+ return mOverrideTaskTransition;
+ }
+
/**
* Update the current values in this ActivityOptions from those supplied
* in <var>otherOptions</var>. Any values
@@ -1789,6 +1835,9 @@
if (mRemoteTransition != null) {
b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder());
}
+ if (mOverrideTaskTransition) {
+ b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition);
+ }
return b;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1f9cb64..e5a04c9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -95,7 +95,6 @@
import android.media.MediaFrameworkPlatformInitializer;
import android.media.MediaServiceManager;
import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
import android.net.Proxy;
import android.net.Uri;
import android.os.AsyncTask;
@@ -6576,25 +6575,6 @@
// Pass the current context to HardwareRenderer
HardwareRenderer.setContextForInit(getSystemContext());
- /**
- * Initialize the default http proxy in this process for the reasons we set the time zone.
- */
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Setup proxies");
- final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- if (b != null) {
- // In pre-boot mode (doing initial launch to collect password), not
- // all system is up. This includes the connectivity service, so don't
- // crash if we can't get it.
- final IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- try {
- Proxy.setHttpProxySystemProperty(service.getProxyForNetwork(null));
- } catch (RemoteException e) {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- throw e.rethrowFromSystemServer();
- }
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
// Instrumentation info affects the class loader, so load it before
// setting up the app context.
final InstrumentationInfo ii;
@@ -6608,6 +6588,23 @@
updateLocaleListFromAppContext(appContext,
mResourcesManager.getConfiguration().getLocales());
+ // Initialize the default http proxy in this process.
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Setup proxies");
+ try {
+ // In pre-boot mode (doing initial launch to collect password), not all system is up.
+ // This includes the connectivity service, so trying to obtain ConnectivityManager at
+ // that point would return null. Check whether the ConnectivityService is available, and
+ // avoid crashing with a NullPointerException if it is not.
+ final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ if (b != null) {
+ final ConnectivityManager cm =
+ appContext.getSystemService(ConnectivityManager.class);
+ Proxy.setHttpProxyConfiguration(cm.getDefaultProxy());
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
if (!Process.isIsolated()) {
final int oldMask = StrictMode.allowThreadDiskWritesMask();
try {
@@ -7522,8 +7519,8 @@
}
public static void updateHttpProxy(@NonNull Context context) {
- final ConnectivityManager cm = ConnectivityManager.from(context);
- Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
+ final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+ Proxy.setHttpProxyConfiguration(cm.getDefaultProxy());
}
@UnsupportedAppUsage
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/core/java/android/app/AnrController.java
similarity index 64%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to core/java/android/app/AnrController.java
index 19b20f2..cfc9d27 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/core/java/android/app/AnrController.java
@@ -14,7 +14,16 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package android.app;
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Interface to control the ANR dialog within the activity manager
+ * {@hide}
+ */
+public interface AnrController {
+ /**
+ * Returns the delay in milliseconds for an ANR dialog that is about to be shown for
+ * {@code packageName}.
+ */
+ long getAnrDelayMillis(String packageName, int uid);
+}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b51d4ac..8ac9139 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -39,11 +39,13 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApkChecksum;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.Checksum;
import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageManager;
@@ -880,10 +882,10 @@
@Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
- @NonNull IntentSender statusReceiver)
+ @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
Objects.requireNonNull(packageName);
- Objects.requireNonNull(statusReceiver);
+ Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(trustedInstallers);
try {
if (trustedInstallers == TRUST_ALL) {
@@ -895,8 +897,17 @@
"trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
+ "list of certificates.");
}
+ IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
+ new IOnChecksumsReadyListener.Stub() {
+ @Override
+ public void onChecksumsReady(List<ApkChecksum> checksums)
+ throws RemoteException {
+ onChecksumsReadyListener.onChecksumsReady(checksums);
+ }
+ };
mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
- encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+ encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
+ getUserId());
} catch (ParcelableException e) {
e.maybeRethrow(PackageManager.NameNotFoundException.class);
throw new RuntimeException(e);
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index a16f6a8..445fdd8 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -16,14 +16,12 @@
package android.app;
-import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.Build;
import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import android.os.PowerWhitelistManager;
+import android.os.PowerWhitelistManager.TempAllowListType;
/**
* Helper class for building an options Bundle that can be used with
@@ -75,25 +73,21 @@
"android:broadcast.allowBackgroundActivityStarts";
/**
- * Allow the temp allowlist behavior, plus allow foreground service start from background.
- */
- public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
- /**
- * Only allow the temp allowlist behavior, not allow foreground service start from
- * background.
- */
- public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
-
- /**
- * The list of temp allowlist types.
* @hide
+ * @deprecated Use {@link android.os.PowerWhitelistManager#
+ * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead.
*/
- @IntDef(flag = true, prefix = { "TEMPORARY_WHITELIST_TYPE_" }, value = {
- TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TempAllowListType {}
+ @Deprecated
+ public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
+ PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+ /**
+ * @hide
+ * @deprecated Use {@link android.os.PowerWhitelistManager#
+ * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} instead.
+ */
+ @Deprecated
+ public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
+ PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
public static BroadcastOptions makeBasic() {
BroadcastOptions opts = new BroadcastOptions();
@@ -125,7 +119,8 @@
android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
public void setTemporaryAppWhitelistDuration(long duration) {
mTemporaryAppWhitelistDuration = duration;
- mTemporaryAppWhitelistType = TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+ mTemporaryAppWhitelistType =
+ PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
}
/**
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 9e1c505..a9e28bb 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -145,18 +145,6 @@
void onTaskSnapshotChanged(int taskId, in TaskSnapshot snapshot);
/**
- * Called when the resumed activity is in size compatibility mode and its override configuration
- * is different from the current one of system.
- *
- * @param displayId Id of the display where the activity resides.
- * @param activityToken Token of the size compatibility mode activity. It will be null when
- * switching to a activity that is not in size compatibility mode or the
- * configuration of the activity.
- * @see com.android.server.wm.ActivityRecord#inSizeCompatMode
- */
- void onSizeCompatModeActivityChanged(int displayId, in IBinder activityToken);
-
- /**
* Reports that an Activity received a back key press when there were no additional activities
* on the back stack.
*
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f8c33b5..7404e53 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -69,6 +69,9 @@
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationManagerImpl;
+import android.content.pm.verify.domain.IDomainVerificationManager;
import android.content.res.Resources;
import android.content.rollback.RollbackManagerFrameworkInitializer;
import android.debug.AdbManager;
@@ -1388,6 +1391,20 @@
}
});
+ // TODO(b/159952358): Only register this service for the domain verification agent?
+ registerService(Context.DOMAIN_VERIFICATION_SERVICE, DomainVerificationManager.class,
+ new CachedServiceFetcher<DomainVerificationManager>() {
+ @Override
+ public DomainVerificationManager createService(ContextImpl context)
+ throws ServiceNotFoundException {
+ IBinder binder = ServiceManager.getServiceOrThrow(
+ Context.DOMAIN_VERIFICATION_SERVICE);
+ IDomainVerificationManager service =
+ IDomainVerificationManager.Stub.asInterface(binder);
+ return new DomainVerificationManagerImpl(context, service);
+ }
+ });
+
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index d1b544d..517ae24 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -21,7 +21,6 @@
import android.content.ComponentName;
import android.os.Binder;
import android.os.Build;
-import android.os.IBinder;
import android.os.RemoteException;
import android.window.TaskSnapshot;
@@ -163,12 +162,6 @@
}
@Override
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken)
- throws RemoteException {
- }
-
- @Override
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)
throws RemoteException {
}
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 14ed414..cbe2995 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -15,8 +15,7 @@
*/
package android.app;
-import static android.view.WindowManagerGlobal.ADD_OKAY;
-import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
+import static android.view.WindowManagerImpl.createWindowContextWindowManager;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,8 +27,8 @@
import android.os.RemoteException;
import android.view.Display;
import android.view.IWindowManager;
+import android.view.WindowManager;
import android.view.WindowManagerGlobal;
-import android.view.WindowManagerImpl;
import com.android.internal.annotations.VisibleForTesting;
@@ -46,10 +45,10 @@
*/
@UiContext
public class WindowContext extends ContextWrapper {
- private final WindowManagerImpl mWindowManager;
+ private final WindowManager mWindowManager;
private final IWindowManager mWms;
private final WindowTokenClient mToken;
- private boolean mOwnsToken;
+ private boolean mListenerRegistered;
/**
* Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
@@ -86,25 +85,14 @@
mToken.attachContext(this);
- mWindowManager = new WindowManagerImpl(this);
- mWindowManager.setDefaultToken(mToken);
+ mWindowManager = createWindowContextWindowManager(this);
- int result;
try {
- // Register the token with WindowManager. This will also call back with the current
- // config back to the client.
- result = mWms.addWindowTokenWithOptions(
- mToken, type, getDisplayId(), options, getPackageName());
+ mListenerRegistered = mWms.registerWindowContextListener(mToken, type, getDisplayId(),
+ options);
} catch (RemoteException e) {
- mOwnsToken = false;
throw e.rethrowFromSystemServer();
}
- if (result == ADD_TOO_MANY_TOKENS) {
- throw new UnsupportedOperationException("createWindowContext failed! Too many unused "
- + "window contexts. Please see Context#createWindowContext documentation for "
- + "detail.");
- }
- mOwnsToken = result == ADD_OKAY;
Reference.reachabilityFence(this);
}
@@ -131,10 +119,10 @@
/** Used for test to invoke because we can't invoke finalize directly. */
@VisibleForTesting
public void release() {
- if (mOwnsToken) {
+ if (mListenerRegistered) {
+ mListenerRegistered = false;
try {
- mWms.removeWindowToken(mToken, getDisplayId());
- mOwnsToken = false;
+ mWms.unregisterWindowContextListener(mToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index 9092ef36..29792ac 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -44,8 +44,8 @@
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}.
* <p>This method must be called before invoking
- * {@link android.view.IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle,
- * String)}.<p/>
+ * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int,
+ * Bundle, boolean)}.<p/>
*
* @param context context to be attached
* @throws IllegalStateException if attached context has already existed.
diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index 8b0c706..9c07f85 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -57,6 +57,13 @@
public abstract int getPermissionPolicy(@UserIdInt int userHandle);
/**
+ * Caches {@link DevicePolicyManager#canAdminGrantSensorsPermissionsForUser(int)} for the
+ * given user.
+ */
+ public abstract boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle);
+
+
+ /**
* Empty implementation.
*/
private static class EmptyDevicePolicyCache extends DevicePolicyCache {
@@ -77,5 +84,10 @@
public int getPermissionPolicy(int userHandle) {
return DevicePolicyManager.PERMISSION_POLICY_PROMPT;
}
+
+ @Override
+ public boolean canAdminGrantSensorsPermissionsForUser(int userHandle) {
+ return false;
+ }
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e84d4a5..82255c8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -980,6 +980,19 @@
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
/**
+ * A boolean extra indicating the admin of a fully-managed device opts out of controlling
+ * permission grants for sensor-related permissions,
+ * see {@link #setPermissionGrantState(ComponentName, String, String, int)}.
+ *
+ * The default for this extra is {@code false} - by default, the admin of a fully-managed
+ * device has the ability to grant sensors-related permissions.
+ *
+ * <p>Use with {@link #ACTION_PROVISION_MANAGED_DEVICE} only.
+ */
+ public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT =
+ "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
+
+ /**
* A String extra holding the URL-safe base64 encoded SHA-256 checksum of any signature of the
* android package archive at the download location specified in {@link
* #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
@@ -1730,8 +1743,12 @@
* Broadcast action to notify ManagedProvisioning that
* {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed.
* @hide
+ * @deprecated No longer needed as ManagedProvisioning no longer handles
+ * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction changing.
*/
+ // TODO(b/177221010): Remove when Managed Provisioning no longer depend on it.
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED =
"android.app.action.DATA_SHARING_RESTRICTION_CHANGED";
@@ -2905,6 +2922,35 @@
return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation);
}
+ private static final String PREFIX_UNSAFE_OPERATION_REASON = "UNSAFE_OPERATION_REASON_";
+
+ /** @hide */
+ @IntDef(prefix = PREFIX_UNSAFE_OPERATION_REASON, value = {
+ UNSAFE_OPERATION_REASON_NONE,
+ UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public static @interface UnsafeOperationReason {
+ }
+
+ /** @hide */
+ @TestApi
+ public static final int UNSAFE_OPERATION_REASON_NONE = -1;
+
+ /**
+ * Indicates that a {@link UnsafeStateException} was thrown because the operation would distract
+ * the driver of the vehicle.
+ */
+ public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1;
+
+ /** @hide */
+ @NonNull
+ @TestApi
+ public static String unsafeOperationReasonToString(@UnsafeOperationReason int reason) {
+ return DebugUtils.constantToString(DevicePolicyManager.class,
+ PREFIX_UNSAFE_OPERATION_REASON, reason);
+ }
+
/** @hide */
public void resetNewUserDisclaimer() {
if (mService != null) {
@@ -5440,26 +5486,6 @@
"android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER";
/**
- * Broadcast action: notify managed provisioning that the device has been provisioned.
- *
- * @hide
- */
- @TestApi
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_PROVISIONED_MANAGED_DEVICE =
- "android.app.action.PROVISIONED_MANAGED_DEVICE";
-
- /**
- * Broadcast action: notify managed provisioning that a new managed profile is created.
- *
- * @hide
- */
- @TestApi
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_MANAGED_PROFILE_CREATED =
- "android.app.action.MANAGED_PROFILE_CREATED";
-
- /**
* Widgets are enabled in keyguard
*/
public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
@@ -10507,6 +10533,13 @@
* As this policy only acts on runtime permission requests, it only applies to applications
* built with a {@code targetSdkVersion} of {@link android.os.Build.VERSION_CODES#M} or later.
*
+ * <p>
+ * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, an auto-grant
+ * policy will not apply to certain sensors-related permissions on some configurations.
+ * See {@link #setPermissionGrantState(ComponentName, String, String, int)} for the list of
+ * permissions affected, and the behavior change for managed profiles and fully-managed
+ * devices.
+ *
* @param admin Which profile or device owner this request is associated with.
* @param policy One of the policy constants {@link #PERMISSION_POLICY_PROMPT},
* {@link #PERMISSION_POLICY_AUTO_GRANT} and {@link #PERMISSION_POLICY_AUTO_DENY}.
@@ -10565,6 +10598,31 @@
* application built with a {@code targetSdkVersion} <
* {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to
* {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted.
+ * <p>
+ * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, control over
+ * the following, sensors-related, permissions is restricted:
+ * <ul>
+ * <li>Manifest.permission.ACCESS_FINE_LOCATION</li>
+ * <li>Manifest.permission.ACCESS_BACKGROUND_LOCATION</li>
+ * <li>Manifest.permission.ACCESS_COARSE_LOCATION</li>
+ * <li>Manifest.permission.CAMERA</li>
+ * <li>Manifest.permission.RECORD_AUDIO</li>
+ * <li>Manifest.permission.RECORD_BACKGROUND_AUDIO</li>
+ * <li>Manifest.permission.ACTIVITY_RECOGNITION</li>
+ * <li>Manifest.permission.BODY_SENSORS</li>
+ * </ul>
+ * <p>
+ * A profile owner may not grant these permissions (i.e. call this method with any of the
+ * permissions listed above and {@code grantState} of {@code #PERMISSION_GRANT_STATE_GRANTED}),
+ * but may deny them.
+ * <p>
+ * A device owner, by default, may continue granting these permissions. However, for increased
+ * user control, the admin may opt out of controlling grants for these permissions by including
+ * {@link #EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT} in the provisioning parameters. In that
+ * case the device owner's control will be limited do denying these permissions.
+ * <p>
+ * Attempts by the admin to grant these permissions, when the admin is restricted from doing
+ * so, will be silently ignored (no exception will be thrown).
*
* @param admin Which profile or device owner this request is associated with.
* @param packageName The application to grant or revoke a permission to.
@@ -13098,10 +13156,11 @@
*/
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS)
- public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) {
+ public void setNextOperationSafety(@DevicePolicyOperation int operation,
+ @UnsafeOperationReason int reason) {
if (mService != null) {
try {
- mService.setNextOperationSafety(operation, safe);
+ mService.setNextOperationSafety(operation, reason);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -13198,7 +13257,8 @@
return null;
}
try {
- return mService.createAndProvisionManagedProfile(provisioningParams);
+ return mService.createAndProvisionManagedProfile(
+ provisioningParams, mContext.getPackageName());
} catch (ServiceSpecificException e) {
throw new ProvisioningException(e, e.errorCode);
} catch (RemoteException e) {
@@ -13229,7 +13289,7 @@
throws ProvisioningException {
if (mService != null) {
try {
- mService.provisionFullyManagedDevice(provisioningParams);
+ mService.provisionFullyManagedDevice(provisioningParams, mContext.getPackageName());
} catch (ServiceSpecificException e) {
throw new ProvisioningException(e, e.errorCode);
} catch (RemoteException re) {
@@ -13237,4 +13297,57 @@
}
}
}
+
+ /**
+ * Resets the default cross profile intent filters that were set during
+ * {@link #createAndProvisionManagedProfile} between {@code userId} and all it's managed
+ * profiles if any.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ if (mService != null) {
+ try {
+ mService.resetDefaultCrossProfileIntentFilters(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ }
+ /**
+ * Returns true if the caller is running on a device where the admin can grant
+ * permissions related to device sensors.
+ * This is a signal that the device is a fully-managed device where personal usage is
+ * discouraged.
+ * The list of permissions is listed in
+ * {@link #setPermissionGrantState(ComponentName, String, String, int)}.
+ *
+ * May be called by any app.
+ * @return true if the app can grant device sensors-related permissions, false otherwise.
+ */
+ public boolean canAdminGrantSensorsPermissions() {
+ return canAdminGrantSensorsPermissionsForUser(myUserId());
+ }
+
+ /**
+ * Returns true if the admin can control grants of sensors-related permissions, for
+ * a given user.
+ *
+ * @hide
+ * @param userId The ID of the user to check.
+ * @return if the admin may grant these permissions, false otherwise.
+ */
+ @SystemApi
+ public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+ if (mService == null) {
+ return false;
+ }
+ try {
+ return mService.canAdminGrantSensorsPermissionsForUser(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicySafetyChecker.java b/core/java/android/app/admin/DevicePolicySafetyChecker.java
index b1a80c5..6c6f2aa 100644
--- a/core/java/android/app/admin/DevicePolicySafetyChecker.java
+++ b/core/java/android/app/admin/DevicePolicySafetyChecker.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import com.android.internal.os.IResultReceiver;
@@ -30,14 +31,16 @@
/**
* Returns whether the given {@code operation} can be safely executed at the moment.
*/
- boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation);
+ @UnsafeOperationReason
+ int getUnsafeOperationReason(@DevicePolicyOperation int operation);
/**
* Returns a new exception for when the given {@code operation} cannot be safely executed.
*/
@NonNull
- default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation) {
- return new UnsafeStateException(operation);
+ default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation,
+ @UnsafeOperationReason int reason) {
+ return new UnsafeStateException(operation, reason);
}
/**
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 83af019..5e1cbad 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -42,6 +42,7 @@
private final long mLocalTime;
@SuppressLint("UseIcu")
@Nullable private final Locale mLocale;
+ private final boolean mDeviceOwnerCanGrantSensorsPermissions;
private FullyManagedDeviceProvisioningParams(
@NonNull ComponentName deviceAdminComponentName,
@@ -49,13 +50,16 @@
boolean leaveAllSystemAppsEnabled,
@Nullable String timeZone,
long localTime,
- @Nullable @SuppressLint("UseIcu") Locale locale) {
+ @Nullable @SuppressLint("UseIcu") Locale locale,
+ boolean deviceOwnerCanGrantSensorsPermissions) {
this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
this.mTimeZone = timeZone;
this.mLocalTime = localTime;
this.mLocale = locale;
+ this.mDeviceOwnerCanGrantSensorsPermissions =
+ deviceOwnerCanGrantSensorsPermissions;
}
private FullyManagedDeviceProvisioningParams(
@@ -64,13 +68,15 @@
boolean leaveAllSystemAppsEnabled,
@Nullable String timeZone,
long localTime,
- @Nullable String localeStr) {
+ @Nullable String localeStr,
+ boolean deviceOwnerCanGrantSensorsPermissions) {
this(deviceAdminComponentName,
ownerName,
leaveAllSystemAppsEnabled,
timeZone,
localTime,
- getLocale(localeStr));
+ getLocale(localeStr),
+ deviceOwnerCanGrantSensorsPermissions);
}
@Nullable
@@ -107,6 +113,14 @@
}
/**
+ * @return true if the device owner can control sensor-related permission grants, false
+ * if the device owner has opted out of it.
+ */
+ public boolean canDeviceOwnerGrantSensorsPermissions() {
+ return mDeviceOwnerCanGrantSensorsPermissions;
+ }
+
+ /**
* Builder class for {@link FullyManagedDeviceProvisioningParams} objects.
*/
public static final class Builder {
@@ -117,6 +131,8 @@
private long mLocalTime;
@SuppressLint("UseIcu")
@Nullable private Locale mLocale;
+ // Default to allowing control over sensor permission grants.
+ boolean mDeviceOwnerCanGrantSensorsPermissions = true;
/**
* Initialize a new {@link Builder} to construct a
@@ -181,6 +197,17 @@
}
/**
+ * Marks that the Device Owner may grant permissions related to device sensors.
+ * See {@link DevicePolicyManager#EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT}.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setDeviceOwnerCanGrantSensorsPermissions(boolean mayGrant) {
+ mDeviceOwnerCanGrantSensorsPermissions = mayGrant;
+ return this;
+ }
+
+ /**
* Combines all of the attributes that have been set on this {@code Builder}
*
* @return a new {@link FullyManagedDeviceProvisioningParams} object.
@@ -193,7 +220,8 @@
mLeaveAllSystemAppsEnabled,
mTimeZone,
mLocalTime,
- mLocale);
+ mLocale,
+ mDeviceOwnerCanGrantSensorsPermissions);
}
}
@@ -211,6 +239,8 @@
+ ", mTimeZone=" + (mTimeZone == null ? "null" : mTimeZone)
+ ", mLocalTime=" + mLocalTime
+ ", mLocale=" + (mLocale == null ? "null" : mLocale)
+ + ", mDeviceOwnerCanGrantSensorsPermissions="
+ + mDeviceOwnerCanGrantSensorsPermissions
+ '}';
}
@@ -222,6 +252,7 @@
dest.writeString(mTimeZone);
dest.writeLong(mLocalTime);
dest.writeString(mLocale == null ? null : mLocale.toLanguageTag());
+ dest.writeBoolean(mDeviceOwnerCanGrantSensorsPermissions);
}
@NonNull
@@ -235,6 +266,7 @@
String timeZone = in.readString();
long localtime = in.readLong();
String locale = in.readString();
+ boolean deviceOwnerCanGrantSensorsPermissions = in.readBoolean();
return new FullyManagedDeviceProvisioningParams(
componentName,
@@ -242,7 +274,8 @@
leaveAllSystemAppsEnabled,
timeZone,
localtime,
- locale);
+ locale,
+ deviceOwnerCanGrantSensorsPermissions);
}
@Override
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3765a67..89f30cc 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -491,11 +491,14 @@
void setManagedProfileMaximumTimeOff(in ComponentName admin, long timeoutMs);
boolean canProfileOwnerResetPasswordWhenLocked(int userId);
- void setNextOperationSafety(int operation, boolean safe);
+ void setNextOperationSafety(int operation, int reason);
String getEnrollmentSpecificId(String callerPackage);
void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId);
- UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams);
- void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams);
+ UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
+ void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
+
+ void resetDefaultCrossProfileIntentFilters(int userId);
+ boolean canAdminGrantSensorsPermissionsForUser(int userId);
}
diff --git a/core/java/android/app/admin/UnsafeStateException.java b/core/java/android/app/admin/UnsafeStateException.java
index 9dcaae4..56eeb06 100644
--- a/core/java/android/app/admin/UnsafeStateException.java
+++ b/core/java/android/app/admin/UnsafeStateException.java
@@ -15,15 +15,21 @@
*/
package android.app.admin;
+import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION;
+import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
+
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
/**
- * Exception thrown when a {@link DevicePolicyManager} operation failed because it was not safe
- * to be executed at that moment.
+ * Exception thrown when a {@link android.app.admin.DevicePolicyManager} operation failed because it
+ * was not safe to be executed at that moment.
*
* <p>For example, it can be thrown on
* {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive devices} when the vehicle
@@ -33,12 +39,19 @@
public final class UnsafeStateException extends IllegalStateException implements Parcelable {
private final @DevicePolicyOperation int mOperation;
+ private final @UnsafeOperationReason int mReason;
/** @hide */
- public UnsafeStateException(@DevicePolicyOperation int operation) {
+ @TestApi
+ public UnsafeStateException(@DevicePolicyOperation int operation,
+ @UnsafeOperationReason int reason) {
super();
-
+ Preconditions.checkArgument(reason == UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
+ "invalid reason %d, must be %d (%s)", reason,
+ UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
+ unsafeOperationReasonToString(UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION));
mOperation = operation;
+ mReason = reason;
}
/** @hide */
@@ -47,6 +60,22 @@
return mOperation;
}
+ /**
+ * Gets the reason the operation is unsafe.
+ *
+ * @return currently, only valid reason is
+ * {@link android.app.admin.DevicePolicyManager#UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION}.
+ */
+ public @UnsafeOperationReason int getReason() {
+ return mReason;
+ }
+
+ /** @hide */
+ @Override
+ public String getMessage() {
+ return DevicePolicyManager.unsafeOperationReasonToString(mReason);
+ }
+
@Override
public int describeContents() {
return 0;
@@ -55,6 +84,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mOperation);
+ dest.writeInt(mReason);
}
@NonNull
@@ -63,7 +93,7 @@
@Override
public UnsafeStateException createFromParcel(Parcel source) {
- return new UnsafeStateException(source.readInt());
+ return new UnsafeStateException(source.readInt(), source.readInt());
}
@Override
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 587e883..742d05c 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -41,6 +41,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
/**
@@ -460,24 +461,40 @@
Set<PathWithRequiredFlags> excludes,
Map<String, Set<PathWithRequiredFlags>> includes)
throws IOException, XmlPullParserException {
+ verifyTopLevelTag(parser, "full-backup-content");
+
+ parseRules(parser, excludes, includes, Optional.empty());
+
+ logParsingResults(excludes, includes);
+ }
+
+ private void verifyTopLevelTag(XmlPullParser parser, String tag)
+ throws XmlPullParserException, IOException {
int event = parser.getEventType(); // START_DOCUMENT
while (event != XmlPullParser.START_TAG) {
event = parser.next();
}
- if (!"full-backup-content".equals(parser.getName())) {
+ if (!tag.equals(parser.getName())) {
throw new XmlPullParserException("Xml file didn't start with correct tag" +
- " (<full-backup-content>). Found \"" + parser.getName() + "\"");
+ " (" + tag + " ). Found \"" + parser.getName() + "\"");
}
if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(TAG_XML_PARSER, "\n");
Log.v(TAG_XML_PARSER, "====================================================");
- Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource.");
+ Log.v(TAG_XML_PARSER, "Found valid " + tag + "; parsing xml resource.");
Log.v(TAG_XML_PARSER, "====================================================");
Log.v(TAG_XML_PARSER, "");
}
+ }
+ private void parseRules(XmlPullParser parser,
+ Set<PathWithRequiredFlags> excludes,
+ Map<String, Set<PathWithRequiredFlags>> includes,
+ Optional<Integer> maybeRequiredFlags)
+ throws IOException, XmlPullParserException {
+ int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
switch (event) {
case XmlPullParser.START_TAG:
@@ -498,13 +515,7 @@
break;
}
- int requiredFlags = 0; // no transport flags are required by default
- if (TAG_INCLUDE.equals(parser.getName())) {
- // requiredFlags are only supported for <include /> tag, for <exclude />
- // we should always leave them as the default = 0
- requiredFlags = getRequiredFlagsFromString(
- parser.getAttributeValue(null, "requireFlags"));
- }
+ int requiredFlags = getRequiredFlagsForRule(parser, maybeRequiredFlags);
// retrieve the include/exclude set we'll be adding this rule to
Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain(
@@ -542,7 +553,7 @@
// Special case for sharedpref files (not dirs) also add ".xml" suffix file.
if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() &&
- !canonicalFile.getCanonicalPath().endsWith(".xml")) {
+ !canonicalFile.getCanonicalPath().endsWith(".xml")) {
final String canonicalXmlPath =
canonicalFile.getCanonicalPath() + ".xml";
activeSet.add(new PathWithRequiredFlags(canonicalXmlPath,
@@ -554,6 +565,10 @@
}
}
}
+ }
+
+ private void logParsingResults(Set<PathWithRequiredFlags> excludes,
+ Map<String, Set<PathWithRequiredFlags>> includes) {
if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(TAG_XML_PARSER, "\n");
Log.v(TAG_XML_PARSER, "Xml resource parsing complete.");
@@ -613,6 +628,24 @@
return flags;
}
+ private int getRequiredFlagsForRule(XmlPullParser parser,
+ Optional<Integer> maybeRequiredFlags) {
+ if (maybeRequiredFlags.isPresent()) {
+ // This is the new config format where required flags are specified for the whole
+ // section, not per rule.
+ return maybeRequiredFlags.get();
+ }
+
+ if (TAG_INCLUDE.equals(parser.getName())) {
+ // In the legacy config, requiredFlags are only supported for <include /> tag,
+ // for <exclude /> we should always leave them as the default = 0.
+ return getRequiredFlagsFromString(
+ parser.getAttributeValue(null, "requireFlags"));
+ }
+
+ return 0;
+ }
+
private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser,
Set<PathWithRequiredFlags> excludes,
Map<String, Set<PathWithRequiredFlags>> includes, String domain)
diff --git a/core/java/android/app/smartspace/SmartspaceAction.java b/core/java/android/app/smartspace/SmartspaceAction.java
index 033cda1..439d851 100644
--- a/core/java/android/app/smartspace/SmartspaceAction.java
+++ b/core/java/android/app/smartspace/SmartspaceAction.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Intent;
@@ -171,6 +172,7 @@
/**
* Returns the extra bundle for this object.
*/
+ @SuppressLint("NullableCollection")
public @Nullable Bundle getExtras() {
return mExtras;
}
@@ -334,7 +336,7 @@
* Sets the extra.
*/
@NonNull
- public Builder setExtras(@Nullable Bundle extras) {
+ public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
mExtras = extras;
return this;
}
diff --git a/core/java/android/app/smartspace/SmartspaceConfig.java b/core/java/android/app/smartspace/SmartspaceConfig.java
index 1f9cbb5..07d7bf0 100644
--- a/core/java/android/app/smartspace/SmartspaceConfig.java
+++ b/core/java/android/app/smartspace/SmartspaceConfig.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Bundle;
@@ -98,6 +99,7 @@
}
@Nullable
+ @SuppressLint("NullableCollection")
public Bundle getExtras() {
return mExtras;
}
@@ -186,7 +188,7 @@
* Used to send a bundle containing extras for the {@link SmartspaceConfig}.
*/
@NonNull
- public Builder setExtras(@NonNull Bundle extras) {
+ public Builder setExtras(@SuppressLint("NullableCollection") @NonNull Bundle extras) {
this.mExtras = extras;
return this;
}
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 1ddfe0d..1d5dc1d 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -28,7 +28,6 @@
import android.net.ConnectivityManager;
import android.net.DataUsageRequest;
import android.net.INetworkStatsService;
-import android.net.NetworkIdentity;
import android.net.NetworkStack;
import android.net.NetworkTemplate;
import android.net.netstats.provider.INetworkStatsProviderCallback;
@@ -47,6 +46,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.NetworkIdentityUtils;
import java.util.Objects;
@@ -628,7 +628,7 @@
default:
throw new IllegalArgumentException("Cannot create template for network type "
+ networkType + ", subscriberId '"
- + NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId) + "'.");
}
return template;
}
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
index 397326c..cec6580 100644
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ b/core/java/android/bluetooth/le/AdvertiseData.java
@@ -44,7 +44,7 @@
@Nullable
private final List<ParcelUuid> mServiceUuids;
- @Nullable
+ @NonNull
private final List<ParcelUuid> mServiceSolicitationUuids;
private final SparseArray<byte[]> mManufacturerSpecificData;
@@ -77,7 +77,7 @@
/**
* Returns a list of service solicitation UUIDs within the advertisement that we invite to connect.
*/
- @Nullable
+ @NonNull
public List<ParcelUuid> getServiceSolicitationUuids() {
return mServiceSolicitationUuids;
}
@@ -221,7 +221,7 @@
public static final class Builder {
@Nullable
private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
- @Nullable
+ @NonNull
private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>();
private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 083ce96..102c98f 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -55,6 +55,8 @@
genBuilder = false)
public final class AssociationRequest implements Parcelable {
+ private static final String LOG_TAG = AssociationRequest.class.getSimpleName();
+
/**
* Device profile: watch.
*
@@ -115,13 +117,6 @@
mDeviceProfilePrivilegesDescription = desc;
}
- private void onConstructed() {
- if (mDeviceProfile != null
- && !Objects.equals(mDeviceProfile, DEVICE_PROFILE_WATCH)) {
- throw new IllegalArgumentException("Invalid device profile: " + mDeviceProfile);
- }
- }
-
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isSingleDevice() {
@@ -252,7 +247,7 @@
this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
- onConstructed();
+ // onConstructed(); // You can define this method to get a callback
}
/**
@@ -386,7 +381,7 @@
this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
- onConstructed();
+ // onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
@@ -404,10 +399,10 @@
};
@DataClass.Generated(
- time = 1610132130920L,
+ time = 1611692924843L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
+ inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index 80a7b16..0581ed5 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.os.Parcel;
@@ -62,6 +63,7 @@
* List of allowlisted activities.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
/**
@@ -73,6 +75,7 @@
* The disabled Activities of the package. key is component name string, value is when they
* will be enabled.
*/
+ @SuppressLint("NullableCollection")
@Nullable
public ArrayMap<String, Long> disabledActivities;
diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java
index ef49e02..c296bb5 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.os.Parcel;
@@ -73,6 +74,7 @@
* for all acitivites in the package).
*/
@Nullable
+ @SuppressLint("NullableCollection")
public final ArraySet<ComponentName> whitelistedComponents;
/**
@@ -96,6 +98,7 @@
*/
public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs,
int textChangeFlushingFrequencyMs, int logHistorySize,
+ @SuppressLint("NullableCollection")
@Nullable ArraySet<ComponentName> whitelistedComponents) {
this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs,
textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4dc41b2..2a402b2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -75,6 +75,7 @@
import android.view.DisplayAdjustments;
import android.view.View;
import android.view.ViewDebug;
+import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
import android.view.autofill.AutofillManager.AutofillClient;
@@ -3551,6 +3552,7 @@
//@hide: NETWORK_SCORE_SERVICE,
USAGE_STATS_SERVICE,
MEDIA_SESSION_SERVICE,
+ MEDIA_COMMUNICATION_SERVICE,
BATTERY_SERVICE,
JOB_SCHEDULER_SERVICE,
//@hide: PERSISTENT_DATA_BLOCK_SERVICE,
@@ -5451,6 +5453,15 @@
public static final String GAME_SERVICE = "game";
/**
+ * Use with {@link #getSystemService(String)} to access domain verification service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ @SystemApi
+ public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
@@ -6101,18 +6112,19 @@
*
* // WindowManager.LayoutParams initialization
* ...
+ * // The types used in addView and createWindowContext must match.
* mParams.type = TYPE_APPLICATION_OVERLAY;
* ...
*
- * mWindowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+ * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
* </pre>
*
* <p>
- * This context's configuration and resources are adjusted to a display area where the windows
- * with provided type will be added. <b>Note that all windows associated with the same context
- * will have an affinity and can only be moved together between different displays or areas on a
- * display.</b> If there is a need to add different window types, or non-associated windows,
- * separate Contexts should be used.
+ * This context's configuration and resources are adjusted to an area of the display where
+ * the windows with provided type will be added. <b>Note that all windows associated with the
+ * same context will have an affinity and can only be moved together between different displays
+ * or areas on a display.</b> If there is a need to add different window types, or
+ * non-associated windows, separate Contexts should be used.
* </p>
* <p>
* Creating a window context is an expensive operation. Misuse of this API may lead to a huge
@@ -6120,7 +6132,43 @@
* An approach is to create one window context with specific window type and display and
* use it everywhere it's needed.
* </p>
+ * <p>
+ * After {@link Build.VERSION_CODES#S}, window context provides the capability to receive
+ * configuration changes for existing token by overriding the
+ * {@link android.view.WindowManager.LayoutParams#token token} of the
+ * {@link android.view.WindowManager.LayoutParams} passed in
+ * {@link WindowManager#addView(View, LayoutParams)}. This is useful when an application needs
+ * to attach its window to an existing activity for window token sharing use-case.
+ * </p>
+ * <p>
+ * Note that the window context in {@link Build.VERSION_CODES#R} didn't have this
+ * capability. This is a no-op for the window context in {@link Build.VERSION_CODES#R}.
+ * </p>
+ * Below is sample code to <b>attach an existing token to a window context:</b>
+ * <pre class="prettyprint">
+ * final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
+ * final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+ * final Context windowContext = anyContext.createWindowContext(primaryDisplay,
+ * TYPE_APPLICATION, null);
*
+ * // Get an existing token.
+ * final IBinder existingToken = activity.getWindow().getAttributes().token;
+ *
+ * // The types used in addView() and createWindowContext() must match.
+ * final WindowManager.LayoutParams params = new WindowManager.LayoutParams(TYPE_APPLICATION);
+ * params.token = existingToken;
+ *
+ * // After WindowManager#addView(), the server side will extract the provided token from
+ * // LayoutParams#token (existingToken in the sample code), and switch to propagate
+ * // configuration changes from the node associated with the provided token.
+ * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+ * </pre>
+ * <p>
+ * Note that using {@link android.app.Application} or {@link android.app.Service} context for
+ * UI-related queries may result in layout or continuity issues on devices with variable screen
+ * sizes (e.g. foldables) or in multi-window modes, since these non-UI contexts may not reflect
+ * the {@link Configuration} changes for the visual container.
+ * </p>
* @param type Window type in {@link WindowManager.LayoutParams}
* @param options A bundle used to pass window-related options
* @return A {@link Context} that can be used to create
@@ -6132,9 +6180,7 @@
* @see #LAYOUT_INFLATER_SERVICE
* @see #WALLPAPER_SERVICE
* @throws UnsupportedOperationException if this {@link Context} does not attach to a display,
- * such as {@link android.app.Application Application} or {@link android.app.Service Service},
- * or the current number of window contexts without adding any view by
- * {@link WindowManager#addView} <b>exceeds five</b>.
+ * such as {@link android.app.Application Application} or {@link android.app.Service Service}.
*/
@UiContext
@NonNull
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1752b48..30b2404 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -37,6 +37,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
@@ -2841,10 +2842,28 @@
* </p>
*
* @hide
+ * @deprecated Superseded by domain verification APIs. See {@link DomainVerificationManager}.
+ */
+ @Deprecated
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION =
+ "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+
+
+ /**
+ * Broadcast Action: Sent to the system domain verification agent when an app's domains need
+ * to be verified. The data contains the domains hosts to be verified against.
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ * </p>
+ *
+ * @hide
*/
@SystemApi
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+ public static final String ACTION_DOMAINS_NEED_VERIFICATION =
+ "android.intent.action.DOMAINS_NEED_VERIFICATION";
/**
* Broadcast Action: Resources for a set of packages (which were
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index e32068f..6ec1169 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -38,6 +38,8 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForBoolean;
import com.android.server.SystemConfig;
import java.lang.annotation.Retention;
@@ -56,6 +58,8 @@
* <application> tag.
*/
public class ApplicationInfo extends PackageItemInfo implements Parcelable {
+ private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
+
/**
* Default task affinity of all activities in this application. See
* {@link ActivityInfo#taskAffinity} for more information. This comes
@@ -1336,6 +1340,51 @@
private @GwpAsanMode int gwpAsanMode;
/**
+ * Default (unspecified) setting of Memtag.
+ */
+ public static final int MEMTAG_DEFAULT = -1;
+
+ /**
+ * Do not enable Memtag in this application or process.
+ */
+ public static final int MEMTAG_OFF = 0;
+
+ /**
+ * Enable Memtag in Async mode in this application or process.
+ */
+ public static final int MEMTAG_ASYNC = 1;
+
+ /**
+ * Enable Memtag in Sync mode in this application or process.
+ */
+ public static final int MEMTAG_SYNC = 2;
+
+ /**
+ * These constants need to match the values of memtagMode in application manifest.
+ * @hide
+ */
+ @IntDef(prefix = {"MEMTAG_"}, value = {
+ MEMTAG_DEFAULT,
+ MEMTAG_OFF,
+ MEMTAG_ASYNC,
+ MEMTAG_SYNC,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MemtagMode {}
+
+ /**
+ * Indicates if the application has requested Memtag to be enabled, disabled, or left
+ * unspecified. Processes can override this setting.
+ */
+ private @MemtagMode int memtagMode;
+
+ /**
+ * Enable automatic zero-initialization of native heap memory allocations.
+ */
+ @Nullable
+ private Boolean nativeHeapZeroInit;
+
+ /**
* Represents the default policy. The actual policy used will depend on other properties of
* the application, e.g. the target SDK version.
* @hide
@@ -1479,6 +1528,12 @@
if (gwpAsanMode != GWP_ASAN_DEFAULT) {
pw.println(prefix + "gwpAsanMode=" + gwpAsanMode);
}
+ if (memtagMode != MEMTAG_DEFAULT) {
+ pw.println(prefix + "memtagMode=" + memtagMode);
+ }
+ if (nativeHeapZeroInit != null) {
+ pw.println(prefix + "nativeHeapZeroInit=" + nativeHeapZeroInit);
+ }
}
super.dumpBack(pw, prefix);
}
@@ -1580,6 +1635,12 @@
if (gwpAsanMode != GWP_ASAN_DEFAULT) {
proto.write(ApplicationInfoProto.Detail.ENABLE_GWP_ASAN, gwpAsanMode);
}
+ if (memtagMode != MEMTAG_DEFAULT) {
+ proto.write(ApplicationInfoProto.Detail.ENABLE_MEMTAG, memtagMode);
+ }
+ if (nativeHeapZeroInit != null) {
+ proto.write(ApplicationInfoProto.Detail.NATIVE_HEAP_ZERO_INIT, nativeHeapZeroInit);
+ }
proto.end(detailToken);
}
proto.end(token);
@@ -1690,6 +1751,8 @@
hiddenUntilInstalled = orig.hiddenUntilInstalled;
zygotePreloadName = orig.zygotePreloadName;
gwpAsanMode = orig.gwpAsanMode;
+ memtagMode = orig.memtagMode;
+ nativeHeapZeroInit = orig.nativeHeapZeroInit;
}
public String toString() {
@@ -1774,6 +1837,8 @@
dest.writeInt(hiddenUntilInstalled ? 1 : 0);
dest.writeString8(zygotePreloadName);
dest.writeInt(gwpAsanMode);
+ dest.writeInt(memtagMode);
+ sForBoolean.parcel(nativeHeapZeroInit, dest, parcelableFlags);
}
public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1855,6 +1920,8 @@
hiddenUntilInstalled = source.readInt() != 0;
zygotePreloadName = source.readString8();
gwpAsanMode = source.readInt();
+ memtagMode = source.readInt();
+ nativeHeapZeroInit = sForBoolean.unparcel(source);
}
/**
@@ -2237,6 +2304,8 @@
/** {@hide} */ public void setBaseResourcePath(String baseResourcePath) { publicSourceDir = baseResourcePath; }
/** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; }
/** {@hide} */ public void setGwpAsanMode(@GwpAsanMode int value) { gwpAsanMode = value; }
+ /** {@hide} */ public void setMemtagMode(@MemtagMode int value) { memtagMode = value; }
+ /** {@hide} */ public void setNativeHeapZeroInit(@Nullable Boolean value) { nativeHeapZeroInit = value; }
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -2250,4 +2319,8 @@
/** {@hide} */ public String[] getSplitResourcePaths() { return splitPublicSourceDirs; }
@GwpAsanMode
public int getGwpAsanMode() { return gwpAsanMode; }
+ @MemtagMode
+ public int getMemtagMode() { return memtagMode; }
+ @Nullable
+ public Boolean isNativeHeapZeroInit() { return nativeHeapZeroInit; }
}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
similarity index 70%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to core/java/android/content/pm/IOnChecksumsReadyListener.aidl
index 19b20f2..7963ce1 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
@@ -14,7 +14,14 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package android.content.pm;
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+import android.content.pm.ApkChecksum;
+
+/**
+ * Listener that gets notified when checksums are available.
+ * {@hide}
+ */
+oneway interface IOnChecksumsReadyListener {
+ void onChecksumsReady(in List<ApkChecksum> checksums);
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 34d1003..a46876e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -27,6 +27,7 @@
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.InstallSourceInfo;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
@@ -627,9 +628,13 @@
void verifyPendingInstall(int id, int verificationCode);
void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
+ /** @deprecated */
void verifyIntentFilter(int id, int verificationCode, in List<String> failedDomains);
+ /** @deprecated */
int getIntentVerificationStatus(String packageName, int userId);
+ /** @deprecated */
boolean updateIntentVerificationStatus(String packageName, int status, int userId);
+ /** @deprecated */
ParceledListSlice getIntentFilterVerifications(String packageName);
ParceledListSlice getAllIntentFilters(String packageName);
@@ -750,7 +755,7 @@
void notifyPackagesReplacedReceived(in String[] packages);
- void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+ void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
//------------------------------------------------------------------------
//
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 6292575..0c0e402 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -450,6 +450,7 @@
FLAG_MATCH_PINNED,
FLAG_MATCH_MANIFEST,
FLAG_MATCH_CACHED,
+ FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
FLAG_GET_KEY_FIELDS_ONLY,
FLAG_GET_PERSONS_DATA,
})
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9f79dd0..b95b991b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -47,6 +47,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.dex.ArtManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -54,6 +55,7 @@
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
@@ -69,6 +71,12 @@
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.permission.PermissionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.gba.GbaService;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.SipDelegateManager;
import android.util.AndroidException;
import android.util.Log;
@@ -86,6 +94,7 @@
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
+import java.util.UUID;
/**
* Class for retrieving various kinds of information related to the application
@@ -2194,8 +2203,10 @@
* {@link PackageManager#verifyIntentFilter} to indicate that the calling
* IntentFilter Verifier confirms that the IntentFilter is verified.
*
+ * @deprecated Use {@link DomainVerificationManager} APIs.
* @hide
*/
+ @Deprecated
@SystemApi
public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1;
@@ -2204,16 +2215,20 @@
* {@link PackageManager#verifyIntentFilter} to indicate that the calling
* IntentFilter Verifier confirms that the IntentFilter is NOT verified.
*
+ * @deprecated Use {@link DomainVerificationManager} APIs.
* @hide
*/
+ @Deprecated
@SystemApi
public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1;
/**
* Internal status code to indicate that an IntentFilter verification result is not specified.
*
+ * @deprecated Use {@link DomainVerificationManager} APIs.
* @hide
*/
+ @Deprecated
@SystemApi
public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0;
@@ -2223,8 +2238,10 @@
* will always be prompted the Intent Disambiguation Dialog if there are two
* or more Intent resolved for the IntentFilter's domain(s).
*
+ * @deprecated Use {@link DomainVerificationManager} APIs.
* @hide
*/
+ @Deprecated
@SystemApi
public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1;
@@ -2235,8 +2252,10 @@
* or more resolution of the Intent. The default App for the domain(s)
* specified in the IntentFilter will also ALWAYS be used.
*
+ * @deprecated Use {@link DomainVerificationManager} APIs.
* @hide
*/
+ @Deprecated
@SystemApi
public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2;
@@ -2247,8 +2266,10 @@
* Intent resolved. The default App for the domain(s) specified in the
* IntentFilter will also NEVER be presented to the User.
*
+ * @deprecated Use {@link DomainVerificationManager} APIs.
* @hide
*/
+ @Deprecated
@SystemApi
public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3;
@@ -2261,8 +2282,10 @@
* more than one candidate app, then a disambiguation is *always* presented
* even if there is another candidate app with the 'always' state.
*
+ * @deprecated Use {@link DomainVerificationManager} APIs.
* @hide
*/
+ @Deprecated
@SystemApi
public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4;
@@ -2935,6 +2958,37 @@
public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * supports a single IMS registration as defined by carrier networks in the IMS service
+ * implementation using the {@link ImsService} API, {@link GbaService} API, and IRadio 1.6 HAL.
+ * <p>
+ * When set, the device must fully support the following APIs for an application to implement
+ * IMS single registration:
+ * <ul>
+ * <li> Updating RCS provisioning status using the {@link ProvisioningManager} API to supply an
+ * RCC.14 defined XML and notify IMS applications of Auto Configuration Server (ACS) or
+ * proprietary server provisioning updates.</li>
+ * <li>Opening a delegate in the device IMS service to forward SIP traffic to the carrier's
+ * network using the {@link SipDelegateManager} API</li>
+ * <li>Listening to EPS dedicated bearer establishment via the
+ * {@link ConnectivityManager#registerQosCallback}
+ * API to indicate to the application when to start/stop media traffic.</li>
+ * <li>Implementing Generic Bootstrapping Architecture (GBA) and providing the associated
+ * authentication keys to applications
+ * requesting this information via the {@link TelephonyManager#bootstrapAuthenticationRequest}
+ * API</li>
+ * <li>Implementing RCS User Capability Exchange using the {@link RcsUceAdapter} API</li>
+ * </ul>
+ * <p>
+ * This feature should only be defined if {@link #FEATURE_TELEPHONY_IMS} is also defined.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION =
+ "android.hardware.telephony.ims.singlereg";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device is capable of communicating with
* other devices via ultra wideband.
@@ -3705,8 +3759,10 @@
* Passed to an intent filter verifier and is used to call back to
* {@link #verifyIntentFilter}
*
+ * @deprecated Use DomainVerificationManager APIs.
* @hide
*/
+ @Deprecated
public static final String EXTRA_INTENT_FILTER_VERIFICATION_ID
= "android.content.pm.extra.INTENT_FILTER_VERIFICATION_ID";
@@ -3716,8 +3772,10 @@
*
* Usually this is "https"
*
+ * @deprecated Use DomainVerificationManager APIs.
* @hide
*/
+ @Deprecated
public static final String EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME
= "android.content.pm.extra.INTENT_FILTER_VERIFICATION_URI_SCHEME";
@@ -3728,8 +3786,10 @@
*
* This is a space delimited list of hosts.
*
+ * @deprecated Use DomainVerificationManager APIs.
* @hide
*/
+ @Deprecated
public static final String EXTRA_INTENT_FILTER_VERIFICATION_HOSTS
= "android.content.pm.extra.INTENT_FILTER_VERIFICATION_HOSTS";
@@ -3739,8 +3799,10 @@
* from the hosts. Each host response will need to include the package name of APK containing
* the intent filter.
*
+ * @deprecated Use DomainVerificationManager APIs.
* @hide
*/
+ @Deprecated
public static final String EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME
= "android.content.pm.extra.INTENT_FILTER_VERIFICATION_PACKAGE_NAME";
@@ -3796,13 +3858,6 @@
public static final String EXTRA_FAILURE_EXISTING_PERMISSION
= "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
- /**
- * Extra field name for the ID of a package pending verification. Passed to
- * a package verifier and is used to call back to
- * @see #requestChecksums
- */
- public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
-
/**
* Permission flag: The permission is set in its current state
* by the user and apps can still request it at runtime.
@@ -5514,7 +5569,7 @@
*
* @hide
*/
- @SuppressWarnings("HiddenAbstractMethod")
+ @SuppressWarnings({"HiddenAbstractMethod", "NullableCollection"})
@TestApi
public abstract @Nullable String[] getNamesForUids(int[] uids);
@@ -6918,8 +6973,10 @@
* @throws SecurityException if the caller does not have the
* INTENT_FILTER_VERIFICATION_AGENT permission.
*
+ * @deprecated Use {@link DomainVerificationManager} APIs.
* @hide
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT)
@@ -6944,8 +7001,10 @@
* {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER} or
* {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED}
*
+ * @deprecated Use {@link DomainVerificationManager} APIs.
* @hide
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
@@ -6970,8 +7029,18 @@
*
* @return true if the status has been set. False otherwise.
*
+ * @deprecated This API represents a very dangerous behavior where Settings or a system app with
+ * the right permissions can force an application to be verified for all of its declared
+ * domains. This has been removed to prevent unintended usage, and no longer does anything,
+ * always returning false. If a caller truly wishes to grant <i></i>every</i> declared web
+ * domain to an application, use
+ * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
+ * passing in all of the domains returned inside
+ * {@link DomainVerificationManager#getDomainVerificationUserSelection(String)}.
+ *
* @hide
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@SystemApi
@RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
@@ -6988,8 +7057,10 @@
*
* @return a list of IntentFilterVerificationInfo for a specific package.
*
+ * @deprecated Use {@link DomainVerificationManager} instead.
* @hide
*/
+ @Deprecated
@SuppressWarnings("HiddenAbstractMethod")
@NonNull
@SystemApi
@@ -8631,9 +8702,20 @@
*/
public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
+ /** Listener that gets notified when checksums are available. */
+ @FunctionalInterface
+ public interface OnChecksumsReadyListener {
+ /**
+ * Called when the checksums are available.
+ *
+ * @param checksums array of checksums.
+ */
+ void onChecksumsReady(@NonNull List<ApkChecksum> checksums);
+ }
+
/**
* Requesting the checksums for APKs within a package.
- * The checksums will be returned asynchronously via statusReceiver.
+ * The checksums will be returned asynchronously via onChecksumsReadyListener.
*
* By default returns all readily available checksums:
* - enforced by platform,
@@ -8652,15 +8734,14 @@
* {@link #TRUST_ALL} will return checksums from any installer,
* {@link #TRUST_NONE} disables optimized installer-enforced checksums,
* otherwise the list has to be non-empty list of certificates.
- * @param statusReceiver called once when the results are available as
- * {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
+ * @param onChecksumsReadyListener called once when the results are available.
* @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
* @throws IllegalArgumentException if the list of trusted installer certificates is empty.
* @throws NameNotFoundException if a package with the given name cannot be found on the system.
*/
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
- @NonNull IntentSender statusReceiver)
+ @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 9925871..5cc74c0 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -77,8 +77,6 @@
public boolean virtualPreload;
public int enabled;
public String lastDisableAppCaller;
- public int domainVerificationStatus;
- public int appLinkGeneration;
public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
public int installReason;
public @PackageManager.UninstallReason int uninstallReason;
@@ -100,8 +98,6 @@
hidden = false;
suspended = false;
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
- domainVerificationStatus =
- PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
installReason = PackageManager.INSTALL_REASON_UNKNOWN;
uninstallReason = PackageManager.UNINSTALL_REASON_UNKNOWN;
}
@@ -120,8 +116,6 @@
virtualPreload = o.virtualPreload;
enabled = o.enabled;
lastDisableAppCaller = o.lastDisableAppCaller;
- domainVerificationStatus = o.domainVerificationStatus;
- appLinkGeneration = o.appLinkGeneration;
categoryHint = o.categoryHint;
installReason = o.installReason;
uninstallReason = o.uninstallReason;
@@ -416,12 +410,6 @@
&& !lastDisableAppCaller.equals(oldState.lastDisableAppCaller))) {
return false;
}
- if (domainVerificationStatus != oldState.domainVerificationStatus) {
- return false;
- }
- if (appLinkGeneration != oldState.appLinkGeneration) {
- return false;
- }
if (categoryHint != oldState.categoryHint) {
return false;
}
@@ -481,8 +469,6 @@
hashCode = 31 * hashCode + Boolean.hashCode(virtualPreload);
hashCode = 31 * hashCode + enabled;
hashCode = 31 * hashCode + Objects.hashCode(lastDisableAppCaller);
- hashCode = 31 * hashCode + domainVerificationStatus;
- hashCode = 31 * hashCode + appLinkGeneration;
hashCode = 31 * hashCode + categoryHint;
hashCode = 31 * hashCode + installReason;
hashCode = 31 * hashCode + uninstallReason;
diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java
index d45ff98..3dd5ee1 100644
--- a/core/java/android/content/pm/ProcessInfo.java
+++ b/core/java/android/content/pm/ProcessInfo.java
@@ -53,16 +53,30 @@
*/
public @ApplicationInfo.GwpAsanMode int gwpAsanMode;
+ /**
+ * Indicates if the process has requested Memtag to be enabled (in sync or async mode),
+ * disabled, or left unspecified.
+ */
+ public @ApplicationInfo.MemtagMode int memtagMode;
+
+ /**
+ * Enable automatic zero-initialization of native heap memory allocations.
+ */
+ @Nullable
+ public Boolean nativeHeapZeroInit;
+
@Deprecated
public ProcessInfo(@NonNull ProcessInfo orig) {
this.name = orig.name;
this.deniedPermissions = orig.deniedPermissions;
this.gwpAsanMode = orig.gwpAsanMode;
+ this.memtagMode = orig.memtagMode;
+ this.nativeHeapZeroInit = orig.nativeHeapZeroInit;
}
- // Code below generated by codegen v1.0.15.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -84,12 +98,19 @@
* If non-null, these are permissions that are not allowed in this process.
* @param gwpAsanMode
* Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified.
+ * @param memtagMode
+ * Indicates if the process has requested Memtag to be enabled (in sync or async mode),
+ * disabled, or left unspecified.
+ * @param nativeHeapZeroInit
+ * Enable automatic zero-initialization of native heap memory allocations.
*/
@DataClass.Generated.Member
public ProcessInfo(
@NonNull String name,
@Nullable ArraySet<String> deniedPermissions,
- @ApplicationInfo.GwpAsanMode int gwpAsanMode) {
+ @ApplicationInfo.GwpAsanMode int gwpAsanMode,
+ @ApplicationInfo.MemtagMode int memtagMode,
+ @Nullable Boolean nativeHeapZeroInit) {
this.name = name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
@@ -97,6 +118,10 @@
this.gwpAsanMode = gwpAsanMode;
com.android.internal.util.AnnotationValidations.validate(
ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+ this.memtagMode = memtagMode;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.MemtagMode.class, null, memtagMode);
+ this.nativeHeapZeroInit = nativeHeapZeroInit;
// onConstructed(); // You can define this method to get a callback
}
@@ -120,10 +145,13 @@
byte flg = 0;
if (deniedPermissions != null) flg |= 0x2;
+ if (nativeHeapZeroInit != null) flg |= 0x10;
dest.writeByte(flg);
dest.writeString(name);
sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
dest.writeInt(gwpAsanMode);
+ dest.writeInt(memtagMode);
+ if (nativeHeapZeroInit != null) dest.writeBoolean(nativeHeapZeroInit);
}
@Override
@@ -141,6 +169,8 @@
String _name = in.readString();
ArraySet<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
int _gwpAsanMode = in.readInt();
+ int _memtagMode = in.readInt();
+ Boolean _nativeHeapZeroInit = (flg & 0x10) == 0 ? null : (Boolean) in.readBoolean();
this.name = _name;
com.android.internal.util.AnnotationValidations.validate(
@@ -149,6 +179,10 @@
this.gwpAsanMode = _gwpAsanMode;
com.android.internal.util.AnnotationValidations.validate(
ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+ this.memtagMode = _memtagMode;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.MemtagMode.class, null, memtagMode);
+ this.nativeHeapZeroInit = _nativeHeapZeroInit;
// onConstructed(); // You can define this method to get a callback
}
@@ -168,10 +202,10 @@
};
@DataClass.Generated(
- time = 1584555730519L,
- codegenVersion = "1.0.15",
+ time = 1611614699049L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/ProcessInfo.java",
- inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+ inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\npublic @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\npublic @android.annotation.Nullable java.lang.Boolean nativeHeapZeroInit\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 8147599..7a01392 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -252,6 +252,10 @@
ParsingPackage setGwpAsanMode(int gwpAsanMode);
+ ParsingPackage setMemtagMode(int memtagMode);
+
+ ParsingPackage setNativeHeapZeroInit(@Nullable Boolean nativeHeapZeroInit);
+
ParsingPackage setCrossProfile(boolean crossProfile);
ParsingPackage setFullBackupContent(int fullBackupContent);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 51ec297..c1a93d8 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -380,6 +380,11 @@
private int autoRevokePermissions;
protected int gwpAsanMode;
+ protected int memtagMode;
+
+ @Nullable
+ @DataClass.ParcelWith(ForBoolean.class)
+ private Boolean nativeHeapZeroInit;
// TODO(chiuwinson): Non-null
@Nullable
@@ -1058,6 +1063,8 @@
appInfo.volumeUuid = volumeUuid;
appInfo.zygotePreloadName = zygotePreloadName;
appInfo.setGwpAsanMode(gwpAsanMode);
+ appInfo.setMemtagMode(memtagMode);
+ appInfo.setNativeHeapZeroInit(nativeHeapZeroInit);
appInfo.setBaseCodePath(mBaseApkPath);
appInfo.setBaseResourcePath(mBaseApkPath);
appInfo.setCodePath(mPath);
@@ -1190,6 +1197,8 @@
dest.writeSparseIntArray(this.minExtensionVersions);
dest.writeLong(this.mBooleans);
dest.writeMap(this.mProperties);
+ dest.writeInt(this.memtagMode);
+ sForBoolean.parcel(this.nativeHeapZeroInit, dest, flags);
}
public ParsingPackageImpl(Parcel in) {
@@ -1310,6 +1319,8 @@
this.minExtensionVersions = in.readSparseIntArray();
this.mBooleans = in.readLong();
this.mProperties = in.createTypedArrayMap(Property.CREATOR);
+ this.memtagMode = in.readInt();
+ this.nativeHeapZeroInit = sForBoolean.unparcel(in);
assignDerivedFields();
}
@@ -2062,6 +2073,17 @@
}
@Override
+ public int getMemtagMode() {
+ return memtagMode;
+ }
+
+ @Nullable
+ @Override
+ public Boolean isNativeHeapZeroInit() {
+ return nativeHeapZeroInit;
+ }
+
+ @Override
public boolean isPartiallyDirectBootAware() {
return getBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE);
}
@@ -2493,6 +2515,18 @@
}
@Override
+ public ParsingPackageImpl setMemtagMode(int value) {
+ memtagMode = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setNativeHeapZeroInit(@Nullable Boolean value) {
+ nativeHeapZeroInit = value;
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) {
return setBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE, value);
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index a102e82..ff4cebd 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -874,6 +874,19 @@
*/
int getGwpAsanMode();
+ /**
+ * @see ApplicationInfo#memtagMode
+ * @see R.styleable#AndroidManifest_memtagMode
+ */
+ int getMemtagMode();
+
+ /**
+ * @see ApplicationInfo#nativeHeapZeroInit
+ * @see R.styleable#AndroidManifest_nativeHeapZeroInit
+ */
+ @Nullable
+ Boolean isNativeHeapZeroInit();
+
// TODO(b/135203078): Hide and enforce going through PackageInfoUtils
ApplicationInfo toAppInfoWithoutState();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 8fbf287..66bdb9b 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1984,6 +1984,11 @@
}
pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
+ pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
+ if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInit)) {
+ pkg.setNativeHeapZeroInit(sa.getBoolean(
+ R.styleable.AndroidManifestApplication_nativeHeapZeroInit, false));
+ }
} finally {
sa.recycle();
}
@@ -2493,6 +2498,10 @@
/**
* Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
+ *
+ * This is distinct from any of the functionality of app links domain verification, and cannot
+ * be converted to remain backwards compatible. It's possible the presence of this flag does
+ * not indicate a valid package for domain verification.
*/
private static boolean hasDomainURLs(ParsingPackage pkg) {
final List<ParsedActivity> activities = pkg.getActivities();
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index cbd2c55..9012b5ce 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -108,17 +108,14 @@
permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
- if (permission.getProtectionFlags() != 0) {
- if ((permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
- && (permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
- == 0
- && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- != PermissionInfo.PROTECTION_SIGNATURE
- && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- != PermissionInfo.PROTECTION_INTERNAL) {
- return input.error("<permission> protectionLevel specifies a non-instant flag "
- + "but is not based on signature or internal type");
- }
+ final int otherProtectionFlags = permission.getProtectionFlags()
+ & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
+ | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY);
+ if (otherProtectionFlags != 0
+ && permission.getProtection() != PermissionInfo.PROTECTION_SIGNATURE
+ && permission.getProtection() != PermissionInfo.PROTECTION_INTERNAL) {
+ return input.error("<permission> protectionLevel specifies a non-instant, non-appop,"
+ + " non-runtimeOnly flag but is not based on signature or internal type");
}
return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index e0ae81b..89fef9d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
@@ -41,7 +42,10 @@
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
protected Set<String> deniedPermissions = emptySet();
- protected int gwpAsanMode = -1;
+ protected int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
+ protected int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
+ @Nullable
+ protected Boolean nativeHeapZeroInit = null;
public ParsedProcess() {
}
@@ -57,7 +61,7 @@
- // Code below generated by codegen v1.0.15.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -74,7 +78,9 @@
public ParsedProcess(
@NonNull String name,
@NonNull Set<String> deniedPermissions,
- int gwpAsanMode) {
+ int gwpAsanMode,
+ int memtagMode,
+ @Nullable Boolean nativeHeapZeroInit) {
this.name = name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
@@ -82,6 +88,8 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
this.gwpAsanMode = gwpAsanMode;
+ this.memtagMode = memtagMode;
+ this.nativeHeapZeroInit = nativeHeapZeroInit;
// onConstructed(); // You can define this method to get a callback
}
@@ -102,6 +110,16 @@
}
@DataClass.Generated.Member
+ public int getMemtagMode() {
+ return memtagMode;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable Boolean getNativeHeapZeroInit() {
+ return nativeHeapZeroInit;
+ }
+
+ @DataClass.Generated.Member
static Parcelling<Set<String>> sParcellingForDeniedPermissions =
Parcelling.Cache.get(
Parcelling.BuiltIn.ForInternedStringSet.class);
@@ -118,9 +136,14 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
+ byte flg = 0;
+ if (nativeHeapZeroInit != null) flg |= 0x10;
+ dest.writeByte(flg);
dest.writeString(name);
sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
dest.writeInt(gwpAsanMode);
+ dest.writeInt(memtagMode);
+ if (nativeHeapZeroInit != null) dest.writeBoolean(nativeHeapZeroInit);
}
@Override
@@ -134,9 +157,12 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
+ byte flg = in.readByte();
String _name = in.readString();
Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
int _gwpAsanMode = in.readInt();
+ int _memtagMode = in.readInt();
+ Boolean _nativeHeapZeroInit = (flg & 0x10) == 0 ? null : (Boolean) in.readBoolean();
this.name = _name;
com.android.internal.util.AnnotationValidations.validate(
@@ -145,6 +171,8 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
this.gwpAsanMode = _gwpAsanMode;
+ this.memtagMode = _memtagMode;
+ this.nativeHeapZeroInit = _nativeHeapZeroInit;
// onConstructed(); // You can define this method to get a callback
}
@@ -164,10 +192,10 @@
};
@DataClass.Generated(
- time = 1584557524776L,
- codegenVersion = "1.0.15",
+ time = 1611615591258L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
- inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected int gwpAsanMode\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+ inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected int gwpAsanMode\nprotected int memtagMode\nprotected @android.annotation.Nullable java.lang.Boolean nativeHeapZeroInit\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index 9bff719..2579774 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -103,6 +103,11 @@
}
proc.gwpAsanMode = sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1);
+ proc.memtagMode = sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1);
+ if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInit)) {
+ proc.nativeHeapZeroInit =
+ sa.getBoolean(R.styleable.AndroidManifestProcess_nativeHeapZeroInit, false);
+ }
} finally {
sa.recycle();
}
diff --git a/core/java/android/service/attestation/ImpressionToken.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl
similarity index 88%
copy from core/java/android/service/attestation/ImpressionToken.aidl
copy to core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl
index 284a4ba..c143cc5 100644
--- a/core/java/android/service/attestation/ImpressionToken.aidl
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.content.pm.verify.domain;
-parcelable ImpressionToken;
\ No newline at end of file
+parcelable DomainVerificationInfo;
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
new file mode 100644
index 0000000..7afbe1f
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.pm.PackageManager;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Contains the state of all domains for a given package on device. Used by the domain verification
+ * agent to determine the domains declared by a package that need to be verified by comparing
+ * against the digital asset links response from the server hosting that domain.
+ * <p>
+ * These values for each domain can be modified through
+ * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
+ *
+ * @hide
+ */
+@SystemApi
+@SuppressWarnings("DefaultAnnotationParam")
+@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
+ genEqualsHashCode = true)
+public final class DomainVerificationInfo implements Parcelable {
+
+ /**
+ * A domain verification ID for use in later API calls. This represents the snapshot of the
+ * domains for a package on device, and will be invalidated whenever the package changes.
+ * <p>
+ * An exception will be thrown at the next API call that receives the ID if it is no longer
+ * valid.
+ * <p>
+ * The caller may also be notified with a broadcast whenever a package and ID is invalidated, at
+ * which point it can use the package name to evict existing requests with an invalid set ID. If
+ * the caller wants to manually check if any IDs have been invalidate, the {@link
+ * PackageManager#getChangedPackages(int)} API will allow tracking the packages changed since
+ * the last query of this method, prompting the caller to re-query.
+ * <p>
+ * This allows the caller to arbitrarily grant or revoke domain verification status, through
+ * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
+ */
+ @NonNull
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForUUID.class)
+ private final UUID mIdentifier;
+
+ /**
+ * The package name that this data corresponds to.
+ */
+ @NonNull
+ private final String mPackageName;
+
+ /**
+ * Map of host names to their current state. State is an integer, which defaults to
+ * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the
+ * domain verification agent (the intended consumer of this API), which can be equal
+ * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or
+ * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for
+ * any unsuccessful response.
+ * <p>
+ * Any value non-inclusive between those 2 values are reserved for use by the system.
+ * The domain verification agent may be able to act on these reserved values, and this
+ * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}.
+ * It is expected that the agent attempt to verify all domains that it can modify the
+ * state of, even if it does not understand the meaning of those values.
+ */
+ @NonNull
+ private final Map<String, Integer> mHostToStateMap;
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new DomainVerificationInfo.
+ *
+ * @param identifier
+ * A domain verification ID for use in later API calls. This represents the snapshot of the
+ * domains for a package on device, and will be invalidated whenever the package changes.
+ * <p>
+ * An exception will be thrown at the next API call that receives the ID if it is no longer
+ * valid.
+ * <p>
+ * The caller may also be notified with a broadcast whenever a package and ID is invalidated, at
+ * which point it can use the package name to evict existing requests with an invalid set ID. If
+ * the caller wants to manually check if any IDs have been invalidate, the {@link
+ * PackageManager#getChangedPackages(int)} API will allow tracking the packages changed since
+ * the last query of this method, prompting the caller to re-query.
+ * <p>
+ * This allows the caller to arbitrarily grant or revoke domain verification status, through
+ * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
+ * @param packageName
+ * The package name that this data corresponds to.
+ * @param hostToStateMap
+ * Map of host names to their current state. State is an integer, which defaults to
+ * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the
+ * domain verification agent (the intended consumer of this API), which can be equal
+ * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or
+ * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for
+ * any unsuccessful response.
+ * <p>
+ * Any value non-inclusive between those 2 values are reserved for use by the system.
+ * The domain verification agent may be able to act on these reserved values, and this
+ * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}.
+ * It is expected that the agent attempt to verify all domains that it can modify the
+ * state of, even if it does not understand the meaning of those values.
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public DomainVerificationInfo(
+ @NonNull UUID identifier,
+ @NonNull String packageName,
+ @NonNull Map<String,Integer> hostToStateMap) {
+ this.mIdentifier = identifier;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mIdentifier);
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mHostToStateMap = hostToStateMap;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHostToStateMap);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * A domain verification ID for use in later API calls. This represents the snapshot of the
+ * domains for a package on device, and will be invalidated whenever the package changes.
+ * <p>
+ * An exception will be thrown at the next API call that receives the ID if it is no longer
+ * valid.
+ * <p>
+ * The caller may also be notified with a broadcast whenever a package and ID is invalidated, at
+ * which point it can use the package name to evict existing requests with an invalid set ID. If
+ * the caller wants to manually check if any IDs have been invalidate, the {@link
+ * PackageManager#getChangedPackages(int)} API will allow tracking the packages changed since
+ * the last query of this method, prompting the caller to re-query.
+ * <p>
+ * This allows the caller to arbitrarily grant or revoke domain verification status, through
+ * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull UUID getIdentifier() {
+ return mIdentifier;
+ }
+
+ /**
+ * The package name that this data corresponds to.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Map of host names to their current state. State is an integer, which defaults to
+ * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the
+ * domain verification agent (the intended consumer of this API), which can be equal
+ * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or
+ * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for
+ * any unsuccessful response.
+ * <p>
+ * Any value non-inclusive between those 2 values are reserved for use by the system.
+ * The domain verification agent may be able to act on these reserved values, and this
+ * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}.
+ * It is expected that the agent attempt to verify all domains that it can modify the
+ * state of, even if it does not understand the meaning of those values.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Map<String,Integer> getHostToStateMap() {
+ return mHostToStateMap;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "DomainVerificationInfo { " +
+ "identifier = " + mIdentifier + ", " +
+ "packageName = " + mPackageName + ", " +
+ "hostToStateMap = " + mHostToStateMap +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(DomainVerificationInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ DomainVerificationInfo that = (DomainVerificationInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mIdentifier, that.mIdentifier)
+ && java.util.Objects.equals(mPackageName, that.mPackageName)
+ && java.util.Objects.equals(mHostToStateMap, that.mHostToStateMap);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mIdentifier);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mHostToStateMap);
+ return _hash;
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<UUID> sParcellingForIdentifier =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForUUID.class);
+ static {
+ if (sParcellingForIdentifier == null) {
+ sParcellingForIdentifier = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForUUID());
+ }
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ sParcellingForIdentifier.parcel(mIdentifier, dest, flags);
+ dest.writeString(mPackageName);
+ dest.writeMap(mHostToStateMap);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ DomainVerificationInfo(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ UUID identifier = sParcellingForIdentifier.unparcel(in);
+ String packageName = in.readString();
+ Map<String,Integer> hostToStateMap = new java.util.LinkedHashMap<>();
+ in.readMap(hostToStateMap, Integer.class.getClassLoader());
+
+ this.mIdentifier = identifier;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mIdentifier);
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mHostToStateMap = hostToStateMap;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHostToStateMap);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<DomainVerificationInfo> CREATOR
+ = new Parcelable.Creator<DomainVerificationInfo>() {
+ @Override
+ public DomainVerificationInfo[] newArray(int size) {
+ return new DomainVerificationInfo[size];
+ }
+
+ @Override
+ public DomainVerificationInfo createFromParcel(@NonNull android.os.Parcel in) {
+ return new DomainVerificationInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1611862790369L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java",
+ inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
new file mode 100644
index 0000000..af12536
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.UserHandle;
+
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * System service to access the domain verification APIs.
+ *
+ * Allows the approved domain verification
+ * agent on the device (the sole holder of
+ * {@link android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status
+ * of domains declared by applications in their AndroidManifest.xml, to allow them to open those
+ * links inside the app when selected by the user. This is done through querying
+ * {@link #getDomainVerificationInfo(String)} and calling
+ * {@link #setDomainVerificationStatus(UUID, Set, int)}.
+ *
+ * Also allows the domain preference settings (holder of
+ * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) to update the
+ * preferences of the user, when they have chosen to explicitly allow an application to open links.
+ * This is done through querying {@link #getDomainVerificationUserSelection(String)} and calling
+ * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)} and
+ * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.DOMAIN_VERIFICATION_SERVICE)
+public interface DomainVerificationManager {
+
+ /**
+ * Extra field name for a {@link DomainVerificationRequest} for the requested packages.
+ * Passed to an the domain verification agent that handles
+ * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}.
+ */
+ String EXTRA_VERIFICATION_REQUEST =
+ "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
+
+ /**
+ * No response has been recorded by either the system or any verification agent.
+ */
+ int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE;
+
+ /** The verification agent has explicitly verified the domain at some point. */
+ int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS;
+
+ /**
+ * The first available custom response code. This and any greater integer, along with
+ * {@link #STATE_SUCCESS} are the only values settable by the verification agent. All values
+ * will be treated as if the domain is unverified.
+ */
+ int STATE_FIRST_VERIFIER_DEFINED = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+
+ /** @hide */
+ @NonNull
+ static String stateToDebugString(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ return "none";
+ case DomainVerificationState.STATE_SUCCESS:
+ return "verified";
+ case DomainVerificationState.STATE_APPROVED:
+ return "approved";
+ case DomainVerificationState.STATE_DENIED:
+ return "denied";
+ case DomainVerificationState.STATE_MIGRATED:
+ return "migrated";
+ case DomainVerificationState.STATE_RESTORED:
+ return "restored";
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ return "legacy_failure";
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ return "system_configured";
+ default:
+ return String.valueOf(state);
+ }
+ }
+
+ /**
+ * Checks if a state considers the corresponding domain to be successfully verified. The
+ * domain verification agent may use this to determine whether or not to re-verify a domain.
+ */
+ static boolean isStateVerified(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_APPROVED:
+ case DomainVerificationState.STATE_MIGRATED:
+ case DomainVerificationState.STATE_RESTORED:
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ return true;
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ case DomainVerificationState.STATE_DENIED:
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if a state is modifiable by the domain verification agent. This is useful as the
+ * platform may add new state codes in newer versions, and older verification agents can use
+ * this method to determine if a state can be changed without having to be aware of what the
+ * new state means.
+ */
+ static boolean isStateModifiable(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_MIGRATED:
+ case DomainVerificationState.STATE_RESTORED:
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ return true;
+ case DomainVerificationState.STATE_APPROVED:
+ case DomainVerificationState.STATE_DENIED:
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ return false;
+ default:
+ return state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+ }
+ }
+
+ /**
+ * For determine re-verify policy. This is hidden from the domain verification agent so that
+ * no behavior is made based on the result.
+ * @hide
+ */
+ static boolean isStateDefault(@DomainVerificationState.State int state) {
+ switch (state) {
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ case DomainVerificationState.STATE_MIGRATED:
+ case DomainVerificationState.STATE_RESTORED:
+ return true;
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_APPROVED:
+ case DomainVerificationState.STATE_DENIED:
+ case DomainVerificationState.STATE_LEGACY_FAILURE:
+ case DomainVerificationState.STATE_SYS_CONFIG:
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Used to iterate all {@link DomainVerificationInfo} values to do cleanup or retries. This is
+ * usually a heavy workload and should be done infrequently.
+ *
+ * @return the current snapshot of package names with valid autoVerify URLs.
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
+ List<String> getValidVerificationPackageNames();
+
+ /**
+ * Retrieves the domain verification state for a given package.
+ *
+ * @return the data for the package, or null if it does not declare any autoVerify domains
+ * @throws NameNotFoundException If the package is unavailable. This is an unrecoverable error
+ * and should not be re-tried except on a time scheduled basis.
+ */
+ @Nullable
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+ })
+ DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+ throws NameNotFoundException;
+
+ /**
+ * Change the verification status of the {@param domains} of the package associated with
+ * {@param domainSetId}.
+ *
+ * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
+ * @param domains List of host names to change the state of.
+ * @param state See {@link DomainVerificationInfo#getHostToStateMap()}.
+ * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
+ * invalid. This usually means the work being processed by the
+ * verification agent is outdated and a new request should
+ * be scheduled, if one has not already been done as part of
+ * the {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}
+ * broadcast.
+ * @throws NameNotFoundException If the ID is known to be good, but the package is
+ * unavailable. This may be because the package is
+ * installed on a volume that is no longer mounted. This
+ * error is unrecoverable until the package is available
+ * again, and should not be re-tried except on a time
+ * scheduled basis.
+ */
+ @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
+ void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+ @DomainVerificationState.State int state) throws NameNotFoundException;
+
+ /**
+ * TODO(b/178525735): This documentation is incorrect in the context of UX changes.
+ * Change whether the given {@param packageName} is allowed to automatically open verified
+ * HTTP/HTTPS domains. The final state is determined along with the verification status for the
+ * specific domain being opened and other system state. An app with this enabled is not
+ * guaranteed to be the sole link handler for its domains.
+ *
+ * By default, all apps are allowed to open verified links. Users must disable them explicitly.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
+ void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed)
+ throws NameNotFoundException;
+
+ /**
+ * Update the recorded user selection for the given {@param domains} for the given {@param
+ * domainSetId}. This state is recorded for the lifetime of a domain for a package on device,
+ * and will never be reset by the system short of an app data clear.
+ *
+ * This state is stored per device user. If another user needs to be changed, the appropriate
+ * permissions must be acquired and
+ * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
+ *
+ * This will be combined with the verification status and other system state to determine which
+ * application is launched to handle an app link.
+ *
+ * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
+ * @param domains The domains to toggle the state of.
+ * @param enabled Whether or not the app should automatically open the domains specified.
+ * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are
+ * invalid.
+ * @throws NameNotFoundException If the ID is known to be good, but the package is
+ * unavailable. This may be because the package is
+ * installed on a volume that is no longer mounted. This
+ * error is unrecoverable until the package is available
+ * again, and should not be re-tried except on a time
+ * scheduled basis.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
+ void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+ @NonNull Set<String> domains, boolean enabled) throws NameNotFoundException;
+
+ /**
+ * Retrieve the user selection data for the given {@param packageName} and the current user.
+ * It is the responsibility of the caller to ensure that the
+ * {@link DomainVerificationUserSelection#getIdentifier()} matches any prior API calls.
+ *
+ * This state is stored per device user. If another user needs to be accessed, the appropriate
+ * permissions must be acquired and
+ * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
+ *
+ * @param packageName The app to query state for.
+ * @return the user selection verification data for the given package for the current user,
+ * or null if the package does not declare any HTTP/HTTPS domains.
+ */
+ @Nullable
+ @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
+ DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String packageName)
+ throws NameNotFoundException;
+
+ /**
+ * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains
+ * provided by the caller is no longer valid. This may be recoverable, and the caller should
+ * re-query the package name associated with the ID using
+ * {@link #getDomainVerificationInfo(String)} in order to check. If that also fails, then the
+ * package is no longer known to the device and thus all pending work for it should be dropped.
+ *
+ * @hide
+ */
+ class InvalidDomainSetException extends IllegalArgumentException {
+
+ public static final int REASON_ID_NULL = 1;
+ public static final int REASON_ID_INVALID = 2;
+ public static final int REASON_SET_NULL_OR_EMPTY = 3;
+ public static final int REASON_UNKNOWN_DOMAIN = 4;
+
+ /** @hide */
+ @IntDef({
+ REASON_ID_NULL,
+ REASON_ID_INVALID,
+ REASON_SET_NULL_OR_EMPTY,
+ REASON_UNKNOWN_DOMAIN
+ })
+ public @interface Reason {
+ }
+
+ public static String buildMessage(@Nullable UUID domainSetId, @Nullable String packageName,
+ @Reason int reason) {
+ switch (reason) {
+ case REASON_ID_NULL:
+ return "Domain set ID cannot be null";
+ case REASON_ID_INVALID:
+ return "Domain set ID " + domainSetId + " has been invalidated";
+ case REASON_SET_NULL_OR_EMPTY:
+ return "Domain set cannot be null or empty";
+ case REASON_UNKNOWN_DOMAIN:
+ return "Domain set contains value that was not declared by the target package "
+ + packageName;
+ default:
+ return "Unknown failure";
+ }
+ }
+
+ @Reason
+ private final int mReason;
+
+ @Nullable
+ private final UUID mDomainSetId;
+
+ @Nullable
+ private final String mPackageName;
+
+ /** @hide */
+ public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName,
+ @Reason int reason) {
+ super(buildMessage(domainSetId, packageName, reason));
+ mDomainSetId = domainSetId;
+ mPackageName = packageName;
+ mReason = reason;
+ }
+
+ @Nullable
+ public UUID getDomainSetId() {
+ return mDomainSetId;
+ }
+
+ @Nullable
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @Reason
+ public int getReason() {
+ return mReason;
+ }
+ }
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
new file mode 100644
index 0000000..5938def
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.IDomainVerificationManager;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @hide
+ */
+@SuppressWarnings("RedundantThrows")
+public class DomainVerificationManagerImpl implements DomainVerificationManager {
+
+ public static final int ERROR_INVALID_DOMAIN_SET = 1;
+ public static final int ERROR_NAME_NOT_FOUND = 2;
+
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_INVALID_DOMAIN_SET,
+ ERROR_NAME_NOT_FOUND,
+ })
+ private @interface Error {
+ }
+
+ private final Context mContext;
+
+ private final IDomainVerificationManager mDomainVerificationManager;
+
+ public DomainVerificationManagerImpl(Context context,
+ IDomainVerificationManager domainVerificationManager) {
+ mContext = context;
+ mDomainVerificationManager = domainVerificationManager;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getValidVerificationPackageNames() {
+ try {
+ return mDomainVerificationManager.getValidVerificationPackageNames();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Nullable
+ @Override
+ public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+ throws NameNotFoundException {
+ try {
+ return mDomainVerificationManager.getDomainVerificationInfo(packageName);
+ } catch (Exception e) {
+ Exception converted = rethrow(e, packageName);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
+
+ @Override
+ public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+ int state) throws IllegalArgumentException, NameNotFoundException {
+ try {
+ mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
+ new ArrayList<>(domains), state);
+ } catch (Exception e) {
+ Exception converted = rethrow(e, domainSetId);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
+
+ @Override
+ public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
+ boolean allowed) throws NameNotFoundException {
+ try {
+ mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
+ allowed, mContext.getUserId());
+ } catch (Exception e) {
+ Exception converted = rethrow(e, packageName);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
+
+ @Override
+ public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+ @NonNull Set<String> domains, boolean enabled)
+ throws IllegalArgumentException, NameNotFoundException {
+ try {
+ mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
+ new ArrayList<>(domains), enabled, mContext.getUserId());
+ } catch (Exception e) {
+ Exception converted = rethrow(e, domainSetId);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public DomainVerificationUserSelection getDomainVerificationUserSelection(
+ @NonNull String packageName) throws NameNotFoundException {
+ try {
+ return mDomainVerificationManager.getDomainVerificationUserSelection(packageName,
+ mContext.getUserId());
+ } catch (Exception e) {
+ Exception converted = rethrow(e, packageName);
+ if (converted instanceof NameNotFoundException) {
+ throw (NameNotFoundException) converted;
+ } else if (converted instanceof RuntimeException) {
+ throw (RuntimeException) converted;
+ } else {
+ throw new RuntimeException(converted);
+ }
+ }
+ }
+
+ private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
+ return rethrow(exception, domainSetId, null);
+ }
+
+ private Exception rethrow(Exception exception, @Nullable String packageName) {
+ return rethrow(exception, null, packageName);
+ }
+
+ private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
+ @Nullable String packageName) {
+ if (exception instanceof ServiceSpecificException) {
+ int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
+ if (packageName == null) {
+ packageName = exception.getMessage();
+ }
+
+ @Error int managerErrorCode = packedErrorCode & 0xFFFF;
+ switch (managerErrorCode) {
+ case ERROR_INVALID_DOMAIN_SET:
+ int errorSpecificCode = packedErrorCode >> 16;
+ return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
+ domainSetId, packageName, errorSpecificCode));
+ case ERROR_NAME_NOT_FOUND:
+ return new NameNotFoundException(packageName);
+ default:
+ return exception;
+ }
+ } else if (exception instanceof RemoteException) {
+ return ((RemoteException) exception).rethrowFromSystemServer();
+ } else {
+ return exception;
+ }
+ }
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
new file mode 100644
index 0000000..473abce
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Set;
+
+/**
+ * Request object sent in the {@link Intent} that's broadcast to the domain verification
+ * agent, retrieved through {@link DomainVerificationManager#EXTRA_VERIFICATION_REQUEST}.
+ * <p>
+ * This contains the set of packages which have been invalidated and will require
+ * re-verification. The exact domains can be retrieved with
+ * {@link DomainVerificationManager#getDomainVerificationInfo(String)}
+ *
+ * @hide
+ */
+@SuppressWarnings("DefaultAnnotationParam")
+@DataClass(genHiddenConstructor = true, genAidl = false, genEqualsHashCode = true)
+@SystemApi
+public final class DomainVerificationRequest implements Parcelable {
+
+ /**
+ * The package names of the apps that need to be verified. The receiver should call
+ * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
+ * these values to get the actual set of domains that need to be acted on.
+ */
+ @NonNull
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForStringSet.class)
+ private final Set<String> mPackageNames;
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new DomainVerificationRequest.
+ *
+ * @param packageNames
+ * The package names of the apps that need to be verified. The receiver should call
+ * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
+ * these values to get the actual set of domains that need to be acted on.
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public DomainVerificationRequest(
+ @NonNull Set<String> packageNames) {
+ this.mPackageNames = packageNames;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageNames);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The package names of the apps that need to be verified. The receiver should call
+ * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
+ * these values to get the actual set of domains that need to be acted on.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Set<String> getPackageNames() {
+ return mPackageNames;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(DomainVerificationRequest other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ DomainVerificationRequest that = (DomainVerificationRequest) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mPackageNames, that.mPackageNames);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPackageNames);
+ return _hash;
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<Set<String>> sParcellingForPackageNames =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForStringSet.class);
+ static {
+ if (sParcellingForPackageNames == null) {
+ sParcellingForPackageNames = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForStringSet());
+ }
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ sParcellingForPackageNames.parcel(mPackageNames, dest, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ DomainVerificationRequest(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ Set<String> packageNames = sParcellingForPackageNames.unparcel(in);
+
+ this.mPackageNames = packageNames;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageNames);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<DomainVerificationRequest> CREATOR
+ = new Parcelable.Creator<DomainVerificationRequest>() {
+ @Override
+ public DomainVerificationRequest[] newArray(int size) {
+ return new DomainVerificationRequest[size];
+ }
+
+ @Override
+ public DomainVerificationRequest createFromParcel(@NonNull android.os.Parcel in) {
+ return new DomainVerificationRequest(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1611862814990L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java",
+ inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set<java.lang.String> mPackageNames\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationState.java b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
new file mode 100644
index 0000000..17593ef
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationState.java
@@ -0,0 +1,100 @@
+/*
+ * 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.content.pm.verify.domain;
+
+import android.annotation.IntDef;
+
+/**
+ * @hide
+ */
+public interface DomainVerificationState {
+
+ /**
+ * @hide
+ */
+ @IntDef({
+ STATE_NO_RESPONSE,
+ STATE_SUCCESS,
+ STATE_MIGRATED,
+ STATE_RESTORED,
+ STATE_APPROVED,
+ STATE_DENIED,
+ STATE_LEGACY_FAILURE,
+ STATE_SYS_CONFIG,
+ STATE_FIRST_VERIFIER_DEFINED
+ })
+ @interface State {
+ }
+
+ // TODO(b/159952358): Document all the places that states need to be updated when one is added
+ /**
+ * @see DomainVerificationManager#STATE_NO_RESPONSE
+ */
+ int STATE_NO_RESPONSE = 0;
+
+ /**
+ * @see DomainVerificationManager#STATE_SUCCESS
+ */
+ int STATE_SUCCESS = 1;
+
+ /**
+ * The system has chosen to ignore the verification agent's opinion on whether the domain should
+ * be verified. This will treat the domain as verified.
+ */
+ int STATE_APPROVED = 2;
+
+ /**
+ * The system has chosen to ignore the verification agent's opinion on whether the domain should
+ * be verified. This will treat the domain as unverified.
+ */
+ int STATE_DENIED = 3;
+
+ /**
+ * The state was migrated from the previous intent filter verification API. This will treat the
+ * domain as verified, but it should be updated by the verification agent. The older API's
+ * collection and handling of verifying domains may lead to improperly migrated state.
+ */
+ int STATE_MIGRATED = 4;
+
+ /**
+ * The state was restored from a user backup or by the system. This is treated as if the domain
+ * was verified, but the verification agent may choose to re-verify this domain to be certain
+ * nothing has changed since the snapshot.
+ */
+ int STATE_RESTORED = 5;
+
+ /**
+ * The domain was failed by a legacy intent filter verification agent from v1 of the API. This
+ * is made distinct from {@link #STATE_FIRST_VERIFIER_DEFINED} to prevent any v2 verification
+ * agent from misinterpreting the result, since {@link #STATE_FIRST_VERIFIER_DEFINED} is agent
+ * specific and can be defined as a special error code.
+ */
+ int STATE_LEGACY_FAILURE = 6;
+
+ /**
+ * The application has been granted auto verification for all domains by configuration on the
+ * system image.
+ *
+ * TODO: Can be stored per-package rather than for all domains for a package to save memory.
+ */
+ int STATE_SYS_CONFIG = 7;
+
+ /**
+ * @see DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED
+ */
+ int STATE_FIRST_VERIFIER_DEFINED = 0b10000000000;
+}
diff --git a/core/java/android/service/attestation/ImpressionToken.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
similarity index 87%
copy from core/java/android/service/attestation/ImpressionToken.aidl
copy to core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
index 284a4ba..ddb5ef8 100644
--- a/core/java/android/service/attestation/ImpressionToken.aidl
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.content.pm.verify.domain;
-parcelable ImpressionToken;
\ No newline at end of file
+parcelable DomainVerificationUserSelection;
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
new file mode 100644
index 0000000..8d16f75
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Contains the user selection state for a package. This means all web HTTP(S) domains
+ * declared by a package in its manifest, whether or not they were marked for auto
+ * verification.
+ * <p>
+ * By default, all apps are allowed to automatically open links with domains that they've
+ * successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}.
+ * The user can decide to disable this, disallowing the application from opening these
+ * links.
+ * <p>
+ * Separately, independent of this toggle, the user can choose specific domains to allow
+ * an app to open, which is reflected as part of {@link #getHostToUserSelectionMap()},
+ * which maps the domain name to the true/false state of whether it was enabled by the user.
+ * <p>
+ * These values can be changed through the
+ * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
+ * boolean)} and
+ * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
+ * boolean)} APIs.
+ * <p>
+ * Note that because state is per user, if a different user needs to be changed, one will
+ * need to use {@link Context#createContextAsUser(UserHandle, int)} and hold the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ *
+ * @hide
+ */
+@SystemApi
+@SuppressWarnings("DefaultAnnotationParam")
+@DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
+ genEqualsHashCode = true)
+public final class DomainVerificationUserSelection implements Parcelable {
+
+ /**
+ * @see DomainVerificationInfo#getIdentifier
+ */
+ @NonNull
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForUUID.class)
+ private final UUID mIdentifier;
+
+ /**
+ * The package name that this data corresponds to.
+ */
+ @NonNull
+ private final String mPackageName;
+
+ /**
+ * The user that this data corresponds to.
+ */
+ @NonNull
+ private final UserHandle mUser;
+
+ /**
+ * Whether or not this package is allowed to open links.
+ */
+ @NonNull
+ private final boolean mLinkHandlingAllowed;
+
+ /**
+ * Retrieve the existing user selection state for the matching
+ * {@link #getPackageName()}, as was previously set by
+ * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
+ * boolean)}.
+ *
+ * @return Map of hosts to enabled state for the given package and user.
+ */
+ @NonNull
+ private final Map<String, Boolean> mHostToUserSelectionMap;
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new DomainVerificationUserSelection.
+ *
+ * @param packageName
+ * The package name that this data corresponds to.
+ * @param user
+ * The user that this data corresponds to.
+ * @param linkHandlingAllowed
+ * Whether or not this package is allowed to open links.
+ * @param hostToUserSelectionMap
+ * Retrieve the existing user selection state for the matching
+ * {@link #getPackageName()}, as was previously set by
+ * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
+ * boolean)}.
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public DomainVerificationUserSelection(
+ @NonNull UUID identifier,
+ @NonNull String packageName,
+ @NonNull UserHandle user,
+ @NonNull boolean linkHandlingAllowed,
+ @NonNull Map<String,Boolean> hostToUserSelectionMap) {
+ this.mIdentifier = identifier;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mIdentifier);
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mUser = user;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mUser);
+ this.mLinkHandlingAllowed = linkHandlingAllowed;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mLinkHandlingAllowed);
+ this.mHostToUserSelectionMap = hostToUserSelectionMap;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHostToUserSelectionMap);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * @see DomainVerificationInfo#getIdentifier
+ */
+ @DataClass.Generated.Member
+ public @NonNull UUID getIdentifier() {
+ return mIdentifier;
+ }
+
+ /**
+ * The package name that this data corresponds to.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * The user that this data corresponds to.
+ */
+ @DataClass.Generated.Member
+ public @NonNull UserHandle getUser() {
+ return mUser;
+ }
+
+ /**
+ * Whether or not this package is allowed to open links.
+ */
+ @DataClass.Generated.Member
+ public @NonNull boolean isLinkHandlingAllowed() {
+ return mLinkHandlingAllowed;
+ }
+
+ /**
+ * Retrieve the existing user selection state for the matching
+ * {@link #getPackageName()}, as was previously set by
+ * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
+ * boolean)}.
+ *
+ * @return Map of hosts to enabled state for the given package and user.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Map<String,Boolean> getHostToUserSelectionMap() {
+ return mHostToUserSelectionMap;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "DomainVerificationUserSelection { " +
+ "identifier = " + mIdentifier + ", " +
+ "packageName = " + mPackageName + ", " +
+ "user = " + mUser + ", " +
+ "linkHandlingAllowed = " + mLinkHandlingAllowed + ", " +
+ "hostToUserSelectionMap = " + mHostToUserSelectionMap +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(DomainVerificationUserSelection other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ DomainVerificationUserSelection that = (DomainVerificationUserSelection) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mIdentifier, that.mIdentifier)
+ && java.util.Objects.equals(mPackageName, that.mPackageName)
+ && java.util.Objects.equals(mUser, that.mUser)
+ && mLinkHandlingAllowed == that.mLinkHandlingAllowed
+ && java.util.Objects.equals(mHostToUserSelectionMap, that.mHostToUserSelectionMap);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mIdentifier);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mUser);
+ _hash = 31 * _hash + Boolean.hashCode(mLinkHandlingAllowed);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mHostToUserSelectionMap);
+ return _hash;
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<UUID> sParcellingForIdentifier =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForUUID.class);
+ static {
+ if (sParcellingForIdentifier == null) {
+ sParcellingForIdentifier = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForUUID());
+ }
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mLinkHandlingAllowed) flg |= 0x8;
+ dest.writeByte(flg);
+ sParcellingForIdentifier.parcel(mIdentifier, dest, flags);
+ dest.writeString(mPackageName);
+ dest.writeTypedObject(mUser, flags);
+ dest.writeMap(mHostToUserSelectionMap);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ DomainVerificationUserSelection(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ boolean linkHandlingAllowed = (flg & 0x8) != 0;
+ UUID identifier = sParcellingForIdentifier.unparcel(in);
+ String packageName = in.readString();
+ UserHandle user = (UserHandle) in.readTypedObject(UserHandle.CREATOR);
+ Map<String,Boolean> hostToUserSelectionMap = new java.util.LinkedHashMap<>();
+ in.readMap(hostToUserSelectionMap, Boolean.class.getClassLoader());
+
+ this.mIdentifier = identifier;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mIdentifier);
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mUser = user;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mUser);
+ this.mLinkHandlingAllowed = linkHandlingAllowed;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mLinkHandlingAllowed);
+ this.mHostToUserSelectionMap = hostToUserSelectionMap;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHostToUserSelectionMap);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<DomainVerificationUserSelection> CREATOR
+ = new Parcelable.Creator<DomainVerificationUserSelection>() {
+ @Override
+ public DomainVerificationUserSelection[] newArray(int size) {
+ return new DomainVerificationUserSelection[size];
+ }
+
+ @Override
+ public DomainVerificationUserSelection createFromParcel(@NonNull android.os.Parcel in) {
+ return new DomainVerificationUserSelection(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1611799495498L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java",
+ inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Boolean> mHostToUserSelectionMap\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
new file mode 100644
index 0000000..21dd623b
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import java.util.List;
+
+/**
+ * @see DomainVerificationManager
+ * @hide
+ */
+interface IDomainVerificationManager {
+
+ List<String> getValidVerificationPackageNames();
+
+ @nullable
+ DomainVerificationInfo getDomainVerificationInfo(String packageName);
+
+ @nullable
+ DomainVerificationUserSelection getDomainVerificationUserSelection(String packageName,
+ int userId);
+
+ void setDomainVerificationStatus(String domainSetId, in List<String> domains, int state);
+
+ void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId);
+
+ void setDomainVerificationUserSelection(String domainSetId, in List<String> domains,
+ boolean enabled, int userId);
+}
diff --git a/core/java/android/content/pm/verify/domain/TEST_MAPPING b/core/java/android/content/pm/verify/domain/TEST_MAPPING
new file mode 100644
index 0000000..c6c9791
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "PackageManagerServiceUnitTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.test.verify.domain"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index f0b218c..abb4f9f 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -261,7 +261,7 @@
@IntRange(from = 0) int baseVersion
) {
try {
- return mIFontManager.updateFont(pfd, signature, baseVersion);
+ return mIFontManager.updateFont(baseVersion, new FontUpdateRequest(pfd, signature));
} catch (RemoteException e) {
Log.e(TAG, "Failed to call updateFont API", e);
return RESULT_ERROR_REMOTE_EXCEPTION;
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/core/java/android/graphics/fonts/FontUpdateRequest.aidl
similarity index 95%
rename from core/java/android/graphics/fonts/SystemFontState.aidl
rename to core/java/android/graphics/fonts/FontUpdateRequest.aidl
index 19b20f2..f6cb373 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/core/java/android/graphics/fonts/FontUpdateRequest.aidl
@@ -17,4 +17,4 @@
package android.graphics.fonts;
/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+parcelable FontUpdateRequest;
\ No newline at end of file
diff --git a/core/java/android/graphics/fonts/FontUpdateRequest.java b/core/java/android/graphics/fonts/FontUpdateRequest.java
new file mode 100644
index 0000000..db047f8
--- /dev/null
+++ b/core/java/android/graphics/fonts/FontUpdateRequest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+/**
+ * Represents a font update request. Currently only font install request is supported.
+ * @hide
+ */
+// TODO: Support font config update.
+public final class FontUpdateRequest implements Parcelable {
+
+ public static final Creator<FontUpdateRequest> CREATOR = new Creator<FontUpdateRequest>() {
+ @Override
+ public FontUpdateRequest createFromParcel(Parcel in) {
+ return new FontUpdateRequest(in);
+ }
+
+ @Override
+ public FontUpdateRequest[] newArray(int size) {
+ return new FontUpdateRequest[size];
+ }
+ };
+
+ @NonNull
+ private final ParcelFileDescriptor mFd;
+ @NonNull
+ private final byte[] mSignature;
+
+ public FontUpdateRequest(@NonNull ParcelFileDescriptor fd, @NonNull byte[] signature) {
+ mFd = fd;
+ mSignature = signature;
+ }
+
+ private FontUpdateRequest(Parcel in) {
+ mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
+ mSignature = in.readBlob();
+ }
+
+ @NonNull
+ public ParcelFileDescriptor getFd() {
+ return mFd;
+ }
+
+ @NonNull
+ public byte[] getSignature() {
+ return mSignature;
+ }
+
+ @Override
+ public int describeContents() {
+ return Parcelable.CONTENTS_FILE_DESCRIPTOR;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mFd, flags);
+ dest.writeBlob(mSignature);
+ }
+}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 4145a72..08b1e24 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -29,7 +29,6 @@
import android.annotation.TestApi;
import android.content.Context;
import android.os.RemoteException;
-import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Slog;
@@ -47,6 +46,13 @@
private static final String TAG = "BiometricManager";
/**
+ * An ID that should match any biometric sensor on the device.
+ *
+ * @hide
+ */
+ public static final int SENSOR_ID_ANY = -1;
+
+ /**
* No error detected.
*/
public static final int BIOMETRIC_SUCCESS =
@@ -139,7 +145,7 @@
*
* <p>This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation.
*
- * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+ * @see android.security.keystore.KeyGenParameterSpec.Builder
*/
int BIOMETRIC_STRONG = 0x000F;
@@ -182,7 +188,7 @@
* <p>This corresponds to {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} during key
* generation.
*
- * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+ * @see android.security.keystore.KeyGenParameterSpec.Builder
*/
int DEVICE_CREDENTIAL = 1 << 15;
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 76cf9b9..4f6a7c7 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -36,7 +36,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.security.identity.IdentityCredential;
-import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import android.util.Log;
@@ -325,7 +324,7 @@
* request authentication with the proper set of authenticators (e.g. match the
* authenticators specified during key generation).
*
- * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+ * @see android.security.keystore.KeyGenParameterSpec.Builder
* @see KeyProperties#AUTH_BIOMETRIC_STRONG
* @see KeyProperties#AUTH_DEVICE_CREDENTIAL
*
@@ -365,6 +364,21 @@
}
/**
+ * If set, authenticate using the biometric sensor with the given ID.
+ *
+ * @param sensorId The ID of a biometric sensor, or -1 to allow any sensor (default).
+ * @return This builder.
+ *
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ @NonNull
+ public Builder setSensorId(int sensorId) {
+ mPromptInfo.setSensorId(sensorId);
+ return this;
+ }
+
+ /**
* Creates a {@link BiometricPrompt}.
*
* @return An instance of {@link BiometricPrompt}.
@@ -589,7 +603,8 @@
*
* <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
* time-based. This is specified during key creation via the timeout parameter of the
- * {@link KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)} API.
+ * {@code setUserAuthenticationParameters(int, int)} method of {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder}.
*
* <p>CryptoObjects are used to unlock auth-per-use keys via
* {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor,
@@ -778,6 +793,27 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
int userId) {
+ authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */);
+ }
+
+ /**
+ * Authenticates for the given user and keystore operation.
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @param userId The user to authenticate
+ * @param operationId The keystore operation associated with authentication
+ *
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void authenticateUserForOperation(
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback,
+ int userId,
+ long operationId) {
if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
@@ -787,7 +823,7 @@
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
- authenticateInternal(null /* crypto */, cancel, executor, callback, userId);
+ authenticateInternal(operationId, cancel, executor, callback, userId);
}
/**
@@ -912,11 +948,31 @@
}
}
- private void authenticateInternal(@Nullable CryptoObject crypto,
+ private void authenticateInternal(
+ @Nullable CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
int userId) {
+
+ mCryptoObject = crypto;
+ final long operationId = crypto != null ? crypto.getOpId() : 0L;
+ authenticateInternal(operationId, cancel, executor, callback, userId);
+ }
+
+ private void authenticateInternal(
+ long operationId,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback,
+ int userId) {
+
+ // Ensure we don't return the wrong crypto object as an auth result.
+ if (mCryptoObject != null && mCryptoObject.getOpId() != operationId) {
+ Log.w(TAG, "CryptoObject operation ID does not match argument; setting field to null");
+ mCryptoObject = null;
+ }
+
try {
if (cancel.isCanceled()) {
Log.w(TAG, "Authentication already canceled");
@@ -925,13 +981,11 @@
cancel.setOnCancelListener(new OnAuthenticationCancelListener());
}
- mCryptoObject = crypto;
mExecutor = executor;
mAuthenticationCallback = callback;
- final long operationId = crypto != null ? crypto.getOpId() : 0;
final PromptInfo promptInfo;
- if (crypto != null) {
+ if (operationId != 0L) {
// Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth.
// Note that we use a new PromptInfo here so as to not overwrite the application's
// preference, since it is possible that the same prompt configuration be used
@@ -952,10 +1006,9 @@
} catch (RemoteException e) {
Log.e(TAG, "Remote exception while authenticating", e);
- mExecutor.execute(() -> {
- callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- mContext.getString(R.string.biometric_error_hw_unavailable));
- });
+ mExecutor.execute(() -> callback.onAuthenticationError(
+ BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ mContext.getString(R.string.biometric_error_hw_unavailable)));
}
}
}
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index c2eff7d..0e99f31 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -40,6 +40,7 @@
private @BiometricManager.Authenticators.Types int mAuthenticators;
private boolean mDisallowBiometricsIfPolicyExists;
private boolean mReceiveSystemEvents;
+ private int mSensorId = -1;
public PromptInfo() {
@@ -59,6 +60,7 @@
mAuthenticators = in.readInt();
mDisallowBiometricsIfPolicyExists = in.readBoolean();
mReceiveSystemEvents = in.readBoolean();
+ mSensorId = in.readInt();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -93,6 +95,7 @@
dest.writeInt(mAuthenticators);
dest.writeBoolean(mDisallowBiometricsIfPolicyExists);
dest.writeBoolean(mReceiveSystemEvents);
+ dest.writeInt(mSensorId);
}
public boolean containsPrivateApiConfigurations() {
@@ -166,6 +169,10 @@
mReceiveSystemEvents = receiveSystemEvents;
}
+ public void setSensorId(int sensorId) {
+ mSensorId = sensorId;
+ }
+
// Getters
public CharSequence getTitle() {
@@ -226,4 +233,8 @@
public boolean isReceiveSystemEvents() {
return mReceiveSystemEvents;
}
+
+ public int getSensorId() {
+ return mSensorId;
+ }
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 29a6ee2..f175e7b 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -16,8 +16,12 @@
package android.hardware.devicestate;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import java.util.concurrent.Executor;
@@ -28,13 +32,19 @@
*
* @hide
*/
+@TestApi
@SystemService(Context.DEVICE_STATE_SERVICE)
public final class DeviceStateManager {
- /** Invalid device state. */
+ /**
+ * Invalid device state.
+ *
+ * @hide
+ */
public static final int INVALID_DEVICE_STATE = -1;
- private DeviceStateManagerGlobal mGlobal;
+ private final DeviceStateManagerGlobal mGlobal;
+ /** @hide */
public DeviceStateManager() {
DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance();
if (global == null) {
@@ -45,23 +55,73 @@
}
/**
+ * Returns the list of device states that are supported and can be requested with
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ */
+ @NonNull
+ public int[] getSupportedStates() {
+ return mGlobal.getSupportedStates();
+ }
+
+ /**
+ * Submits a {@link DeviceStateRequest request} to modify the device state.
+ * <p>
+ * By default, the request is kept active until a call to
+ * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs:
+ * <ul>
+ * <li>Another processes submits a request succeeding this request in which case the request
+ * will be suspended until the interrupting request is canceled.
+ * <li>The requested state has become unsupported.
+ * <li>The process submitting the request dies.
+ * </ul>
+ * However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
+ *
+ * @throws IllegalArgumentException if the requested state is unsupported.
+ * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
+ * permission is not held.
+ *
+ * @see DeviceStateRequest
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void requestState(@NonNull DeviceStateRequest request,
+ @Nullable @CallbackExecutor Executor executor,
+ @Nullable DeviceStateRequest.Callback callback) {
+ mGlobal.requestState(request, callback, executor);
+ }
+
+ /**
+ * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ * <p>
+ * This method is noop if the {@code request} has not been submitted with a call to
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ *
+ * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
+ * permission is not held.
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void cancelRequest(@NonNull DeviceStateRequest request) {
+ mGlobal.cancelRequest(request);
+ }
+
+ /**
* Registers a listener to receive notifications about changes in device state.
*
- * @param listener the listener to register.
* @param executor the executor to process notifications.
+ * @param listener the listener to register.
*
* @see DeviceStateListener
*/
- public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
- @NonNull Executor executor) {
+ public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull DeviceStateListener listener) {
mGlobal.registerDeviceStateListener(listener, executor);
}
/**
* Unregisters a listener previously registered with
- * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
+ * {@link #addDeviceStateListener(Executor, DeviceStateListener)}.
*/
- public void unregisterDeviceStateListener(@NonNull DeviceStateListener listener) {
+ public void removeDeviceStateListener(@NonNull DeviceStateListener listener) {
mGlobal.unregisterDeviceStateListener(listener);
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index c8905038..b9ae88e 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -20,9 +20,11 @@
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateListener;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -67,6 +69,9 @@
@GuardedBy("mLock")
private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>();
+ @GuardedBy("mLock")
+ private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>();
+
@Nullable
@GuardedBy("mLock")
private Integer mLastReceivedState;
@@ -77,9 +82,84 @@
}
/**
+ * Returns the set of supported device states.
+ *
+ * @see DeviceStateManager#getSupportedStates()
+ */
+ public int[] getSupportedStates() {
+ try {
+ return mDeviceStateManager.getSupportedDeviceStates();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Submits a {@link DeviceStateRequest request} to modify the device state.
+ *
+ * @see DeviceStateManager#requestState(DeviceStateRequest,
+ * Executor, DeviceStateRequest.Callback)
+ * @see DeviceStateRequest
+ */
+ public void requestState(@NonNull DeviceStateRequest request,
+ @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ if (callback == null && executor != null) {
+ throw new IllegalArgumentException("Callback must be supplied with executor.");
+ } else if (executor == null && callback != null) {
+ throw new IllegalArgumentException("Executor must be supplied with callback.");
+ }
+
+ synchronized (mLock) {
+ registerCallbackIfNeededLocked();
+
+ if (findRequestTokenLocked(request) != null) {
+ // This request has already been submitted.
+ return;
+ }
+
+ // Add the request wrapper to the mRequests array before requesting the state as the
+ // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
+ // same process as this instance.
+ IBinder token = new Binder();
+ mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor));
+
+ try {
+ mDeviceStateManager.requestState(token, request.getState(), request.getFlags());
+ } catch (RemoteException ex) {
+ mRequests.remove(token);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * {@link #requestState(DeviceStateRequest, DeviceStateRequest.Callback, Executor)}.
+ *
+ * @see DeviceStateManager#cancelRequest(DeviceStateRequest)
+ */
+ public void cancelRequest(@NonNull DeviceStateRequest request) {
+ synchronized (mLock) {
+ registerCallbackIfNeededLocked();
+
+ final IBinder token = findRequestTokenLocked(request);
+ if (token == null) {
+ // This request has not been submitted.
+ return;
+ }
+
+ try {
+ mDeviceStateManager.cancelRequest(token);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Registers a listener to receive notifications about changes in device state.
*
- * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
+ * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
@@ -112,7 +192,7 @@
* Unregisters a listener previously registered with
* {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
*
- * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
+ * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void unregisterDeviceStateListener(DeviceStateListener listener) {
@@ -144,6 +224,17 @@
return -1;
}
+ @Nullable
+ private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) {
+ for (int i = 0; i < mRequests.size(); i++) {
+ if (mRequests.valueAt(i).mRequest.equals(request)) {
+ return mRequests.keyAt(i);
+ }
+ }
+ return null;
+ }
+
+ /** Handles a call from the server that the device state has changed. */
private void handleDeviceStateChanged(int newDeviceState) {
ArrayList<DeviceStateListenerWrapper> listeners;
synchronized (mLock) {
@@ -156,11 +247,68 @@
}
}
+ /**
+ * Handles a call from the server that a request for the supplied {@code token} has become
+ * active.
+ */
+ private void handleRequestActive(IBinder token) {
+ DeviceStateRequestWrapper request;
+ synchronized (mLock) {
+ request = mRequests.get(token);
+ }
+ if (request != null) {
+ request.notifyRequestActive();
+ }
+ }
+
+ /**
+ * Handles a call from the server that a request for the supplied {@code token} has become
+ * suspended.
+ */
+ private void handleRequestSuspended(IBinder token) {
+ DeviceStateRequestWrapper request;
+ synchronized (mLock) {
+ request = mRequests.get(token);
+ }
+ if (request != null) {
+ request.notifyRequestSuspended();
+ }
+ }
+
+ /**
+ * Handles a call from the server that a request for the supplied {@code token} has become
+ * canceled.
+ */
+ private void handleRequestCanceled(IBinder token) {
+ DeviceStateRequestWrapper request;
+ synchronized (mLock) {
+ request = mRequests.remove(token);
+ }
+ if (request != null) {
+ request.notifyRequestCanceled();
+ }
+ }
+
private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
@Override
public void onDeviceStateChanged(int deviceState) {
handleDeviceStateChanged(deviceState);
}
+
+ @Override
+ public void onRequestActive(IBinder token) {
+ handleRequestActive(token);
+ }
+
+ @Override
+ public void onRequestSuspended(IBinder token) {
+ handleRequestSuspended(token);
+ }
+
+ @Override
+ public void onRequestCanceled(IBinder token) {
+ handleRequestCanceled(token);
+ }
}
private static final class DeviceStateListenerWrapper {
@@ -176,4 +324,43 @@
mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState));
}
}
+
+ private static final class DeviceStateRequestWrapper {
+ private final DeviceStateRequest mRequest;
+ @Nullable
+ private final DeviceStateRequest.Callback mCallback;
+ @Nullable
+ private final Executor mExecutor;
+
+ DeviceStateRequestWrapper(@NonNull DeviceStateRequest request,
+ @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ mRequest = request;
+ mCallback = callback;
+ mExecutor = executor;
+ }
+
+ void notifyRequestActive() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onRequestActivated(mRequest));
+ }
+
+ void notifyRequestSuspended() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+ }
+
+ void notifyRequestCanceled() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+ }
+ }
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java
new file mode 100644
index 0000000..70f7002
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.hardware.devicestate;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * A request to alter the state of the device managed by {@link DeviceStateManager}.
+ * <p>
+ * Once constructed, a {@link DeviceStateRequest request} can be submitted with a call to
+ * {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)}.
+ * <p>
+ * By default, the request is kept active until a call to
+ * {@link DeviceStateManager#cancelRequest(DeviceStateRequest)} or until one of the following
+ * occurs:
+ * <ul>
+ * <li>Another processes submits a request succeeding this request in which case the request
+ * will be suspended until the interrupting request is canceled.
+ * <li>The requested state has become unsupported.
+ * <li>The process submitting the request dies.
+ * </ul>
+ * However, this behavior can be changed by setting flags on the request. For example, the
+ * {@link #FLAG_CANCEL_WHEN_BASE_CHANGES} flag will extend this behavior to also cancel the
+ * request whenever the base (non-override) device state changes.
+ *
+ * @see DeviceStateManager
+ *
+ * @hide
+ */
+@TestApi
+public final class DeviceStateRequest {
+ /**
+ * Flag that indicates the request should be canceled automatically when the base
+ * (non-override) device state changes. Useful when the requestor only wants the request to
+ * remain active while the base state remains constant and automatically cancel when the user
+ * manipulates the device into a different state.
+ */
+ public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1 << 0;
+
+ /** @hide */
+ @IntDef(prefix = {"FLAG_"}, flag = true, value = {
+ FLAG_CANCEL_WHEN_BASE_CHANGES,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestFlags {}
+
+ /**
+ * Creates a new {@link Builder} for a {@link DeviceStateRequest}. Must be one of the supported
+ * states for the device which can be queried with a call to
+ * {@link DeviceStateManager#getSupportedStates()}.
+ *
+ * @param requestedState the device state being requested.
+ */
+ @NonNull
+ public static Builder newBuilder(int requestedState) {
+ return new Builder(requestedState);
+ }
+
+ /**
+ * Builder for {@link DeviceStateRequest}. An instance can be obtained through
+ * {@link #newBuilder(int)}.
+ */
+ public static final class Builder {
+ private final int mRequestedState;
+ private int mFlags;
+
+ private Builder(int requestedState) {
+ mRequestedState = requestedState;
+ }
+
+ /**
+ * Sets the flag bits provided within {@code flags} with all other bits remaining
+ * unchanged.
+ */
+ @NonNull
+ public Builder setFlags(@RequestFlags int flags) {
+ mFlags |= flags;
+ return this;
+ }
+
+ /**
+ * Returns a new {@link DeviceStateRequest} object whose state matches the state set on the
+ * builder.
+ */
+ @NonNull
+ public DeviceStateRequest build() {
+ return new DeviceStateRequest(mRequestedState, mFlags);
+ }
+ }
+
+ /** Callback to track the status of a request. */
+ public interface Callback {
+ /**
+ * Called to indicate the request has become active and the device state will match the
+ * requested state.
+ * <p>
+ * Guaranteed to be called after a call to
+ * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state
+ * matching the requested state.
+ */
+ default void onRequestActivated(@NonNull DeviceStateRequest request) {}
+
+ /**
+ * Called to indicate the request has been temporarily suspended.
+ * <p>
+ * Guaranteed to be called before a call to
+ * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+ */
+ default void onRequestSuspended(@NonNull DeviceStateRequest request) {}
+
+ /**
+ * Called to indicate the request has been canceled. The request can be resubmitted with
+ * another call to {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)}.
+ * <p>
+ * Guaranteed to be called before a call to
+ * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+ * <p>
+ * Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to
+ * occur before this method.
+ */
+ default void onRequestCanceled(@NonNull DeviceStateRequest request) {}
+ }
+
+ private final int mRequestedState;
+ @RequestFlags
+ private final int mFlags;
+
+ private DeviceStateRequest(int requestedState, @RequestFlags int flags) {
+ mRequestedState = requestedState;
+ mFlags = flags;
+ }
+
+ public int getState() {
+ return mRequestedState;
+ }
+
+ @RequestFlags
+ public int getFlags() {
+ return mFlags;
+ }
+}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index a157b33..323ad21 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -20,5 +20,45 @@
/** @hide */
interface IDeviceStateManager {
+ /**
+ * Registers a callback to receive notifications from the device state manager. Only one
+ * callback can be registered per-process.
+ * <p>
+ * As the callback mechanism is used to alert the caller of changes to request status a callback
+ * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or
+ * {@link #cancelRequest(IBinder)}. Otherwise an exception will be thrown.
+ *
+ * @throws SecurityException if a callback is already registered for the calling process.
+ */
void registerCallback(in IDeviceStateManagerCallback callback);
+
+ /** Returns the array of supported device state identifiers. */
+ int[] getSupportedDeviceStates();
+
+ /**
+ * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been
+ * previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
+ * call to this method.
+ *
+ * @param token the request token previously registered with
+ * {@link #requestState(IBinder, int, int)}
+ *
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
+ * @throws IllegalStateException if the supplied {@code token} has already been registered.
+ * @throws IllegalArgumentException if the supplied {@code state} is not supported.
+ */
+ void requestState(IBinder token, int state, int flags);
+
+ /**
+ * Cancels a request previously submitted with a call to
+ * {@link #requestState(IBinder, int, int)}.
+ *
+ * @param token the request token previously registered with
+ * {@link #requestState(IBinder, int, int)}
+ *
+ * @throws IllegalStateException if the supplied {@code token} has not been previously
+ * requested.
+ */
+ void cancelRequest(IBinder token);
}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
index d1c5813..ee2a071 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
@@ -18,5 +18,42 @@
/** @hide */
interface IDeviceStateManagerCallback {
+ /**
+ * Called in response to a change in device state. Guaranteed to be called once with the initial
+ * value on registration of the callback.
+ *
+ * @param deviceState the new state of the device.
+ */
oneway void onDeviceStateChanged(int deviceState);
+
+ /**
+ * Called to notify the callback that a request has become active. Guaranteed to be called
+ * after a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming active
+ * resulted in a device state change.
+ *
+ * @param token the request token previously registered with
+ * {@link IDeviceStateManager#requestState(IBinder, int, int)}
+ */
+ oneway void onRequestActive(IBinder token);
+
+ /**
+ * Called to notify the callback that a request has become suspended. Guaranteed to be called
+ * before a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming
+ * suspended resulted in a device state change.
+ *
+ * @param token the request token previously registered with
+ * {@link IDeviceStateManager#requestState(IBinder, int, int)}
+ */
+ oneway void onRequestSuspended(IBinder token);
+
+ /**
+ * Called to notify the callback that a request has become canceled. No further callbacks will
+ * be triggered for this request. Guaranteed to be called before a subsequent call to
+ * {@link #onDeviceStateChanged(int)} if the request becoming canceled resulted in a device
+ * state change.
+ *
+ * @param token the request token previously registered with
+ * {@link IDeviceStateManager#requestState(IBinder, int, int)}
+ */
+ oneway void onRequestCanceled(IBinder token);
}
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index e2d836c..a6c6b46 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -65,6 +65,23 @@
/** If night mode color filter is active this will be the temperature in kelvin */
public final int colorTemperature;
+ /** Whether the bright color reduction color transform is active */
+ public final boolean reduceBrightColors;
+
+ /** How strong the bright color reduction color transform is set (only applicable if active),
+ * specified as an integer from 0 - 100, inclusive. This value (scaled to 0-1, inclusive) is
+ * then used in Ynew = (a * scaledStrength^2 + b * scaledStrength + c) * Ycurrent, where a, b,
+ * and c are coefficients provided in the bright color reduction coefficient matrix, and
+ * Ycurrent is the current hardware brightness in nits.
+ */
+ public final int reduceBrightColorsStrength;
+
+ /** Applied offset for the bright color reduction color transform (only applicable if active).
+ * The offset is computed by summing the coefficients a, b, and c, from the coefficient matrix
+ * and multiplying by the current brightness.
+ */
+ public final float reduceBrightColorsOffset;
+
/** Brightness level before slider adjustment */
public final float lastBrightness;
@@ -105,8 +122,9 @@
private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
float powerBrightnessFactor, boolean nightMode, int colorTemperature,
- float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness,
- long[] colorValueBuckets, long colorSampleDuration) {
+ boolean reduceBrightColors, int reduceBrightColorsStrength,
+ float reduceBrightColorsOffset, float lastBrightness, boolean isDefaultBrightnessConfig,
+ boolean isUserSetBrightness, long[] colorValueBuckets, long colorSampleDuration) {
this.brightness = brightness;
this.timeStamp = timeStamp;
this.packageName = packageName;
@@ -117,6 +135,9 @@
this.powerBrightnessFactor = powerBrightnessFactor;
this.nightMode = nightMode;
this.colorTemperature = colorTemperature;
+ this.reduceBrightColors = reduceBrightColors;
+ this.reduceBrightColorsStrength = reduceBrightColorsStrength;
+ this.reduceBrightColorsOffset = reduceBrightColorsOffset;
this.lastBrightness = lastBrightness;
this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
this.isUserSetBrightness = isUserSetBrightness;
@@ -136,6 +157,9 @@
this.powerBrightnessFactor = other.powerBrightnessFactor;
this.nightMode = other.nightMode;
this.colorTemperature = other.colorTemperature;
+ this.reduceBrightColors = other.reduceBrightColors;
+ this.reduceBrightColorsStrength = other.reduceBrightColorsStrength;
+ this.reduceBrightColorsOffset = other.reduceBrightColorsOffset;
this.lastBrightness = other.lastBrightness;
this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
this.isUserSetBrightness = other.isUserSetBrightness;
@@ -154,6 +178,9 @@
powerBrightnessFactor = source.readFloat();
nightMode = source.readBoolean();
colorTemperature = source.readInt();
+ reduceBrightColors = source.readBoolean();
+ reduceBrightColorsStrength = source.readInt();
+ reduceBrightColorsOffset = source.readFloat();
lastBrightness = source.readFloat();
isDefaultBrightnessConfig = source.readBoolean();
isUserSetBrightness = source.readBoolean();
@@ -188,6 +215,9 @@
dest.writeFloat(powerBrightnessFactor);
dest.writeBoolean(nightMode);
dest.writeInt(colorTemperature);
+ dest.writeBoolean(reduceBrightColors);
+ dest.writeInt(reduceBrightColorsStrength);
+ dest.writeFloat(reduceBrightColorsOffset);
dest.writeFloat(lastBrightness);
dest.writeBoolean(isDefaultBrightnessConfig);
dest.writeBoolean(isUserSetBrightness);
@@ -207,6 +237,9 @@
private float mPowerBrightnessFactor;
private boolean mNightMode;
private int mColorTemperature;
+ private boolean mReduceBrightColors;
+ private int mReduceBrightColorsStrength;
+ private float mReduceBrightColorsOffset;
private float mLastBrightness;
private boolean mIsDefaultBrightnessConfig;
private boolean mIsUserSetBrightness;
@@ -273,6 +306,24 @@
return this;
}
+ /** {@see BrightnessChangeEvent#reduceBrightColors} */
+ public Builder setReduceBrightColors(boolean reduceBrightColors) {
+ mReduceBrightColors = reduceBrightColors;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#reduceBrightColorsStrength} */
+ public Builder setReduceBrightColorsStrength(int strength) {
+ mReduceBrightColorsStrength = strength;
+ return this;
+ }
+
+ /** {@see BrightnessChangeEvent#reduceBrightColorsOffset} */
+ public Builder setReduceBrightColorsOffset(float offset) {
+ mReduceBrightColorsOffset = offset;
+ return this;
+ }
+
/** {@see BrightnessChangeEvent#lastBrightness} */
public Builder setLastBrightness(float lastBrightness) {
mLastBrightness = lastBrightness;
@@ -304,7 +355,8 @@
public BrightnessChangeEvent build() {
return new BrightnessChangeEvent(mBrightness, mTimeStamp,
mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
- mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
+ mPowerBrightnessFactor, mNightMode, mColorTemperature, mReduceBrightColors,
+ mReduceBrightColorsStrength, mReduceBrightColorsOffset, mLastBrightness,
mIsDefaultBrightnessConfig, mIsUserSetBrightness, mColorValueBuckets,
mColorSampleDuration);
}
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index efeb89e..e247df3 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -438,6 +438,56 @@
}
/**
+ * Enables or disables reduce bright colors.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setReduceBrightColorsActivated(boolean activated) {
+ return mManager.setReduceBrightColorsActivated(activated);
+ }
+
+ /**
+ * Returns whether reduce bright colors is currently enabled.
+ *
+ * @hide
+ */
+ public boolean isReduceBrightColorsActivated() {
+ return mManager.isReduceBrightColorsActivated();
+ }
+
+ /**
+ * Set the strength level of bright color reduction to apply to the display.
+ *
+ * @param strength 0-100 (inclusive), where 100 is full strength
+ * @return whether the change was applied successfully
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setReduceBrightColorsStrength(@IntRange(from = 0, to = 100) int strength) {
+ return mManager.setReduceBrightColorsStrength(strength);
+ }
+
+ /**
+ * Gets the strength of the bright color reduction transform.
+ *
+ * @hide
+ */
+ public int getReduceBrightColorsStrength() {
+ return mManager.getReduceBrightColorsStrength();
+ }
+
+ /**
+ * Gets the brightness impact of the bright color reduction transform, as in the factor by which
+ * the current brightness (in nits) should be multiplied to obtain the brightness offset 'b'.
+ *
+ * @hide
+ */
+ public float getReduceBrightColorsOffsetFactor() {
+ return mManager.getReduceBrightColorsOffsetFactor();
+ }
+
+ /**
* Returns {@code true} if Night Display is supported by the device.
*
* @hide
@@ -478,6 +528,15 @@
}
/**
+ * Returns {@code true} if reduce bright colors is supported by the device.
+ *
+ * @hide
+ */
+ public static boolean isReduceBrightColorsAvailable(Context context) {
+ return context.getResources().getBoolean(R.bool.config_reduceBrightColorsAvailable);
+ }
+
+ /**
* Check if the color transforms are color accelerated. Some transforms are experimental only
* on non-accelerated platforms due to the performance implications.
*
@@ -678,6 +737,46 @@
}
}
+ boolean isReduceBrightColorsActivated() {
+ try {
+ return mCdm.isReduceBrightColorsActivated();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean setReduceBrightColorsActivated(boolean activated) {
+ try {
+ return mCdm.setReduceBrightColorsActivated(activated);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ int getReduceBrightColorsStrength() {
+ try {
+ return mCdm.getReduceBrightColorsStrength();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean setReduceBrightColorsStrength(int strength) {
+ try {
+ return mCdm.setReduceBrightColorsStrength(strength);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ float getReduceBrightColorsOffsetFactor() {
+ try {
+ return mCdm.getReduceBrightColorsOffsetFactor();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
int getColorMode() {
try {
return mCdm.getColorMode();
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
index 7b1033d..ef4f5f9 100644
--- a/core/java/android/hardware/display/IColorDisplayManager.aidl
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -45,4 +45,10 @@
boolean isDisplayWhiteBalanceEnabled();
boolean setDisplayWhiteBalanceEnabled(boolean enabled);
+
+ boolean isReduceBrightColorsActivated();
+ boolean setReduceBrightColorsActivated(boolean activated);
+ int getReduceBrightColorsStrength();
+ boolean setReduceBrightColorsStrength(int strength);
+ float getReduceBrightColorsOffsetFactor();
}
\ No newline at end of file
diff --git a/core/java/android/hardware/face/FaceAuthenticationFrame.java b/core/java/android/hardware/face/FaceAuthenticationFrame.java
new file mode 100644
index 0000000..f39d634
--- /dev/null
+++ b/core/java/android/hardware/face/FaceAuthenticationFrame.java
@@ -0,0 +1,75 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face authentication.
+ *
+ * @hide
+ */
+public final class FaceAuthenticationFrame implements Parcelable {
+ @NonNull private final FaceDataFrame mData;
+
+ /**
+ * Data model for a frame captured during face authentication.
+ *
+ * @param data Information about the current frame.
+ */
+ public FaceAuthenticationFrame(@NonNull FaceDataFrame data) {
+ mData = data;
+ }
+
+ /**
+ * @return Information about the current frame.
+ */
+ @NonNull
+ public FaceDataFrame getData() {
+ return mData;
+ }
+
+ private FaceAuthenticationFrame(@NonNull Parcel source) {
+ mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mData, flags);
+ }
+
+ public static final Creator<FaceAuthenticationFrame> CREATOR =
+ new Creator<FaceAuthenticationFrame>() {
+
+ @Override
+ public FaceAuthenticationFrame createFromParcel(Parcel source) {
+ return new FaceAuthenticationFrame(source);
+ }
+
+ @Override
+ public FaceAuthenticationFrame[] newArray(int size) {
+ return new FaceAuthenticationFrame[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceDataFrame.java b/core/java/android/hardware/face/FaceDataFrame.java
new file mode 100644
index 0000000..3a0e09b
--- /dev/null
+++ b/core/java/android/hardware/face/FaceDataFrame.java
@@ -0,0 +1,151 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+ *
+ * @hide
+ */
+public final class FaceDataFrame implements Parcelable {
+ private final int mAcquiredInfo;
+ private final int mVendorCode;
+ private final float mPan;
+ private final float mTilt;
+ private final float mDistance;
+ private final boolean mIsCancellable;
+
+ /**
+ * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+ *
+ * @param acquiredInfo An integer corresponding to a known acquired message.
+ * @param vendorCode An integer representing a custom vendor-specific message. Ignored unless
+ * {@code acquiredInfo} is {@code FACE_ACQUIRED_VENDOR}.
+ * @param pan The horizontal pan of the detected face. Values in the range [-1, 1] indicate a
+ * good capture.
+ * @param tilt The vertical tilt of the detected face. Values in the range [-1, 1] indicate a
+ * good capture.
+ * @param distance The distance of the detected face from the device. Values in the range
+ * [-1, 1] indicate a good capture.
+ * @param isCancellable Whether the ongoing face operation should be canceled.
+ */
+ public FaceDataFrame(
+ int acquiredInfo,
+ int vendorCode,
+ float pan,
+ float tilt,
+ float distance,
+ boolean isCancellable) {
+ mAcquiredInfo = acquiredInfo;
+ mVendorCode = vendorCode;
+ mPan = pan;
+ mTilt = tilt;
+ mDistance = distance;
+ mIsCancellable = isCancellable;
+ }
+
+ /**
+ * @return An integer corresponding to a known acquired message.
+ *
+ * @see android.hardware.biometrics.BiometricFaceConstants
+ */
+ public int getAcquiredInfo() {
+ return mAcquiredInfo;
+ }
+
+ /**
+ * @return An integer representing a custom vendor-specific message. Ignored unless
+ * {@code acquiredInfo} is {@link
+ * android.hardware.biometrics.BiometricFaceConstants#FACE_ACQUIRED_VENDOR}.
+ *
+ * @see android.hardware.biometrics.BiometricFaceConstants
+ */
+ public int getVendorCode() {
+ return mVendorCode;
+ }
+
+ /**
+ * @return The horizontal pan of the detected face. Values in the range [-1, 1] indicate a good
+ * capture.
+ */
+ public float getPan() {
+ return mPan;
+ }
+
+ /**
+ * @return The vertical tilt of the detected face. Values in the range [-1, 1] indicate a good
+ * capture.
+ */
+ public float getTilt() {
+ return mTilt;
+ }
+
+ /**
+ * @return The distance of the detected face from the device. Values in the range [-1, 1]
+ * indicate a good capture.
+ */
+ public float getDistance() {
+ return mDistance;
+ }
+
+ /**
+ * @return Whether the ongoing face operation should be canceled.
+ */
+ public boolean isCancellable() {
+ return mIsCancellable;
+ }
+
+ private FaceDataFrame(@NonNull Parcel source) {
+ mAcquiredInfo = source.readInt();
+ mVendorCode = source.readInt();
+ mPan = source.readFloat();
+ mTilt = source.readFloat();
+ mDistance = source.readFloat();
+ mIsCancellable = source.readBoolean();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mAcquiredInfo);
+ dest.writeInt(mVendorCode);
+ dest.writeFloat(mPan);
+ dest.writeFloat(mTilt);
+ dest.writeFloat(mDistance);
+ dest.writeBoolean(mIsCancellable);
+ }
+
+ public static final Creator<FaceDataFrame> CREATOR = new Creator<FaceDataFrame>() {
+ @Override
+ public FaceDataFrame createFromParcel(Parcel source) {
+ return new FaceDataFrame(source);
+ }
+
+ @Override
+ public FaceDataFrame[] newArray(int size) {
+ return new FaceDataFrame[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollCell.java b/core/java/android/hardware/face/FaceEnrollCell.java
new file mode 100644
index 0000000..8415419
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollCell.java
@@ -0,0 +1,96 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollCell implements Parcelable {
+ private final int mX;
+ private final int mY;
+ private final int mZ;
+
+ /**
+ * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+ *
+ * @param x The horizontal coordinate of this cell.
+ * @param y The vertical coordinate of this cell.
+ * @param z The depth coordinate of this cell.
+ */
+ public FaceEnrollCell(int x, int y, int z) {
+ mX = x;
+ mY = y;
+ mZ = z;
+ }
+
+ /**
+ * @return The horizontal coordinate of this cell.
+ */
+ public int getX() {
+ return mX;
+ }
+
+ /**
+ * @return The vertical coordinate of this cell.
+ */
+ public int getY() {
+ return mY;
+ }
+
+ /**
+ * @return The depth coordinate of this cell.
+ */
+ public int getZ() {
+ return mZ;
+ }
+
+ private FaceEnrollCell(@NonNull Parcel source) {
+ mX = source.readInt();
+ mY = source.readInt();
+ mZ = source.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mX);
+ dest.writeInt(mY);
+ dest.writeInt(mZ);
+ }
+
+ public static final Creator<FaceEnrollCell> CREATOR = new Creator<FaceEnrollCell>() {
+ @Override
+ public FaceEnrollCell createFromParcel(Parcel source) {
+ return new FaceEnrollCell(source);
+ }
+
+ @Override
+ public FaceEnrollCell[] newArray(int size) {
+ return new FaceEnrollCell[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollFrame.java b/core/java/android/hardware/face/FaceEnrollFrame.java
new file mode 100644
index 0000000..551139d
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollFrame.java
@@ -0,0 +1,103 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollFrame implements Parcelable {
+ @Nullable private final FaceEnrollCell mCell;
+ @FaceEnrollStage private final int mStage;
+ @NonNull private final FaceDataFrame mData;
+
+ /**
+ * Data model for a frame captured during face enrollment.
+ *
+ * @param cell The cell captured during this frame of enrollment, if any.
+ * @param stage An integer representing the current stage of enrollment.
+ * @param data Information about the current frame.
+ */
+ public FaceEnrollFrame(
+ @Nullable FaceEnrollCell cell,
+ @FaceEnrollStage int stage,
+ @NonNull FaceDataFrame data) {
+ mCell = cell;
+ mStage = stage;
+ mData = data;
+ }
+
+ /**
+ * @return The cell captured during this frame of enrollment, if any.
+ */
+ @Nullable
+ public FaceEnrollCell getCell() {
+ return mCell;
+ }
+
+ /**
+ * @return An integer representing the current stage of enrollment.
+ */
+ @FaceEnrollStage
+ public int getStage() {
+ return mStage;
+ }
+
+ /**
+ * @return Information about the current frame.
+ */
+ @NonNull
+ public FaceDataFrame getData() {
+ return mData;
+ }
+
+ private FaceEnrollFrame(@NonNull Parcel source) {
+ mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader());
+ mStage = source.readInt();
+ mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mCell, flags);
+ dest.writeInt(mStage);
+ dest.writeParcelable(mData, flags);
+ }
+
+ public static final Creator<FaceEnrollFrame> CREATOR = new Creator<FaceEnrollFrame>() {
+ @Override
+ public FaceEnrollFrame createFromParcel(Parcel source) {
+ return new FaceEnrollFrame(source);
+ }
+
+ @Override
+ public FaceEnrollFrame[] newArray(int size) {
+ return new FaceEnrollFrame[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java
new file mode 100644
index 0000000..03dba55
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollStage.java
@@ -0,0 +1,68 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A stage that may occur during face enrollment.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.SOURCE)
+@IntDef({
+ FaceEnrollStage.FIRST_FRAME_RECEIVED,
+ FaceEnrollStage.WAITING_FOR_CENTERING,
+ FaceEnrollStage.HOLD_STILL_IN_CENTER,
+ FaceEnrollStage.ENROLLING_MOVEMENT_1,
+ FaceEnrollStage.ENROLLING_MOVEMENT_2,
+ FaceEnrollStage.ENROLLMENT_FINISHED
+})
+public @interface FaceEnrollStage {
+ /**
+ * Enrollment has just begun. No action is needed from the user yet.
+ */
+ int FIRST_FRAME_RECEIVED = 0;
+
+ /**
+ * The user must center their face in the frame.
+ */
+ int WAITING_FOR_CENTERING = 1;
+
+ /**
+ * The user must keep their face centered in the frame.
+ */
+ int HOLD_STILL_IN_CENTER = 2;
+
+ /**
+ * The user must follow a first set of movement instructions.
+ */
+ int ENROLLING_MOVEMENT_1 = 3;
+
+ /**
+ * The user must follow a second set of movement instructions.
+ */
+ int ENROLLING_MOVEMENT_2 = 4;
+
+ /**
+ * Enrollment has completed. No more action is needed from the user.
+ */
+ int ENROLLMENT_FINISHED = 5;
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index d932865..188a2a4 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -93,17 +93,26 @@
private static final int MSG_UDFPS_POINTER_UP = 109;
/**
- * Request authentication with any single sensor.
* @hide
*/
- public static final int SENSOR_ID_ANY = -1;
+ public static final int ENROLL_FIND_SENSOR = 1;
+ /**
+ * @hide
+ */
+ public static final int ENROLL_ENROLL = 2;
/**
* @hide
*/
- @IntDef({SENSOR_ID_ANY})
+ @IntDef({ENROLL_FIND_SENSOR, ENROLL_ENROLL})
@Retention(RetentionPolicy.SOURCE)
- public @interface SensorId {}
+ public @interface EnrollReason {}
+
+ /**
+ * Request authentication with any single sensor.
+ * @hide
+ */
+ public static final int SENSOR_ID_ANY = -1;
private IFingerprintService mService;
private Context mContext;
@@ -508,8 +517,8 @@
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- @NonNull AuthenticationCallback callback, Handler handler, @SensorId int sensorId,
- int userId) {
+ @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) {
+
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
@@ -590,7 +599,7 @@
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
- EnrollmentCallback callback, boolean shouldLogMetrics) {
+ EnrollmentCallback callback, @EnrollReason int enrollReason) {
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentUserId();
}
@@ -611,7 +620,7 @@
try {
mEnrollmentCallback = callback;
mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
- mContext.getOpPackageName(), shouldLogMetrics);
+ mContext.getOpPackageName(), enrollReason);
} 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 try
@@ -653,15 +662,12 @@
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void generateChallenge(int userId, GenerateChallengeCallback callback) {
- final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
- getSensorPropertiesInternal();
- if (fingerprintSensorProperties.isEmpty()) {
+ final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor();
+ if (sensorProps == null) {
Slog.e(TAG, "No sensors");
return;
}
-
- final int sensorId = fingerprintSensorProperties.get(0).sensorId;
- generateChallenge(sensorId, userId, callback);
+ generateChallenge(sensorProps.sensorId, userId, callback);
}
/**
@@ -681,18 +687,18 @@
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void revokeChallenge(int userId, long challenge) {
- if (mService != null) try {
- final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
- getSensorPropertiesInternal();
- if (fingerprintSensorProperties.isEmpty()) {
- Slog.e(TAG, "No sensors");
- return;
+ if (mService != null) {
+ try {
+ final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor();
+ if (sensorProps == null) {
+ Slog.e(TAG, "No sensors");
+ return;
+ }
+ mService.revokeChallenge(mToken, sensorProps.sensorId, userId,
+ mContext.getOpPackageName(), challenge);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- final int sensorId = fingerprintSensorProperties.get(0).sensorId;
- mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(),
- challenge);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
}
@@ -1161,6 +1167,12 @@
}
}
+ @Nullable
+ private FingerprintSensorPropertiesInternal getFirstFingerprintSensor() {
+ final List<FingerprintSensorPropertiesInternal> allSensors = getSensorPropertiesInternal();
+ return allSensors.isEmpty() ? null : allSensors.get(0);
+ }
+
private void cancelEnrollment() {
if (mService != null) try {
mService.cancelEnrollment(mToken);
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 2650dc5..663a704 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -19,6 +19,8 @@
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
+import android.annotation.NonNull;
+import android.content.Context;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.Parcel;
@@ -81,10 +83,37 @@
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
@FingerprintSensorProperties.SensorType int sensorType,
boolean resetLockoutRequiresHardwareAuthToken) {
- // TODO: Value should be provided from the HAL
+ // TODO(b/179175438): Value should be provided from the HAL
this(sensorId, strength, maxEnrollmentsPerUser, sensorType,
- resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */,
- 1769 /* sensorLocationY */, 130 /* sensorRadius */);
+ resetLockoutRequiresHardwareAuthToken, 0 /* sensorLocationX */,
+ 0 /* sensorLocationY */, 0 /* sensorRadius */);
+ }
+
+ /**
+ * Initializes SensorProperties with specified values and values obtained from resources using
+ * context.
+ */
+ // TODO(b/179175438): Remove this constructor once all HALs move to AIDL.
+ public FingerprintSensorPropertiesInternal(@NonNull Context context, int sensorId,
+ @SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
+ @FingerprintSensorProperties.SensorType int sensorType,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ super(sensorId, strength, maxEnrollmentsPerUser);
+ this.sensorType = sensorType;
+ this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+
+ int[] props = context.getResources().getIntArray(
+ com.android.internal.R.array.config_udfps_sensor_props);
+ if (props != null && props.length == 3) {
+ this.sensorLocationX = props[0];
+ this.sensorLocationY = props[1];
+ this.sensorRadius = props[2];
+ } else {
+ // Fake coordinates that could be used for the fake UDFPS mode.
+ this.sensorLocationX = 540;
+ this.sensorLocationY = 1636;
+ this.sensorRadius = 130;
+ }
}
protected FingerprintSensorPropertiesInternal(Parcel in) {
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 3657a83..8888247 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -78,7 +78,7 @@
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
- String opPackageName, boolean shouldLogMetrics);
+ String opPackageName, int enrollReason);
// Cancel enrollment in progress
void cancelEnrollment(IBinder token);
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index c093489..81c7d89 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -21,10 +21,11 @@
*/
oneway interface IUdfpsOverlayController {
const int REASON_UNKNOWN = 0;
- const int REASON_ENROLL = 1;
- const int REASON_AUTH_BP = 2; // BiometricPrompt
- const int REASON_AUTH_FPM_KEYGUARD = 3; // FingerprintManager usage from Keyguard
- const int REASON_AUTH_FPM_OTHER = 4; // Other FingerprintManager usage
+ const int REASON_ENROLL_FIND_SENSOR = 1;
+ const int REASON_ENROLL_ENROLLING = 2;
+ const int REASON_AUTH_BP = 3; // BiometricPrompt
+ const int REASON_AUTH_FPM_KEYGUARD = 4; // FingerprintManager usage from Keyguard
+ const int REASON_AUTH_FPM_OTHER = 5; // Other FingerprintManager usage
// Shows the overlay.
void showUdfpsOverlay(int sensorId, int reason);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 44a2e97..7e2be01 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1732,7 +1732,7 @@
// If app window has portrait orientation, regardless of what display orientation
// is, IME shouldn't use fullscreen-mode.
|| (mInputEditorInfo.internalImeOptions
- & EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT) != 0) {
+ & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0) {
return false;
}
return true;
diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/inputmethodservice/OWNERS
+++ b/core/java/android/inputmethodservice/OWNERS
@@ -2,3 +2,5 @@
set noparent
include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 84a2acc..b016ed6 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -82,4 +82,5 @@
boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
boolean isUidRestrictedOnMeteredNetworks(int uid);
+ boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted);
}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 06d0fc1..5d8122b 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -22,11 +22,12 @@
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.os.Build;
import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.NetworkIdentityUtils;
+
import java.util.Objects;
/**
@@ -90,7 +91,8 @@
builder.append(mSubType);
}
if (mSubscriberId != null) {
- builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
+ builder.append(", subscriberId=")
+ .append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
@@ -111,7 +113,8 @@
// Not dumping mSubType, subtypes are no longer supported.
if (mSubscriberId != null) {
- proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
+ proto.write(NetworkIdentityProto.SUBSCRIBER_ID,
+ NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
@@ -150,32 +153,6 @@
}
/**
- * Scrub given IMSI on production builds.
- */
- public static String scrubSubscriberId(String subscriberId) {
- if (Build.IS_ENG) {
- return subscriberId;
- } else if (subscriberId != null) {
- // TODO: parse this as MCC+MNC instead of hard-coding
- return subscriberId.substring(0, Math.min(6, subscriberId.length())) + "...";
- } else {
- return "null";
- }
- }
-
- /**
- * Scrub given IMSI on production builds.
- */
- public static String[] scrubSubscriberId(String[] subscriberId) {
- if (subscriberId == null) return null;
- final String[] res = new String[subscriberId.length];
- for (int i = 0; i < res.length; i++) {
- res[i] = NetworkIdentity.scrubSubscriberId(subscriberId[i]);
- }
- return res;
- }
-
- /**
* Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType},
* assuming that any mobile networks are using the current IMSI. The subType if applicable,
* should be set as one of the TelephonyManager.NETWORK_TYPE_* constants, or
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index ed169e7..3e6237d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -464,6 +464,31 @@
}
/**
+ * Figure out if networking is blocked for a given set of conditions.
+ *
+ * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+ * take any locks.
+ *
+ * @param uid The target uid.
+ * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+ * @param isNetworkMetered True if the network is metered.
+ * @param isBackgroundRestricted True if data saver is enabled.
+ *
+ * @return true if networking is blocked for the UID under the specified conditions.
+ *
+ * @hide
+ */
+ public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+ boolean isNetworkMetered, boolean isBackgroundRestricted) {
+ try {
+ return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Check that the given uid is restricted from doing networking on metered networks.
*
* @param uid The target uid.
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index dc33cc7..aa61e03 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -48,6 +48,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -296,11 +297,11 @@
builder.append("matchRule=").append(getMatchRuleName(mMatchRule));
if (mSubscriberId != null) {
builder.append(", subscriberId=").append(
- NetworkIdentity.scrubSubscriberId(mSubscriberId));
+ NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mMatchSubscriberIds != null) {
builder.append(", matchSubscriberIds=").append(
- Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds)));
+ Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index 6a8e3f9..5e56164 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -18,14 +18,14 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.os.Bundle;
import android.os.Parcelable;
-import android.util.SparseArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
/** @hide */
@@ -60,16 +60,16 @@
public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
@NonNull
- private final SparseArray<List<String>> mNetworkMappings;
+ private final Bundle mNetworkMappings;
@NonNull
- public SparseArray<List<String>> getNetworkPreferences() {
- return mNetworkMappings.clone();
+ public Map<String, Integer> getNetworkPreferences() {
+ return convertToUnmodifiableMap(mNetworkMappings);
}
- private OemNetworkPreferences(@NonNull SparseArray<List<String>> networkMappings) {
+ private OemNetworkPreferences(@NonNull final Bundle networkMappings) {
Objects.requireNonNull(networkMappings);
- mNetworkMappings = networkMappings.clone();
+ mNetworkMappings = (Bundle) networkMappings.clone();
}
@Override
@@ -99,26 +99,45 @@
* @hide
*/
public static final class Builder {
- private final SparseArray<List<String>> mNetworkMappings;
+ private final Bundle mNetworkMappings;
public Builder() {
- mNetworkMappings = new SparseArray<>();
+ mNetworkMappings = new Bundle();
+ }
+
+ public Builder(@NonNull final OemNetworkPreferences preferences) {
+ Objects.requireNonNull(preferences);
+ mNetworkMappings = (Bundle) preferences.mNetworkMappings.clone();
}
/**
- * Add a network preference for a list of packages.
+ * Add a network preference for a given package. Previously stored values for the given
+ * package will be overwritten.
*
- * @param preference the desired network preference to use
- * @param packages full package names (e.g.: "com.google.apps.contacts") for apps to use
- * the given preference
+ * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app
+ * to use the given preference
+ * @param preference the desired network preference to use
* @return The builder to facilitate chaining.
*/
@NonNull
- public Builder addNetworkPreference(@OemNetworkPreference final int preference,
- @NonNull List<String> packages) {
- Objects.requireNonNull(packages);
- mNetworkMappings.put(preference,
- Collections.unmodifiableList(new ArrayList<>(packages)));
+ public Builder addNetworkPreference(@NonNull final String packageName,
+ @OemNetworkPreference final int preference) {
+ Objects.requireNonNull(packageName);
+ mNetworkMappings.putInt(packageName, preference);
+ return this;
+ }
+
+ /**
+ * Remove a network preference for a given package.
+ *
+ * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app to
+ * remove a preference for.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder removeNetworkPreference(@NonNull final String packageName) {
+ Objects.requireNonNull(packageName);
+ mNetworkMappings.remove(packageName);
return this;
}
@@ -131,6 +150,14 @@
}
}
+ private static Map<String, Integer> convertToUnmodifiableMap(@NonNull final Bundle bundle) {
+ final Map<String, Integer> networkPreferences = new HashMap<>();
+ for (final String key : bundle.keySet()) {
+ networkPreferences.put(key, bundle.getInt(key));
+ }
+ return Collections.unmodifiableMap(networkPreferences);
+ }
+
/** @hide */
@IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
OEM_NETWORK_PREFERENCE_DEFAULT,
@@ -168,7 +195,7 @@
@Override
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- dest.writeSparseArray(mNetworkMappings);
+ dest.writeBundle(mNetworkMappings);
}
@Override
@@ -187,7 +214,7 @@
@Override
public OemNetworkPreferences createFromParcel(@NonNull android.os.Parcel in) {
return new OemNetworkPreferences(
- in.readSparseArray(getClass().getClassLoader()));
+ in.readBundle(getClass().getClassLoader()));
}
};
}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 71d2177..f2b466d 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -70,17 +70,6 @@
public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
/**
- * Modeled power components are used for testing only. They are returned if the
- * {@link BatteryUsageStatsQuery#FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED} is set.
- * The modeled power components are retrieved with {@link #getConsumedPowerForCustomComponent}.
- * The ID of a modeled power component is calculated as
- * (FIRST_MODELED_POWER_COMPONENT_ID + powerComponentId), e.g.
- * FIRST_MODELED_POWER_COMPONENT_ID + POWER_COMPONENT_CPU.
- */
- public static final int FIRST_MODELED_POWER_COMPONENT_ID = 10000;
- public static final int LAST_MODELED_POWER_COMPONENT_ID = 19999;
-
- /**
* Time usage component, describing the particular part of the system
* that was used for the corresponding amount of time.
*
@@ -182,10 +171,9 @@
protected abstract static class BaseBuilder<T extends BaseBuilder<?>> {
final PowerComponents.Builder mPowerComponentsBuilder;
- public BaseBuilder(int customPowerComponentCount, int customTimeComponentCount,
- boolean includeModeledComponents) {
+ public BaseBuilder(int customPowerComponentCount, int customTimeComponentCount) {
mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentCount,
- customTimeComponentCount, includeModeledComponents);
+ customTimeComponentCount);
}
/**
@@ -216,16 +204,6 @@
}
/**
- * Sets the total amount of power consumed since BatteryStats reset, mAh.
- */
- @SuppressWarnings("unchecked")
- @NonNull
- public T setConsumedPower(double consumedPower) {
- mPowerComponentsBuilder.setTotalPowerConsumed(consumedPower);
- return (T) this;
- }
-
- /**
* Sets the amount of time used by the specified component, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the time component, e.g.
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b846142..cc86a60 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1556,8 +1556,7 @@
public int statSoftIrqTime;
public int statIdlTime;
- // Platform-level low power state stats
- public String statPlatformIdleState;
+ // Low power state stats
public String statSubsystemPowerState;
public HistoryStepDetails() {
@@ -1589,7 +1588,6 @@
out.writeInt(statIrqTime);
out.writeInt(statSoftIrqTime);
out.writeInt(statIdlTime);
- out.writeString(statPlatformIdleState);
out.writeString(statSubsystemPowerState);
}
@@ -1611,7 +1609,6 @@
statIrqTime = in.readInt();
statSoftIrqTime = in.readInt();
statIdlTime = in.readInt();
- statPlatformIdleState = in.readString();
statSubsystemPowerState = in.readString();
}
}
@@ -1656,6 +1653,7 @@
public byte batteryPlugType;
public short batteryTemperature;
+ // Battery voltage in millivolts (mV).
@UnsupportedAppUsage
public char batteryVoltage;
@@ -6599,9 +6597,6 @@
item.append(sb);
item.append(")");
}
- item.append(", PlatformIdleStat ");
- item.append(rec.stepDetails.statPlatformIdleState);
- item.append("\n");
item.append(", SubsystemPowerState ");
item.append(rec.stepDetails.statSubsystemPowerState);
@@ -6639,12 +6634,6 @@
item.append(',');
item.append(rec.stepDetails.statIdlTime);
item.append(',');
- if (rec.stepDetails.statPlatformIdleState != null) {
- item.append(rec.stepDetails.statPlatformIdleState);
- if (rec.stepDetails.statSubsystemPowerState != null) {
- item.append(',');
- }
- }
if (rec.stepDetails.statSubsystemPowerState != null) {
item.append(rec.stepDetails.statSubsystemPowerState);
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index f21a812..04e529e 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -32,6 +32,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/**
* This class provides an API surface for internal system components to report events that are
@@ -183,8 +184,20 @@
@RequiresPermission(android.Manifest.permission.BATTERY_STATS)
@NonNull
public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+ return getBatteryUsageStats(List.of(query)).get(0);
+ }
+
+ /**
+ * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+ * and per-UID basis.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+ @NonNull
+ public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
try {
- return mBatteryStats.getBatteryUsageStats(query);
+ return mBatteryStats.getBatteryUsageStats(queries);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index a6df87d..af8e8de 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -114,7 +114,6 @@
public static final class Builder {
private final int mCustomPowerComponentCount;
private final int mCustomTimeComponentCount;
- private final boolean mIncludeModeledComponents;
private double mConsumedPower;
private int mDischargePercentage;
private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders =
@@ -122,11 +121,9 @@
private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders =
new SparseArray<>();
- public Builder(int customPowerComponentCount, int customTimeComponentCount,
- boolean includeModeledComponents) {
+ public Builder(int customPowerComponentCount, int customTimeComponentCount) {
mCustomPowerComponentCount = customPowerComponentCount;
mCustomTimeComponentCount = customTimeComponentCount;
- mIncludeModeledComponents = includeModeledComponents;
}
/**
@@ -169,7 +166,7 @@
UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
if (builder == null) {
builder = new UidBatteryConsumer.Builder(mCustomPowerComponentCount,
- mCustomTimeComponentCount, mIncludeModeledComponents, batteryStatsUid);
+ mCustomTimeComponentCount, batteryStatsUid);
mUidBatteryConsumerBuilders.put(uid, builder);
}
return builder;
@@ -185,7 +182,7 @@
SystemBatteryConsumer.Builder builder = mSystemBatteryConsumerBuilders.get(drainType);
if (builder == null) {
builder = new SystemBatteryConsumer.Builder(mCustomPowerComponentCount,
- mCustomTimeComponentCount, mIncludeModeledComponents, drainType);
+ mCustomTimeComponentCount, drainType);
mSystemBatteryConsumerBuilders.put(drainType, builder);
}
return builder;
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 9a00679..48e7389 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -38,18 +38,19 @@
* @hide
*/
@IntDef(flag = true, prefix = { "FLAG_BATTERY_USAGE_STATS_" }, value = {
- FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED,
+ FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface BatteryUsageStatsFlags {}
/**
- * Indicates that modeled battery usage stats should be returned along with
- * measured ones.
+ * Indicates that power estimations should be based on the usage time and
+ * average power constants provided in the PowerProfile, even if on-device power monitoring
+ * is available.
*
* @hide
*/
- public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED = 1;
+ public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 1;
private final int mFlags;
@@ -112,11 +113,13 @@
}
/**
- * Requests to include modeled battery usage stats along with measured ones.
+ * Requests to return modeled battery usage stats only, even if on-device
+ * power monitoring data is available.
+ *
* Should only be used for testing and debugging.
*/
- public Builder includeModeled() {
- mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED;
+ public Builder powerProfileModeledOnly() {
+ mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL;
return this;
}
}
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 0185ba4..16d041a 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -547,7 +547,8 @@
}
try {
- return transactNative(code, data, reply, flags);
+ boolean replyOwnsNative = (reply == null) ? false : reply.ownsNativeParcelObject();
+ return transactNative(code, data, reply, replyOwnsNative, flags);
} finally {
AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
@@ -572,7 +573,7 @@
* Native implementation of transact() for proxies
*/
public native boolean transactNative(int code, Parcel data, Parcel reply,
- int flags) throws RemoteException;
+ boolean replyOwnsNativeParcelObject, int flags) throws RemoteException;
/**
* See {@link IBinder#linkToDeath(DeathRecipient, int)}
*/
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index fc65090..5f5a910 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3729,4 +3729,9 @@
public long getBlobAshmemSize() {
return nativeGetBlobAshmemSize(mNativePtr);
}
+
+ /** @hide */
+ /*package*/ boolean ownsNativeParcelObject() {
+ return mOwnsNativeParcelObject;
+ }
}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 18dca68..1337d55 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -33,22 +33,21 @@
private final double[] mPowerComponents;
private final long[] mTimeComponents;
private final int mCustomPowerComponentCount;
- private final int mModeledPowerComponentOffset;
PowerComponents(@NonNull Builder builder) {
- mTotalPowerConsumed = builder.mTotalPowerConsumed;
mCustomPowerComponentCount = builder.mCustomPowerComponentCount;
- mModeledPowerComponentOffset = builder.mModeledPowerComponentOffset;
mPowerComponents = builder.mPowerComponents;
mTimeComponents = builder.mTimeComponents;
+ double totalPower = 0;
+ for (int i = mPowerComponents.length - 1; i >= 0; i--) {
+ totalPower += mPowerComponents[i];
+ }
+ mTotalPowerConsumed = totalPower;
}
PowerComponents(@NonNull Parcel source) {
mTotalPowerConsumed = source.readDouble();
mCustomPowerComponentCount = source.readInt();
- mModeledPowerComponentOffset =
- BatteryConsumer.POWER_COMPONENT_COUNT + mCustomPowerComponentCount
- - BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID;
mPowerComponents = source.createDoubleArray();
mTimeComponents = source.createLongArray();
}
@@ -102,14 +101,6 @@
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
}
- } else if (componentId >= BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
- && componentId < BatteryConsumer.LAST_MODELED_POWER_COMPONENT_ID) {
- try {
- return mPowerComponents[mModeledPowerComponentOffset + componentId];
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IllegalArgumentException(
- "Unsupported modeled power component ID: " + componentId);
- }
} else {
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
@@ -158,38 +149,20 @@
* Builder for PowerComponents.
*/
static final class Builder {
- private double mTotalPowerConsumed;
private final double[] mPowerComponents;
private final int mCustomPowerComponentCount;
private final long[] mTimeComponents;
- private final int mModeledPowerComponentOffset;
- Builder(int customPowerComponentCount, int customTimeComponentCount,
- boolean includeModeledPowerComponents) {
+ Builder(int customPowerComponentCount, int customTimeComponentCount) {
mCustomPowerComponentCount = customPowerComponentCount;
int powerComponentCount =
BatteryConsumer.POWER_COMPONENT_COUNT + customPowerComponentCount;
- if (includeModeledPowerComponents) {
- powerComponentCount += BatteryConsumer.POWER_COMPONENT_COUNT;
- }
mPowerComponents = new double[powerComponentCount];
- mModeledPowerComponentOffset =
- BatteryConsumer.POWER_COMPONENT_COUNT + mCustomPowerComponentCount
- - BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID;
mTimeComponents =
new long[BatteryConsumer.TIME_COMPONENT_COUNT + customTimeComponentCount];
}
/**
- * Sets the sum amount of power consumed since BatteryStats reset.
- */
- @NonNull
- public Builder setTotalPowerConsumed(double totalPowerConsumed) {
- mTotalPowerConsumed = totalPowerConsumed;
- return this;
- }
-
- /**
* Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
*
* @param componentId The ID of the power component, e.g.
@@ -228,14 +201,6 @@
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
}
- } else if (componentId >= BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
- && componentId < BatteryConsumer.LAST_MODELED_POWER_COMPONENT_ID) {
- try {
- mPowerComponents[mModeledPowerComponentOffset + componentId] = componentPower;
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IllegalArgumentException(
- "Unsupported modeled power component ID: " + componentId);
- }
} else {
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java
index 49bf084..fc4aa93 100644
--- a/core/java/android/os/SystemBatteryConsumer.java
+++ b/core/java/android/os/SystemBatteryConsumer.java
@@ -123,8 +123,8 @@
private List<UidBatteryConsumer.Builder> mUidBatteryConsumers;
Builder(int customPowerComponentCount, int customTimeComponentCount,
- boolean includeModeledComponents, @DrainType int drainType) {
- super(customPowerComponentCount, customTimeComponentCount, includeModeledComponents);
+ @DrainType int drainType) {
+ super(customPowerComponentCount, customTimeComponentCount);
mDrainType = drainType;
}
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 3161766..3ffaa9e 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -98,8 +98,8 @@
private boolean mSystemComponent;
public Builder(int customPowerComponentCount, int customTimeComponentCount,
- boolean includeModeledComponents, BatteryStats.Uid batteryStatsUid) {
- super(customPowerComponentCount, customTimeComponentCount, includeModeledComponents);
+ BatteryStats.Uid batteryStatsUid) {
+ super(customPowerComponentCount, customTimeComponentCount);
mBatteryStatsUid = batteryStatsUid;
mUid = batteryStatsUid.getUid();
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 7e50ebc..2119e7b 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -326,6 +326,19 @@
}
/**
+ * Returns the uid that is composed from the userHandle and the appId.
+ *
+ * @param userHandle the UserHandle to compose the uid
+ * @param appId the AppId to compose the uid
+ * @return the uid that is composed from the userHandle and the appId
+ * @hide
+ */
+ @SystemApi
+ public static int getUid(@NonNull UserHandle userHandle, @AppIdInt int appId) {
+ return getUid(userHandle.getIdentifier(), appId);
+ }
+
+ /**
* Returns the app id (or base uid) for a given uid, stripping out the user id from it.
* @hide
*/
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 77183ac..ea1ce37 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1694,10 +1694,13 @@
}
/**
- * @hide
- * @return Whether the device is running in a headless system user mode. It means the headless
- * user (system user) runs system services and system UI, but is not associated with any real
- * person. Secondary users can be created to be associated with real person.
+ * Checks whether the device is running in a headless system user mode.
+ *
+ * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system
+ * services and some system UI, but it is not associated with any real person and additional
+ * users must be created to be associated with real persons.
+ *
+ * @return whether the device is running in a headless system user mode.
*/
public static boolean isHeadlessSystemUserMode() {
return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER;
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 7e7057f..0ff68fc 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -304,20 +304,16 @@
}
/**
- * Called when a callback wants to stop listen to the loading progress of an installed package.
- * Decrease the count of the callbacks on the associated to the corresponding storage.
- * If the count becomes zero, unregister the storage listener.
+ * Called to stop all listeners from listening to loading progress of an installed package.
* @param codePath Path of the installed package
- * @return True if the package name and associated storage id are valid. False otherwise.
*/
- public boolean unregisterLoadingProgressCallback(@NonNull String codePath,
- @NonNull IPackageLoadingProgressCallback callback) {
+ public void unregisterLoadingProgressCallbacks(@NonNull String codePath) {
final IncrementalStorage storage = openStorage(codePath);
if (storage == null) {
// storage does not exist, package not installed
- return false;
+ return;
}
- return mLoadingProgressCallbacks.unregisterCallback(storage, callback);
+ mLoadingProgressCallbacks.cleanUpCallbacks(storage);
}
private static class LoadingProgressCallbacks extends IStorageLoadingProgressListener.Stub {
@@ -325,7 +321,6 @@
private final SparseArray<RemoteCallbackList<IPackageLoadingProgressCallback>> mCallbacks =
new SparseArray<>();
- // TODO(b/165841827): disable callbacks when app state changes to fully loaded
public void cleanUpCallbacks(@NonNull IncrementalStorage storage) {
final int storageId = storage.getId();
final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS
index cb1509a..cb06515 100644
--- a/core/java/android/provider/OWNERS
+++ b/core/java/android/provider/OWNERS
@@ -1,5 +1,6 @@
per-file *BlockedNumber* = file:/telephony/OWNERS
per-file *Telephony* = file:/telephony/OWNERS
+per-file *SimPhonebook* = file:/telephony/OWNERS
per-file *CallLog* = file:platform/packages/providers/ContactsProvider:/OWNERS
per-file *Contacts* = file:platform/packages/providers/ContactsProvider:/OWNERS
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a29fcb1..9603f4d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13580,6 +13580,28 @@
public static final String POWER_BUTTON_VERY_LONG_PRESS =
"power_button_very_long_press";
+
+ /**
+ * Keyguard should be on the left hand side of the screen, for wide screen layouts.
+ *
+ * @hide
+ */
+ public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0;
+
+ /**
+ * Keyguard should be on the right hand side of the screen, for wide screen layouts.
+ *
+ * @hide
+ */
+ public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1;
+ /**
+ * In one handed mode, which side the keyguard should be on. Allowable values are one of
+ * the ONE_HANDED_KEYGUARD_SIDE_* constants.
+ *
+ * @hide
+ */
+ public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
+
/**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
new file mode 100644
index 0000000..2efc212
--- /dev/null
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN_PATH_SEGMENT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The contract between the provider of contact records on the device's SIM cards and applications.
+ * Contains definitions of the supported URIs and columns.
+ *
+ * <p>This content provider does not support any of the QUERY_ARG_SQL* bundle arguments. An
+ * IllegalArgumentException will be thrown if these are included.
+ */
+public final class SimPhonebookContract {
+
+ /** The authority for the SIM phonebook provider. */
+ public static final String AUTHORITY = "com.android.simphonebook";
+ /** The content:// style uri to the authority for the SIM phonebook provider. */
+ @NonNull
+ public static final Uri AUTHORITY_URI = Uri.parse("content://com.android.simphonebook");
+ /**
+ * The Uri path element used to indicate that the following path segment is a subscription ID
+ * for the SIM card that will be operated on.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+
+ private SimPhonebookContract() {
+ }
+
+ /**
+ * Returns the Uri path segment used to reference the specified elementary file type for Uris
+ * returned by this API.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
+ switch (efType) {
+ case EF_ADN:
+ return EF_ADN_PATH_SEGMENT;
+ case EF_FDN:
+ return EF_FDN_PATH_SEGMENT;
+ case EF_SDN:
+ return EF_SDN_PATH_SEGMENT;
+ default:
+ throw new IllegalArgumentException("Unsupported EfType " + efType);
+ }
+ }
+
+ /** Constants for the contact records on a SIM card. */
+ public static final class SimRecords {
+
+ /**
+ * The subscription ID of the SIM the record is from.
+ *
+ * @see SubscriptionInfo#getSubscriptionId()
+ */
+ public static final String SUBSCRIPTION_ID = "subscription_id";
+ /**
+ * The type of the elementary file the record is from.
+ *
+ * @see ElementaryFiles#EF_ADN
+ * @see ElementaryFiles#EF_FDN
+ * @see ElementaryFiles#EF_SDN
+ */
+ public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+ /**
+ * The 1-based offset of the record in the elementary file that contains it.
+ *
+ * <p>This can be used to access individual SIM records by appending it to the
+ * elementary file URIs but it is not like a normal database ID because it is not
+ * auto-incrementing and it is not unique across SIM cards or elementary files. Hence, care
+ * should be taken when using it to ensure that it is applied to the correct SIM and EF.
+ *
+ * @see #getItemUri(int, int, int)
+ */
+ public static final String RECORD_NUMBER = "record_number";
+ /**
+ * The name for this record.
+ *
+ * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+ * exceeds the maximum supported length or contains unsupported characters.
+ * {@link #validateName(ContentResolver, int, int, String)} )} can be used to
+ * check whether the name is supported.
+ *
+ * @see ElementaryFiles#NAME_MAX_LENGTH
+ * @see #validateName(ContentResolver, int, int, String) )
+ */
+ public static final String NAME = "name";
+ /**
+ * The phone number for this record.
+ *
+ * <p>Only dialable characters are supported.
+ *
+ * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+ * exceeds the maximum supported length or contains unsupported characters.
+ *
+ * @see ElementaryFiles#PHONE_NUMBER_MAX_LENGTH
+ * @see android.telephony.PhoneNumberUtils#isDialable(char)
+ */
+ public static final String PHONE_NUMBER = "phone_number";
+
+ /** The MIME type of a CONTENT_URI subdirectory of a single SIM record. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+ /** The MIME type of CONTENT_URI providing a directory of SIM records. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+
+ /**
+ * The path segment that is appended to {@link #getContentUri(int, int)} which indicates
+ * that the following path segment contains a name to be validated.
+ *
+ * @hide
+ * @see #validateName(ContentResolver, int, int, String)
+ */
+ @SystemApi
+ public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+
+ /**
+ * The key for a cursor extra that contains the result of a validate name query.
+ *
+ * @hide
+ * @see #validateName(ContentResolver, int, int, String)
+ */
+ @SystemApi
+ public static final String EXTRA_NAME_VALIDATION_RESULT =
+ "android.provider.extra.NAME_VALIDATION_RESULT";
+
+
+ /**
+ * Key for the PIN2 needed to modify FDN record that should be passed in the Bundle
+ * passed to {@link ContentResolver#insert(Uri, ContentValues, Bundle)},
+ * {@link ContentResolver#update(Uri, ContentValues, Bundle)}
+ * and {@link ContentResolver#delete(Uri, Bundle)}.
+ *
+ * <p>Modifying FDN records also requires either
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
+ * {@link TelephonyManager#hasCarrierPrivileges()}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+
+ private SimRecords() {
+ }
+
+ /**
+ * Returns the content Uri for the specified elementary file on the specified SIM.
+ *
+ * <p>When queried this Uri will return all of the contact records in the specified
+ * elementary file on the specified SIM. The available subscriptionIds and efTypes can
+ * be discovered by querying {@link ElementaryFiles#CONTENT_URI}.
+ *
+ * <p>If a SIM with the provided subscription ID does not exist or the SIM with the provided
+ * subscription ID doesn't support the specified entity file then queries will return
+ * and empty cursor and inserts will throw an {@link IllegalArgumentException}
+ *
+ * @param subscriptionId the subscriptionId of the SIM card that this Uri will reference
+ * @param efType the elementary file on the SIM that this Uri will reference
+ * @see ElementaryFiles#EF_ADN
+ * @see ElementaryFiles#EF_FDN
+ * @see ElementaryFiles#EF_SDN
+ */
+ @NonNull
+ public static Uri getContentUri(int subscriptionId, @ElementaryFiles.EfType int efType) {
+ return buildContentUri(subscriptionId, efType).build();
+ }
+
+ /**
+ * Content Uri for the specific SIM record with the provided {@link #RECORD_NUMBER}.
+ *
+ * <p>When queried this will return the record identified by the provided arguments.
+ *
+ * <p>For a non-existent record:
+ * <ul>
+ * <li>query will return an empty cursor</li>
+ * <li>update will return 0</li>
+ * <li>delete will return 0</li>
+ * </ul>
+ *
+ * @param subscriptionId the subscription ID of the SIM containing the record. If no SIM
+ * with this subscription ID exists then it will be treated as a
+ * non-existent record
+ * @param efType the elementary file type containing the record. If the specified
+ * SIM doesn't support this elementary file then it will be treated
+ * as a non-existent record.
+ * @param recordNumber the record number of the record this Uri should reference. This
+ * must be greater than 0. If there is no record with this record
+ * number in the specified entity file then it will be treated as a
+ * non-existent record.
+ */
+ @NonNull
+ public static Uri getItemUri(
+ int subscriptionId, @ElementaryFiles.EfType int efType, int recordNumber) {
+ // Elementary file record indices are 1-based.
+ Preconditions.checkArgument(recordNumber > 0, "Invalid recordNumber");
+
+ return buildContentUri(subscriptionId, efType)
+ .appendPath(String.valueOf(recordNumber))
+ .build();
+ }
+
+ /**
+ * Validates a value that is being provided for the {@link #NAME} column.
+ *
+ * <p>The return value can be used to check if the name is valid. If it is not valid then
+ * inserts and updates to the specified elementary file that use the provided name value
+ * will throw an {@link IllegalArgumentException}.
+ *
+ * <p>If the specified SIM or elementary file don't exist then
+ * {@link NameValidationResult#getMaxEncodedLength()} will be zero and
+ * {@link NameValidationResult#isValid()} will return false.
+ */
+ @NonNull
+ @WorkerThread
+ public static NameValidationResult validateName(
+ @NonNull ContentResolver resolver, int subscriptionId,
+ @ElementaryFiles.EfType int efType,
+ @NonNull String name) {
+ Bundle queryArgs = new Bundle();
+ queryArgs.putString(SimRecords.NAME, name);
+ try (Cursor cursor =
+ resolver.query(buildContentUri(subscriptionId, efType)
+ .appendPath(VALIDATE_NAME_PATH_SEGMENT)
+ .build(), null, queryArgs, null)) {
+ NameValidationResult result = cursor.getExtras()
+ .getParcelable(EXTRA_NAME_VALIDATION_RESULT);
+ return result != null ? result : new NameValidationResult(name, "", 0, 0);
+ }
+ }
+
+ private static Uri.Builder buildContentUri(
+ int subscriptionId, @ElementaryFiles.EfType int efType) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(AUTHORITY)
+ .appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+ .appendPath(String.valueOf(subscriptionId))
+ .appendPath(getEfUriPath(efType));
+ }
+
+ /** Contains details about the validity of a value provided for the {@link #NAME} column. */
+ public static final class NameValidationResult implements Parcelable {
+
+ @NonNull
+ public static final Creator<NameValidationResult> CREATOR =
+ new Creator<NameValidationResult>() {
+
+ @Override
+ public NameValidationResult createFromParcel(@NonNull Parcel in) {
+ return new NameValidationResult(in);
+ }
+
+ @NonNull
+ @Override
+ public NameValidationResult[] newArray(int size) {
+ return new NameValidationResult[size];
+ }
+ };
+
+ private final String mName;
+ private final String mSanitizedName;
+ private final int mEncodedLength;
+ private final int mMaxEncodedLength;
+
+ /** Creates a new instance from the provided values. */
+ public NameValidationResult(@NonNull String name, @NonNull String sanitizedName,
+ int encodedLength, int maxEncodedLength) {
+ this.mName = Objects.requireNonNull(name);
+ this.mSanitizedName = Objects.requireNonNull(sanitizedName);
+ this.mEncodedLength = encodedLength;
+ this.mMaxEncodedLength = maxEncodedLength;
+ }
+
+ private NameValidationResult(Parcel in) {
+ this(in.readString(), in.readString(), in.readInt(), in.readInt());
+ }
+
+ /** Returns the original name that is being validated. */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns a sanitized copy of the original name with all unsupported characters
+ * replaced with spaces.
+ */
+ @NonNull
+ public String getSanitizedName() {
+ return mSanitizedName;
+ }
+
+ /**
+ * Returns whether the original name isValid.
+ *
+ * <p>If this returns false then inserts and updates using the name will throw an
+ * {@link IllegalArgumentException}
+ */
+ public boolean isValid() {
+ return mMaxEncodedLength > 0 && mEncodedLength <= mMaxEncodedLength
+ && Objects.equals(
+ mName, mSanitizedName);
+ }
+
+ /** Returns whether the character at the specified position is supported by the SIM. */
+ public boolean isSupportedCharacter(int position) {
+ return mName.charAt(position) == mSanitizedName.charAt(position);
+ }
+
+ /**
+ * Returns the number of bytes required to save the name.
+ *
+ * <p>This may be more than the number of characters in the name.
+ */
+ public int getEncodedLength() {
+ return mEncodedLength;
+ }
+
+ /**
+ * Returns the maximum number of bytes that are supported for the name.
+ *
+ * @see ElementaryFiles#NAME_MAX_LENGTH
+ */
+ public int getMaxEncodedLength() {
+ return mMaxEncodedLength;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeString(mSanitizedName);
+ dest.writeInt(mEncodedLength);
+ dest.writeInt(mMaxEncodedLength);
+ }
+ }
+ }
+
+ /** Constants for metadata about the elementary files of the SIM cards in the phone. */
+ public static final class ElementaryFiles {
+
+ /** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */
+ public static final String SLOT_INDEX = "slot_index";
+ /** {@link SubscriptionInfo#getSubscriptionId()} of the SIM for this row. */
+ public static final String SUBSCRIPTION_ID = "subscription_id";
+ /**
+ * The elementary file type for this row.
+ *
+ * @see ElementaryFiles#EF_ADN
+ * @see ElementaryFiles#EF_FDN
+ * @see ElementaryFiles#EF_SDN
+ */
+ public static final String EF_TYPE = "ef_type";
+ /** The maximum number of records supported by the elementary file. */
+ public static final String MAX_RECORDS = "max_records";
+ /** Count of the number of records that are currently stored in the elementary file. */
+ public static final String RECORD_COUNT = "record_count";
+ /** The maximum length supported for the name of a record in the elementary file. */
+ public static final String NAME_MAX_LENGTH = "name_max_length";
+ /**
+ * The maximum length supported for the phone number of a record in the elementary file.
+ */
+ public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+
+ /**
+ * A value for an elementary file that is not recognized.
+ *
+ * <p>Generally this should be ignored. If new values are added then this will be used
+ * for apps that target SDKs where they aren't defined.
+ */
+ public static final int EF_UNKNOWN = 0;
+ /**
+ * Type for accessing records in the "abbreviated dialing number" (ADN) elementary file on
+ * the SIM.
+ *
+ * <p>ADN records are typically user created.
+ */
+ public static final int EF_ADN = 1;
+ /**
+ * Type for accessing records in the "fixed dialing number" (FDN) elementary file on the
+ * SIM.
+ *
+ * <p>FDN numbers are the numbers that are allowed to dialed for outbound calls when FDN is
+ * enabled.
+ *
+ * <p>FDN records cannot be modified by applications. Hence, insert, update and
+ * delete methods operating on this Uri will throw UnsupportedOperationException
+ */
+ public static final int EF_FDN = 2;
+ /**
+ * Type for accessing records in the "service dialing number" (SDN) elementary file on the
+ * SIM.
+ *
+ * <p>Typically SDNs are preset numbers provided by the carrier for common operations (e.g.
+ * voicemail, check balance, etc).
+ *
+ * <p>SDN records cannot be modified by applications. Hence, insert, update and delete
+ * methods operating on this Uri will throw UnsupportedOperationException
+ */
+ public static final int EF_SDN = 3;
+ /** @hide */
+ @SystemApi
+ public static final String EF_ADN_PATH_SEGMENT = "adn";
+ /** @hide */
+ @SystemApi
+ public static final String EF_FDN_PATH_SEGMENT = "fdn";
+ /** @hide */
+ @SystemApi
+ public static final String EF_SDN_PATH_SEGMENT = "sdn";
+ /** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+ /** The MIME type of a CONTENT_URI subdirectory of a single ADN-like elementary file. */
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/sim-elementary-file";
+ /**
+ * The Uri path segment used to construct Uris for the metadata defined in this class.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+
+ /** Content URI for the ADN-like elementary files available on the device. */
+ @NonNull
+ public static final Uri CONTENT_URI = AUTHORITY_URI
+ .buildUpon()
+ .appendPath(ELEMENTARY_FILES_PATH_SEGMENT).build();
+
+ private ElementaryFiles() {
+ }
+
+ /**
+ * Returns a content uri for a specific elementary file.
+ *
+ * <p>If a SIM with the specified subscriptionId is not present an exception will be thrown.
+ * If the SIM doesn't support the specified elementary file it will have a zero value for
+ * {@link #MAX_RECORDS}.
+ */
+ @NonNull
+ public static Uri getItemUri(int subscriptionId, @EfType int efType) {
+ return CONTENT_URI.buildUpon().appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+ .appendPath(String.valueOf(subscriptionId))
+ .appendPath(getEfUriPath(efType))
+ .build();
+ }
+
+ /**
+ * Annotation for the valid elementary file types.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"EF"},
+ value = {EF_UNKNOWN, EF_ADN, EF_FDN, EF_SDN})
+ public @interface EfType {
+ }
+ }
+}
diff --git a/core/java/android/service/attestation/ImpressionAttestationService.java b/core/java/android/service/attestation/ImpressionAttestationService.java
deleted file mode 100644
index 968d533..0000000
--- a/core/java/android/service/attestation/ImpressionAttestationService.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.attestation;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteCallback;
-
-/**
- * A service that handles generating and verify ImpressionTokens.
- *
- * The service will generate an ImpressionToken based on arguments passed in. Then later that same
- * ImpressionToken can be verified to determine that it was created by the system.
- *
- * @hide
- */
-@SystemApi
-public abstract class ImpressionAttestationService extends Service {
- /** @hide **/
- public static final String EXTRA_IMPRESSION_TOKEN =
- "android.service.attestation.extra.IMPRESSION_TOKEN";
-
- /** @hide **/
- public static final String EXTRA_VERIFICATION_STATUS =
- "android.service.attestation.extra.VERIFICATION_STATUS";
-
- /**
- * Manifest metadata key for the resource string array containing the names of all impression
- * attestation algorithms provided by the service.
- *
- * @hide
- */
- public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
- "android.attestation.available_algorithms";
-
- /**
- * The {@link Intent} action that must be declared as handled by a service in its manifest
- * for the system to recognize it as an impression attestation providing service.
- *
- * @hide
- */
- public static final String SERVICE_INTERFACE =
- "android.service.attestation.ImpressionAttestationService";
-
- private ImpressionAttestationServiceWrapper mWrapper;
- private Handler mHandler;
-
- public ImpressionAttestationService() {
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mWrapper = new ImpressionAttestationServiceWrapper();
- mHandler = new Handler(Looper.getMainLooper(), null, true);
- }
-
- @NonNull
- @Override
- public final IBinder onBind(@NonNull Intent intent) {
- return mWrapper;
- }
-
- /**
- * Generates the impression token that can be used to validate that the system
- * generated the token.
- *
- * @param salt The salt to use when generating the hmac. This should be unique to the
- * caller so the token cannot be verified by any other process.
- * @param screenshot The screenshot buffer for the content to attest.
- * @param bounds The size and position of the content being attested in the window.
- * @param hashAlgorithm The String for the hashing algorithm to use based values in
- * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
- * @return An impression token that can be used to validate information about the content.
- * Returns null when the arguments sent are invalid.
- */
- @Nullable
- public abstract ImpressionToken onGenerateImpressionToken(@NonNull byte[] salt,
- @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
- @NonNull String hashAlgorithm);
-
- /**
- * Call to verify that the impressionToken passed in was generated by the system.
- *
- * @param salt The salt value to use when verifying the hmac. This should be the
- * same value that was passed to
- * {@link #onGenerateImpressionToken(byte[],
- * HardwareBuffer, Rect, String)} to
- * generate the token.
- * @param impressionToken The token to verify that it was generated by the system.
- * @return true if the token can be verified that it was generated by the system.
- */
- public abstract boolean onVerifyImpressionToken(@NonNull byte[] salt,
- @NonNull ImpressionToken impressionToken);
-
- private void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
- String hashAlgorithm, RemoteCallback callback) {
- ImpressionToken impressionToken = onGenerateImpressionToken(salt, screenshot, bounds,
- hashAlgorithm);
- final Bundle data = new Bundle();
- data.putParcelable(EXTRA_IMPRESSION_TOKEN, impressionToken);
- callback.sendResult(data);
- }
-
- private void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
- RemoteCallback callback) {
- boolean verificationStatus = onVerifyImpressionToken(salt, impressionToken);
- final Bundle data = new Bundle();
- data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
- callback.sendResult(data);
- }
-
- private final class ImpressionAttestationServiceWrapper extends
- IImpressionAttestationService.Stub {
- @Override
- public void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
- String hashAlgorithm, RemoteCallback callback) {
- mHandler.sendMessage(
- obtainMessage(ImpressionAttestationService::generateImpressionToken,
- ImpressionAttestationService.this, salt, screenshot, bounds,
- hashAlgorithm, callback));
- }
-
- @Override
- public void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
- RemoteCallback callback) {
- mHandler.sendMessage(obtainMessage(ImpressionAttestationService::verifyImpressionToken,
- ImpressionAttestationService.this, salt, impressionToken, callback));
- }
- }
-}
diff --git a/core/java/android/service/attestation/OWNERS b/core/java/android/service/attestation/OWNERS
deleted file mode 100644
index b9e7b99..0000000
--- a/core/java/android/service/attestation/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chaviw@google.com
-ogunwale@google.com
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
index 4ebaa96..ad49ffd 100644
--- a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -87,7 +87,9 @@
* Implementation for wrapping the opaque blob used for resume-on-reboot prior to
* reboot. The service should not assume any structure of the blob to be wrapped. The
* implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
- * if it's unable to complete the action.
+ * if it's unable to complete the action due to retry-able errors (e.g network errors)
+ * and {@link IllegalArgumentException} if {@code wrapBlob} fails due to fatal errors
+ * (e.g corrupted blob).
*
* @param blob The opaque blob with size on the order of 100 bytes.
* @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
@@ -95,7 +97,8 @@
* this function after expiration should
* fail.
* @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
- * @throws IOException if the implementation is unable to wrap the blob successfully.
+ * @throws IOException if the implementation is unable to wrap the blob successfully due to
+ * retry-able errors.
*/
@NonNull
public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
@@ -106,12 +109,13 @@
* operation would happen after reboot during direct boot mode (i.e before device is unlocked
* for the first time). The implementation should unwrap the wrapped blob in a reasonable time
* and returns the result or throw {@link IOException} if it's unable to complete the action
- * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
- * stale.
+ * due to retry-able errors (e.g network error) and {@link IllegalArgumentException}
+ * if {@code unwrapBlob} fails due to fatal errors (e.g stale or corrupted blob).
*
* @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
* @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
- * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+ * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully
+ * due to retry-able errors.
*/
@NonNull
public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
index 94a6052..8e76e2f 100644
--- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java
+++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Surface;
/**
* This class represents a request to an {@link RotationResolverService}. The request contains
@@ -54,7 +55,7 @@
mTimeoutMillis = timeoutMillis;
}
- public int getProposedRotation() {
+ @Surface.Rotation public int getProposedRotation() {
return mProposedRotation;
}
diff --git a/core/java/android/service/rotationresolver/RotationResolverService.java b/core/java/android/service/rotationresolver/RotationResolverService.java
index 593a642..604dd0a 100644
--- a/core/java/android/service/rotationresolver/RotationResolverService.java
+++ b/core/java/android/service/rotationresolver/RotationResolverService.java
@@ -146,11 +146,8 @@
}
mPendingCallback = new RotationResolverCallbackWrapper(callback, this);
mCancellationSignal = CancellationSignal.fromTransport(transport);
- try {
- onResolveRotation(request, mCancellationSignal, mPendingCallback);
- } catch (UnsupportedOperationException e) {
- reportFailures(callback, ROTATION_RESULT_FAILURE_CANCELLED);
- }
+
+ onResolveRotation(request, mCancellationSignal, mPendingCallback);
}
@MainThread
diff --git a/core/java/android/service/attestation/IImpressionAttestationService.aidl b/core/java/android/service/screenshot/IScreenshotHasherService.aidl
similarity index 66%
rename from core/java/android/service/attestation/IImpressionAttestationService.aidl
rename to core/java/android/service/screenshot/IScreenshotHasherService.aidl
index 5ff8f17..d14d147 100644
--- a/core/java/android/service/attestation/IImpressionAttestationService.aidl
+++ b/core/java/android/service/screenshot/IScreenshotHasherService.aidl
@@ -14,43 +14,43 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.service.screenshot;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.RemoteCallback;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
/**
- * Service used to handle impression attestation requests.
+ * Service used to handle ScreenshotHash requests.
*
* @hide
*/
-oneway interface IImpressionAttestationService {
+oneway interface IScreenshotHasherService {
/**
- * Generates the impression token that can be used to validate that the system generated the
+ * Generates the ScreenshotHash token that can be used to validate that the system generated the
* token.
*
* @param salt The salt to use when generating the hmac. This should be unique to the caller so
* the token cannot be verified by any other process.
* @param screenshot The screenshot to generate the hash and add to the token.
- * @param bounds The size and position of the content being attested in the window.
+ * @param bounds The size and position of the content being screenshot in the window.
* @param hashAlgorithm The String for the hashing algorithm to use based on values in
* {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}.
- * @param Callback The callback invoked to send back the impression token.
+ * @param Callback The callback invoked to send back the ScreenshotHash token.
*/
- void generateImpressionToken(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
+ void generateScreenshotHash(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
in String hashAlgorithm, in RemoteCallback callback);
/**
- * Call to verify that the impressionToken passed in was generated by the system. The result
+ * Call to verify that the ScreenshotHash passed in was generated by the system. The result
* will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFICATION_STATUS}.
*
* @param salt The salt value to use when verifying the hmac. This should be the same value that
- * was passed to {@link generateImpressionToken()} to generate the token.
- * @param impressionToken The token to verify that it was generated by the system.
+ * was passed to {@link generateScreenshotHash()} to generate the token.
+ * @param screenshotHash The hash to verify that it was generated by the system.
* @param callback The callback invoked to send back the verification status.
*/
- void verifyImpressionToken(in byte[] salt, in ImpressionToken impressionToken,
+ void verifyScreenshotHash(in byte[] salt, in ScreenshotHash screenshotHash,
in RemoteCallback callback);
}
diff --git a/core/java/android/service/screenshot/OWNERS b/core/java/android/service/screenshot/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/java/android/service/screenshot/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/service/attestation/ImpressionToken.aidl b/core/java/android/service/screenshot/ScreenshotHash.aidl
similarity index 90%
rename from core/java/android/service/attestation/ImpressionToken.aidl
rename to core/java/android/service/screenshot/ScreenshotHash.aidl
index 284a4ba..a7c50db 100644
--- a/core/java/android/service/attestation/ImpressionToken.aidl
+++ b/core/java/android/service/screenshot/ScreenshotHash.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.service.screenshot;
-parcelable ImpressionToken;
\ No newline at end of file
+parcelable ScreenshotHash;
diff --git a/core/java/android/service/attestation/ImpressionToken.java b/core/java/android/service/screenshot/ScreenshotHash.java
similarity index 81%
rename from core/java/android/service/attestation/ImpressionToken.java
rename to core/java/android/service/screenshot/ScreenshotHash.java
index 4355dc0..9ae4192 100644
--- a/core/java/android/service/attestation/ImpressionToken.java
+++ b/core/java/android/service/screenshot/ScreenshotHash.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.service.screenshot;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -25,14 +25,14 @@
import com.android.internal.util.DataClass;
/**
- * The ads impression token used to validate information about what was present on screen.
+ * The screenshot hash used to validate information about what was present on screen.
* @hide
*
* TODO: Remove hide and SystemAPI since this will be a public class
*/
@SystemApi
@DataClass(genToString = true, genAidl = true)
-public final class ImpressionToken implements Parcelable {
+public final class ScreenshotHash implements Parcelable {
/**
* The timestamp when the screenshot was generated.
*/
@@ -42,23 +42,27 @@
* The bounds of the requested area to take the screenshot. This is in window space passed in
* by the client.
*/
- private @NonNull final Rect mBoundsInWindow;
+ @NonNull
+ private final Rect mBoundsInWindow;
/**
* The selected hashing algorithm that generated the image hash.
*/
- private @NonNull final String mHashingAlgorithm;
+ @NonNull
+ private final String mHashingAlgorithm;
/**
- * The image hash generated when creating the impression token from the screenshot taken.
+ * The image hash generated when creating the ScreenshotHash from the screenshot taken.
*/
- private @NonNull final byte[] mImageHash;
+ @NonNull
+ private final byte[] mImageHash;
/**
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*/
- private @NonNull final byte[] mHmac;
+ @NonNull
+ private final byte[] mHmac;
/**
* The hmac generated by the system and used to verify whether this token was generated by
@@ -67,19 +71,21 @@
* @hide
*/
@SystemApi
- public @NonNull byte[] getHmac() {
+ @NonNull
+ public byte[] getHmac() {
return mHmac;
}
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/attestation/ImpressionToken.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/screenshot
+ // /ScreenshotHash.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -87,7 +93,7 @@
/**
- * Creates a new ImpressionToken.
+ * Creates a new ScreenshotHash.
*
* @param screenshotTimeMillis
* The timestamp when the screenshot was generated.
@@ -97,13 +103,13 @@
* @param hashingAlgorithm
* The selected hashing algorithm that generated the image hash.
* @param imageHash
- * The image hash generated when creating the impression token from the screenshot taken.
+ * The image hash generated when creating the ScreenshotHash from the screenshot taken.
* @param hmac
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*/
@DataClass.Generated.Member
- public ImpressionToken(
+ public ScreenshotHash(
long screenshotTimeMillis,
@NonNull Rect boundsInWindow,
@NonNull String hashingAlgorithm,
@@ -152,7 +158,7 @@
}
/**
- * The image hash generated when creating the impression token from the screenshot taken.
+ * The image hash generated when creating the ScreenshotHash from the screenshot taken.
*/
@DataClass.Generated.Member
public @NonNull byte[] getImageHash() {
@@ -165,7 +171,7 @@
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
- return "ImpressionToken { " +
+ return "ScreenshotHash { " +
"screenshotTimeMillis = " + mScreenshotTimeMillis + ", " +
"boundsInWindow = " + mBoundsInWindow + ", " +
"hashingAlgorithm = " + mHashingAlgorithm + ", " +
@@ -194,7 +200,7 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ ImpressionToken(@NonNull Parcel in) {
+ /* package-private */ ScreenshotHash(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -222,24 +228,24 @@
}
@DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<ImpressionToken> CREATOR
- = new Parcelable.Creator<ImpressionToken>() {
+ public static final @NonNull Parcelable.Creator<ScreenshotHash> CREATOR
+ = new Parcelable.Creator<ScreenshotHash>() {
@Override
- public ImpressionToken[] newArray(int size) {
- return new ImpressionToken[size];
+ public ScreenshotHash[] newArray(int size) {
+ return new ScreenshotHash[size];
}
@Override
- public ImpressionToken createFromParcel(@NonNull Parcel in) {
- return new ImpressionToken(in);
+ public ScreenshotHash createFromParcel(@NonNull Parcel in) {
+ return new ScreenshotHash(in);
}
};
@DataClass.Generated(
- time = 1604539951959L,
- codegenVersion = "1.0.18",
- sourceFile = "frameworks/base/core/java/android/service/attestation/ImpressionToken.java",
- inputSignatures = "private final long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ImpressionToken extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
+ time = 1612383172822L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/service/screenshot/ScreenshotHash.java",
+ inputSignatures = "private final long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ScreenshotHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/screenshot/ScreenshotHasherService.java b/core/java/android/service/screenshot/ScreenshotHasherService.java
new file mode 100644
index 0000000..d96cc7e
--- /dev/null
+++ b/core/java/android/service/screenshot/ScreenshotHasherService.java
@@ -0,0 +1,160 @@
+/*
+ * 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.service.screenshot;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+
+/**
+ * A service that handles generating and verify {@link ScreenshotHash}.
+ *
+ * The service will generate a ScreenshotHash based on arguments passed in. Then later that
+ * same ScreenshotHash can be verified to determine that it was created by the system.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class ScreenshotHasherService extends Service {
+ /** @hide **/
+ public static final String EXTRA_SCREENSHOT_HASH =
+ "android.service.screenshot.extra.SCREENSHOT_HASH";
+
+ /** @hide **/
+ public static final String EXTRA_VERIFICATION_STATUS =
+ "android.service.screenshot.extra.VERIFICATION_STATUS";
+
+ /**
+ * Manifest metadata key for the resource string array containing the names of all hashing
+ * algorithms provided by the service.
+ *
+ * @hide
+ */
+ public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
+ "android.screenshot.available_algorithms";
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service in its manifest
+ * for the system to recognize it as a ScreenshotHash providing service.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SERVICE_INTERFACE =
+ "android.service.screenshot.ScreenshotHasherService";
+
+ private ScreenshotHasherServiceWrapper mWrapper;
+ private Handler mHandler;
+
+ public ScreenshotHasherService() {
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWrapper = new ScreenshotHasherServiceWrapper();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @NonNull
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ return mWrapper;
+ }
+
+ /**
+ * Generates the ScreenshotHash that can be used to validate that the system generated the
+ * token.
+ *
+ * @param salt The salt to use when generating the hmac. This should be unique to the
+ * caller so the token cannot be verified by any other process.
+ * @param screenshot The screenshot buffer for the content.
+ * @param bounds The size and position of the content being screenshot in the window.
+ * @param hashAlgorithm The String for the hashing algorithm to use based values in
+ * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
+ * @return A ScreenshotHash that can be used to validate information about the content.
+ * Returns null when the arguments sent are invalid.
+ */
+ @Nullable
+ public abstract ScreenshotHash onGenerateScreenshotHash(@NonNull byte[] salt,
+ @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
+ @NonNull String hashAlgorithm);
+
+ /**
+ * Call to verify that the ScreenshotHash passed in was generated by the system.
+ *
+ * @param salt The salt value to use when verifying the hmac. This should be the
+ * same value that was passed to
+ * {@link #onGenerateScreenshotHash(byte[],
+ * HardwareBuffer, Rect, String)} to
+ * generate the token.
+ * @param screenshotHash The token to verify that it was generated by the system.
+ * @return true if the token can be verified that it was generated by the system.
+ */
+ public abstract boolean onVerifyScreenshotHash(@NonNull byte[] salt,
+ @NonNull ScreenshotHash screenshotHash);
+
+ private void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
+ String hashAlgorithm, RemoteCallback callback) {
+ ScreenshotHash screenshotHash = onGenerateScreenshotHash(salt, screenshot,
+ bounds,
+ hashAlgorithm);
+ final Bundle data = new Bundle();
+ data.putParcelable(EXTRA_SCREENSHOT_HASH, screenshotHash);
+ callback.sendResult(data);
+ }
+
+ private void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
+ RemoteCallback callback) {
+ boolean verificationStatus = onVerifyScreenshotHash(salt, screenshotHash);
+ final Bundle data = new Bundle();
+ data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
+ callback.sendResult(data);
+ }
+
+ private final class ScreenshotHasherServiceWrapper extends
+ IScreenshotHasherService.Stub {
+ @Override
+ public void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
+ String hashAlgorithm, RemoteCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(ScreenshotHasherService::generateScreenshotHash,
+ ScreenshotHasherService.this, salt, screenshot, bounds,
+ hashAlgorithm, callback));
+ }
+
+ @Override
+ public void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
+ RemoteCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(ScreenshotHasherService::verifyScreenshotHash,
+ ScreenshotHasherService.this, salt, screenshotHash,
+ callback));
+ }
+ }
+}
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index 0123c36..a750b68 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -158,7 +158,7 @@
* @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
* @param bytes number of bytes which need to be freed
*/
- public void onFreeCacheRequested(@NonNull UUID volumeUuid, @BytesLong long bytes) {
+ public void onFreeCache(@NonNull UUID volumeUuid, @BytesLong long bytes) throws IOException {
throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
}
@@ -202,7 +202,7 @@
RemoteCallback callback) {
mHandler.post(() -> {
try {
- onFreeCacheRequested(StorageManager.convert(volumeUuid), bytes);
+ onFreeCache(StorageManager.convert(volumeUuid), bytes);
sendResult(sessionId, null /* throwable */, callback);
} catch (Throwable t) {
sendResult(sessionId, t, callback);
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
index f7710e6..ff03cc1 100644
--- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.pm.PackageManager;
@@ -44,6 +45,7 @@
private ServiceInfo mServiceInfo;
private String mSessionService;
private String mRecognitionService;
+ private String mHotwordDetectionService;
private String mSettingsActivity;
private boolean mSupportsAssist;
private boolean mSupportsLaunchFromKeyguard;
@@ -133,6 +135,8 @@
false);
mSupportsLocalInteraction = array.getBoolean(com.android.internal.
R.styleable.VoiceInteractionService_supportsLocalInteraction, false);
+ mHotwordDetectionService = array.getString(com.android.internal.R.styleable
+ .VoiceInteractionService_hotwordDetectionService);
array.recycle();
if (mSessionService == null) {
mParseError = "No sessionService specified";
@@ -181,4 +185,9 @@
public boolean getSupportsLocalInteraction() {
return mSupportsLocalInteraction;
}
+
+ @Nullable
+ public String getHotwordDetectionService() {
+ return mHotwordDetectionService;
+ }
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 507dc7a..82e0b4a 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -880,8 +880,8 @@
InputChannel inputChannel = new InputChannel();
if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
- mDisplay.getDisplayId(), mInsetsState, mWinFrames.frame,
- inputChannel, mInsetsState, mTempControls) < 0) {
+ mDisplay.getDisplayId(), mInsetsState, inputChannel, mInsetsState,
+ mTempControls) < 0) {
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
return;
}
@@ -1494,6 +1494,16 @@
private void doDetachEngine() {
mActiveEngines.remove(mEngine);
mEngine.detach();
+ // Some wallpapers will not trigger the rendering threads of the remaining engines even
+ // if they are visible, so we need to toggle the state to get their attention.
+ if (!mDetached.get()) {
+ for (Engine eng : mActiveEngines) {
+ if (eng.mVisible) {
+ eng.doVisibilityChanged(false);
+ eng.doVisibilityChanged(true);
+ }
+ }
+ }
}
@Override
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 86120d1..6718e93 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -16,6 +16,7 @@
package android.util;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
@@ -23,6 +24,8 @@
import libcore.util.EmptyArray;
+import java.util.Objects;
+
/**
* <code>SparseArray</code> maps integers to Objects and, unlike a normal array of Objects,
* its indices can contain gaps. <code>SparseArray</code> is intended to be more memory-efficient
@@ -505,4 +508,44 @@
buffer.append('}');
return buffer.toString();
}
+
+ /**
+ * For backwards compatibility reasons, {@link Object#equals(Object)} cannot be implemented,
+ * so this serves as a manually invoked alternative.
+ */
+ public boolean contentEquals(@Nullable SparseArray<E> other) {
+ if (other == null) {
+ return false;
+ }
+
+ int size = size();
+ if (size != other.size()) {
+ return false;
+ }
+
+ for (int index = 0; index < size; index++) {
+ int key = keyAt(index);
+ if (!Objects.equals(valueAt(index), other.get(key))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * For backwards compatibility, {@link Object#hashCode()} cannot be implemented, so this serves
+ * as a manually invoked alternative.
+ */
+ public int contentHashCode() {
+ int hash = 0;
+ int size = size();
+ for (int index = 0; index < size; index++) {
+ int key = keyAt(index);
+ E value = valueAt(index);
+ hash = 31 * hash + Objects.hashCode(key);
+ hash = 31 * hash + Objects.hashCode(value);
+ }
+ return hash;
+ }
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 8de8be8..0ba1dfe 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -25,8 +25,8 @@
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.KeyguardManager;
-import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -59,8 +59,12 @@
* an application window, excluding the system decorations. The application display area may
* be smaller than the real display area because the system subtracts the space needed
* for decor elements such as the status bar. Use {@link WindowMetrics#getBounds()} to query the
- * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to
- * query the metrics and perform UI-related actions.</li>
+ * application window bounds.</li>
+ * <li>The real display area specifies the part of the display that contains content
+ * including the system decorations. Even so, the real display area may be smaller than the
+ * physical size of the display if the window manager is emulating a smaller display
+ * using (adb shell wm size). Use the following methods to query the
+ * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li>
* </ul>
* </p><p>
* A logical display does not necessarily represent a particular physical display device
@@ -673,9 +677,9 @@
@UnsupportedAppUsage
public DisplayAdjustments getDisplayAdjustments() {
if (mResources != null) {
- final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
- if (!mDisplayAdjustments.equals(currentAdjustments)) {
- mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
+ final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
+ if (!mDisplayAdjustments.equals(currentAdjustements)) {
+ mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
}
}
@@ -896,6 +900,32 @@
}
/**
+ * Returns the {@link RoundedCorner} of the given position if there is one.
+ *
+ * @param position the position of the rounded corner on the display.
+ *
+ * @return the rounded corner of the given position. Returns {@code null} if there is none.
+ */
+ @SuppressLint("VisiblySynchronized")
+ @Nullable
+ public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ RoundedCorners roundedCorners;
+ if (mMayAdjustByFixedRotation) {
+ roundedCorners = getDisplayAdjustments().adjustRoundedCorner(
+ mDisplayInfo.roundedCorners,
+ mDisplayInfo.rotation,
+ mDisplayInfo.logicalWidth,
+ mDisplayInfo.logicalHeight);
+ } else {
+ roundedCorners = mDisplayInfo.roundedCorners;
+ }
+ return roundedCorners == null ? null : roundedCorners.getRoundedCorner(position);
+ }
+ }
+
+ /**
* Gets the pixel format of the display.
* @return One of the constants defined in {@link android.graphics.PixelFormat}.
*
@@ -1187,34 +1217,30 @@
}
/**
- * Provides the largest {@link Point outSize} an app may expect in the current system state,
- * without subtracting any window decor.
+ * Gets the real size of the display without subtracting any window decor or
+ * applying any compatibility scale factors.
* <p>
- * The size describes the largest potential area the window might occupy. The size is adjusted
- * based on the current rotation of the display.
+ * The size is adjusted based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
- * </p>
+ * </p><p>
+ * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
+ * report the same bounds except that certain areas of the display may not be available to
+ * windows created in the {@link WindowManager}'s {@link Context}.
+ *
+ * For example, imagine a device which has a multi-task mode that limits windows to half of the
+ * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
+ * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
+ * still reports the bounds of the whole display.
*
* @param outSize Set to the real size of the display.
+ *
+ * @see WindowManager#getMaximumWindowMetrics()
*/
public void getRealSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
- if (shouldReportMaxBounds()) {
- final Rect bounds = mResources.getConfiguration()
- .windowConfiguration.getMaxBounds();
- outSize.x = bounds.width();
- outSize.y = bounds.height();
- if (DEBUG) {
- Log.d(TAG, "getRealSize determined from max bounds: " + outSize
- + " for uid " + Process.myUid());
- }
- // Skip adjusting by fixed rotation, since if it is necessary, the configuration
- // should already reflect the expected rotation.
- return;
- }
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
if (mMayAdjustByFixedRotation) {
@@ -1224,11 +1250,9 @@
}
/**
- * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current
- * system state, without subtracting any window decor.
+ * Gets display metrics based on the real size of this display.
* <p>
- * The size describes the largest potential area the window might occupy. The size is adjusted
- * based on the current rotation of the display.
+ * The size is adjusted based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
@@ -1239,18 +1263,6 @@
public void getRealMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
- if (shouldReportMaxBounds()) {
- mDisplayInfo.getMaxBoundsMetrics(outMetrics,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
- mResources.getConfiguration());
- if (DEBUG) {
- Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics
- + " for uid " + Process.myUid());
- }
- // Skip adjusting by fixed rotation, since if it is necessary, the configuration
- // should already reflect the expected rotation.
- return;
- }
mDisplayInfo.getLogicalMetrics(outMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
if (mMayAdjustByFixedRotation) {
@@ -1260,20 +1272,6 @@
}
/**
- * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
- * display dimensions. The max bounds field may be smaller than the logical dimensions
- * when apps need to be sandboxed.
- * @return {@code true} when max bounds should be applied.
- */
- private boolean shouldReportMaxBounds() {
- if (mResources == null) {
- return false;
- }
- final Configuration config = mResources.getConfiguration();
- return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
- }
-
- /**
* Gets the state of the display, such as whether it is on or off.
*
* @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index 5d5771c..e307eff 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -151,6 +151,23 @@
: realCutout;
}
+ /**
+ * Returns the adjusted {@link RoundedCorners} if available. Otherwise the original
+ * {@link RoundedCorners} is returned.
+ */
+ @Nullable
+ public RoundedCorners adjustRoundedCorner(@Nullable RoundedCorners realRoundedCorners,
+ @Surface.Rotation int realRotation, int displayWidth, int displayHeight) {
+ final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
+ if (realRoundedCorners == null || rotationAdjustments == null
+ || rotationAdjustments.mRotation == realRotation) {
+ return realRoundedCorners;
+ }
+
+ return realRoundedCorners.rotate(
+ rotationAdjustments.mRotation, displayWidth, displayHeight);
+ }
+
/** Returns the adjusted rotation if available. Otherwise the original rotation is returned. */
@Surface.Rotation
public int getRotation(@Surface.Rotation int realRotation) {
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 7cabb04..2a00b5a 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,7 +24,6 @@
import static android.view.DisplayInfoProto.NAME;
import android.annotation.Nullable;
-import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -302,6 +301,12 @@
*/
public float brightnessDefault;
+ /**
+ * The {@link RoundedCorners} if present, otherwise {@code null}.
+ */
+ @Nullable
+ public RoundedCorners roundedCorners;
+
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
@@ -370,7 +375,8 @@
&& refreshRateOverride == other.refreshRateOverride
&& brightnessMinimum == other.brightnessMinimum
&& brightnessMaximum == other.brightnessMaximum
- && brightnessDefault == other.brightnessDefault;
+ && brightnessDefault == other.brightnessDefault
+ && Objects.equals(roundedCorners, other.roundedCorners);
}
@Override
@@ -419,6 +425,7 @@
brightnessMinimum = other.brightnessMinimum;
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
+ roundedCorners = other.roundedCorners;
}
public void readFromParcel(Parcel source) {
@@ -469,6 +476,7 @@
brightnessMinimum = source.readFloat();
brightnessMaximum = source.readFloat();
brightnessDefault = source.readFloat();
+ roundedCorners = source.readTypedObject(RoundedCorners.CREATOR);
}
@Override
@@ -518,6 +526,7 @@
dest.writeFloat(brightnessMinimum);
dest.writeFloat(brightnessMaximum);
dest.writeFloat(brightnessDefault);
+ dest.writeTypedObject(roundedCorners, flags);
}
@Override
@@ -606,29 +615,11 @@
getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
}
- /**
- * Populates {@code outMetrics} with details of the logical display. Bounds are limited
- * by the logical size of the display.
- *
- * @param outMetrics the {@link DisplayMetrics} to be populated
- * @param compatInfo the {@link CompatibilityInfo} to be applied
- * @param configuration the {@link Configuration}
- */
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration) {
getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
}
- /**
- * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
- * {@link WindowConfiguration#getMaxBounds()}
- */
- public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
- Configuration configuration) {
- Rect bounds = configuration.windowConfiguration.getMaxBounds();
- getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
- }
-
public int getNaturalWidth() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalWidth : logicalHeight;
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index c2566cc..5937499 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -149,7 +149,7 @@
* <p>
* The time value that was used in all the vsync listeners and drawing for
* the frame (Choreographer frame callbacks, animations,
- * {@link View#getDrawingTime()}, etc…)
+ * {@link View#getDrawingTime()}, etc.)
* </p>
*/
public static final int VSYNC_TIMESTAMP = 11;
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
index 423e23d..1f64fb8 100644
--- a/core/java/android/view/IRemoteAnimationRunner.aidl
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -30,11 +30,15 @@
/**
* Called when the process needs to start the remote animation.
*
+ * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
* @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
* @param finishedCallback The callback to invoke when the animation is finished.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
+ void onAnimationStart(int transit, in RemoteAnimationTarget[] apps,
+ in RemoteAnimationTarget[] wallpapers, in RemoteAnimationTarget[] nonApps,
in IRemoteAnimationFinishedCallback finishedCallback);
/**
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 68a6de8..62f4b86 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,7 +32,7 @@
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
@@ -117,23 +117,17 @@
// These can only be called when holding the MANAGE_APP_TOKENS permission.
void setEventDispatching(boolean enabled);
- /** @return {@code true} if this binder is a registered window token. */
+ /** Returns {@code true} if this binder is a registered window token. */
boolean isWindowToken(in IBinder binder);
/**
* Adds window token for a given type.
*
* @param token Token to be registered.
* @param type Window type to be used with this token.
- * @param options A bundle used to pass window-related options.
* @param displayId The ID of the display where this token should be added.
- * @param packageName The name of package to request to add window token. Could be {@code null}
- * if callers holds the MANAGE_APP_TOKENS permission.
- * @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code
- * otherwise.
+ * @param options A bundle used to pass window-related options.
*/
- int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options,
- String packageName);
- void addWindowToken(IBinder token, int type, int displayId);
+ void addWindowToken(IBinder token, int type, int displayId, in Bundle options);
/**
* Remove window token on a specific display.
*
@@ -765,25 +759,29 @@
/**
* Gets an array of support hashing algorithms that can be used to generate the hash of the
* screenshot. The String value of one algorithm should be used when requesting to generate
- * the impression attestation token.
+ * the ScreenshotHash.
*
* @return a String array of supported hashing algorithms.
*/
- String[] getSupportedImpressionAlgorithms();
+ String[] getSupportedScreenshotHashingAlgorithms();
/**
- * Validate the impression token was generated by the system. The impression token passed in
- * should be the token generated when calling {@link IWindowSession#generateImpressionToken}
+ * Validate the ScreenshotHash was generated by the system. The ScreenshotHash passed in
+ * should be the token generated when calling {@link IWindowSession#generateScreenshotHash}
*
- * @param impressionToken The token to verify that it was generated by the system.
+ * @param ScreenshotHash The token to verify that it was generated by the system.
* @return true if the token was generated by the system or false if the token cannot be
* verified.
*/
- boolean verifyImpressionToken(in ImpressionToken impressionToken);
+ boolean verifyScreenshotHash(in ScreenshotHash screenshotHash);
/**
* Registers a listener for a {@link android.app.WindowContext} to handle configuration changes
* from the server side.
+ * <p>
+ * Note that this API should be invoked after calling
+ * {@link android.app.WindowTokenClient#attachContext(WindowContext)}
+ * </p>
*
* @param clientToken the window context's token
* @param type Window type of the window context
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 85498cb..990b7bd 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,7 +22,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.InputChannel;
@@ -47,11 +47,11 @@
interface IWindowSession {
int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility,
- out Rect outFrame, out InputChannel outInputChannel, out InsetsState insetsState,
+ out InputChannel outInputChannel, out InsetsState insetsState,
out InsetsSourceControl[] activeControls);
int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in int userId,
- in InsetsState requestedVisibility, out Rect outFrame, out InputChannel outInputChannel,
+ in InsetsState requestedVisibility, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out InsetsState insetsState);
@@ -345,14 +345,14 @@
void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus);
/**
- * Generates an impression token that can be used to validate whether specific content was on
+ * Generates an ScreenshotHash that can be used to validate whether specific content was on
* screen.
*
- * @param window The token for the window where the view to attest is shown.
+ * @param window The token for the window where the view to screenshot is shown.
* @param boundsInWindow The size and position of the ads view in the window
* @param hashAlgorithm The String for the hashing algorithm to use based on values returned
- * from {@link IWindowManager#getSupportedImpressionAlgorithms()}
+ * from {@link IWindowManager#getSupportedHashingAlgorithms()}
*/
- ImpressionToken generateImpressionToken(IWindow window, in Rect boundsInWindow,
+ ScreenshotHash generateScreenshotHash(IWindow window, in Rect boundsInWindow,
in String hashAlgorithm);
}
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index bc03222..59e4931 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -455,7 +455,6 @@
* Called by native code
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@VisibleForTesting
public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 0939336..6a34a15 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -111,6 +111,9 @@
mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
types, mCallbacks, durationMs, interpolator, animationType, translator);
InsetsAnimationThread.getHandler().post(() -> {
+ if (mControl.isCancelled()) {
+ return;
+ }
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
"InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
listener.onReady(mControl, types);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b4e1172..e681c0e 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -653,6 +653,7 @@
private void updateState(InsetsState newState) {
mState.setDisplayFrame(newState.getDisplayFrame());
mState.setDisplayCutout(newState.getDisplayCutout());
+ mState.setRoundedCorners(newState.getRoundedCorners());
@InsetsType int disabledUserAnimationTypes = 0;
@InsetsType int[] cancelledUserAnimationTypes = {0};
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index d68e903..219190f 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -173,6 +173,9 @@
private final DisplayCutout.ParcelableWrapper mDisplayCutout =
new DisplayCutout.ParcelableWrapper();
+ /** The rounded corners on the display */
+ private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
+
public InsetsState() {
}
@@ -256,7 +259,8 @@
}
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
- alwaysConsumeSystemBars, calculateRelativeCutout(frame), compatInsetsTypes,
+ alwaysConsumeSystemBars, calculateRelativeCutout(frame),
+ calculateRelativeRoundedCorners(frame), compatInsetsTypes,
(legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
}
@@ -281,6 +285,20 @@
return raw.inset(insetLeft, insetTop, insetRight, insetBottom);
}
+ private RoundedCorners calculateRelativeRoundedCorners(Rect frame) {
+ if (mDisplayFrame.equals(frame)) {
+ return mRoundedCorners;
+ }
+ if (frame == null) {
+ return RoundedCorners.NO_ROUNDED_CORNERS;
+ }
+ final int insetLeft = frame.left - mDisplayFrame.left;
+ final int insetTop = frame.top - mDisplayFrame.top;
+ final int insetRight = mDisplayFrame.right - frame.right;
+ final int insetBottom = mDisplayFrame.bottom - frame.bottom;
+ return mRoundedCorners.inset(insetLeft, insetTop, insetRight, insetBottom);
+ }
+
public Rect calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
Insets insets = Insets.NONE;
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
@@ -462,6 +480,14 @@
return mDisplayCutout.get();
}
+ public void setRoundedCorners(RoundedCorners roundedCorners) {
+ mRoundedCorners = roundedCorners;
+ }
+
+ public RoundedCorners getRoundedCorners() {
+ return mRoundedCorners;
+ }
+
/**
* Modifies the state of this class to exclude a certain type to make it ready for dispatching
* to the client.
@@ -493,6 +519,7 @@
public void scale(float scale) {
mDisplayFrame.scale(scale);
mDisplayCutout.scale(scale);
+ mRoundedCorners = mRoundedCorners.scale(scale);
for (int i = 0; i < SIZE; i++) {
final InsetsSource source = mSources[i];
if (source != null) {
@@ -512,6 +539,7 @@
public void set(InsetsState other, boolean copySources) {
mDisplayFrame.set(other.mDisplayFrame);
mDisplayCutout.set(other.mDisplayCutout);
+ mRoundedCorners = other.getRoundedCorners();
if (copySources) {
for (int i = 0; i < SIZE; i++) {
InsetsSource source = other.mSources[i];
@@ -576,13 +604,13 @@
return Type.CAPTION_BAR;
case ITYPE_IME:
return Type.IME;
- case ITYPE_TOP_GESTURES:
- case ITYPE_BOTTOM_GESTURES:
case ITYPE_TOP_MANDATORY_GESTURES:
case ITYPE_BOTTOM_MANDATORY_GESTURES:
case ITYPE_LEFT_MANDATORY_GESTURES:
case ITYPE_RIGHT_MANDATORY_GESTURES:
return Type.MANDATORY_SYSTEM_GESTURES;
+ case ITYPE_TOP_GESTURES:
+ case ITYPE_BOTTOM_GESTURES:
case ITYPE_LEFT_GESTURES:
case ITYPE_RIGHT_GESTURES:
return Type.SYSTEM_GESTURES;
@@ -619,11 +647,15 @@
}
public void dump(String prefix, PrintWriter pw) {
+ final String newPrefix = prefix + " ";
pw.println(prefix + "InsetsState");
+ pw.println(newPrefix + "mDisplayFrame=" + mDisplayFrame);
+ pw.println(newPrefix + "mDisplayCutout=" + mDisplayCutout.get());
+ pw.println(newPrefix + "mRoundedCorners=" + mRoundedCorners);
for (int i = 0; i < SIZE; i++) {
InsetsSource source = mSources[i];
if (source == null) continue;
- source.dump(prefix + " ", pw);
+ source.dump(newPrefix + " ", pw);
}
}
@@ -713,7 +745,8 @@
InsetsState state = (InsetsState) o;
if (!mDisplayFrame.equals(state.mDisplayFrame)
- || !mDisplayCutout.equals(state.mDisplayCutout)) {
+ || !mDisplayCutout.equals(state.mDisplayCutout)
+ || !mRoundedCorners.equals(state.mRoundedCorners)) {
return false;
}
for (int i = 0; i < SIZE; i++) {
@@ -737,7 +770,8 @@
@Override
public int hashCode() {
- return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources));
+ return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources),
+ mRoundedCorners);
}
public InsetsState(Parcel in) {
@@ -754,6 +788,7 @@
mDisplayFrame.writeToParcel(dest, flags);
mDisplayCutout.writeToParcel(dest, flags);
dest.writeParcelableArray(mSources, 0);
+ dest.writeTypedObject(mRoundedCorners, flags);
}
public static final @android.annotation.NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
@@ -771,6 +806,7 @@
mDisplayFrame.set(Rect.CREATOR.createFromParcel(in));
mDisplayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
mSources = in.readParcelableArray(null, InsetsSource.class);
+ mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR);
}
@Override
@@ -785,6 +821,7 @@
return "InsetsState: {"
+ "mDisplayFrame=" + mDisplayFrame
+ ", mDisplayCutout=" + mDisplayCutout
+ + ", mRoundedCorners=" + mRoundedCorners
+ ", mSources= { " + joiner
+ " }";
}
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
new file mode 100644
index 0000000..cc7525b
--- /dev/null
+++ b/core/java/android/view/RoundedCorner.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents a rounded corner of the display.
+ *
+ * <code>
+ * ________
+ * / ^
+ * / | Radius
+ * | v
+ * | X <- Center point
+ * |<----->
+ * Radius
+ * </code>
+ *
+ * <p>Note: The rounded corner formed by the radius and the center is an approximation.</p>
+ *
+ * <p>{@link RoundedCorner} is immutable.</p>
+ */
+public final class RoundedCorner implements Parcelable {
+
+ /**
+ * The rounded corner is at the top-left of the screen.
+ */
+ public static final int POSITION_TOP_LEFT = 0;
+ /**
+ * The rounded corner is at the top-right of the screen.
+ */
+ public static final int POSITION_TOP_RIGHT = 1;
+ /**
+ * The rounded corner is at the bottom-right of the screen.
+ */
+ public static final int POSITION_BOTTOM_RIGHT = 2;
+ /**
+ * The rounded corner is at the bottom-left of the screen.
+ */
+ public static final int POSITION_BOTTOM_LEFT = 3;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "POSITION_" }, value = {
+ POSITION_TOP_LEFT,
+ POSITION_TOP_RIGHT,
+ POSITION_BOTTOM_RIGHT,
+ POSITION_BOTTOM_LEFT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Position {}
+
+ private final @Position int mPosition;
+ private final int mRadius;
+ @NonNull
+ private final Point mCenter;
+
+ /**
+ * Creates an empty {@link RoundedCorner} on the given position.
+ * @hide
+ */
+ @VisibleForTesting
+ public RoundedCorner(@Position int position) {
+ mPosition = position;
+ mRadius = 0;
+ mCenter = new Point(0, 0);
+ }
+
+ /**
+ * Creates a {@link RoundedCorner}.
+ *
+ * <p>Note that this is only useful for tests. For production code, developers should always
+ * use a {@link RoundedCorner} obtained from the system via
+ * {@link WindowInsets#getRoundedCorner} or {@link Display#getRoundedCorner}.</p>
+ *
+ * @param position the position of the rounded corner.
+ * @param radius the radius of the rounded corner.
+ * @param centerX the x of center point of the rounded corner.
+ * @param centerY the y of center point of the rounded corner.
+ *
+ */
+ public RoundedCorner(@Position int position, int radius, int centerX,
+ int centerY) {
+ mPosition = position;
+ mRadius = radius;
+ mCenter = new Point(centerX, centerY);
+ }
+
+ /**
+ * Creates a {@link RoundedCorner} from a passed in {@link RoundedCorner}.
+ *
+ * @hide
+ */
+ RoundedCorner(RoundedCorner rc) {
+ mPosition = rc.getPosition();
+ mRadius = rc.getRadius();
+ mCenter = new Point(rc.getCenter());
+ }
+
+ /**
+ * Get the position of this {@link RoundedCorner}.
+ *
+ * @see #POSITION_TOP_LEFT
+ * @see #POSITION_TOP_RIGHT
+ * @see #POSITION_BOTTOM_RIGHT
+ * @see #POSITION_BOTTOM_LEFT
+ */
+ public @Position int getPosition() {
+ return mPosition;
+ }
+
+ /**
+ * Returns the radius of a quarter circle approximation of this {@link RoundedCorner}.
+ *
+ * @return the rounded corner radius of this {@link RoundedCorner}. Returns 0 if there is no
+ * rounded corner.
+ */
+ public int getRadius() {
+ return mRadius;
+ }
+
+ /**
+ * Returns the circle center of a quarter circle approximation of this {@link RoundedCorner}.
+ *
+ * @return the center point of this {@link RoundedCorner} in the application's coordinate.
+ */
+ @NonNull
+ public Point getCenter() {
+ return new Point(mCenter);
+ }
+
+ /**
+ * Checks whether this {@link RoundedCorner} exists and is inside the application's bounds.
+ *
+ * @return {@code false} if there is a rounded corner and is contained in the application's
+ * bounds. Otherwise return {@code true}.
+ *
+ * @hide
+ */
+ public boolean isEmpty() {
+ return mRadius == 0 || mCenter.x == 0 || mCenter.y == 0;
+ }
+
+ private String getPositionString(@Position int position) {
+ switch (position) {
+ case POSITION_TOP_LEFT:
+ return "TopLeft";
+ case POSITION_TOP_RIGHT:
+ return "TopRight";
+ case POSITION_BOTTOM_RIGHT:
+ return "BottomRight";
+ case POSITION_BOTTOM_LEFT:
+ return "BottomLeft";
+ default:
+ return "Invalid";
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof RoundedCorner) {
+ RoundedCorner r = (RoundedCorner) o;
+ return mPosition == r.mPosition && mRadius == r.mRadius
+ && mCenter.equals(r.mCenter);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ result = 31 * result + mPosition;
+ result = 31 * result + mRadius;
+ result = 31 * result + mCenter.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "RoundedCorner{"
+ + "position=" + getPositionString(mPosition)
+ + ", radius=" + mRadius
+ + ", center=" + mCenter
+ + '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mPosition);
+ out.writeInt(mRadius);
+ out.writeInt(mCenter.x);
+ out.writeInt(mCenter.y);
+ }
+
+ public static final @NonNull Creator<RoundedCorner> CREATOR = new Creator<RoundedCorner>() {
+ @Override
+ public RoundedCorner createFromParcel(Parcel in) {
+ return new RoundedCorner(in.readInt(), in.readInt(), in.readInt(), in.readInt());
+ }
+
+ @Override
+ public RoundedCorner[] newArray(int size) {
+ return new RoundedCorner[size];
+ }
+ };
+}
diff --git a/core/java/android/service/attestation/ImpressionToken.aidl b/core/java/android/view/RoundedCorners.aidl
similarity index 74%
copy from core/java/android/service/attestation/ImpressionToken.aidl
copy to core/java/android/view/RoundedCorners.aidl
index 284a4ba..0a901c0 100644
--- a/core/java/android/service/attestation/ImpressionToken.aidl
+++ b/core/java/android/view/RoundedCorners.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
+/**
+ * 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
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.attestation;
+package android.view;
-parcelable ImpressionToken;
\ No newline at end of file
+parcelable RoundedCorners;
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
new file mode 100644
index 0000000..015e804
--- /dev/null
+++ b/core/java/android/view/RoundedCorners.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+import android.view.RoundedCorner.Position;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+
+/**
+ * A class to create & manage all the {@link RoundedCorner} on the display.
+ *
+ * @hide
+ */
+public class RoundedCorners implements Parcelable {
+
+ public static final RoundedCorners NO_ROUNDED_CORNERS = new RoundedCorners(
+ new RoundedCorner(POSITION_TOP_LEFT), new RoundedCorner(POSITION_TOP_RIGHT),
+ new RoundedCorner(POSITION_BOTTOM_RIGHT), new RoundedCorner(POSITION_BOTTOM_LEFT));
+
+ /**
+ * The number of possible positions at which rounded corners can be located.
+ */
+ public static final int ROUNDED_CORNER_POSITION_LENGTH = 4;
+
+ private static final Object CACHE_LOCK = new Object();
+
+ @GuardedBy("CACHE_LOCK")
+ private static int sCachedDisplayWidth;
+ @GuardedBy("CACHE_LOCK")
+ private static int sCachedDisplayHeight;
+ @GuardedBy("CACHE_LOCK")
+ private static Pair<Integer, Integer> sCachedRadii;
+ @GuardedBy("CACHE_LOCK")
+ private static RoundedCorners sCachedRoundedCorners;
+
+ @VisibleForTesting
+ public final RoundedCorner[] mRoundedCorners;
+
+ public RoundedCorners(RoundedCorner[] roundedCorners) {
+ mRoundedCorners = roundedCorners;
+ }
+
+ public RoundedCorners(RoundedCorner topLeft, RoundedCorner topRight, RoundedCorner bottomRight,
+ RoundedCorner bottomLeft) {
+ mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ mRoundedCorners[POSITION_TOP_LEFT] = topLeft;
+ mRoundedCorners[POSITION_TOP_RIGHT] = topRight;
+ mRoundedCorners[POSITION_BOTTOM_RIGHT] = bottomRight;
+ mRoundedCorners[POSITION_BOTTOM_LEFT] = bottomLeft;
+ }
+
+ public RoundedCorners(RoundedCorners roundedCorners) {
+ mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
+ mRoundedCorners[i] = new RoundedCorner(roundedCorners.mRoundedCorners[i]);
+ }
+ }
+
+ /**
+ * Creates the rounded corners according to @android:dimen/rounded_corner_radius,
+ * @android:dimen/rounded_corner_radius_top and @android:dimen/rounded_corner_radius_bottom
+ */
+ public static RoundedCorners fromResources(
+ Resources res, int displayWidth, int displayHeight) {
+ return fromRadii(loadRoundedCornerRadii(res), displayWidth, displayHeight);
+ }
+
+ /**
+ * Creates the rounded corners from radius
+ */
+ @VisibleForTesting
+ public static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int displayWidth,
+ int displayHeight) {
+ if (radii == null) {
+ return null;
+ }
+
+ synchronized (CACHE_LOCK) {
+ if (radii.equals(sCachedRadii) && sCachedDisplayWidth == displayWidth
+ && sCachedDisplayHeight == displayHeight) {
+ return sCachedRoundedCorners;
+ }
+ }
+
+ final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ final int topRadius = radii.first > 0 ? radii.first : 0;
+ final int bottomRadius = radii.second > 0 ? radii.second : 0;
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
+ roundedCorners[i] = createRoundedCorner(
+ i,
+ i <= POSITION_TOP_RIGHT ? topRadius : bottomRadius,
+ displayWidth,
+ displayHeight);
+ }
+
+ final RoundedCorners result = new RoundedCorners(roundedCorners);
+ synchronized (CACHE_LOCK) {
+ sCachedDisplayWidth = displayWidth;
+ sCachedDisplayHeight = displayHeight;
+ sCachedRadii = radii;
+ sCachedRoundedCorners = result;
+ }
+ return result;
+ }
+
+ /**
+ * Loads the rounded corner radii from resources.
+ *
+ * @param res
+ * @return a Pair of radius. The first is the top rounded corner radius and second is the
+ * bottom corner radius.
+ */
+ @Nullable
+ private static Pair<Integer, Integer> loadRoundedCornerRadii(Resources res) {
+ final int radiusDefault = res.getDimensionPixelSize(R.dimen.rounded_corner_radius);
+ final int radiusTop = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_top);
+ final int radiusBottom = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_bottom);
+ if (radiusDefault == 0 && radiusTop == 0 && radiusBottom == 0) {
+ return null;
+ }
+ final Pair<Integer, Integer> radii = new Pair<>(
+ radiusTop > 0 ? radiusTop : radiusDefault,
+ radiusBottom > 0 ? radiusBottom : radiusDefault);
+ return radii;
+ }
+
+ /**
+ * Insets the reference frame of the rounded corners.
+ *
+ * @return a copy of this instance which has been inset
+ */
+ public RoundedCorners inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
+ final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
+ roundedCorners[i] = insetRoundedCorner(i, insetLeft, insetTop, insetRight, insetBottom);
+ }
+ return new RoundedCorners(roundedCorners);
+ }
+
+ private RoundedCorner insetRoundedCorner(@Position int position, int insetLeft,
+ int insetTop, int insetRight, int insetBottom) {
+ if (mRoundedCorners[position].isEmpty()) {
+ return new RoundedCorner(position);
+ }
+
+ final int radius = mRoundedCorners[position].getRadius();
+ final Point center = mRoundedCorners[position].getCenter();
+ boolean hasRoundedCorner;
+ switch (position) {
+ case POSITION_TOP_LEFT:
+ hasRoundedCorner = radius > insetTop || radius > insetLeft;
+ break;
+ case POSITION_TOP_RIGHT:
+ hasRoundedCorner = radius > insetTop || radius > insetRight;
+ break;
+ case POSITION_BOTTOM_RIGHT:
+ hasRoundedCorner = radius > insetBottom || radius > insetRight;
+ break;
+ case POSITION_BOTTOM_LEFT:
+ hasRoundedCorner = radius > insetBottom || radius > insetLeft;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "The position is not one of the RoundedCornerPosition =" + position);
+ }
+ return new RoundedCorner(
+ position, radius,
+ hasRoundedCorner ? center.x - insetLeft : 0,
+ hasRoundedCorner ? center.y - insetTop : 0);
+ }
+
+ /**
+ * Returns the {@link RoundedCorner} of the given position if there is one.
+ *
+ * @param position the position of the rounded corner on the display.
+ * @return the rounded corner of the given position. Returns {@code null} if
+ * {@link RoundedCorner#isEmpty()} is {@code true}.
+ */
+ @Nullable
+ public RoundedCorner getRoundedCorner(@Position int position) {
+ return mRoundedCorners[position].isEmpty()
+ ? null : new RoundedCorner(mRoundedCorners[position]);
+ }
+
+ /**
+ * Sets the rounded corner of given position.
+ *
+ * @param position the position of this rounded corner
+ * @param roundedCorner the rounded corner or null if there is none
+ */
+ public void setRoundedCorner(@Position int position, @Nullable RoundedCorner roundedCorner) {
+ mRoundedCorners[position] = roundedCorner == null
+ ? new RoundedCorner(position) : roundedCorner;
+ }
+
+ /**
+ * Returns an array of {@link RoundedCorner}s. Ordinal value of RoundedCornerPosition is used
+ * as an index of the array.
+ *
+ * @return an array of {@link RoundedCorner}s, one for each rounded corner area.
+ */
+ public RoundedCorner[] getAllRoundedCorners() {
+ RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
+ roundedCorners[i] = new RoundedCorner(roundedCorners[i]);
+ }
+ return roundedCorners;
+ }
+
+ /**
+ * Returns a scaled RoundedCorners.
+ */
+ public RoundedCorners scale(float scale) {
+ if (scale == 1f) {
+ return this;
+ }
+
+ RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
+ final RoundedCorner roundedCorner = mRoundedCorners[i];
+ roundedCorners[i] = new RoundedCorner(
+ i,
+ (int) (roundedCorner.getRadius() * scale),
+ (int) (roundedCorner.getCenter().x * scale),
+ (int) (roundedCorner.getCenter().y * scale));
+ }
+ return new RoundedCorners(roundedCorners);
+ }
+
+ /**
+ * Returns a rotated RoundedCorners.
+ */
+ public RoundedCorners rotate(@Surface.Rotation int rotation, int initialDisplayWidth,
+ int initialDisplayHeight) {
+ if (rotation == ROTATION_0) {
+ return this;
+ }
+ final boolean isSizeFlipped = rotation == ROTATION_90 || rotation == ROTATION_270;
+ RoundedCorner[] newCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ int newPosistion;
+ for (int i = 0; i < mRoundedCorners.length; i++) {
+ newPosistion = getRotatedIndex(i, rotation);
+ newCorners[newPosistion] = createRoundedCorner(
+ newPosistion,
+ mRoundedCorners[i].getRadius(),
+ isSizeFlipped ? initialDisplayHeight : initialDisplayWidth,
+ isSizeFlipped ? initialDisplayWidth : initialDisplayHeight);
+ }
+ return new RoundedCorners(newCorners);
+ }
+
+ private static RoundedCorner createRoundedCorner(@Position int position,
+ int radius, int displayWidth, int displayHeight) {
+ switch (position) {
+ case POSITION_TOP_LEFT:
+ return new RoundedCorner(
+ POSITION_TOP_LEFT,
+ radius,
+ radius > 0 ? radius : 0,
+ radius > 0 ? radius : 0);
+ case POSITION_TOP_RIGHT:
+ return new RoundedCorner(
+ POSITION_TOP_RIGHT,
+ radius,
+ radius > 0 ? displayWidth - radius : 0,
+ radius > 0 ? radius : 0);
+ case POSITION_BOTTOM_RIGHT:
+ return new RoundedCorner(
+ POSITION_BOTTOM_RIGHT,
+ radius,
+ radius > 0 ? displayWidth - radius : 0,
+ radius > 0 ? displayHeight - radius : 0);
+ case POSITION_BOTTOM_LEFT:
+ return new RoundedCorner(
+ POSITION_BOTTOM_LEFT,
+ radius,
+ radius > 0 ? radius : 0,
+ radius > 0 ? displayHeight - radius : 0);
+ default:
+ throw new IllegalArgumentException(
+ "The position is not one of the RoundedCornerPosition =" + position);
+ }
+ }
+
+ private static int getRotatedIndex(int position, int rotation) {
+ return (position - rotation + ROUNDED_CORNER_POSITION_LENGTH) % 4;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ for (RoundedCorner roundedCorner : mRoundedCorners) {
+ result = result * 31 + roundedCorner.hashCode();
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof RoundedCorners) {
+ RoundedCorners r = (RoundedCorners) o;
+ return Arrays.deepEquals(mRoundedCorners, ((RoundedCorners) o).mRoundedCorners);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "RoundedCorners{" + Arrays.toString(mRoundedCorners) + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (equals(NO_ROUNDED_CORNERS)) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ dest.writeTypedArray(mRoundedCorners, flags);
+ }
+ }
+
+ public static final @NonNull Creator<RoundedCorners> CREATOR = new Creator<RoundedCorners>() {
+ @Override
+ public RoundedCorners createFromParcel(Parcel in) {
+ int variant = in.readInt();
+ if (variant == 0) {
+ return NO_ROUNDED_CORNERS;
+ }
+ RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
+ in.readTypedArray(roundedCorners, RoundedCorner.CREATOR);
+ return new RoundedCorners(roundedCorners);
+ }
+
+ @Override
+ public RoundedCorners[] newArray(int size) {
+ return new RoundedCorners[size];
+ }
+ };
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index acd2507..98b4acd 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -188,8 +188,6 @@
IBinder displayToken, int mode);
private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
long barrierObject, long frame);
- private static native void nativeReparentChildren(long transactionObj, long nativeObject,
- long newParentObject);
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
@@ -2970,15 +2968,6 @@
}
/**
- * @hide
- */
- public Transaction reparentChildren(SurfaceControl sc, SurfaceControl newParent) {
- checkPreconditions(sc);
- nativeReparentChildren(mNativeObject, sc.mNativeObject, newParent.mNativeObject);
- return this;
- }
-
- /**
* Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
* crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
* parent Surface.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f603ef7..70ec2d4 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -877,6 +877,10 @@
synchronized (mSurfaceControlLock) {
mSurface.release();
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
+ mBlastBufferQueue = null;
+ }
if (mRtHandlingPositionUpdates) {
mRtReleaseSurfaces = true;
@@ -901,10 +905,6 @@
transaction.remove(mBlastSurfaceControl);
mBlastSurfaceControl = null;
}
- if (mBlastBufferQueue != null) {
- mBlastBufferQueue.destroy();
- mBlastBufferQueue = null;
- }
}
private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 749c0df..e1ccc51 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8740,14 +8740,17 @@
/**
* Populates a {@link ViewStructure} for content capture.
*
- * <p>This method is called after a view is that is eligible for content capture
- * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for
- * the user, and the activity rendering the view is enabled for content capture) is laid out and
- * is visible.
- *
- * <p>The populated structure is then passed to the service through
+ * <p>This method is called after a view that is eligible for content capture
+ * (for example, if it {@link #isImportantForContentCapture()}, an intelligence service is
+ * enabled for the user, and the activity rendering the view is enabled for content capture)
+ * is laid out and is visible. The populated structure is then passed to the service through
* {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}.
*
+ * <p>The default implementation of this method sets the most relevant properties based on
+ * related {@link View} methods, and views in the standard Android widgets library also
+ * override it to set their relevant properties. Therefore, if overriding this method, it
+ * is recommended to call {@code super.onProvideContentCaptureStructure()}.
+ *
* <p><b>Note: </b>views that manage a virtual structure under this view must populate just
* the node representing this view and return right away, then asynchronously report (not
* necessarily in the UI thread) when the children nodes appear, disappear or have their text
@@ -8755,7 +8758,7 @@
* {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)},
* {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and
* {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)}
- * respectively. The structure for the a child must be created using
+ * respectively. The structure for a child must be created using
* {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the
* {@code autofillId} for a child can be obtained either through
* {@code childStructure.getAutofillId()} or
@@ -8900,7 +8903,7 @@
/**
* Called when assist structure is being retrieved from a view as part of
* {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to
- * generate additional virtual structure under this view. The defaullt implementation
+ * generate additional virtual structure under this view. The default implementation
* uses {@link #getAccessibilityNodeProvider()} to try to generate this from the
* view's virtual accessibility nodes, if any. You can override this for a more
* optimal implementation providing this data.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5e3599d..036a703 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -138,6 +138,7 @@
import android.os.UserHandle;
import android.sysprop.DisplayProperties;
import android.util.AndroidRuntimeException;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -157,6 +158,7 @@
import android.view.View.FocusDirection;
import android.view.View.MeasureSpec;
import android.view.Window.OnContentApplyWindowInsetsListener;
+import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -313,7 +315,7 @@
* In that case we receive a call back from {@link ActivityThread} and this flag is used to
* preserve the initial value.
*
- * @see #performConfigurationChange(Configuration, Configuration, boolean, int)
+ * @see #performConfigurationChange(MergedConfiguration, boolean, int)
*/
private boolean mForceNextConfigUpdate;
@@ -926,6 +928,33 @@
}
}
+ // TODO(b/161810301): Make this private after window layout is moved to the client side.
+ public static void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
+ Rect displayFrame, Rect outBounds) {
+ final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
+ final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
+ final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
+ final Rect df = displayFrame;
+ Insets insets = Insets.of(0, 0, 0, 0);
+ for (int i = types.size() - 1; i >= 0; i--) {
+ final InsetsSource source = state.peekSource(types.valueAt(i));
+ if (source == null) {
+ continue;
+ }
+ insets = Insets.max(insets, source.calculateInsets(
+ df, attrs.isFitInsetsIgnoringVisibility()));
+ }
+ final int left = (sidesToFit & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
+ final int top = (sidesToFit & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
+ final int right = (sidesToFit & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
+ final int bottom = (sidesToFit & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
+ outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom);
+ }
+
+ private Configuration getConfiguration() {
+ return mContext.getResources().getConfiguration();
+ }
+
/**
* We have one child
*/
@@ -1057,18 +1086,15 @@
controlInsetsForCompatibility(mWindowAttributes);
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
- mInsetsController.getRequestedVisibility(), mTmpFrames.frame,
- inputChannel, mTempInsets, mTempControls);
+ mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
+ mTempControls);
if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
}
- setFrame(mTmpFrames.frame);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
- inputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
@@ -1084,6 +1110,9 @@
mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls);
+ computeWindowBounds(mWindowAttributes, mInsetsController.getState(),
+ getConfiguration().windowConfiguration.getBounds(), mTmpFrames.frame);
+ setFrame(mTmpFrames.frame);
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -1357,7 +1386,7 @@
}
private int getNightMode() {
- return mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
}
private void updateForceDarkMode() {
@@ -2333,7 +2362,7 @@
/* package */ WindowInsets getWindowInsets(boolean forceConstruct) {
if (mLastWindowInsets == null || forceConstruct) {
- final Configuration config = mContext.getResources().getConfiguration();
+ final Configuration config = getConfiguration();
mLastWindowInsets = mInsetsController.calculateInsets(
config.isScreenRound(), mAttachInfo.mAlwaysConsumeSystemBars,
mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
@@ -2469,7 +2498,7 @@
mFullRedrawNeeded = true;
mLayoutRequested = true;
- final Configuration config = mContext.getResources().getConfiguration();
+ final Configuration config = getConfiguration();
if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
@@ -4762,7 +4791,7 @@
}
// TODO: Centralize this sanitization? Why do we let setting bad modes?
// Alternatively, can we just let HWUI figure it out? Do we need to care here?
- if (!mContext.getResources().getConfiguration().isScreenWideColorGamut()) {
+ if (!getConfiguration().isScreenWideColorGamut()) {
colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
}
mAttachInfo.mThreadedRenderer.setColorMode(colorMode);
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 7a5561c..41c38a1 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -82,6 +82,7 @@
@Nullable private Rect mTempRect;
private final boolean mIsRound;
@Nullable private final DisplayCutout mDisplayCutout;
+ @Nullable private final RoundedCorners mRoundedCorners;
/**
* In multi-window we force show the navigation bar. Because we don't want that the surface size
@@ -125,11 +126,12 @@
* @hide
* @deprecated Use {@link WindowInsets(SparseArray, SparseArray, boolean, boolean, DisplayCutout)}
*/
- public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect,
- boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
+ @Deprecated
+ public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect, boolean isRound,
+ boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)),
- isRound, alwaysConsumeSystemBars, displayCutout, systemBars(),
+ isRound, alwaysConsumeSystemBars, displayCutout, null, systemBars(),
false /* compatIgnoreVisibility */);
}
@@ -150,7 +152,8 @@
boolean[] typeVisibilityMap,
boolean isRound,
boolean alwaysConsumeSystemBars, DisplayCutout displayCutout,
- @InsetsType int compatInsetsTypes, boolean compatIgnoreVisibility) {
+ RoundedCorners roundedCorners, @InsetsType int compatInsetsTypes,
+ boolean compatIgnoreVisibility) {
mSystemWindowInsetsConsumed = typeInsetsMap == null;
mTypeInsetsMap = mSystemWindowInsetsConsumed
? new Insets[SIZE]
@@ -170,6 +173,8 @@
mDisplayCutoutConsumed = displayCutout == null;
mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty())
? null : displayCutout;
+
+ mRoundedCorners = roundedCorners;
}
/**
@@ -182,6 +187,7 @@
src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
src.mTypeVisibilityMap, src.mIsRound,
src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
+ src.mRoundedCorners,
src.mCompatInsetsTypes,
src.mCompatIgnoreVisibility);
}
@@ -235,7 +241,7 @@
@UnsupportedAppUsage
public WindowInsets(Rect systemWindowInsets) {
this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null,
- systemBars(), false /* compatIgnoreVisibility */);
+ null, systemBars(), false /* compatIgnoreVisibility */);
}
/**
@@ -466,7 +472,7 @@
public boolean hasInsets() {
return !getInsets(mTypeInsetsMap, all()).equals(Insets.NONE)
|| !getInsets(mTypeMaxInsetsMap, all()).equals(Insets.NONE)
- || mDisplayCutout != null;
+ || mDisplayCutout != null || mRoundedCorners != null;
}
/**
@@ -487,6 +493,23 @@
}
/**
+ * Returns the {@link RoundedCorner} of the given position if there is one.
+ *
+ * @param position the position of the rounded corner on the display. The value should be one of
+ * the following:
+ * {@link RoundedCorner#POSITION_TOP_LEFT},
+ * {@link RoundedCorner#POSITION_TOP_RIGHT},
+ * {@link RoundedCorner#POSITION_BOTTOM_RIGHT},
+ * {@link RoundedCorner#POSITION_BOTTOM_LEFT}.
+ * @return the rounded corner of the given position. Returns {@code null} if there is none or
+ * the rounded corner area is not inside the application's bounds.
+ */
+ @Nullable
+ public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
+ return mRoundedCorners == null ? null : mRoundedCorners.getRoundedCorner(position);
+ }
+
+ /**
* Returns a copy of this WindowInsets with the cutout fully consumed.
*
* @return A modified copy of this WindowInsets
@@ -501,7 +524,7 @@
mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
- null /* displayCutout */,
+ null /* displayCutout */, mRoundedCorners,
mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -553,7 +576,7 @@
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
displayCutoutCopyConstructorArgument(this),
- mCompatInsetsTypes, mCompatIgnoreVisibility);
+ mRoundedCorners, mCompatInsetsTypes, mCompatIgnoreVisibility);
}
// TODO(b/119190588): replace @code with @link below
@@ -856,6 +879,8 @@
result.append(mDisplayCutout != null ? "cutout=" + mDisplayCutout : "");
result.append("\n ");
+ result.append(mRoundedCorners != null ? "roundedCorners=" + mRoundedCorners : "");
+ result.append("\n ");
result.append(isRound() ? "round" : "");
result.append("}");
return result.toString();
@@ -947,6 +972,9 @@
: mDisplayCutout == null
? DisplayCutout.NO_CUTOUT
: mDisplayCutout.inset(left, top, right, bottom),
+ mRoundedCorners == null
+ ? RoundedCorners.NO_ROUNDED_CORNERS
+ : mRoundedCorners.inset(left, top, right, bottom),
mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -964,13 +992,14 @@
&& Arrays.equals(mTypeInsetsMap, that.mTypeInsetsMap)
&& Arrays.equals(mTypeMaxInsetsMap, that.mTypeMaxInsetsMap)
&& Arrays.equals(mTypeVisibilityMap, that.mTypeVisibilityMap)
- && Objects.equals(mDisplayCutout, that.mDisplayCutout);
+ && Objects.equals(mDisplayCutout, that.mDisplayCutout)
+ && Objects.equals(mRoundedCorners, that.mRoundedCorners);
}
@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
- Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout,
+ Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners,
mAlwaysConsumeSystemBars, mSystemWindowInsetsConsumed, mStableInsetsConsumed,
mDisplayCutoutConsumed);
}
@@ -1032,6 +1061,7 @@
private boolean mStableInsetsConsumed = true;
private DisplayCutout mDisplayCutout;
+ private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
private boolean mIsRound;
private boolean mAlwaysConsumeSystemBars;
@@ -1057,6 +1087,7 @@
mSystemInsetsConsumed = insets.mSystemWindowInsetsConsumed;
mStableInsetsConsumed = insets.mStableInsetsConsumed;
mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
+ mRoundedCorners = insets.mRoundedCorners;
mIsRound = insets.mIsRound;
mAlwaysConsumeSystemBars = insets.mAlwaysConsumeSystemBars;
}
@@ -1262,6 +1293,29 @@
/** @hide */
@NonNull
+ public Builder setRoundedCorners(RoundedCorners roundedCorners) {
+ mRoundedCorners = roundedCorners != null
+ ? roundedCorners : RoundedCorners.NO_ROUNDED_CORNERS;
+ return this;
+ }
+
+ /**
+ * Sets the rounded corner of given position.
+ *
+ * @see #getRoundedCorner(int)
+ * @param position the position of this rounded corner
+ * @param roundedCorner the rounded corner or null if there is none
+ * @return itself
+ */
+ @NonNull
+ public Builder setRoundedCorner(@RoundedCorner.Position int position,
+ @Nullable RoundedCorner roundedCorner) {
+ mRoundedCorners.setRoundedCorner(position, roundedCorner);
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
public Builder setRound(boolean round) {
mIsRound = round;
return this;
@@ -1283,7 +1337,7 @@
public WindowInsets build() {
return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout,
+ mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout, mRoundedCorners,
systemBars(), false /* compatIgnoreVisibility */);
}
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 63f1eed..9e87c95 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1511,9 +1511,7 @@
* Use {@link #dimAmount} to control the amount of dim. */
public static final int FLAG_DIM_BEHIND = 0x00000002;
- /** Window flag: blur everything behind this window.
- * @deprecated Blurring is no longer supported. */
- @Deprecated
+ /** Window flag: enable blur behind for this window. */
public static final int FLAG_BLUR_BEHIND = 0x00000004;
/** Window flag: this window won't ever get key input focus, so the
@@ -3233,11 +3231,16 @@
public boolean preferMinimalPostProcessing = false;
/**
- * Indicates that this window wants to have blurred content behind it.
+ * Specifies the amount of blur to be used to blur everything behind the window.
+ * The effect is similar to the dimAmount, but instead of dimming, the content behind
+ * will be blurred.
*
- * @hide
+ * The blur behind radius range starts at 0, which means no blur, and increases until 150
+ * for the densest blur.
+ *
+ * @see #FLAG_BLUR_BEHIND
*/
- public int backgroundBlurRadius = 0;
+ public int blurBehindRadius = 0;
/**
* The color mode requested by this window. The target display may
@@ -3626,7 +3629,7 @@
out.writeInt(mFitInsetsSides);
out.writeBoolean(mFitInsetsIgnoringVisibility);
out.writeBoolean(preferMinimalPostProcessing);
- out.writeInt(backgroundBlurRadius);
+ out.writeInt(blurBehindRadius);
if (providesInsetsTypes != null) {
out.writeInt(providesInsetsTypes.length);
out.writeIntArray(providesInsetsTypes);
@@ -3695,7 +3698,7 @@
mFitInsetsSides = in.readInt();
mFitInsetsIgnoringVisibility = in.readBoolean();
preferMinimalPostProcessing = in.readBoolean();
- backgroundBlurRadius = in.readInt();
+ blurBehindRadius = in.readInt();
int insetsTypesLength = in.readInt();
if (insetsTypesLength > 0) {
providesInsetsTypes = new int[insetsTypesLength];
@@ -3940,8 +3943,8 @@
changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED;
}
- if (backgroundBlurRadius != o.backgroundBlurRadius) {
- backgroundBlurRadius = o.backgroundBlurRadius;
+ if (blurBehindRadius != o.blurBehindRadius) {
+ blurBehindRadius = o.blurBehindRadius;
changes |= BACKGROUND_BLUR_RADIUS_CHANGED;
}
@@ -4108,9 +4111,9 @@
sb.append(" preferMinimalPostProcessing=");
sb.append(preferMinimalPostProcessing);
}
- if (backgroundBlurRadius != 0) {
- sb.append(" backgroundBlurRadius=");
- sb.append(backgroundBlurRadius);
+ if (blurBehindRadius != 0) {
+ sb.append(" blurBehindRadius=");
+ sb.append(blurBehindRadius);
}
sb.append(System.lineSeparator());
sb.append(prefix).append(" fl=").append(
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index dd0ab65..47ac1ee 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -116,14 +116,6 @@
*/
public static final int RELAYOUT_INSETS_PENDING = 0x1;
- /**
- * Flag for relayout: the client may be currently using the current surface,
- * so if it is to be destroyed as a part of the relayout the destroy must
- * be deferred until later. The client will call performDeferredDestroy()
- * when it is okay.
- */
- public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
-
public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1;
public static final int ADD_FLAG_APP_VISIBLE = 0x2;
public static final int ADD_FLAG_USE_TRIPLE_BUFFERING = 0x4;
@@ -147,7 +139,6 @@
public static final int ADD_INVALID_DISPLAY = -9;
public static final int ADD_INVALID_TYPE = -10;
public static final int ADD_INVALID_USER = -11;
- public static final int ADD_TOO_MANY_TOKENS = -12;
@UnsupportedAppUsage
private static WindowManagerGlobal sDefaultWindowManager;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index dd56c15..b85f1079 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -24,7 +24,7 @@
import android.graphics.Region;
import android.os.IBinder;
import android.os.RemoteException;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.util.Log;
import android.util.MergedConfiguration;
import android.window.ClientWindowFrames;
@@ -135,7 +135,7 @@
*/
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
+ int viewVisibility, int displayId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
@@ -171,10 +171,10 @@
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
- Rect outFrame, InputChannel outInputChannel, InsetsState outInsetsState,
+ InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibility,
- outFrame, outInputChannel, outInsetsState, outActiveControls);
+ outInputChannel, outInsetsState, outActiveControls);
}
@Override
@@ -466,7 +466,7 @@
}
@Override
- public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+ public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
String hashAlgorithm) {
return null;
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 415b3a7..37220fe 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -164,12 +164,12 @@
/**
* Default implementation calls {@link #finishComposingText()} and
- * {@code setImeTemporarilyConsumesInput(false)}.
+ * {@code setImeConsumesInput(false)}.
*/
@CallSuper
public void closeConnection() {
finishComposingText();
- setImeTemporarilyConsumesInput(false);
+ setImeConsumesInput(false);
}
/**
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 2df75f6..bde4cb7 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -299,7 +299,7 @@
* {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} mode.
* @hide
*/
- public static final int IME_FLAG_APP_WINDOW_PORTRAIT = 0x80000000;
+ public static final int IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT = 0x00000001;
/**
* Generic unspecified type for {@link #imeOptions}.
@@ -321,7 +321,6 @@
* 1 1 IME_ACTION_NEXT
* 11 IME_ACTION_DONE
* 111 IME_ACTION_PREVIOUS
- * 1 IME_FLAG_APP_WINDOW_PORTRAIT
* 1 IME_FLAG_NO_PERSONALIZED_LEARNING
* 1 IME_FLAG_NO_FULLSCREEN
* 1 IME_FLAG_NAVIGATE_PREVIOUS
@@ -356,7 +355,7 @@
* Masks for {@link internalImeOptions}
*
* <pre>
- * 1 IME_FLAG_APP_WINDOW_PORTRAIT
+ * 1 IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT
* |-------|-------|-------|-------|</pre>
*/
@@ -984,6 +983,7 @@
dest.writeInt(inputType);
dest.writeInt(imeOptions);
dest.writeString(privateImeOptions);
+ dest.writeInt(internalImeOptions);
TextUtils.writeToParcel(actionLabel, dest, flags);
dest.writeInt(actionId);
dest.writeInt(initialSelStart);
@@ -1019,6 +1019,7 @@
res.inputType = source.readInt();
res.imeOptions = source.readInt();
res.privateImeOptions = source.readString();
+ res.internalImeOptions = source.readInt();
res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.actionId = source.readInt();
res.initialSelStart = source.readInt();
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 73962d7..10fd0e0 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -82,6 +82,7 @@
public static InlineSuggestionInfo newInlineSuggestionInfo(
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
+ @SuppressLint("NullableCollection")
@Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned) {
return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index f3111bd..34a60bb 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1004,20 +1004,19 @@
@Nullable Bundle opts);
/**
- * Called by the input method to indicate that it temporarily consumes all input for itself,
- * or no longer does so.
+ * Called by the input method to indicate that it consumes all input for itself, or no longer
+ * does so.
*
- * <p>Editors should reflect that they are temporarily not receiving input by hiding the
- * cursor if {@code imeTemporarilyConsumesInput} is {@code true}, and resume showing the
- * cursor if it is {@code false}.
+ * <p>Editors should reflect that they are not receiving input by hiding the cursor if
+ * {@code imeConsumesInput} is {@code true}, and resume showing the cursor if it is
+ * {@code false}.
*
- * @param imeTemporarilyConsumesInput {@code true} when the IME is temporarily consuming input
- * and the cursor should be hidden, {@code false} when input to the editor resumes and the
- * cursor should be shown again.
+ * @param imeConsumesInput {@code true} when the IME is consuming input and the cursor should be
+ * hidden, {@code false} when input to the editor resumes and the cursor should be shown again.
* @return {@code true} on success, {@code false} if the input connection is no longer valid, or
* the protocol is not supported.
*/
- default boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+ default boolean setImeConsumesInput(boolean imeConsumesInput) {
return false;
}
}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index b29149f..b1501a4 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -341,7 +341,7 @@
* @throws NullPointerException if the target is {@code null}.
*/
@Override
- public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
- return mTarget.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+ public boolean setImeConsumesInput(boolean imeConsumesInput) {
+ return mTarget.setImeConsumesInput(imeConsumesInput);
}
}
diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/view/inputmethod/OWNERS
+++ b/core/java/android/view/inputmethod/OWNERS
@@ -2,3 +2,5 @@
set noparent
include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index fa46146..b49d3c0 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -101,15 +101,17 @@
}
break;
case STATE_UI_TRANSLATION_PAUSED:
- runForEachView((view) -> view.onPauseUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+ runForEachView(View::onPauseUiTranslation);
break;
case STATE_UI_TRANSLATION_RESUMED:
- runForEachView((view) -> view.onRestoreUiTranslation(),
- STATE_UI_TRANSLATION_PAUSED);
+ runForEachView(View::onRestoreUiTranslation);
break;
case STATE_UI_TRANSLATION_FINISHED:
destroyTranslators();
- runForEachView((view) -> view.onFinishUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+ runForEachView(View::onFinishUiTranslation);
+ synchronized (mLock) {
+ mViews.clear();
+ }
break;
default:
Log.w(TAG, "onAutoTranslationStateChange(): unknown state: " + state);
@@ -191,9 +193,6 @@
*/
private void onUiTranslationStarted(Translator translator, List<AutofillId> views) {
synchronized (mLock) {
- if (views == null || views.size() == 0) {
- throw new IllegalArgumentException("Invalid empty views: " + views);
- }
// Find Views collect the translation data
// TODO(b/178084101): try to optimize, e.g. to this in a single traversal
final int viewCounts = views.size();
@@ -223,22 +222,18 @@
}
}
- private void runForEachView(Consumer<View> action, @UiTranslationState int state) {
+ private void runForEachView(Consumer<View> action) {
synchronized (mLock) {
+ final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews);
mActivity.runOnUiThread(() -> {
- final int viewCounts = mViews.size();
+ final int viewCounts = views.size();
for (int i = 0; i < viewCounts; i++) {
- final View view = mViews.valueAt(i).get();
+ final View view = views.valueAt(i).get();
if (view == null) {
- Log.w(TAG, "The View for autofill id " + mViews.keyAt(i)
- + " may be gone for state " + stateToString(state));
continue;
}
action.accept(view);
}
- if (state == STATE_UI_TRANSLATION_FINISHED) {
- mViews.clear();
- }
});
}
}
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 950dc73..c7eac6c 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -218,4 +218,15 @@
public String getDataDirectorySuffix() {
return WebViewFactory.getDataDirectorySuffix();
}
+
+ /**
+ * Returns an array of startup timestamps. For the specification of array
+ * see {@link WebViewFactory.Timestamp}.
+ * This method must be called on the same thread where the
+ * WebViewChromiumFactoryProvider#create method was invoked.
+ */
+ @NonNull
+ public long[] getTimestamps() {
+ return WebViewFactory.getTimestamps();
+ }
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index b91e7d3..2e75834 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -35,6 +37,8 @@
import android.util.Log;
import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
/**
@@ -67,6 +71,33 @@
private static boolean sWebViewDisabled;
private static String sDataDirectorySuffix; // stored here so it can be set without loading WV
+ // Indices in sTimestamps array.
+ /** @hide */
+ @IntDef(value = {
+ WEBVIEW_LOAD_START, CREATE_CONTEXT_START, CREATE_CONTEXT_END,
+ ADD_ASSETS_START, ADD_ASSETS_END, GET_CLASS_LOADER_START, GET_CLASS_LOADER_END,
+ NATIVE_LOAD_START, NATIVE_LOAD_END,
+ PROVIDER_CLASS_FOR_NAME_START, PROVIDER_CLASS_FOR_NAME_END})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Timestamp {
+ }
+
+ public static final int WEBVIEW_LOAD_START = 0;
+ public static final int CREATE_CONTEXT_START = 1;
+ public static final int CREATE_CONTEXT_END = 2;
+ public static final int ADD_ASSETS_START = 3;
+ public static final int ADD_ASSETS_END = 4;
+ public static final int GET_CLASS_LOADER_START = 5;
+ public static final int GET_CLASS_LOADER_END = 6;
+ public static final int NATIVE_LOAD_START = 7;
+ public static final int NATIVE_LOAD_END = 8;
+ public static final int PROVIDER_CLASS_FOR_NAME_START = 9;
+ public static final int PROVIDER_CLASS_FOR_NAME_END = 10;
+ private static final int TIMESTAMPS_SIZE = 11;
+
+ // WebView startup timestamps. To access elements use {@link Timestamp}.
+ private static long[] sTimestamps = new long[TIMESTAMPS_SIZE];
+
// Error codes for loadWebViewNativeLibraryFromPackage
public static final int LIBLOAD_SUCCESS = 0;
public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
@@ -230,6 +261,7 @@
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
+ sTimestamps[WEBVIEW_LOAD_START] = System.currentTimeMillis();
final int uid = android.os.Process.myUid();
if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
|| uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
@@ -369,6 +401,7 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
+ sTimestamps[CREATE_CONTEXT_START] = System.currentTimeMillis();
try {
// Construct an app context to load the Java code into the current app.
Context webViewContext = initialApplication.createApplicationContext(
@@ -377,6 +410,7 @@
sPackageInfo = newPackageInfo;
return webViewContext;
} finally {
+ sTimestamps[CREATE_CONTEXT_END] = System.currentTimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (RemoteException | PackageManager.NameNotFoundException e) {
@@ -402,20 +436,26 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {
+ sTimestamps[ADD_ASSETS_START] = System.currentTimeMillis();
for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) {
initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath);
}
+ sTimestamps[ADD_ASSETS_END] = sTimestamps[GET_CLASS_LOADER_START] =
+ System.currentTimeMillis();
ClassLoader clazzLoader = webViewContext.getClassLoader();
-
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
+ sTimestamps[GET_CLASS_LOADER_END] = sTimestamps[NATIVE_LOAD_START] =
+ System.currentTimeMillis();
WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
getWebViewLibrary(sPackageInfo.applicationInfo));
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
-
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
+ sTimestamps[NATIVE_LOAD_END] = sTimestamps[PROVIDER_CLASS_FOR_NAME_START] =
+ System.currentTimeMillis();
try {
return getWebViewProviderClass(clazzLoader);
} finally {
+ sTimestamps[PROVIDER_CLASS_FOR_NAME_END] = System.currentTimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (ClassNotFoundException e) {
@@ -477,4 +517,9 @@
return IWebViewUpdateService.Stub.asInterface(
ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
}
+
+ @NonNull
+ static long[] getTimestamps() {
+ return sTimestamps;
+ }
}
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 98738ef..93b2d8a 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -30,14 +30,19 @@
import android.graphics.drawable.Icon;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.RemotableViewMethod;
import android.view.View;
+import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import java.time.Clock;
+import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
+import java.util.Formatter;
+import java.util.Locale;
/**
* This widget display an analogic clock with two hands for hours and
@@ -47,15 +52,19 @@
* @attr ref android.R.styleable#AnalogClock_hand_hour
* @attr ref android.R.styleable#AnalogClock_hand_minute
* @attr ref android.R.styleable#AnalogClock_hand_second
+ * @attr ref android.R.styleable#AnalogClock_timeZone
* @deprecated This widget is no longer supported.
*/
@RemoteView
@Deprecated
public class AnalogClock extends View {
+ private static final String LOG_TAG = "AnalogClock";
/** How often the clock should refresh to make the seconds hand advance at ~15 FPS. */
private static final long SECONDS_TICK_FREQUENCY_MS = 1000 / 15;
private Clock mClock;
+ @Nullable
+ private ZoneId mTimeZone;
@UnsupportedAppUsage
private Drawable mHourHand;
@@ -114,7 +123,8 @@
mSecondHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_second);
- mClock = Clock.systemDefaultZone();
+ mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone));
+ createClock();
mDialWidth = mDial.getIntrinsicWidth();
mDialHeight = mDial.getIntrinsicHeight();
@@ -162,6 +172,46 @@
invalidate();
}
+ /**
+ * Indicates which time zone is currently used by this view.
+ *
+ * @return The ID of the current time zone or null if the default time zone,
+ * as set by the user, must be used
+ *
+ * @see java.util.TimeZone
+ * @see java.util.TimeZone#getAvailableIDs()
+ * @see #setTimeZone(String)
+ */
+ @InspectableProperty
+ @Nullable
+ public String getTimeZone() {
+ ZoneId zoneId = mTimeZone;
+ return zoneId == null ? null : zoneId.getId();
+ }
+
+ /**
+ * Sets the specified time zone to use in this clock. When the time zone
+ * is set through this method, system time zone changes (when the user
+ * sets the time zone in settings for instance) will be ignored.
+ *
+ * @param timeZone The desired time zone's ID as specified in {@link java.util.TimeZone}
+ * or null to user the time zone specified by the user
+ * (system time zone)
+ *
+ * @see #getTimeZone()
+ * @see java.util.TimeZone#getAvailableIDs()
+ * @see java.util.TimeZone#getTimeZone(String)
+ *
+ * @attr ref android.R.styleable#AnalogClock_timeZone
+ */
+ @RemotableViewMethod
+ public void setTimeZone(@Nullable String timeZone) {
+ mTimeZone = toZoneId(timeZone);
+
+ createClock();
+ onTimeChanged();
+ }
+
@Override
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
@@ -198,8 +248,8 @@
// NOTE: It's safe to do these after registering the receiver since the receiver always runs
// in the main thread, therefore the receiver can't run before this method returns.
- // The time zone may have changed while the receiver wasn't registered, so update the Time
- mClock = Clock.systemDefaultZone();
+ // The time zone may have changed while the receiver wasn't registered, so update the clock.
+ createClock();
// Make sure we update to the current time
onTimeChanged();
@@ -340,8 +390,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
- mClock = Clock.system(ZoneId.of(tz));
+ createClock();
}
onTimeChanged();
@@ -365,9 +414,26 @@
}
};
+ private void createClock() {
+ ZoneId zoneId = mTimeZone;
+ if (zoneId == null) {
+ mClock = Clock.systemDefaultZone();
+ } else {
+ mClock = Clock.system(zoneId);
+ }
+ }
+
private void updateContentDescription(long timeMillis) {
final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR;
- String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags);
+ String contentDescription =
+ DateUtils.formatDateRange(
+ mContext,
+ new Formatter(new StringBuilder(50), Locale.getDefault()),
+ timeMillis /* startMillis */,
+ timeMillis /* endMillis */,
+ flags,
+ getTimeZone())
+ .toString();
setContentDescription(contentDescription);
}
@@ -378,4 +444,22 @@
Instant instant = Instant.ofEpochMilli(timeMillis);
return LocalDateTime.ofInstant(instant, zoneId);
}
+
+ /**
+ * Tries to parse a {@link ZoneId} from {@code timeZone}, returning null if it is null or there
+ * is an error parsing.
+ */
+ @Nullable
+ private static ZoneId toZoneId(@Nullable String timeZone) {
+ if (timeZone == null) {
+ return null;
+ }
+
+ try {
+ return ZoneId.of(timeZone);
+ } catch (DateTimeException e) {
+ Log.w(LOG_TAG, "Failed to parse time zone from " + timeZone, e);
+ return null;
+ }
+ }
}
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index 046f75f..2452e4c 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.widget.RemoteViews.RemoteView;
/**
* <p>
@@ -52,6 +53,7 @@
* {@link android.R.styleable#View View Attributes}
* </p>
*/
+@RemoteView
public class CheckBox extends CompoundButton {
public CheckBox(Context context) {
this(context, null);
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 135ff9f..63f8ee7 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -27,11 +27,13 @@
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
+import android.view.RemotableViewMethod;
import android.view.SoundEffectConstants;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
@@ -275,6 +277,7 @@
* @param resId the resource identifier of the drawable
* @attr ref android.R.styleable#CompoundButton_button
*/
+ @RemotableViewMethod(asyncImpl = "setButtonDrawableAsync")
public void setButtonDrawable(@DrawableRes int resId) {
final Drawable d;
if (resId != 0) {
@@ -285,6 +288,12 @@
setButtonDrawable(d);
}
+ /** @hide **/
+ public Runnable setButtonDrawableAsync(@DrawableRes int resId) {
+ Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+ return () -> setButtonDrawable(drawable);
+ }
+
/**
* Sets a drawable as the compound button image.
*
@@ -336,6 +345,23 @@
}
/**
+ * Sets the button of this CompoundButton to the specified Icon.
+ *
+ * @param icon an Icon holding the desired button, or {@code null} to clear
+ * the button
+ */
+ @RemotableViewMethod(asyncImpl = "setButtonIconAsync")
+ public void setButtonIcon(@Nullable Icon icon) {
+ setButtonDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+ }
+
+ /** @hide **/
+ public Runnable setButtonIconAsync(@Nullable Icon icon) {
+ Drawable button = icon == null ? null : icon.loadDrawable(getContext());
+ return () -> setButtonDrawable(button);
+ }
+
+ /**
* Applies a tint to the button drawable. Does not modify the current tint
* mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
@@ -350,6 +376,7 @@
* @see #setButtonTintList(ColorStateList)
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setButtonTintList(@Nullable ColorStateList tint) {
mButtonTintList = tint;
mHasButtonTint = true;
@@ -394,6 +421,7 @@
* @see #getButtonTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setButtonTintBlendMode(@Nullable BlendMode tintMode) {
mButtonBlendMode = tintMode;
mHasButtonBlendMode = true;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 26dd5e3..012352d 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1705,15 +1705,23 @@
}
private void updateFloatingToolbarVisibility(MotionEvent event) {
- if (mTextActionMode != null) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_MOVE:
- hideFloatingToolbar(ActionMode.DEFAULT_HIDE_DURATION);
- break;
- case MotionEvent.ACTION_UP: // fall through
- case MotionEvent.ACTION_CANCEL:
+ if (mTextActionMode == null) {
+ return;
+ }
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_MOVE:
+ hideFloatingToolbar(ActionMode.DEFAULT_HIDE_DURATION);
+ break;
+ case MotionEvent.ACTION_UP: // fall through
+ case MotionEvent.ACTION_CANCEL:
+ final SelectionModifierCursorController selectionController =
+ getSelectionController();
+ final InsertionPointCursorController insertionController = getInsertionController();
+ if ((selectionController != null && selectionController.isCursorBeingModified())
+ || (insertionController != null
+ && insertionController.isCursorBeingModified())) {
showFloatingToolbar();
- }
+ }
}
}
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index a04d7c3..9b35034 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -49,6 +50,7 @@
* {@link android.R.styleable#View View Attributes}
* </p>
*/
+@RemoteView
public class RadioButton extends CompoundButton {
public RadioButton(Context context) {
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 4722fdc..d445fdc 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -31,6 +31,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -59,6 +60,7 @@
* @see RadioButton
*
*/
+@RemoteView
public class RadioGroup extends LinearLayout {
private static final String LOG_TAG = RadioGroup.class.getSimpleName();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 8dafc5d..dfef7ca 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,6 +17,7 @@
package android.widget;
import android.annotation.ColorInt;
+import android.annotation.ColorRes;
import android.annotation.DimenRes;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
@@ -25,6 +26,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
+import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -63,6 +65,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -129,6 +132,13 @@
* <li>{@link android.widget.TextClock}</li>
* <li>{@link android.widget.TextView}</li>
* </ul>
+ * <p>As of API 31, the following widgets and layouts may also be used:</p>
+ * <ul>
+ * <li>{@link android.widget.CheckBox}</li>
+ * <li>{@link android.widget.RadioButton}</li>
+ * <li>{@link android.widget.RadioGroup}</li>
+ * <li>{@link android.widget.Switch}</li>
+ * </ul>
* <p>Descendants of these classes are not supported.</p>
*/
public class RemoteViews implements Parcelable, Filter {
@@ -180,6 +190,10 @@
private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
private static final int SET_INT_TAG_TAG = 22;
private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
+ private static final int RESOURCE_REFLECTION_ACTION_TAG = 24;
+ private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
+ private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26;
+ private static final int SET_RADIO_GROUP_CHECKED = 27;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
@@ -980,6 +994,45 @@
return rect;
}
+ private static Class<?> getParameterType(int type) {
+ switch (type) {
+ case BaseReflectionAction.BOOLEAN:
+ return boolean.class;
+ case BaseReflectionAction.BYTE:
+ return byte.class;
+ case BaseReflectionAction.SHORT:
+ return short.class;
+ case BaseReflectionAction.INT:
+ return int.class;
+ case BaseReflectionAction.LONG:
+ return long.class;
+ case BaseReflectionAction.FLOAT:
+ return float.class;
+ case BaseReflectionAction.DOUBLE:
+ return double.class;
+ case BaseReflectionAction.CHAR:
+ return char.class;
+ case BaseReflectionAction.STRING:
+ return String.class;
+ case BaseReflectionAction.CHAR_SEQUENCE:
+ return CharSequence.class;
+ case BaseReflectionAction.URI:
+ return Uri.class;
+ case BaseReflectionAction.BITMAP:
+ return Bitmap.class;
+ case BaseReflectionAction.BUNDLE:
+ return Bundle.class;
+ case BaseReflectionAction.INTENT:
+ return Intent.class;
+ case BaseReflectionAction.COLOR_STATE_LIST:
+ return ColorStateList.class;
+ case BaseReflectionAction.ICON:
+ return Icon.class;
+ default:
+ return null;
+ }
+ }
+
private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
boolean async) {
MethodArgs result;
@@ -1282,7 +1335,8 @@
@Override
public void apply(View root, ViewGroup rootParent,
OnClickHandler handler) throws ActionException {
- ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP,
+ ReflectionAction ra = new ReflectionAction(viewId, methodName,
+ BaseReflectionAction.BITMAP,
bitmap);
ra.apply(root, rootParent, handler);
}
@@ -1301,7 +1355,7 @@
/**
* Base class for the reflection actions.
*/
- private final class ReflectionAction extends Action {
+ private abstract class BaseReflectionAction extends Action {
static final int BOOLEAN = 1;
static final int BYTE = 2;
static final int SHORT = 3;
@@ -1324,17 +1378,14 @@
@UnsupportedAppUsage
String methodName;
int type;
- @UnsupportedAppUsage
- Object value;
- ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
+ BaseReflectionAction(@IdRes int viewId, String methodName, int type) {
this.viewId = viewId;
this.methodName = methodName;
this.type = type;
- this.value = value;
}
- ReflectionAction(Parcel in) {
+ BaseReflectionAction(Parcel in) {
this.viewId = in.readInt();
this.methodName = in.readString8();
this.type = in.readInt();
@@ -1343,7 +1394,125 @@
Log.d(LOG_TAG, "read viewId=0x" + Integer.toHexString(this.viewId)
+ " methodName=" + this.methodName + " type=" + this.type);
}
+ }
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(this.viewId);
+ out.writeString8(this.methodName);
+ out.writeInt(this.type);
+ }
+
+ /**
+ * Returns the value to use as parameter for the method.
+ *
+ * The view might be passed as {@code null} if the parameter value is requested outside of
+ * inflation. If the parameter cannot be determined at that time, the method should return
+ * {@code null} but not raise any exception.
+ */
+ @Nullable
+ protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
+
+ @Override
+ public final void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ final View view = root.findViewById(viewId);
+ if (view == null) return;
+
+ Class<?> param = getParameterType(this.type);
+ if (param == null) {
+ throw new ActionException("bad type: " + this.type);
+ }
+ Object value = getParameterValue(view);
+ try {
+ getMethod(view, this.methodName, param, false /* async */).invoke(view, value);
+ } catch (Throwable ex) {
+ throw new ActionException(ex);
+ }
+ }
+
+ @Override
+ public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
+ OnClickHandler handler) {
+ final View view = root.findViewById(viewId);
+ if (view == null) return ACTION_NOOP;
+
+ Class<?> param = getParameterType(this.type);
+ if (param == null) {
+ throw new ActionException("bad type: " + this.type);
+ }
+
+ Object value = getParameterValue(view);
+ try {
+ MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
+
+ if (method != null) {
+ Runnable endAction = (Runnable) method.invoke(view, value);
+ if (endAction == null) {
+ return ACTION_NOOP;
+ }
+ // Special case view stub
+ if (endAction instanceof ViewStub.ViewReplaceRunnable) {
+ root.createTree();
+ // Replace child tree
+ root.findViewTreeById(viewId).replaceView(
+ ((ViewStub.ViewReplaceRunnable) endAction).view);
+ }
+ return new RunnableAction(endAction);
+ }
+ } catch (Throwable ex) {
+ throw new ActionException(ex);
+ }
+
+ return this;
+ }
+
+ public final int mergeBehavior() {
+ // smoothScrollBy is cumulative, everything else overwites.
+ if (methodName.equals("smoothScrollBy")) {
+ return MERGE_APPEND;
+ } else {
+ return MERGE_REPLACE;
+ }
+ }
+
+ @Override
+ public final String getUniqueKey() {
+ // Each type of reflection action corresponds to a setter, so each should be seen as
+ // unique from the standpoint of merging.
+ return super.getUniqueKey() + this.methodName + this.type;
+ }
+
+ @Override
+ public final boolean prefersAsyncApply() {
+ return this.type == URI || this.type == ICON;
+ }
+
+ @Override
+ public final void visitUris(@NonNull Consumer<Uri> visitor) {
+ switch (this.type) {
+ case URI:
+ final Uri uri = (Uri) getParameterValue(null);
+ if (uri != null) visitor.accept(uri);
+ break;
+ case ICON:
+ final Icon icon = (Icon) getParameterValue(null);
+ if (icon != null) visitIconUri(icon, visitor);
+ break;
+ }
+ }
+ }
+
+ /** Class for the reflection actions. */
+ private final class ReflectionAction extends BaseReflectionAction {
+ @UnsupportedAppUsage
+ Object value;
+
+ ReflectionAction(@IdRes int viewId, String methodName, int type, Object value) {
+ super(viewId, methodName, type);
+ this.value = value;
+ }
+
+ ReflectionAction(Parcel in) {
+ super(in);
// For some values that may have been null, we first check a flag to see if they were
// written to the parcel.
switch (this.type) {
@@ -1354,7 +1523,7 @@
this.value = in.readByte();
break;
case SHORT:
- this.value = (short)in.readInt();
+ this.value = (short) in.readInt();
break;
case INT:
this.value = in.readInt();
@@ -1369,7 +1538,7 @@
this.value = in.readDouble();
break;
case CHAR:
- this.value = (char)in.readInt();
+ this.value = (char) in.readInt();
break;
case STRING:
this.value = in.readString8();
@@ -1400,15 +1569,7 @@
}
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(this.viewId);
- out.writeString8(this.methodName);
- out.writeInt(this.type);
- //noinspection ConstantIfStatement
- if (false) {
- Log.d(LOG_TAG, "write viewId=0x" + Integer.toHexString(this.viewId)
- + " methodName=" + this.methodName + " type=" + this.type);
- }
-
+ super.writeToParcel(out, flags);
// For some values which are null, we record an integer flag to indicate whether
// we have written a valid value to the parcel.
switch (this.type) {
@@ -1434,13 +1595,13 @@
out.writeDouble((Double) this.value);
break;
case CHAR:
- out.writeInt((int)((Character)this.value).charValue());
+ out.writeInt((int) ((Character) this.value).charValue());
break;
case STRING:
- out.writeString8((String)this.value);
+ out.writeString8((String) this.value);
break;
case CHAR_SEQUENCE:
- TextUtils.writeToParcel((CharSequence)this.value, out, flags);
+ TextUtils.writeToParcel((CharSequence) this.value, out, flags);
break;
case BUNDLE:
out.writeBundle((Bundle) this.value);
@@ -1457,135 +1618,134 @@
}
}
- private Class<?> getParameterType() {
- switch (this.type) {
- case BOOLEAN:
- return boolean.class;
- case BYTE:
- return byte.class;
- case SHORT:
- return short.class;
- case INT:
- return int.class;
- case LONG:
- return long.class;
- case FLOAT:
- return float.class;
- case DOUBLE:
- return double.class;
- case CHAR:
- return char.class;
- case STRING:
- return String.class;
- case CHAR_SEQUENCE:
- return CharSequence.class;
- case URI:
- return Uri.class;
- case BITMAP:
- return Bitmap.class;
- case BUNDLE:
- return Bundle.class;
- case INTENT:
- return Intent.class;
- case COLOR_STATE_LIST:
- return ColorStateList.class;
- case ICON:
- return Icon.class;
- default:
- return null;
- }
- }
-
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final View view = root.findViewById(viewId);
- if (view == null) return;
-
- Class<?> param = getParameterType();
- if (param == null) {
- throw new ActionException("bad type: " + this.type);
- }
- try {
- getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
- } catch (Throwable ex) {
- throw new ActionException(ex);
- }
- }
-
- @Override
- public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
- final View view = root.findViewById(viewId);
- if (view == null) return ACTION_NOOP;
-
- Class<?> param = getParameterType();
- if (param == null) {
- throw new ActionException("bad type: " + this.type);
- }
-
- try {
- MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
-
- if (method != null) {
- Runnable endAction = (Runnable) method.invoke(view, this.value);
- if (endAction == null) {
- return ACTION_NOOP;
- } else {
- // Special case view stub
- if (endAction instanceof ViewStub.ViewReplaceRunnable) {
- root.createTree();
- // Replace child tree
- root.findViewTreeById(viewId).replaceView(
- ((ViewStub.ViewReplaceRunnable) endAction).view);
- }
- return new RunnableAction(endAction);
- }
- }
- } catch (Throwable ex) {
- throw new ActionException(ex);
- }
-
- return this;
- }
-
- public int mergeBehavior() {
- // smoothScrollBy is cumulative, everything else overwites.
- if (methodName.equals("smoothScrollBy")) {
- return MERGE_APPEND;
- } else {
- return MERGE_REPLACE;
- }
+ protected Object getParameterValue(View view) throws ActionException {
+ return this.value;
}
@Override
public int getActionTag() {
return REFLECTION_ACTION_TAG;
}
+ }
- @Override
- public String getUniqueKey() {
- // Each type of reflection action corresponds to a setter, so each should be seen as
- // unique from the standpoint of merging.
- return super.getUniqueKey() + this.methodName + this.type;
+ private final class ResourceReflectionAction extends BaseReflectionAction {
+
+ static final int DIMEN_RESOURCE = 1;
+ static final int COLOR_RESOURCE = 2;
+ static final int STRING_RESOURCE = 3;
+
+ private final int mResourceType;
+ private final int mResId;
+
+ ResourceReflectionAction(@IdRes int viewId, String methodName, int parameterType,
+ int resourceType, int resId) {
+ super(viewId, methodName, parameterType);
+ this.mResourceType = resourceType;
+ this.mResId = resId;
+ }
+
+ ResourceReflectionAction(Parcel in) {
+ super(in);
+ this.mResourceType = in.readInt();
+ this.mResId = in.readInt();
}
@Override
- public boolean prefersAsyncApply() {
- return this.type == URI || this.type == ICON;
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(this.mResourceType);
+ dest.writeInt(this.mResId);
}
@Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- switch (this.type) {
- case URI:
- final Uri uri = (Uri) this.value;
- visitor.accept(uri);
- break;
- case ICON:
- final Icon icon = (Icon) this.value;
- visitIconUri(icon, visitor);
- break;
+ protected @NonNull Object getParameterValue(View view) throws ActionException {
+ Resources resources = view.getContext().getResources();
+ try {
+ switch (this.mResourceType) {
+ case DIMEN_RESOURCE:
+ if (this.type == BaseReflectionAction.INT) {
+ return resources.getDimensionPixelSize(this.mResId);
+ }
+ return resources.getDimension(this.mResId);
+ case COLOR_RESOURCE:
+ switch(this.type) {
+ case BaseReflectionAction.INT:
+ return view.getContext().getColor(this.mResId);
+ case BaseReflectionAction.COLOR_STATE_LIST:
+ return view.getContext().getColorStateList(this.mResId);
+ default:
+ throw new ActionException(
+ "color resources must be used as int or ColorStateList, "
+ + "not " + this.type);
+ }
+ case STRING_RESOURCE:
+ return resources.getText(this.mResId);
+ default:
+ throw new ActionException("unknown resource type: " + this.mResourceType);
+ }
+ } catch (Throwable t) {
+ throw new ActionException(t);
}
}
+
+ @Override
+ public int getActionTag() {
+ return RESOURCE_REFLECTION_ACTION_TAG;
+ }
+ }
+
+ private final class ComplexUnitDimensionReflectionAction extends BaseReflectionAction {
+
+ private final float mValue;
+ @ComplexDimensionUnit
+ private final int mUnit;
+
+ ComplexUnitDimensionReflectionAction(int viewId, String methodName, int parameterType,
+ float value, @ComplexDimensionUnit int unit) {
+ super(viewId, methodName, parameterType);
+ this.mValue = value;
+ this.mUnit = unit;
+ }
+
+ ComplexUnitDimensionReflectionAction(Parcel in) {
+ super(in);
+ this.mValue = in.readFloat();
+ this.mUnit = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeFloat(this.mValue);
+ dest.writeInt(this.mUnit);
+ }
+
+ @Override
+ protected Object getParameterValue(View view) throws ActionException {
+ DisplayMetrics dm = view.getContext().getResources().getDisplayMetrics();
+ try {
+ int data = TypedValue.createComplexDimension(this.mValue, this.mUnit);
+ switch (this.type) {
+ case ReflectionAction.INT:
+ return TypedValue.complexToDimensionPixelSize(data, dm);
+ case ReflectionAction.FLOAT:
+ return TypedValue.complexToDimension(data, dm);
+ default:
+ throw new ActionException(
+ "parameter type must be INT or FLOAT, not " + this.type);
+ }
+ } catch (ActionException ex) {
+ throw ex;
+ } catch (Throwable t) {
+ throw new ActionException(t);
+ }
+ }
+
+ @Override
+ public int getActionTag() {
+ return COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG;
+ }
}
/**
@@ -2401,6 +2561,87 @@
}
}
+ private static class SetCompoundButtonCheckedAction extends Action {
+
+ private final boolean mChecked;
+
+ SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) {
+ this.viewId = viewId;
+ mChecked = checked;
+ }
+
+ SetCompoundButtonCheckedAction(Parcel in) {
+ viewId = in.readInt();
+ mChecked = in.readBoolean();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeBoolean(mChecked);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ throws ActionException {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ if (!(target instanceof CompoundButton)) {
+ Log.w(LOG_TAG, "Cannot set checked to view "
+ + viewId + " because it is not a CompoundButton");
+ return;
+ }
+
+ ((CompoundButton) target).setChecked(mChecked);
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_COMPOUND_BUTTON_CHECKED_TAG;
+ }
+ }
+
+ private static class SetRadioGroupCheckedAction extends Action {
+
+ @IdRes private final int mCheckedId;
+
+ SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) {
+ this.viewId = viewId;
+ mCheckedId = checkedId;
+ }
+
+ SetRadioGroupCheckedAction(Parcel in) {
+ viewId = in.readInt();
+ mCheckedId = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeInt(mCheckedId);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ throws ActionException {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ if (!(target instanceof RadioGroup)) {
+ Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup");
+ return;
+ }
+
+ ((RadioGroup) target).check(mCheckedId);
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_RADIO_GROUP_CHECKED;
+ }
+ }
+
/**
* Create a new RemoteViews object that will display the views contained
* in the specified layout file.
@@ -2611,6 +2852,14 @@
return new SetIntTagAction(parcel);
case REMOVE_FROM_PARENT_ACTION_TAG:
return new RemoveFromParentAction(parcel);
+ case RESOURCE_REFLECTION_ACTION_TAG:
+ return new ResourceReflectionAction(parcel);
+ case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG:
+ return new ComplexUnitDimensionReflectionAction(parcel);
+ case SET_COMPOUND_BUTTON_CHECKED_TAG:
+ return new SetCompoundButtonCheckedAction(parcel);
+ case SET_RADIO_GROUP_CHECKED:
+ return new SetRadioGroupCheckedAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -3113,7 +3362,7 @@
*/
public void setProgressTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setProgressTintList",
- ReflectionAction.COLOR_STATE_LIST, tint));
+ BaseReflectionAction.COLOR_STATE_LIST, tint));
}
/**
@@ -3125,7 +3374,7 @@
*/
public void setProgressBackgroundTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setProgressBackgroundTintList",
- ReflectionAction.COLOR_STATE_LIST, tint));
+ BaseReflectionAction.COLOR_STATE_LIST, tint));
}
/**
@@ -3137,7 +3386,7 @@
*/
public void setProgressIndeterminateTintList(@IdRes int viewId, ColorStateList tint) {
addAction(new ReflectionAction(viewId, "setIndeterminateTintList",
- ReflectionAction.COLOR_STATE_LIST, tint));
+ BaseReflectionAction.COLOR_STATE_LIST, tint));
}
/**
@@ -3159,8 +3408,8 @@
* @param colors the text colors to set
*/
public void setTextColor(@IdRes int viewId, ColorStateList colors) {
- addAction(new ReflectionAction(viewId, "setTextColor", ReflectionAction.COLOR_STATE_LIST,
- colors));
+ addAction(new ReflectionAction(viewId, "setTextColor",
+ BaseReflectionAction.COLOR_STATE_LIST, colors));
}
/**
@@ -3353,7 +3602,7 @@
* @param value The value to pass to the method.
*/
public void setBoolean(@IdRes int viewId, String methodName, boolean value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BOOLEAN, value));
}
/**
@@ -3364,7 +3613,7 @@
* @param value The value to pass to the method.
*/
public void setByte(@IdRes int viewId, String methodName, byte value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BYTE, value));
}
/**
@@ -3375,7 +3624,7 @@
* @param value The value to pass to the method.
*/
public void setShort(@IdRes int viewId, String methodName, short value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.SHORT, value));
}
/**
@@ -3386,10 +3635,59 @@
* @param value The value to pass to the method.
*/
public void setInt(@IdRes int viewId, String methodName, int value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INT, value));
}
/**
+ * Call a method taking one int, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param dimenResource The resource to resolve and pass as argument to the method.
+ */
+ public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
+ @DimenRes int dimenResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
+ ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
+ }
+
+ /**
+ * Call a method taking one int, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the specified dimension at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param value The value of the dimension.
+ * @param unit The unit in which the value is specified.
+ */
+ public void setIntDimen(@IdRes int viewId, @NonNull String methodName,
+ float value, @ComplexDimensionUnit int unit) {
+ addAction(new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.INT,
+ value, unit));
+ }
+
+ /**
+ * Call a method taking one int, a color, on a view in the layout for this RemoteViews.
+ *
+ * The ColorStateList will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param colorResource The resource to resolve and pass as argument to the method.
+ */
+ public void setColor(@IdRes int viewId, @NonNull String methodName,
+ @ColorRes int colorResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.INT,
+ ResourceReflectionAction.COLOR_RESOURCE, colorResource));
+ }
+
+
+ /**
* Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view on which to call the method.
@@ -3399,10 +3697,25 @@
* @hide
*/
public void setColorStateList(@IdRes int viewId, String methodName, ColorStateList value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.COLOR_STATE_LIST,
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.COLOR_STATE_LIST,
value));
}
+ /**
+ * Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
+ *
+ * The ColorStateList will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param colorResource The resource to resolve and pass as argument to the method.
+ */
+ public void setColorStateList(@IdRes int viewId, @NonNull String methodName,
+ @ColorRes int colorResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName,
+ BaseReflectionAction.COLOR_STATE_LIST, ResourceReflectionAction.COLOR_RESOURCE,
+ colorResource));
+ }
/**
* Call a method taking one long on a view in the layout for this RemoteViews.
@@ -3412,7 +3725,7 @@
* @param value The value to pass to the method.
*/
public void setLong(@IdRes int viewId, String methodName, long value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.LONG, value));
}
/**
@@ -3423,7 +3736,41 @@
* @param value The value to pass to the method.
*/
public void setFloat(@IdRes int viewId, String methodName, float value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT, value));
+ }
+
+ /**
+ * Call a method taking one float, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param dimenResource The resource to resolve and pass as argument to the method.
+ */
+ public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
+ @DimenRes int dimenResource) {
+ addAction(new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.FLOAT,
+ ResourceReflectionAction.DIMEN_RESOURCE, dimenResource));
+ }
+
+ /**
+ * Call a method taking one float, a size in pixels, on a view in the layout for this
+ * RemoteViews.
+ *
+ * The dimension will be resolved from the specified dimension at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param value The value of the dimension.
+ * @param unit The unit in which the value is specified.
+ */
+ public void setFloatDimen(@IdRes int viewId, @NonNull String methodName,
+ float value, @ComplexDimensionUnit int unit) {
+ addAction(
+ new ComplexUnitDimensionReflectionAction(viewId, methodName, ReflectionAction.FLOAT,
+ value, unit));
}
/**
@@ -3434,7 +3781,7 @@
* @param value The value to pass to the method.
*/
public void setDouble(@IdRes int viewId, String methodName, double value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.DOUBLE, value));
}
/**
@@ -3445,7 +3792,7 @@
* @param value The value to pass to the method.
*/
public void setChar(@IdRes int viewId, String methodName, char value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR, value));
}
/**
@@ -3456,7 +3803,7 @@
* @param value The value to pass to the method.
*/
public void setString(@IdRes int viewId, String methodName, String value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.STRING, value));
}
/**
@@ -3467,7 +3814,24 @@
* @param value The value to pass to the method.
*/
public void setCharSequence(@IdRes int viewId, String methodName, CharSequence value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
+ value));
+ }
+
+ /**
+ * Call a method taking one CharSequence on a view in the layout for this RemoteViews.
+ *
+ * The CharSequence will be resolved from the resources at the time of inflation.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param stringResource The resource to resolve and pass as argument to the method.
+ */
+ public void setCharSequence(@IdRes int viewId, @NonNull String methodName,
+ @StringRes int stringResource) {
+ addAction(
+ new ResourceReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
+ ResourceReflectionAction.STRING_RESOURCE, stringResource));
}
/**
@@ -3485,7 +3849,7 @@
value.checkFileUriExposed("RemoteViews.setUri()");
}
}
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.URI, value));
}
/**
@@ -3510,7 +3874,7 @@
* @param value The value to pass to the method.
*/
public void setBundle(@IdRes int viewId, String methodName, Bundle value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BUNDLE, value));
}
/**
@@ -3521,7 +3885,7 @@
* @param value The {@link android.content.Intent} to pass the method.
*/
public void setIntent(@IdRes int viewId, String methodName, Intent value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INTENT, value));
}
/**
@@ -3532,7 +3896,7 @@
* @param value The {@link android.graphics.drawable.Icon} to pass the method.
*/
public void setIcon(@IdRes int viewId, String methodName, Icon value) {
- addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value));
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.ICON, value));
}
/**
@@ -3576,6 +3940,26 @@
}
/**
+ * Equivalent to calling {@link android.widget.CompoundButton#setChecked(boolean)}.
+ *
+ * @param viewId The id of the view whose property to set.
+ * @param checked true to check the button, false to uncheck it.
+ */
+ public void setCompoundButtonChecked(@IdRes int viewId, boolean checked) {
+ addAction(new SetCompoundButtonCheckedAction(viewId, checked));
+ }
+
+ /**
+ * Equivalent to calling {@link android.widget.RadioGroup#check(int)}.
+ *
+ * @param viewId The id of the view whose property to set.
+ * @param checkedId The unique id of the radio button to select in the group.
+ */
+ public void setRadioGroupChecked(@IdRes int viewId, @IdRes int checkedId) {
+ addAction(new SetRadioGroupCheckedAction(viewId, checkedId));
+ }
+
+ /**
* Provides an alternate layout ID, which can be used to inflate this view. This layout will be
* used by the host when the widgets displayed on a light-background where foreground elements
* and text can safely draw using a dark color without any additional background protection.
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 3295fd2..d3600ef 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -35,6 +35,7 @@
import android.graphics.Region.Op;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.text.Layout;
@@ -48,12 +49,14 @@
import android.util.MathUtils;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.RemotableViewMethod;
import android.view.SoundEffectConstants;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.ViewStructure;
import android.view.accessibility.AccessibilityEvent;
import android.view.inspector.InspectableProperty;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -84,6 +87,7 @@
* @attr ref android.R.styleable#Switch_thumbTextPadding
* @attr ref android.R.styleable#Switch_track
*/
+@RemoteView
public class Switch extends CompoundButton {
private static final int THUMB_ANIMATION_DURATION = 250;
@@ -441,6 +445,7 @@
*
* @attr ref android.R.styleable#Switch_switchPadding
*/
+ @RemotableViewMethod
public void setSwitchPadding(int pixels) {
mSwitchPadding = pixels;
requestLayout();
@@ -466,6 +471,7 @@
*
* @attr ref android.R.styleable#Switch_switchMinWidth
*/
+ @RemotableViewMethod
public void setSwitchMinWidth(int pixels) {
mSwitchMinWidth = pixels;
requestLayout();
@@ -491,6 +497,7 @@
*
* @attr ref android.R.styleable#Switch_thumbTextPadding
*/
+ @RemotableViewMethod
public void setThumbTextPadding(int pixels) {
mThumbTextPadding = pixels;
requestLayout();
@@ -533,10 +540,17 @@
*
* @attr ref android.R.styleable#Switch_track
*/
+ @RemotableViewMethod(asyncImpl = "setTrackResourceAsync")
public void setTrackResource(@DrawableRes int resId) {
setTrackDrawable(getContext().getDrawable(resId));
}
+ /** @hide **/
+ public Runnable setTrackResourceAsync(@DrawableRes int resId) {
+ Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+ return () -> setTrackDrawable(drawable);
+ }
+
/**
* Get the drawable used for the track that the switch slides within.
*
@@ -550,6 +564,23 @@
}
/**
+ * Set the drawable used for the track that the switch slides within to the specified Icon.
+ *
+ * @param icon an Icon holding the desired track, or {@code null} to clear
+ * the track
+ */
+ @RemotableViewMethod(asyncImpl = "setTrackIconAsync")
+ public void setTrackIcon(@Nullable Icon icon) {
+ setTrackDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+ }
+
+ /** @hide **/
+ public Runnable setTrackIconAsync(@Nullable Icon icon) {
+ Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+ return () -> setTrackDrawable(track);
+ }
+
+ /**
* Applies a tint to the track drawable. Does not modify the current
* tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
@@ -563,6 +594,7 @@
* @see #getTrackTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setTrackTintList(@Nullable ColorStateList tint) {
mTrackTintList = tint;
mHasTrackTint = true;
@@ -607,6 +639,7 @@
* @see #getTrackTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setTrackTintBlendMode(@Nullable BlendMode blendMode) {
mTrackBlendMode = blendMode;
mHasTrackTintMode = true;
@@ -686,10 +719,17 @@
*
* @attr ref android.R.styleable#Switch_thumb
*/
+ @RemotableViewMethod(asyncImpl = "setThumbResourceAsync")
public void setThumbResource(@DrawableRes int resId) {
setThumbDrawable(getContext().getDrawable(resId));
}
+ /** @hide **/
+ public Runnable setThumbResourceAsync(@DrawableRes int resId) {
+ Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+ return () -> setThumbDrawable(drawable);
+ }
+
/**
* Get the drawable used for the switch "thumb" - the piece that the user
* can physically touch and drag along the track.
@@ -704,6 +744,24 @@
}
/**
+ * Set the drawable used for the switch "thumb" - the piece that the user
+ * can physically touch and drag along the track - to the specified Icon.
+ *
+ * @param icon an Icon holding the desired thumb, or {@code null} to clear
+ * the thumb
+ */
+ @RemotableViewMethod(asyncImpl = "setThumbIconAsync")
+ public void setThumbIcon(@Nullable Icon icon) {
+ setThumbDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+ }
+
+ /** @hide **/
+ public Runnable setThumbIconAsync(@Nullable Icon icon) {
+ Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+ return () -> setThumbDrawable(track);
+ }
+
+ /**
* Applies a tint to the thumb drawable. Does not modify the current
* tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
@@ -717,6 +775,7 @@
* @see #getThumbTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setThumbTintList(@Nullable ColorStateList tint) {
mThumbTintList = tint;
mHasThumbTint = true;
@@ -761,6 +820,7 @@
* @see #getThumbTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setThumbTintBlendMode(@Nullable BlendMode blendMode) {
mThumbBlendMode = blendMode;
mHasThumbTintMode = true;
@@ -822,6 +882,7 @@
*
* @attr ref android.R.styleable#Switch_splitTrack
*/
+ @RemotableViewMethod
public void setSplitTrack(boolean splitTrack) {
mSplitTrack = splitTrack;
invalidate();
@@ -852,6 +913,7 @@
*
* @attr ref android.R.styleable#Switch_textOn
*/
+ @RemotableViewMethod
public void setTextOn(CharSequence textOn) {
mTextOn = textOn;
requestLayout();
@@ -875,6 +937,7 @@
*
* @attr ref android.R.styleable#Switch_textOff
*/
+ @RemotableViewMethod
public void setTextOff(CharSequence textOff) {
mTextOff = textOff;
requestLayout();
@@ -889,6 +952,7 @@
* @param showText {@code true} to display on/off text
* @attr ref android.R.styleable#Switch_showText
*/
+ @RemotableViewMethod
public void setShowText(boolean showText) {
if (mShowText != showText) {
mShowText = showText;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8cfbca8..fe37c53 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -497,9 +497,9 @@
private TextUtils.TruncateAt mEllipsize;
// A flag to indicate the cursor was hidden by IME.
- private boolean mImeTemporarilyConsumesInput;
+ private boolean mImeIsConsumingInput;
- // Whether cursor is visible without regard to {@link mImeTemporarilyConsumesInput}.
+ // Whether cursor is visible without regard to {@link mImeConsumesInput}.
// {code true} is the default value.
private boolean mCursorVisibleFromAttr = true;
@@ -8750,7 +8750,7 @@
}
}
if (getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT) {
- outAttrs.internalImeOptions |= EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT;
+ outAttrs.internalImeOptions |= EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT;
}
if (isMultilineInputType(outAttrs.inputType)) {
// Multi-line text editors should always show an enter key.
@@ -10506,8 +10506,8 @@
/**
* Set whether the cursor is visible. The default is true. Note that this property only
- * makes sense for editable TextView. If IME is temporarily consuming the input, the cursor will
- * be always invisible, visibility will be updated as the last state when IME does not consume
+ * makes sense for editable TextView. If IME is consuming the input, the cursor will always be
+ * invisible, visibility will be updated as the last state when IME does not consume
* the input anymore.
*
* @see #isCursorVisible()
@@ -10521,20 +10521,20 @@
}
/**
- * Sets the IME is temporarily consuming the input and make the cursor invisible if
- * {@code imeTemporarilyConsumesInput} is {@code true}. Otherwise, make the cursor visible.
+ * Sets the IME is consuming the input and make the cursor invisible if {@code imeConsumesInput}
+ * is {@code true}. Otherwise, make the cursor visible.
*
- * @param imeTemporarilyConsumesInput {@code true} if IME is temporarily consuming the input
+ * @param imeConsumesInput {@code true} if IME is consuming the input
*
* @hide
*/
- public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
- mImeTemporarilyConsumesInput = imeTemporarilyConsumesInput;
+ public void setImeConsumesInput(boolean imeConsumesInput) {
+ mImeIsConsumingInput = imeConsumesInput;
updateCursorVisibleInternal();
}
private void updateCursorVisibleInternal() {
- boolean visible = mCursorVisibleFromAttr && !mImeTemporarilyConsumesInput;
+ boolean visible = mCursorVisibleFromAttr && !mImeIsConsumingInput;
if (visible && mEditor == null) return; // visible is the default value with no edit data
createEditorIfNeeded();
if (mEditor.mCursorVisible != visible) {
@@ -10550,7 +10550,7 @@
/**
* @return whether or not the cursor is visible (assuming this TextView is editable). This
- * method may return {@code false} when the IME is temporarily consuming the input even if the
+ * method may return {@code false} when the IME is consuming the input even if the
* {@code mEditor.mCursorVisible} attribute is {@code true} or {@code #setCursorVisible(true)}
* is called.
*
diff --git a/core/java/com/android/internal/annotations/CompositeRWLock.java b/core/java/com/android/internal/annotations/CompositeRWLock.java
new file mode 100644
index 0000000..b6ddfc4
--- /dev/null
+++ b/core/java/com/android/internal/annotations/CompositeRWLock.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 com.android.internal.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies a list of locks which are required for read/write operations on a data field.
+ *
+ * <p>
+ * To annotate methods accessing the data field with the annotation {@link CompositeRWLock},
+ * use {@link GuardedBy#value} to annotate method w/ write and/or read access to the data field,
+ * use {@link GuardedBy#anyOf} to annotate method w/ read only access to the data field.
+ * </p>
+ *
+ * <p>
+ * When its {@link #value()} consists of multiple locks:
+ * <ul>
+ * <li>To write to the protected data, acquire <b>all</b> of the locks
+ * in the order of the appearance in the {@link #value}.</li>
+ * <li>To read from the protected data, acquire any of the locks in the {@link #value}.</li>
+ * </ul>
+ * </p>
+ */
+@Target({FIELD})
+@Retention(RetentionPolicy.CLASS)
+public @interface CompositeRWLock {
+ String[] value() default {};
+}
diff --git a/core/java/com/android/internal/annotations/GuardedBy.java b/core/java/com/android/internal/annotations/GuardedBy.java
index 0e63214..c05c4ab 100644
--- a/core/java/com/android/internal/annotations/GuardedBy.java
+++ b/core/java/com/android/internal/annotations/GuardedBy.java
@@ -16,7 +16,9 @@
package com.android.internal.annotations;
-import java.lang.annotation.ElementType;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -25,8 +27,32 @@
* Annotation type used to mark a method or field that can only be accessed when
* holding the referenced locks.
*/
-@Target({ ElementType.FIELD, ElementType.METHOD })
+@Target({FIELD, METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface GuardedBy {
- String[] value();
+ /**
+ * Specifies a list of locks to be held in order to access the field/method
+ * annotated with this; when used in conjunction with the {@link CompositeRWLock}, locks
+ * should be acquired in the order of the appearance in the {@link #value} here.
+ *
+ * <p>
+ * If specified, {@link #anyOf()} must be null.
+ * </p>
+ *
+ * @see CompositeRWLock
+ */
+ String[] value() default {};
+
+ /**
+ * Specifies a list of locks where at least one of them must be held in order to access
+ * the field/method annotated with this; it should be <em>only</em> used in the conjunction
+ * with the {@link CompositeRWLock}.
+ *
+ * <p>
+ * If specified, {@link #allOf()} must be null.
+ * </p>
+ *
+ * @see CompositeRWLock
+ */
+ String[] anyOf() default {};
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 55f8c40..c1952c7 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -52,7 +52,7 @@
// Remaining methods are only used in Java.
- BatteryUsageStats getBatteryUsageStats(in BatteryUsageStatsQuery query);
+ List<BatteryUsageStats> getBatteryUsageStats(in List<BatteryUsageStatsQuery> queries);
@UnsupportedAppUsage
byte[] getStatistics();
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index a5eb5f6..7aca36a 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -179,9 +179,10 @@
*
* @param changeId the ID of the change that was overridden
* @param packageName the app package name that was overridden
+ * @return {@code true} if an override existed
* @throws SecurityException if overriding changes is not permitted
*/
- void clearOverrideForTest(long changeId, String packageName);
+ boolean clearOverrideForTest(long changeId, String packageName);
/**
* Enables all compatibility changes that have enabledSinceTargetSdk ==
diff --git a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
index cafe0de..dfcc914 100644
--- a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
+++ b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
@@ -17,8 +17,8 @@
package com.android.internal.graphics.fonts;
import android.os.ParcelFileDescriptor;
+import android.graphics.fonts.FontUpdateRequest;
import android.text.FontConfig;
-import android.graphics.fonts.SystemFontState;
/**
* System private interface for talking with
@@ -28,5 +28,5 @@
interface IFontManager {
FontConfig getFontConfig();
- int updateFont(in ParcelFileDescriptor fd, in byte[] signature, int baseVersion);
+ int updateFont(int baseVersion, in FontUpdateRequest request);
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index e0f9554..3d896c8 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -59,6 +59,7 @@
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
+import com.android.internal.util.PerfettoTrigger;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/com/android/internal/jank/PerfettoTrigger.java b/core/java/com/android/internal/jank/PerfettoTrigger.java
deleted file mode 100644
index 643d24a..0000000
--- a/core/java/com/android/internal/jank/PerfettoTrigger.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//TODO (165884885): Make PerfettoTrigger more generic and move it to another package.
-package com.android.internal.jank;
-
-import android.annotation.NonNull;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-/**
- * A trigger implementation with perfetto backend.
- * @hide
- */
-public class PerfettoTrigger {
- private static final String TAG = PerfettoTrigger.class.getSimpleName();
- private static final boolean DEBUG = false;
- private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
-
- /**
- * @param triggerName The name of the trigger. Must match the value defined in the AOT
- * Perfetto config.
- */
- public static void trigger(String triggerName) {
- try {
- ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
- if (DEBUG) {
- StringBuilder sb = new StringBuilder();
- for (String arg : pb.command()) {
- sb.append(arg).append(" ");
- }
- Log.d(TAG, "Triggering " + sb.toString());
- }
- Process process = pb.start();
- if (DEBUG) {
- readConsoleOutput(process);
- }
- } catch (IOException | InterruptedException e) {
- Log.w(TAG, "Failed to trigger " + triggerName, e);
- }
- }
-
- private static void readConsoleOutput(@NonNull Process process)
- throws IOException, InterruptedException {
- process.waitFor();
- try (BufferedReader errReader =
- new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
- StringBuilder errLine = new StringBuilder();
- String line;
- while ((line = errReader.readLine()) != null) {
- errLine.append(line).append("\n");
- }
- errLine.append(", code=").append(process.exitValue());
- Log.d(TAG, "err message=" + errLine.toString());
- }
- }
-}
diff --git a/core/java/com/android/internal/listeners/ListenerTransport.java b/core/java/com/android/internal/listeners/ListenerTransport.java
index 9d6210e..1a6870c 100644
--- a/core/java/com/android/internal/listeners/ListenerTransport.java
+++ b/core/java/com/android/internal/listeners/ListenerTransport.java
@@ -16,54 +16,43 @@
package com.android.internal.listeners;
-
-import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.internal.util.Preconditions;
-
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
- * A listener registration object which holds data associated with a listener, such the executor
- * the listener should run on.
+ * A listener transport object which can run listener operations on an executor.
*
* @param <TListener> listener type
*/
-public class ListenerTransport<TListener> {
-
- private final Executor mExecutor;
-
- private volatile @Nullable TListener mListener;
-
- protected ListenerTransport(@NonNull Executor executor, @NonNull TListener listener) {
- Preconditions.checkArgument(executor != null, "invalid null executor");
- Preconditions.checkArgument(listener != null, "invalid null listener/callback");
- mExecutor = executor;
- mListener = listener;
- }
+public interface ListenerTransport<TListener> {
/**
- * Prevents any listener invocations that happen-after this call.
+ * Should return a valid listener until {@link #unregister()} is invoked, and must return
+ * null after that. Recommended (but not required) that this is implemented via a volatile
+ * variable.
*/
- public final void unregister() {
- mListener = null;
- }
+ @Nullable TListener getListener();
+
+ /**
+ * Must be implemented so that {@link #getListener()} returns null after this is invoked.
+ */
+ void unregister();
/**
* Executes the given operation for the listener.
*/
- public final void execute(@NonNull Consumer<TListener> operation) {
+ default void execute(Executor executor, Consumer<TListener> operation) {
Objects.requireNonNull(operation);
- if (mListener == null) {
+ if (getListener() == null) {
return;
}
- mExecutor.execute(() -> {
- TListener listener = mListener;
+ executor.execute(() -> {
+ TListener listener = getListener();
if (listener == null) {
return;
}
@@ -71,15 +60,4 @@
operation.accept(listener);
});
}
-
- @Override
- public final boolean equals(Object obj) {
- // intentionally bound to reference equality so removal works as expected
- return this == obj;
- }
-
- @Override
- public final int hashCode() {
- return super.hashCode();
- }
}
diff --git a/core/java/com/android/internal/listeners/ListenerTransportManager.java b/core/java/com/android/internal/listeners/ListenerTransportManager.java
new file mode 100644
index 0000000..0d5d1b7b
--- /dev/null
+++ b/core/java/com/android/internal/listeners/ListenerTransportManager.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.listeners;
+
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * A listener transport manager which handles mappings between the client facing listener and system
+ * server facing transport. Supports transports which may be removed either from the client side or
+ * from the system server side without leaking memory.
+ *
+ * @param <TTransport>> transport type
+ */
+public abstract class ListenerTransportManager<TTransport extends ListenerTransport<?>> {
+
+ @GuardedBy("mRegistrations")
+ private final Map<Object, WeakReference<TTransport>> mRegistrations;
+
+ protected ListenerTransportManager() {
+ // using weakhashmap means that the transport may be GCed if the server drops its reference,
+ // and thus the listener may be GCed as well if the client drops that reference. if the
+ // server will never drop a reference without warning (ie, transport removal may only be
+ // initiated from the client side), then arraymap or similar may be used without fear of
+ // memory leaks.
+ mRegistrations = new WeakHashMap<>();
+ }
+
+ /**
+ * Adds a new transport with the given listener key.
+ */
+ public final void addListener(Object key, TTransport transport) {
+ try {
+ synchronized (mRegistrations) {
+ // ordering of operations is important so that if an error occurs at any point we
+ // are left in a reasonable state
+ registerTransport(transport);
+ WeakReference<TTransport> oldTransportRef = mRegistrations.put(key,
+ new WeakReference<>(transport));
+ if (oldTransportRef != null) {
+ TTransport oldTransport = oldTransportRef.get();
+ if (oldTransport != null) {
+ oldTransport.unregister();
+ unregisterTransport(oldTransport);
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes the transport with the given listener key.
+ */
+ public final void removeListener(Object key) {
+ try {
+ synchronized (mRegistrations) {
+ // ordering of operations is important so that if an error occurs at any point we
+ // are left in a reasonable state
+ WeakReference<TTransport> transportRef = mRegistrations.remove(key);
+ if (transportRef != null) {
+ TTransport transport = transportRef.get();
+ if (transport != null) {
+ transport.unregister();
+ unregisterTransport(transport);
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ protected abstract void registerTransport(TTransport transport) throws RemoteException;
+
+ protected abstract void unregisterTransport(TTransport transport) throws RemoteException;
+}
diff --git a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java b/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
deleted file mode 100644
index fc1d69f..0000000
--- a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.listeners;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Build;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.IndentingPrintWriter;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.io.FileDescriptor;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * A listener multiplexer designed for use by client-side code. This class ensures that listeners
- * are never invoked while a lock is held. This class is only useful for multiplexing listeners -
- * if all client listeners can be combined into a single server request, and all server results will
- * be delivered to all clients.
- *
- * By default, the multiplexer will replace requests on the server simply by registering the new
- * request and trusting the server to know this is replacing the old request. If the server needs to
- * have the old request unregistered first, subclasses should override
- * {@link #reregisterWithServer(Object, Object)}.
- *
- * @param <TRequest> listener request type, may be Void
- * @param <TListener> listener type
- */
-public abstract class ListenerTransportMultiplexer<TRequest, TListener> {
-
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> mRegistrations =
- new ArrayMap<>();
-
- @GuardedBy("mLock")
- private boolean mServiceRegistered = false;
-
- @GuardedBy("mLock")
- private TRequest mCurrentRequest;
-
- /**
- * Should be implemented to register the given merged request with the server.
- *
- * @see #reregisterWithServer(Object, Object)
- */
- protected abstract void registerWithServer(TRequest mergedRequest) throws RemoteException;
-
- /**
- * Invoked when the server already has a request registered, and it is being replaced with a new
- * request. The default implementation simply registers the new request, trusting the server to
- * overwrite the old request.
- */
- protected void reregisterWithServer(TRequest oldMergedRequest, TRequest mergedRequest)
- throws RemoteException {
- registerWithServer(mergedRequest);
- }
-
- /**
- * Should be implemented to unregister from the server.
- */
- protected abstract void unregisterWithServer() throws RemoteException;
-
- /**
- * Called in order to generate a merged request from the given requests. The list of requests
- * will never be empty.
- */
- protected @Nullable TRequest mergeRequests(Collection<TRequest> requests) {
- if (Build.IS_DEBUGGABLE) {
- for (TRequest request : requests) {
- // if using non-null requests then implementations must override this method
- Preconditions.checkState(request == null);
- }
- }
-
- return null;
- }
-
- /**
- * Adds a new listener with no request, using the listener as the key.
- */
- public void addListener(@NonNull TListener listener, @NonNull Executor executor) {
- addListener(listener, null, listener, executor);
- }
-
- /**
- * Adds a new listener with the given request, using the listener as the key.
- */
- public void addListener(@Nullable TRequest request, @NonNull TListener listener,
- @NonNull Executor executor) {
- addListener(listener, request, listener, executor);
- }
-
- /**
- * Adds a new listener with the given request using a custom key.
- */
- public void addListener(@NonNull Object key, @Nullable TRequest request,
- @NonNull TListener listener, @NonNull Executor executor) {
- Objects.requireNonNull(key);
- RequestListenerTransport<TRequest, TListener> registration =
- new RequestListenerTransport<>(request, executor, listener);
-
- synchronized (mLock) {
- ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> newRegistrations =
- new ArrayMap<>(mRegistrations.size() + 1);
- newRegistrations.putAll(mRegistrations);
- RequestListenerTransport<TRequest, TListener> old = newRegistrations.put(key,
- registration);
- mRegistrations = newRegistrations;
-
- if (old != null) {
- old.unregister();
- }
-
- updateService();
- }
- }
-
- /**
- * Removes the listener with the given key.
- */
- public void removeListener(@NonNull Object key) {
- Objects.requireNonNull(key);
-
- synchronized (mLock) {
- if (!mRegistrations.containsKey(key)) {
- return;
- }
-
- ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> newRegistrations =
- new ArrayMap<>(mRegistrations);
- RequestListenerTransport<TRequest, TListener> old = newRegistrations.remove(key);
- mRegistrations = newRegistrations;
-
- if (old != null) {
- old.unregister();
- updateService();
- }
- }
- }
-
- private void updateService() {
- synchronized (mLock) {
- if (mRegistrations.isEmpty()) {
- mCurrentRequest = null;
- if (mServiceRegistered) {
- try {
- mServiceRegistered = false;
- unregisterWithServer();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return;
- }
-
- ArrayList<TRequest> requests = new ArrayList<>(mRegistrations.size());
- for (int i = 0; i < mRegistrations.size(); i++) {
- requests.add(mRegistrations.valueAt(i).getRequest());
- }
-
- TRequest merged = mergeRequests(requests);
- if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
- TRequest old = mCurrentRequest;
- mCurrentRequest = null;
- try {
- if (mServiceRegistered) {
- // if a remote exception is thrown the service should not be registered
- mServiceRegistered = false;
- reregisterWithServer(old, merged);
- } else {
- registerWithServer(merged);
- }
- mCurrentRequest = merged;
- mServiceRegistered = true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
- }
-
- protected final void deliverToListeners(Consumer<TListener> operation) {
- ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> registrations;
- synchronized (mLock) {
- registrations = mRegistrations;
- }
-
- try {
- for (int i = 0; i < registrations.size(); i++) {
- registrations.valueAt(i).execute(operation);
- }
- } finally {
- onOperationFinished(operation);
- }
- }
-
- /**
- * Invoked when an operation is finished. This method will always be called once for every call
- * to {@link #deliverToListeners(Consumer)}, regardless of whether the operation encountered any
- * error or failed to execute in any way for any listeners.
- */
- protected void onOperationFinished(@NonNull Consumer<TListener> operation) {}
-
- /**
- * Dumps debug information.
- */
- public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> registrations;
- synchronized (mLock) {
- registrations = mRegistrations;
-
- ipw.print("service: ");
- if (mServiceRegistered) {
- if (mCurrentRequest == null) {
- ipw.print("request registered");
- } else {
- ipw.print("request registered - " + mCurrentRequest);
- }
- } else {
- ipw.print("unregistered");
- }
- ipw.println();
- }
-
- if (!registrations.isEmpty()) {
- ipw.println("listeners:");
-
- ipw.increaseIndent();
- for (int i = 0; i < registrations.size(); i++) {
- ipw.print(registrations.valueAt(i));
- }
- ipw.decreaseIndent();
- }
- }
-}
diff --git a/core/java/com/android/internal/listeners/RequestListenerTransport.java b/core/java/com/android/internal/listeners/RequestListenerTransport.java
deleted file mode 100644
index 178de06..0000000
--- a/core/java/com/android/internal/listeners/RequestListenerTransport.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.listeners;
-
-import android.annotation.Nullable;
-
-import java.util.concurrent.Executor;
-
-/**
- * A listener transport with an associated request.
- *
- * @param <TRequest> request type
- * @param <TListener> listener type
- */
-public class RequestListenerTransport<TRequest, TListener> extends ListenerTransport<TListener> {
-
- private final @Nullable TRequest mRequest;
-
- protected RequestListenerTransport(@Nullable TRequest request, Executor executor,
- TListener listener) {
- super(executor, listener);
- mRequest = request;
- }
-
- /**
- * Returns the request associated with this transport.
- */
- public final @Nullable TRequest getRequest() {
- return mRequest;
- }
-}
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 6609ebe..8fe17fb 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -54,6 +54,7 @@
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah)
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs);
}
+ // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats.
}
/**
@@ -66,7 +67,8 @@
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType);
- final double powerMah = mPowerEstimator.calculatePower(durationMs);
+ final double powerMah = getMeasuredOrEstimatedPower(
+ batteryStats.getScreenDozeEnergy(), durationMs);
if (powerMah > 0) {
BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0);
bs.usagePowerMah = powerMah;
@@ -79,4 +81,12 @@
private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
return batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000;
}
+
+ private double getMeasuredOrEstimatedPower(long measuredEnergyUJ, long durationMs) {
+ if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
+ return mAhToUJ(measuredEnergyUJ);
+ } else {
+ return mPowerEstimator.calculatePower(durationMs);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index c8805dd..af61f91 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -35,6 +35,8 @@
/**
* Smeared power from screen usage.
* We split the screen usage power and smear them among apps, based on activity time.
+ * The actual screen usage power may be measured or estimated, affecting the granularity and
+ * accuracy of the smearing, but the smearing algorithm is essentially the same.
*/
public double screenPowerMah;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index fcf8bb4..aa5015a 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -37,7 +37,6 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2b034b0..1f7a7aa 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -21,8 +21,6 @@
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
-import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -107,7 +105,7 @@
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.power.MeasuredEnergyStats;
-import com.android.internal.power.MeasuredEnergyStats.EnergyBucket;
+import com.android.internal.power.MeasuredEnergyStats.StandardEnergyBucket;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
@@ -173,7 +171,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 191 + (USE_OLD_HISTORY ? 1000 : 0);
+ static final int VERSION = 193 + (USE_OLD_HISTORY ? 1000 : 0);
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -362,7 +360,6 @@
public interface PlatformIdleStateCallback {
public void fillLowPowerStats(RpmStats rpmStats);
- public String getPlatformLowPowerStats();
public String getSubsystemLowPowerStats();
}
@@ -3482,11 +3479,6 @@
}
if (computeStepDetails) {
if (mPlatformIdleStateCallback != null) {
- mCurHistoryStepDetails.statPlatformIdleState =
- mPlatformIdleStateCallback.getPlatformLowPowerStats();
- if (DEBUG) Slog.i(TAG, "WRITE PlatformIdleState:" +
- mCurHistoryStepDetails.statPlatformIdleState);
-
mCurHistoryStepDetails.statSubsystemPowerState =
mPlatformIdleStateCallback.getSubsystemLowPowerStats();
if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" +
@@ -7167,8 +7159,8 @@
if (mGlobalMeasuredEnergyStats == null) {
return ENERGY_DATA_UNAVAILABLE;
}
- return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy(
- MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+ return mGlobalMeasuredEnergyStats
+ .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
}
@Override
@@ -7176,8 +7168,8 @@
if (mGlobalMeasuredEnergyStats == null) {
return ENERGY_DATA_UNAVAILABLE;
}
- return mGlobalMeasuredEnergyStats.getAccumulatedBucketEnergy(
- MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
+ return mGlobalMeasuredEnergyStats
+ .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
}
@Override public long getStartClockTime() {
@@ -7941,27 +7933,27 @@
return mUidMeasuredEnergyStats;
}
- /** Adds the given energy to the given energy bucket for this uid. */
- private void addEnergyToEnergyBucketLocked(long energyDeltaUJ,
- @MeasuredEnergyStats.EnergyBucket int energyBucket, boolean accumulate) {
+ /** Adds the given energy to the given standard energy bucket for this uid. */
+ private void addEnergyToStandardBucketLocked(long energyDeltaUJ,
+ @StandardEnergyBucket int energyBucket, boolean accumulate) {
getOrCreateMeasuredEnergyStatsLocked()
- .updateBucket(energyBucket, energyDeltaUJ, accumulate);
+ .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate);
}
/**
- * Returns the energy used by this uid for an energy bucket of interest.
- * @param bucket energy bucket of interest
+ * Returns the energy used by this uid for a standard energy bucket of interest.
+ * @param bucket standard energy bucket of interest
* @return energy (in microjoules) used by this uid for this energy bucket
*/
- public long getMeasuredEnergyMicroJoules(@MeasuredEnergyStats.EnergyBucket int bucket) {
+ public long getMeasuredEnergyMicroJoules(@StandardEnergyBucket int bucket) {
if (mBsi.mGlobalMeasuredEnergyStats == null
- || !mBsi.mGlobalMeasuredEnergyStats.isEnergyBucketSupported(bucket)) {
+ || !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
return ENERGY_DATA_UNAVAILABLE;
}
if (mUidMeasuredEnergyStats == null) {
return 0L; // It is supported, but was never filled, so it must be 0
}
- return mUidMeasuredEnergyStats.getAccumulatedBucketEnergy(bucket);
+ return mUidMeasuredEnergyStats.getAccumulatedStandardBucketEnergy(bucket);
}
/**
@@ -8633,11 +8625,7 @@
@Override
public long getScreenOnEnergy() {
- if (mUidMeasuredEnergyStats == null) {
- return ENERGY_DATA_UNAVAILABLE;
- }
- return mUidMeasuredEnergyStats.getAccumulatedBucketEnergy(
- MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+ return getMeasuredEnergyMicroJoules(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
}
void initNetworkActivityLocked() {
@@ -12406,7 +12394,7 @@
return;
}
- final @EnergyBucket int energyBucket =
+ final @StandardEnergyBucket int energyBucket =
MeasuredEnergyStats.getDisplayEnergyBucket(mScreenStateAtLastEnergyMeasurement);
mScreenStateAtLastEnergyMeasurement = screenState;
@@ -12425,7 +12413,7 @@
return;
}
- mGlobalMeasuredEnergyStats.updateBucket(energyBucket, energyUJ, true);
+ mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ, true);
// Now we blame individual apps, but only if the display was ON.
if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) {
@@ -12463,7 +12451,7 @@
final long appDisplayEnergyMJ =
(totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2))
/ totalFgTimeMs;
- uid.addEnergyToEnergyBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true);
+ uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true);
// To mitigate round-off errors, remove this app from numerator & denominator totals
totalDisplayEnergyMJ -= appDisplayEnergyMJ;
@@ -14148,33 +14136,34 @@
/**
* Initialize the measured energy stats data structures.
*
- * @param supportedEnergyBuckets boolean array indicating which buckets are currently supported
+ * @param supportedStandardBuckets boolean array indicating which {@link StandardEnergyBucket}s
+ * are currently supported.
+ * If null, none are supported (regardless of numCustomBuckets).
+ * @param numCustomBuckets number of custom (OTHER) EnergyConsumers on this device
*/
@GuardedBy("this")
- public void initMeasuredEnergyStatsLocked(boolean[] supportedEnergyBuckets) {
+ public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets,
+ int numCustomBuckets) {
boolean supportedBucketMismatch = false;
mScreenStateAtLastEnergyMeasurement = mScreenState;
- if (supportedEnergyBuckets == null) {
+ if (supportedStandardBuckets == null) {
if (mGlobalMeasuredEnergyStats != null) {
// Measured energy buckets no longer supported, wipe out the existing data.
supportedBucketMismatch = true;
}
} else if (mGlobalMeasuredEnergyStats == null) {
- mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets);
+ mGlobalMeasuredEnergyStats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
return;
} else {
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (mGlobalMeasuredEnergyStats.isEnergyBucketSupported(i)
- != supportedEnergyBuckets[i]) {
- mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets);
- supportedBucketMismatch = true;
- break;
- }
- }
+ supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo(
+ supportedStandardBuckets, numCustomBuckets);
}
if (supportedBucketMismatch) {
+ mGlobalMeasuredEnergyStats = supportedStandardBuckets == null ?
+ null : new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
// Supported energy buckets changed since last boot.
// Existing data is no longer reliable.
resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime());
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index ea2fb88..e76e34f 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -83,7 +83,7 @@
/**
* Returns a snapshot of battery attribution data.
*/
- public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+ public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
// TODO(b/174186345): instead of BatteryStatsHelper, use PowerCalculators directly.
final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext,
@@ -100,28 +100,27 @@
batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, users);
+ ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
+ for (int i = 0; i < queries.size(); i++) {
+ results.add(getBatteryUsageStats(queries.get(i), batteryStatsHelper, users));
+ }
+ return results;
+ }
+
+ private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
+ BatteryStatsHelper batteryStatsHelper, SparseArray<UserHandle> users) {
// TODO(b/174186358): read extra power component number from configuration
final int customPowerComponentCount = 0;
final int customTimeComponentCount = 0;
- final boolean includeModeledComponents =
- (query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED)
- != 0;
-
final BatteryUsageStats.Builder batteryUsageStatsBuilder =
- new BatteryUsageStats.Builder(customPowerComponentCount, customTimeComponentCount,
- includeModeledComponents)
+ new BatteryUsageStats.Builder(customPowerComponentCount, customTimeComponentCount)
.setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0))
.setConsumedPower(batteryStatsHelper.getTotalPower());
- final List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
- for (int i = 0; i < usageList.size(); i++) {
- final BatterySipper sipper = usageList.get(i);
- if (sipper.drainType == BatterySipper.DrainType.APP) {
- batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(sipper.uidObj)
- .setPackageWithHighestDrain(sipper.packageWithHighestDrain)
- .setConsumedPower(sipper.sumPower());
- }
+ SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
+ for (int i = uidStats.size() - 1; i >= 0; i--) {
+ batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
}
final long realtimeUs = SystemClock.elapsedRealtime() * 1000;
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 7972924..11c8761 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -122,11 +122,5 @@
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, cpuTimeMs)
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, cpuFgTimeMs)
.setPackageWithHighestDrain(packageWithHighestDrain);
-
- if ((query.getFlags()
- & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_MODELED) != 0) {
- app.setConsumedPowerForCustomComponent(BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
- + BatteryConsumer.POWER_COMPONENT_CPU, cpuPowerMah);
- }
}
}
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
index 974894f..05fcc70 100644
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -102,7 +102,7 @@
// TODO(b/175156498): Temporary code during the transition from BatterySippers to
// BatteryConsumers.
- UidBatteryConsumer.Builder builder = new UidBatteryConsumer.Builder(0, 0, false, u);
+ UidBatteryConsumer.Builder builder = new UidBatteryConsumer.Builder(0, 0, u);
calculateApp(builder, u, rawRealtimeUs, rawUptimeUs, BatteryUsageStatsQuery.DEFAULT);
final UidBatteryConsumer uidBatteryConsumer = builder.build();
app.cpuPowerMah = uidBatteryConsumer.getConsumedPower(
@@ -162,4 +162,10 @@
// Use English locale because this is never used in UI (only in checkin and dump).
return String.format(Locale.ENGLISH, format, power);
}
+
+ static double mAhToUJ(long energyUJ) {
+ // TODO(b/173765509): Convert properly. This is mJ / V * (h/3600s) = mAh with V = 3.7 fixed.
+ // Leaving for later since desired units of energy have yet to be decided
+ return energyUJ / 1000.0 / 3.7 / 3600;
+ }
}
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index 9c4a267..c86c795 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -24,7 +24,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateUtils;
-import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseLongArray;
@@ -62,7 +62,8 @@
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah);
}
- // TODO(b/178140704): Attribute screen usage similar to smearScreenBatterySipper.
+ // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats.
+ // TODO(b/178140704): Attribute (measured/smeared) usage *per app* for BatteryUsageStats.
}
/**
@@ -71,19 +72,45 @@
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType);
- final double powerMah = computePower(batteryStats, rawRealtimeUs, statsType, durationMs);
- if (powerMah != 0) {
- final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0);
- bs.usagePowerMah = powerMah;
- bs.usageTimeMs = durationMs;
- bs.sumPower();
- sippers.add(bs);
+ final long energyUJ = batteryStats.getScreenOnEnergy();
+ final boolean isMeasuredDataAvailable = energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE;
+
+ final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType);
+ final double powerMah = getMeasuredOrComputedPower(
+ energyUJ, batteryStats, rawRealtimeUs, statsType, durationMs);
+ if (powerMah == 0) {
+ return;
+ }
+
+ // First deal with the SCREEN BatterySipper (since we need this for smearing over apps).
+ final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0);
+ bs.usagePowerMah = powerMah;
+ bs.usageTimeMs = durationMs;
+ bs.sumPower();
+ sippers.add(bs);
+
+ // Now deal with each app's BatterySipper. The results are stored in the screenPowerMah
+ // field, which is considered smeared, but the method depends on the data source.
+ if (isMeasuredDataAvailable) {
+ super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
+ } else {
smearScreenBatterySipper(sippers, bs);
}
}
+ @Override
+ protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ final long energyUJ = u.getScreenOnEnergy();
+ if (energyUJ < 0) {
+ Slog.wtf(TAG, "Screen energy not supported, so calculateApp shouldn't de called");
+ return;
+ }
+ if (energyUJ == 0) return;
+ app.screenPowerMah = mAhToUJ(u.getScreenOnEnergy());
+ }
+
private long computeDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000;
}
@@ -97,7 +124,7 @@
final double binPowerMah = mScreenFullPowerEstimator.calculatePower(brightnessTime)
* (i + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
if (DEBUG && binPowerMah != 0) {
- Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
+ Slog.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime
+ " power=" + formatCharge(binPowerMah));
}
power += binPowerMah;
@@ -105,6 +132,16 @@
return power;
}
+ private double getMeasuredOrComputedPower(long measuredEnergyUJ,
+ BatteryStats batteryStats, long rawRealtimeUs, int statsType, long durationMs) {
+
+ if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
+ return mAhToUJ(measuredEnergyUJ);
+ } else {
+ return computePower(batteryStats, rawRealtimeUs, statsType, durationMs);
+ }
+ }
+
/**
* Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
* time, and store this in the {@link BatterySipper#screenPowerMah} field.
@@ -124,10 +161,11 @@
}
if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
- final double screenPowerMah = screenSipper.totalPowerMah;
+ final double totalScreenPowerMah = screenSipper.totalPowerMah;
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper sipper = sippers.get(i);
- sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
+ sipper.screenPowerMah = totalScreenPowerMah
+ * activityTimeArray.get(sipper.getUid(), 0)
/ totalActivityTimeMs;
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fda87be..d196d4a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -103,7 +103,7 @@
*/
public static final int PROFILE_FROM_SHELL = 1 << 15;
- /*
+ /**
* Enable using the ART app image startup cache
*/
public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
@@ -116,14 +116,9 @@
*/
public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
- /**
- * Disable runtime access to {@link android.annotation.TestApi} annotated members.
- *
- * <p>This only takes effect if Hidden API access restrictions are enabled as well.
- */
- public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
-
public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
+
+ public static final int MEMORY_TAG_LEVEL_NONE = 0;
/**
* Enable pointer tagging in this process.
* Tags are checked during memory deallocation, but not on access.
@@ -167,7 +162,12 @@
* GWP-ASan is activated unconditionally (but still, only a small subset of
* allocations is protected).
*/
- public static final int GWP_ASAN_LEVEL_ALWAYS = 2 << 21;
+ public static final int GWP_ASAN_LEVEL_ALWAYS = 1 << 22;
+
+ /**
+ * Enable automatic zero-initialization of native heap memory allocations.
+ */
+ public static final int NATIVE_HEAP_ZERO_INIT = 1 << 23;
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index a4ce027..585ddf6 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -492,10 +492,12 @@
long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
- // Normalize the poll timeout value when the time between one poll event and the
- // next pushes us over the delay value. This prevents poll receiving a 0
- // timeout value, which would result in it returning immediately.
- pollTimeoutMs = -1;
+ // The refill delay has elapsed during the period between poll invocations.
+ // We will now check for any currently ready file descriptors before refilling
+ // the USAP pool.
+ pollTimeoutMs = 0;
+ mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+ mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else if (elapsedTimeMs <= 0) {
// This can occur if the clock used by currentTimeMillis is reset, which is
@@ -517,9 +519,11 @@
}
if (pollReturnValue == 0) {
- // The poll timeout has been exceeded. This only occurs when we have finished the
- // USAP pool refill delay period.
-
+ // The poll returned zero results either when the timeout value has been exceeded
+ // or when a non-blocking poll is issued and no FDs are ready. In either case it
+ // is time to refill the pool. This will result in a duplicate assignment when
+ // the non-blocking poll returns zero results, but it avoids an additional
+ // conditional in the else branch.
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 141dc79..5df175e 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2540,8 +2540,15 @@
}
}
- params.backgroundBlurRadius = a.getDimensionPixelSize(
- R.styleable.Window_windowBackgroundBlurRadius, 0);
+ if (a.getBoolean(R.styleable.Window_windowBlurBehindEnabled, false)) {
+ if ((getForcedWindowFlags() & WindowManager.LayoutParams.FLAG_BLUR_BEHIND) == 0) {
+ params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
+ }
+
+ params.blurBehindRadius = a.getDimensionPixelSize(
+ android.R.styleable.Window_windowBlurBehindRadius, 0);
+ }
+
if (params.windowAnimations == 0) {
params.windowAnimations = a.getResourceId(
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index b744a5d..e310f8d 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -20,8 +20,10 @@
import static android.os.BatteryStats.ENERGY_DATA_UNAVAILABLE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
+import android.util.DebugUtils;
import android.util.Slog;
import android.view.Display;
@@ -33,7 +35,9 @@
import java.lang.annotation.RetentionPolicy;
/**
- * Tracks the measured energy usage of various subsystems according to their {@link EnergyBucket}.
+ * Tracks the measured energy usage of various subsystems according to their
+ * {@link StandardEnergyBucket} or custom energy bucket (which is tied to
+ * {@link android.hardware.power.stats.EnergyConsumer.ordinal}).
*
* This class doesn't use a TimeBase, and instead requires manually decisions about when to
* accumulate since it is trivial. However, in the future, a TimeBase could be used instead.
@@ -42,15 +46,13 @@
public class MeasuredEnergyStats {
private static final String TAG = "MeasuredEnergyStats";
- // Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if energy
- // bucket integers are modified.
+ // Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} MUST be updated if standard
+ // energy bucket integers are modified/added/removed.
public static final int ENERGY_BUCKET_UNKNOWN = -1;
public static final int ENERGY_BUCKET_SCREEN_ON = 0;
public static final int ENERGY_BUCKET_SCREEN_DOZE = 1;
public static final int ENERGY_BUCKET_SCREEN_OTHER = 2;
- public static final int NUMBER_ENERGY_BUCKETS = 3;
- private static final String[] ENERGY_BUCKET_NAMES =
- {"screen-on", "screen-doze", "screen-other"};
+ public static final int NUMBER_STANDARD_ENERGY_BUCKETS = 3; // Buckets above this are custom.
@IntDef(prefix = {"ENERGY_BUCKET_"}, value = {
ENERGY_BUCKET_UNKNOWN,
@@ -59,28 +61,37 @@
ENERGY_BUCKET_SCREEN_OTHER,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface EnergyBucket {
+ public @interface StandardEnergyBucket {
}
/**
- * Total energy (in microjoules) that an {@link EnergyBucket} has accumulated since the last
- * reset. Values MUST be non-zero or ENERGY_DATA_UNAVAILABLE. Accumulation only occurs
+ * Total energy (in microjoules) that an energy bucket (including both
+ * {@link StandardEnergyBucket} and custom buckets) has accumulated since the last reset.
+ * Values MUST be non-zero or ENERGY_DATA_UNAVAILABLE. Accumulation only occurs
* while the necessary conditions are satisfied (e.g. on battery).
*
+ * Energy for both {@link StandardEnergyBucket}s and custom energy buckets are stored in this
+ * array, and may internally both referred to as 'buckets'. This is an implementation detail;
+ * externally, we differentiate between these two data sources.
+ *
* Warning: Long array is used for access speed. If the number of supported subsystems
* becomes large, consider using an alternate data structure such as a SparseLongArray.
*/
- private final long[] mAccumulatedEnergiesMicroJoules = new long[NUMBER_ENERGY_BUCKETS];
+ private final long[] mAccumulatedEnergiesMicroJoules;
/**
* Creates a MeasuredEnergyStats set to support the provided energy buckets.
- * supportedEnergyBuckets should generally be of size {@link #NUMBER_ENERGY_BUCKETS}.
+ * supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_ENERGY_BUCKETS}.
+ * numCustomBuckets >= 0 is the number of (non-standard) custom energy buckets on the device.
*/
- public MeasuredEnergyStats(boolean[] supportedEnergyBuckets) {
+ public MeasuredEnergyStats(boolean[] supportedStandardBuckets, int numCustomBuckets) {
+ final int numTotalBuckets = NUMBER_STANDARD_ENERGY_BUCKETS + numCustomBuckets;
+ mAccumulatedEnergiesMicroJoules = new long[numTotalBuckets];
// Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE.
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- if (!supportedEnergyBuckets[bucket]) {
- mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE;
+ // All custom buckets are, by definition, supported, so their values stay at 0.
+ for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
+ if (!supportedStandardBuckets[stdBucket]) {
+ mAccumulatedEnergiesMicroJoules[stdBucket] = ENERGY_DATA_UNAVAILABLE;
}
}
}
@@ -90,10 +101,13 @@
* supported. This certainly does NOT produce an exact clone of the template.
*/
private MeasuredEnergyStats(MeasuredEnergyStats template) {
+ final int numIndices = template.getNumberOfIndices();
+ mAccumulatedEnergiesMicroJoules = new long[numIndices];
// Initialize to all zeros where supported, otherwise ENERGY_DATA_UNAVAILABLE.
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- if (!template.isEnergyBucketSupported(bucket)) {
- mAccumulatedEnergiesMicroJoules[bucket] = ENERGY_DATA_UNAVAILABLE;
+ // All custom buckets are, by definition, supported, so their values stay at 0.
+ for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
+ if (!template.isIndexSupported(stdBucket)) {
+ mAccumulatedEnergiesMicroJoules[stdBucket] = ENERGY_DATA_UNAVAILABLE;
}
}
}
@@ -108,18 +122,22 @@
/**
* Constructor for creating a temp MeasuredEnergyStats.
- * See {@link #readSummaryFromParcel(MeasuredEnergyStats, Parcel)}.
+ * See {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}.
*/
- private MeasuredEnergyStats() {
+ private MeasuredEnergyStats(int numIndices) {
+ mAccumulatedEnergiesMicroJoules = new long[numIndices];
}
/** Construct from parcel. */
public MeasuredEnergyStats(Parcel in) {
+ final int size = in.readInt();
+ mAccumulatedEnergiesMicroJoules = new long[size];
in.readLongArray(mAccumulatedEnergiesMicroJoules);
}
/** Write to parcel */
public void writeToParcel(Parcel out) {
+ out.writeInt(mAccumulatedEnergiesMicroJoules.length);
out.writeLongArray(mAccumulatedEnergiesMicroJoules);
}
@@ -129,16 +147,18 @@
* summary parcel was written. Availability has already been correctly set in the constructor.
* Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary
* parceling changes.
+ *
+ * Corresponding write performed by {@link #writeSummaryToParcel(Parcel, boolean)}.
*/
private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) {
- final int size = in.readInt();
- for (int i = 0; i < size; i++) {
- final int bucket = in.readInt();
+ final int numWrittenEntries = in.readInt();
+ for (int entry = 0; entry < numWrittenEntries; entry++) {
+ final int index = in.readInt();
final long energyUJ = in.readLong();
if (overwriteAvailability) {
- mAccumulatedEnergiesMicroJoules[bucket] = energyUJ;
+ mAccumulatedEnergiesMicroJoules[index] = energyUJ;
} else {
- setValueIfSupported(bucket, energyUJ);
+ setValueIfSupported(index, energyUJ);
}
}
}
@@ -146,52 +166,90 @@
/**
* Write to summary parcel.
* Note: Measured subsystem availability may be different when the summary parcel is read.
+ *
+ * Corresponding read performed by {@link #readSummaryFromParcel(Parcel, boolean)}.
*/
private void writeSummaryToParcel(Parcel out, boolean skipZero) {
- final int sizePos = out.dataPosition();
+ final int posOfNumWrittenEntries = out.dataPosition();
out.writeInt(0);
- int size = 0;
- // Write only the supported buckets with non-zero energy.
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- final long energy = mAccumulatedEnergiesMicroJoules[i];
+ int numWrittenEntries = 0;
+ // Write only the supported buckets (with non-zero energy, if applicable).
+ for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
+ final long energy = mAccumulatedEnergiesMicroJoules[index];
if (energy < 0) continue;
if (energy == 0 && skipZero) continue;
- out.writeInt(i);
- out.writeLong(mAccumulatedEnergiesMicroJoules[i]);
- size++;
+ out.writeInt(index);
+ out.writeLong(mAccumulatedEnergiesMicroJoules[index]);
+ numWrittenEntries++;
}
final int currPos = out.dataPosition();
- out.setDataPosition(sizePos);
- out.writeInt(size);
+ out.setDataPosition(posOfNumWrittenEntries);
+ out.writeInt(numWrittenEntries);
out.setDataPosition(currPos);
}
- /** Updates the given bucket with the given energy iff accumulate is true. */
- public void updateBucket(@EnergyBucket int bucket, long energyDeltaUJ, boolean accumulate) {
+ /** Get number of possible buckets, including both standard and custom ones. */
+ private int getNumberOfIndices() {
+ return mAccumulatedEnergiesMicroJoules.length;
+ }
+
+ // TODO: Get rid of the 'accumulate' boolean. It's always true.
+ /** Updates the given standard energy bucket with the given energy if accumulate is true. */
+ public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ,
+ boolean accumulate) {
+ checkValidStandardBucket(bucket);
+ updateEntry(bucket, energyDeltaUJ, accumulate);
+ }
+
+ /** Updates the given custom energy bucket with the given energy if accumulate is true. */
+ public void updateCustomBucket(int customBucket, long energyDeltaUJ, boolean accumulate) {
+ if (!isValidCustomBucket(customBucket)) {
+ Slog.e(TAG, "Attempted to update invalid custom bucket " + customBucket);
+ return;
+ }
+ final int index = customBucketToIndex(customBucket);
+ updateEntry(index, energyDeltaUJ, accumulate);
+ }
+
+ /** Updates the given index with the given energy if accumulate is true. */
+ private void updateEntry(int index, long energyDeltaUJ, boolean accumulate) {
if (accumulate) {
- if (mAccumulatedEnergiesMicroJoules[bucket] >= 0L) {
- mAccumulatedEnergiesMicroJoules[bucket] += energyDeltaUJ;
+ if (mAccumulatedEnergiesMicroJoules[index] >= 0L) {
+ mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ;
} else {
Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket "
- + ENERGY_BUCKET_NAMES[bucket] + " whose value was "
- + mAccumulatedEnergiesMicroJoules[bucket]);
+ + getBucketName(index) + " whose value was "
+ + mAccumulatedEnergiesMicroJoules[index]);
}
}
}
/**
- * Return accumulated energy (in microjoules) for the given energy bucket since last reset.
- * Returns {@link BatteryStats#ENERGY_DATA_UNAVAILABLE} if this energy data is unavailable.
+ * Return accumulated energy (in microjoules) for a standard energy bucket since last reset.
+ * Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable.
+ * @throws IllegalArgumentException if no such {@link StandardEnergyBucket}.
*/
- public long getAccumulatedBucketEnergy(@EnergyBucket int bucket) {
+ public long getAccumulatedStandardBucketEnergy(@StandardEnergyBucket int bucket) {
+ checkValidStandardBucket(bucket);
return mAccumulatedEnergiesMicroJoules[bucket];
}
/**
- * Map {@link MeasuredEnergySubsystem} and device state to a Display {@link EnergyBucket}.
+ * Return accumulated energy (in microjoules) for the a custom energy bucket since last reset.
+ * Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable.
*/
- public static @EnergyBucket int getDisplayEnergyBucket(int screenState) {
+ public long getAccumulatedCustomBucketEnergy(int customBucket) {
+ if (!isValidCustomBucket(customBucket)) {
+ return ENERGY_DATA_UNAVAILABLE;
+ }
+ return mAccumulatedEnergiesMicroJoules[customBucketToIndex(customBucket)];
+ }
+
+ /**
+ * Map {@link MeasuredEnergySubsystem} and device state to Display {@link StandardEnergyBucket}.
+ */
+ public static @StandardEnergyBucket int getDisplayEnergyBucket(int screenState) {
if (Display.isOnState(screenState)) {
return ENERGY_BUCKET_SCREEN_ON;
}
@@ -204,15 +262,20 @@
/**
* Create a MeasuredEnergyStats object from a summary parcel.
*
+ * Corresponding write performed by
+ * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean)}.
+ *
* @return a new MeasuredEnergyStats object as described.
* Returns null if the parcel indicates there is no data to populate.
*/
public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) {
+ final int arraySize = in.readInt();
// Check if any MeasuredEnergyStats exists on the parcel
- if (in.readInt() == 0) return null;
+ if (arraySize == 0) return null;
- final MeasuredEnergyStats stats =
- new MeasuredEnergyStats(new boolean[NUMBER_ENERGY_BUCKETS]);
+ final int numCustomBuckets = arraySize - NUMBER_STANDARD_ENERGY_BUCKETS;
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(
+ new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], numCustomBuckets);
stats.readSummaryFromParcel(in, true);
return stats;
}
@@ -221,6 +284,12 @@
* Create a MeasuredEnergyStats using the template to determine which buckets are supported,
* and populate this new object from the given parcel.
*
+ * The parcel must be consistent with the template in terms of the number of
+ * possible (not necessarily supported) standard and custom buckets.
+ *
+ * Corresponding write performed by
+ * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel, boolean)}.
+ *
* @return a new MeasuredEnergyStats object as described.
* Returns null if the stats contain no non-0 information (such as if template is null
* or if the parcel indicates there is no data to populate).
@@ -229,12 +298,22 @@
*/
public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in,
@Nullable MeasuredEnergyStats template) {
+ final int arraySize = in.readInt();
// Check if any MeasuredEnergyStats exists on the parcel
- if (in.readInt() == 0) return null;
+ if (arraySize == 0) return null;
if (template == null) {
- // Nothing supported now. Create placeholder object just to consume the parcel data.
- final MeasuredEnergyStats mes = new MeasuredEnergyStats();
+ // Nothing supported anymore. Create placeholder object just to consume the parcel data.
+ final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize);
+ mes.readSummaryFromParcel(in, false);
+ return null;
+ }
+
+ if (arraySize != template.getNumberOfIndices()) {
+ Slog.wtf(TAG, "Size of MeasuredEnergyStats parcel (" + arraySize
+ + ") does not match template (" + template.getNumberOfIndices() + ").");
+ // Something is horribly wrong. Just consume the parcel and return null.
+ final MeasuredEnergyStats mes = new MeasuredEnergyStats(arraySize);
mes.readSummaryFromParcel(in, false);
return null;
}
@@ -251,14 +330,17 @@
/** Returns true iff any of the buckets are supported and non-zero. */
private boolean containsInterestingData() {
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- if (mAccumulatedEnergiesMicroJoules[bucket] > 0) return true;
+ for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
+ if (mAccumulatedEnergiesMicroJoules[index] > 0) return true;
}
return false;
}
/**
* Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0.
+ *
+ * Corresponding read performed by {@link #createAndReadSummaryFromParcel(Parcel)}
+ * and {@link #createAndReadSummaryFromParcel(Parcel, MeasuredEnergyStats)}.
*/
public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats,
Parcel dest, boolean skipZero) {
@@ -266,14 +348,15 @@
dest.writeInt(0);
return;
}
- dest.writeInt(1);
+ dest.writeInt(stats.getNumberOfIndices());
stats.writeSummaryToParcel(dest, skipZero);
}
/** Reset accumulated energy. */
private void reset() {
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- setValueIfSupported(bucket, 0L);
+ final int numIndices = getNumberOfIndices();
+ for (int index = 0; index < numIndices; index++) {
+ setValueIfSupported(index, 0L);
}
}
@@ -282,33 +365,93 @@
if (stats != null) stats.reset();
}
- /** If the bucket is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */
- private void setValueIfSupported(@EnergyBucket int bucket, long value) {
- if (mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE) {
- mAccumulatedEnergiesMicroJoules[bucket] = value;
+ /** If the index is AVAILABLE, overwrite its value; otherwise leave it as UNAVAILABLE. */
+ private void setValueIfSupported(int index, long value) {
+ if (mAccumulatedEnergiesMicroJoules[index] != ENERGY_DATA_UNAVAILABLE) {
+ mAccumulatedEnergiesMicroJoules[index] = value;
}
}
- /** Check if measuring the energy of the given bucket is supported by this device. */
- public boolean isEnergyBucketSupported(@EnergyBucket int bucket) {
- return mAccumulatedEnergiesMicroJoules[bucket] != ENERGY_DATA_UNAVAILABLE;
+ /**
+ * Check if measuring the energy of the given bucket is supported by this device.
+ * @throws IllegalArgumentException if not a valid {@link StandardEnergyBucket}.
+ */
+ public boolean isStandardBucketSupported(@StandardEnergyBucket int bucket) {
+ checkValidStandardBucket(bucket);
+ return isIndexSupported(bucket);
+ }
+
+ private boolean isIndexSupported(int index) {
+ return mAccumulatedEnergiesMicroJoules[index] != ENERGY_DATA_UNAVAILABLE;
+ }
+
+ /** Check if the supported energy buckets are precisely those given. */
+ public boolean isSupportEqualTo(
+ @NonNull boolean[] queriedStandardBuckets, int numCustomBuckets) {
+
+ final int numBuckets = getNumberOfIndices();
+ // TODO(b/178504428): Detect whether custom buckets have changed qualitatively, not just
+ // quantitatively, and treat as mismatch if so.
+ if (numBuckets != NUMBER_STANDARD_ENERGY_BUCKETS + numCustomBuckets) {
+ return false;
+ }
+ for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_ENERGY_BUCKETS; stdBucket++) {
+ if (isStandardBucketSupported(stdBucket) != queriedStandardBuckets[stdBucket]) {
+ return false;
+ }
+ }
+ return true;
}
/** Dump debug data. */
public void dump(PrintWriter pw) {
pw.println("Accumulated energy since last reset (microjoules):");
pw.print(" ");
- for (int bucket = 0; bucket < NUMBER_ENERGY_BUCKETS; bucket++) {
- pw.print(ENERGY_BUCKET_NAMES[bucket]);
+ for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
+ pw.print(getBucketName(index));
pw.print(" : ");
- pw.print(mAccumulatedEnergiesMicroJoules[bucket]);
- if (!isEnergyBucketSupported(bucket)) {
+ pw.print(mAccumulatedEnergiesMicroJoules[index]);
+ if (!isIndexSupported(index)) {
pw.print(" (unsupported)");
}
- if (bucket != NUMBER_ENERGY_BUCKETS - 1) {
+ if (index != mAccumulatedEnergiesMicroJoules.length - 1) {
pw.print(", ");
}
}
pw.println();
}
+
+ /**
+ * If the index is a standard bucket, returns its name; otherwise returns its prefixed custom
+ * bucket number.
+ */
+ private static String getBucketName(int index) {
+ if (isValidStandardBucket(index)) {
+ return DebugUtils.valueToString(MeasuredEnergyStats.class, "ENERGY_BUCKET_", index);
+ }
+ return "CUSTOM_" + indexToCustomBucket(index);
+ }
+
+ private static int customBucketToIndex(int customBucket) {
+ return customBucket + NUMBER_STANDARD_ENERGY_BUCKETS;
+ }
+
+ private static int indexToCustomBucket(int index) {
+ return index - NUMBER_STANDARD_ENERGY_BUCKETS;
+ }
+
+ private static void checkValidStandardBucket(@StandardEnergyBucket int bucket) {
+ if (!isValidStandardBucket(bucket)) {
+ throw new IllegalArgumentException("Illegal StandardEnergyBucket " + bucket);
+ }
+ }
+
+ private static boolean isValidStandardBucket(@StandardEnergyBucket int bucket) {
+ return bucket >= 0 && bucket < NUMBER_STANDARD_ENERGY_BUCKETS;
+ }
+
+ private boolean isValidCustomBucket(int customBucket) {
+ return customBucket >= 0
+ && customBucketToIndex(customBucket) < mAccumulatedEnergiesMicroJoules.length;
+ }
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 3cf00ae..c6fd6ee 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -35,6 +35,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.function.IntFunction;
/**
@@ -599,6 +600,20 @@
return cur;
}
+ /**
+ * Similar to {@link Set#addAll(Collection)}}, but with support for set values of {@code null}.
+ */
+ public static @NonNull <T> ArraySet<T> addAll(@Nullable ArraySet<T> cur,
+ @Nullable Collection<T> val) {
+ if (cur == null) {
+ cur = new ArraySet<>();
+ }
+ if (val != null) {
+ cur.addAll(val);
+ }
+ return cur;
+ }
+
public static @Nullable <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) {
if (cur == null) {
return null;
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 30cd94c..f42f468 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -14,6 +14,7 @@
package com.android.internal.util;
+import android.annotation.IntDef;
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
@@ -23,9 +24,12 @@
import android.util.Log;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.EventLogTags;
import com.android.internal.os.BackgroundThread;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -91,6 +95,34 @@
*/
public static final int ACTION_START_RECENTS_ANIMATION = 8;
+ private static final int[] ACTIONS_ALL = {
+ ACTION_EXPAND_PANEL,
+ ACTION_TOGGLE_RECENTS,
+ ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+ ACTION_CHECK_CREDENTIAL,
+ ACTION_CHECK_CREDENTIAL_UNLOCKED,
+ ACTION_TURN_ON_SCREEN,
+ ACTION_ROTATE_SCREEN,
+ ACTION_FACE_WAKE_AND_UNLOCK,
+ ACTION_START_RECENTS_ANIMATION
+ };
+
+ /** @hide */
+ @IntDef({
+ ACTION_EXPAND_PANEL,
+ ACTION_TOGGLE_RECENTS,
+ ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+ ACTION_CHECK_CREDENTIAL,
+ ACTION_CHECK_CREDENTIAL_UNLOCKED,
+ ACTION_TURN_ON_SCREEN,
+ ACTION_ROTATE_SCREEN,
+ ACTION_FACE_WAKE_AND_UNLOCK,
+ ACTION_START_RECENTS_ANIMATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Action {
+ }
+
private static final int[] STATSD_ACTION = new int[]{
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
@@ -105,9 +137,14 @@
private static LatencyTracker sLatencyTracker;
+ private final Object mLock = new Object();
private final SparseLongArray mStartRtc = new SparseLongArray();
- private volatile int mSamplingInterval;
- private volatile boolean mEnabled;
+ @GuardedBy("mLock")
+ private final int[] mTraceThresholdPerAction = new int[ACTIONS_ALL.length];
+ @GuardedBy("mLock")
+ private int mSamplingInterval;
+ @GuardedBy("mLock")
+ private boolean mEnabled;
public static LatencyTracker getInstance(Context context) {
if (sLatencyTracker == null) {
@@ -132,20 +169,26 @@
}
private void updateProperties(DeviceConfig.Properties properties) {
- mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
- DEFAULT_SAMPLING_INTERVAL);
- mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+ synchronized (mLock) {
+ mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+ DEFAULT_SAMPLING_INTERVAL);
+ mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+ for (int action : ACTIONS_ALL) {
+ mTraceThresholdPerAction[action] =
+ properties.getInt(getTraceTriggerNameForAction(action), -1);
+ }
+ }
}
/**
* A helper method to translate action type to name.
*
- * @param action the action type defined in AtomsProto.java
+ * @param atomsProtoAction the action type defined in AtomsProto.java
* @return the name of the action
*/
- public static String getNameOfAction(int action) {
+ public static String getNameOfAction(int atomsProtoAction) {
// Defined in AtomsProto.java
- switch (action) {
+ switch (atomsProtoAction) {
case 0:
return "UNKNOWN";
case 1:
@@ -171,16 +214,22 @@
}
}
- private static String getTraceNameOfAction(int action) {
+ private static String getTraceNameOfAction(@Action int action) {
return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
}
+ private static String getTraceTriggerNameForAction(@Action int action) {
+ return "latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
+ }
+
public static boolean isEnabled(Context ctx) {
return getInstance(ctx).isEnabled();
}
public boolean isEnabled() {
- return mEnabled;
+ synchronized (mLock) {
+ return mEnabled;
+ }
}
/**
@@ -188,7 +237,7 @@
*
* @param action The action to start. One of the ACTION_* values.
*/
- public void onActionStart(int action) {
+ public void onActionStart(@Action int action) {
if (!isEnabled()) {
return;
}
@@ -201,7 +250,7 @@
*
* @param action The action to end. One of the ACTION_* values.
*/
- public void onActionEnd(int action) {
+ public void onActionEnd(@Action int action) {
if (!isEnabled()) {
return;
}
@@ -221,19 +270,30 @@
* @param action The action to end. One of the ACTION_* values.
* @param duration The duration of the action in ms.
*/
- public void logAction(int action, int duration) {
- boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ public void logAction(@Action int action, int duration) {
+ boolean shouldSample;
+ int traceThreshold;
+ synchronized (mLock) {
+ shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ traceThreshold = mTraceThresholdPerAction[action];
+ }
+
+ if (traceThreshold > 0 && duration >= traceThreshold) {
+ PerfettoTrigger.trigger(getTraceTriggerNameForAction(action));
+ }
+
logActionDeprecated(action, duration, shouldSample);
}
/**
* Logs an action that has started and ended. This needs to be called from the main thread.
*
- * @param action The action to end. One of the ACTION_* values.
- * @param duration The duration of the action in ms.
+ * @param action The action to end. One of the ACTION_* values.
+ * @param duration The duration of the action in ms.
* @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
*/
- public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) {
+ public static void logActionDeprecated(
+ @Action int action, int duration, boolean writeToStatsLog) {
Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java
index dd64c40..1ab316d 100644
--- a/core/java/com/android/internal/util/Parcelling.java
+++ b/core/java/com/android/internal/util/Parcelling.java
@@ -27,6 +27,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import java.util.regex.Pattern;
/**
@@ -291,5 +292,19 @@
return s == null ? null : Pattern.compile(s);
}
}
+
+ class ForUUID implements Parcelling<UUID> {
+
+ @Override
+ public void parcel(UUID item, Parcel dest, int parcelFlags) {
+ dest.writeString(item == null ? null : item.toString());
+ }
+
+ @Override
+ public UUID unparcel(Parcel source) {
+ String string = source.readString();
+ return string == null ? null : UUID.fromString(string);
+ }
+ }
}
}
diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java
new file mode 100644
index 0000000..9c87c69
--- /dev/null
+++ b/core/java/com/android/internal/util/PerfettoTrigger.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * A trigger implementation with perfetto backend.
+ * @hide
+ */
+public class PerfettoTrigger {
+ private static final String TAG = "PerfettoTrigger";
+ private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
+
+ /**
+ * @param triggerName The name of the trigger. Must match the value defined in the AOT
+ * Perfetto config.
+ */
+ public static void trigger(String triggerName) {
+ try {
+ ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
+ Log.v(TAG, "Triggering " + String.join(" ", pb.command()));
+ Process process = pb.start();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to trigger " + triggerName, e);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index e7b7bf4..19506a3 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -79,7 +79,7 @@
private static final int DO_CLOSE_CONNECTION = 150;
private static final int DO_COMMIT_CONTENT = 160;
private static final int DO_GET_SURROUNDING_TEXT = 41;
- private static final int DO_SET_IME_TEMPORARILY_CONSUMES_INPUT = 170;
+ private static final int DO_SET_IME_CONSUMES_INPUT = 170;
@GuardedBy("mLock")
@@ -268,13 +268,12 @@
}
/**
- * Dispatches the request for setting ime temporarily consumes input.
+ * Dispatches the request for setting ime consumes input.
*
- * <p>See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}.
+ * <p>See {@link InputConnection#setImeConsumesInput(boolean)}.
*/
- public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
- dispatchMessage(obtainMessageB(DO_SET_IME_TEMPORARILY_CONSUMES_INPUT,
- imeTemporarilyConsumesInput));
+ public void setImeConsumesInput(boolean imeConsumesInput) {
+ dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput));
}
void dispatchMessage(Message msg) {
@@ -822,17 +821,17 @@
}
return;
}
- case DO_SET_IME_TEMPORARILY_CONSUMES_INPUT: {
+ case DO_SET_IME_CONSUMES_INPUT: {
Trace.traceBegin(Trace.TRACE_TAG_INPUT,
- "InputConnection#setImeTemporarilyConsumesInput");
+ "InputConnection#setImeConsumesInput");
try {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG,
- "setImeTemporarilyConsumesInput on inactive InputConnection");
+ "setImeConsumesInput on inactive InputConnection");
return;
}
- ic.setImeTemporarilyConsumesInput(msg.arg1 == 1);
+ ic.setImeConsumesInput(msg.arg1 == 1);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 586404c..b06b4e5 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -86,5 +86,5 @@
void getSurroundingText(int beforeLength, int afterLength, int flags,
ISurroundingTextResultCallback callback);
- void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput);
+ void setImeConsumesInput(boolean imeConsumesInput);
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 84c92ca..0e9d135 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -525,12 +525,12 @@
}
/**
- * See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}.
+ * See {@link InputConnection#setImeConsumesInput(boolean)}.
*/
@AnyThread
- public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+ public boolean setImeConsumesInput(boolean imeConsumesInput) {
try {
- mIInputContext.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+ mIInputContext.setImeConsumesInput(imeConsumesInput);
return true;
} catch (RemoteException e) {
return false;
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index ae1a815..4b9a160 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -59,6 +59,11 @@
public static final int TYPE_RECYCLING = 2;
/**
+ * The ViewGroup scrolls, but has no child views in
+ */
+ private static final int TYPE_OPAQUE = 3;
+
+ /**
* Performs tests on the given View and determines:
* 1. If scrolling is possible
* 2. What mechanisms are used for scrolling.
@@ -95,8 +100,15 @@
}
return TYPE_RECYCLING;
}
+ // At least one child view is required.
+ if (((ViewGroup) view).getChildCount() < 1) {
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "scrollable with no children");
+ }
+ return TYPE_OPAQUE;
+ }
if (DEBUG_VERBOSE) {
- Log.v(TAG, "hint: less than two child views");
+ Log.v(TAG, "hint: single child view");
}
//Because recycling containers don't use scrollY, a non-zero value means Scroll view.
if (view.getScrollY() != 0) {
diff --git a/core/java/com/android/internal/widget/DisableImageView.java b/core/java/com/android/internal/widget/DisableImageView.java
new file mode 100644
index 0000000..0d9bf71
--- /dev/null
+++ b/core/java/com/android/internal/widget/DisableImageView.java
@@ -0,0 +1,68 @@
+/*
+ * 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.internal.widget;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+/**
+ * ImageView which applies a saturation image filter, used by AppWidgets to represent disabled icon
+ */
+@RemoteViews.RemoteView
+public class DisableImageView extends ImageView {
+
+ public DisableImageView(Context context) {
+ this(context, null, 0, 0);
+ }
+
+ public DisableImageView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0, 0);
+ }
+
+ public DisableImageView(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DisableImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ // Apply the disabled filter
+ ColorMatrix brightnessMatrix = new ColorMatrix();
+ float brightnessF = 0.5f;
+ int brightnessI = (int) (255 * brightnessF);
+ // Brightness: C-new = C-old*(1-amount) + amount
+ float scale = 1f - brightnessF;
+ float[] mat = brightnessMatrix.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = brightnessI;
+ mat[9] = brightnessI;
+ mat[14] = brightnessI;
+
+ ColorMatrix filterMatrix = new ColorMatrix();
+ filterMatrix.setSaturation(0);
+ filterMatrix.preConcat(brightnessMatrix);
+ setColorFilter(new ColorMatrixColorFilter(filterMatrix));
+ }
+}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 4ccf9ce..3d054a5 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -245,11 +245,11 @@
}
@Override
- public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+ public boolean setImeConsumesInput(boolean imeConsumesInput) {
if (mTextView == null) {
- return super.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+ return super.setImeConsumesInput(imeConsumesInput);
}
- mTextView.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+ mTextView.setImeConsumesInput(imeConsumesInput);
return true;
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index bad21d2..73c5460 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -148,6 +148,7 @@
private int mRegularColor;
private int mErrorColor;
private int mSuccessColor;
+ private int mDotColor;
private final Interpolator mFastOutSlowInInterpolator;
private final Interpolator mLinearOutSlowInInterpolator;
@@ -318,6 +319,7 @@
mRegularColor = a.getColor(R.styleable.LockPatternView_regularColor, 0);
mErrorColor = a.getColor(R.styleable.LockPatternView_errorColor, 0);
mSuccessColor = a.getColor(R.styleable.LockPatternView_successColor, 0);
+ mDotColor = a.getColor(R.styleable.LockPatternView_dotColor, mRegularColor);
int pathColor = a.getColor(R.styleable.LockPatternView_pathColor, mRegularColor);
mPathPaint.setColor(pathColor);
@@ -522,7 +524,7 @@
getCenterYForRow(cellState.row) + startTranslationY);
cellState.hwCenterX = CanvasProperty.createFloat(getCenterXForColumn(cellState.col));
cellState.hwRadius = CanvasProperty.createFloat(mDotSize/2 * startScale);
- mPaint.setColor(getCurrentColor(false));
+ mPaint.setColor(getDotColor());
mPaint.setAlpha((int) (startAlpha * 255));
cellState.hwPaint = CanvasProperty.createPaint(new Paint(mPaint));
@@ -1163,29 +1165,6 @@
final Path currentPath = mCurrentPath;
currentPath.rewind();
- // draw the circles
- for (int i = 0; i < 3; i++) {
- float centerY = getCenterYForRow(i);
- for (int j = 0; j < 3; j++) {
- CellState cellState = mCellStates[i][j];
- float centerX = getCenterXForColumn(j);
- float translationY = cellState.translationY;
-
- if (mUseLockPatternDrawable) {
- drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
- } else {
- if (isHardwareAccelerated() && cellState.hwAnimating) {
- RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
- recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
- cellState.hwRadius, cellState.hwPaint);
- } else {
- drawCircle(canvas, (int) centerX, (int) centerY + translationY,
- cellState.radius, drawLookup[i][j], cellState.alpha);
- }
- }
- }
- }
-
// TODO: the path should be created and cached every time we hit-detect a cell
// only the last segment of the path should be computed here
// draw the path of the pattern (unless we are in stealth mode)
@@ -1256,6 +1235,29 @@
canvas.drawPath(currentPath, mPathPaint);
}
}
+
+ // draw the circles
+ for (int i = 0; i < 3; i++) {
+ float centerY = getCenterYForRow(i);
+ for (int j = 0; j < 3; j++) {
+ CellState cellState = mCellStates[i][j];
+ float centerX = getCenterXForColumn(j);
+ float translationY = cellState.translationY;
+
+ if (mUseLockPatternDrawable) {
+ drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
+ } else {
+ if (isHardwareAccelerated() && cellState.hwAnimating) {
+ RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
+ cellState.hwRadius, cellState.hwPaint);
+ } else {
+ drawCircle(canvas, (int) centerX, (int) centerY + translationY,
+ cellState.radius, drawLookup[i][j], cellState.alpha);
+ }
+ }
+ }
+ }
}
private float calculateLastSegmentAlpha(float x, float y, float lastX, float lastY) {
@@ -1266,6 +1268,17 @@
return Math.min(1f, Math.max(0f, (frac - 0.3f) * 4f));
}
+ private int getDotColor() {
+ if (mInStealthMode) {
+ // Always use the default color in this case
+ return mDotColor;
+ } else if (mPatternDisplayMode == DisplayMode.Wrong) {
+ // the pattern is wrong
+ return mErrorColor;
+ }
+ return mDotColor;
+ }
+
private int getCurrentColor(boolean partOfPattern) {
if (!partOfPattern || mInStealthMode || mPatternInProgress) {
// unselected circle
@@ -1286,7 +1299,7 @@
*/
private void drawCircle(Canvas canvas, float centerX, float centerY, float radius,
boolean partOfPattern, float alpha) {
- mPaint.setColor(getCurrentColor(partOfPattern));
+ mPaint.setColor(getDotColor());
mPaint.setAlpha((int) (alpha * 255));
canvas.drawCircle(centerX, centerY, radius, mPaint);
}
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index ae566c3..d284d51 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -5,3 +5,17 @@
per-file *LockScreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *LockSettings* = file:/services/core/java/com/android/server/locksettings/OWNERS
+
+# Notification related
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *People* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ImageFloatingTextView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ObservableTextView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file RemeasuringLinearLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ViewClippingUtil.java = file:/services/core/java/com/android/server/notification/OWNERS
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index b4d8e50..95999a7 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -23,7 +23,6 @@
import android.os.Build;
import android.os.DropBoxManager;
import android.os.Environment;
-import android.os.FileObserver;
import android.os.FileUtils;
import android.os.RecoverySystem;
import android.os.RemoteException;
@@ -74,7 +73,6 @@
SystemProperties.getInt("ro.debuggable", 0) == 1 ? 196608 : 65536;
private static final int GMSCORE_LASTK_LOG_SIZE = 196608;
- private static final File TOMBSTONE_DIR = new File("/data/tombstones");
private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
// The pre-froyo package and class of the system updater, which
@@ -85,9 +83,6 @@
private static final String OLD_UPDATER_CLASS =
"com.google.android.systemupdater.SystemUpdateReceiver";
- // Keep a reference to the observer so the finalizer doesn't disable it.
- private static FileObserver sTombstoneObserver = null;
-
private static final String LOG_FILES_FILE = "log-files.xml";
private static final AtomicFile sFile = new AtomicFile(new File(
Environment.getDataSystemDirectory(), LOG_FILES_FILE), "log-files");
@@ -153,7 +148,7 @@
Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
}
- private String getPreviousBootHeaders() {
+ private static String getPreviousBootHeaders() {
try {
return FileUtils.readTextFile(lastHeaderFile, 0, null);
} catch (IOException e) {
@@ -161,7 +156,7 @@
}
}
- private String getCurrentBootHeaders() throws IOException {
+ private static String getCurrentBootHeaders() throws IOException {
return new StringBuilder(512)
.append("Build: ").append(Build.FINGERPRINT).append("\n")
.append("Hardware: ").append(Build.BOARD).append("\n")
@@ -175,7 +170,7 @@
}
- private String getBootHeadersToLogAndUpdate() throws IOException {
+ private static String getBootHeadersToLogAndUpdate() throws IOException {
final String oldHeaders = getPreviousBootHeaders();
final String newHeaders = getCurrentBootHeaders();
@@ -247,38 +242,27 @@
logFsMountTime();
addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
logSystemServerShutdownTimeMetrics();
-
- // Scan existing tombstones (in case any new ones appeared)
- File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
- for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
- if (tombstoneFiles[i].isFile()) {
- addFileToDropBox(db, timestamps, headers, tombstoneFiles[i].getPath(),
- LOG_SIZE, "SYSTEM_TOMBSTONE");
- }
- }
-
writeTimestamps(timestamps);
+ }
- // Start watching for new tombstone files; will record them as they occur.
- // This gets registered with the singleton file observer thread.
- sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
- @Override
- public void onEvent(int event, String path) {
- HashMap<String, Long> timestamps = readTimestamps();
- try {
- File file = new File(TOMBSTONE_DIR, path);
- if (file.isFile() && file.getName().startsWith("tombstone_")) {
- addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
- TAG_TOMBSTONE);
- }
- } catch (IOException e) {
- Slog.e(TAG, "Can't log tombstone", e);
- }
- writeTimestamps(timestamps);
- }
- };
-
- sTombstoneObserver.startWatching();
+ /**
+ * Add a tombstone to the DropBox.
+ *
+ * @param ctx Context
+ * @param tombstone path to the tombstone
+ */
+ public static void addTombstoneToDropBox(Context ctx, File tombstone) {
+ final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+ final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+ HashMap<String, Long> timestamps = readTimestamps();
+ try {
+ final String headers = getBootHeadersToLogAndUpdate();
+ addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
+ TAG_TOMBSTONE);
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't log tombstone", e);
+ }
+ writeTimestamps(timestamps);
}
private static void addLastkToDropBox(
@@ -761,7 +745,7 @@
}
}
- private void writeTimestamps(HashMap<String, Long> timestamps) {
+ private static void writeTimestamps(HashMap<String, Long> timestamps) {
synchronized (sFile) {
final FileOutputStream stream;
try {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index e5bc470..cb586d6 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -178,8 +178,9 @@
// be delivered anonymously even to apps which target O+.
final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>();
- // These are the package names of apps which should be in the 'always'
- // URL-handling state upon factory reset.
+ // These are the package names of apps which should be automatically granted domain verification
+ // for all of their domains. The only way these apps can be overridden by the user is by
+ // explicitly disabling overall link handling support in app info.
final ArraySet<String> mLinkedApps = new ArraySet<>();
// These are the components that are enabled by default as VR mode listener services.
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0b48e72..8edc8a1 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -207,9 +207,9 @@
],
shared_libs: [
- "audioclient-types-aidl-unstable-cpp",
- "audioflinger-aidl-unstable-cpp",
- "av-types-aidl-unstable-cpp",
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "av-types-aidl-cpp",
"libandroidicu",
"libbpf_android",
"libnetdbpf",
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 66753e4..f1998a5 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -47,6 +47,7 @@
static struct assetsprovider_offsets_t {
jclass classObject;
jmethodID loadAssetFd;
+ jmethodID toString;
} gAssetsProviderOffsets;
static struct {
@@ -72,9 +73,22 @@
class LoaderAssetsProvider : public AssetsProvider {
public:
static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
- return (!assets_provider) ? nullptr
+ return (!assets_provider) ? EmptyAssetsProvider::Create()
: std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider(
- env->NewGlobalRef(assets_provider)));
+ env, assets_provider));
+ }
+
+ bool ForEachFile(const std::string& /* root_path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ return true;
+ }
+
+ const std::string& GetDebugName() const override {
+ return debug_name_;
+ }
+
+ bool IsUpToDate() const override {
+ return true;
}
~LoaderAssetsProvider() override {
@@ -142,20 +156,26 @@
*file_exists = true;
}
- return ApkAssets::CreateAssetFromFd(base::unique_fd(fd),
- nullptr /* path */,
- static_cast<off64_t>(mOffset),
- static_cast<off64_t>(mLength));
+ return AssetsProvider::CreateAssetFromFd(base::unique_fd(fd),
+ nullptr /* path */,
+ static_cast<off64_t>(mOffset),
+ static_cast<off64_t>(mLength));
}
private:
DISALLOW_COPY_AND_ASSIGN(LoaderAssetsProvider);
- explicit LoaderAssetsProvider(jobject assets_provider)
- : assets_provider_(assets_provider) { }
+ explicit LoaderAssetsProvider(JNIEnv* env, jobject assets_provider) {
+ assets_provider_ = env->NewGlobalRef(assets_provider);
+ auto string_result = static_cast<jstring>(env->CallObjectMethod(
+ assets_provider_, gAssetsProviderOffsets.toString));
+ ScopedUtfChars str(env, string_result);
+ debug_name_ = std::string(str.c_str(), str.size());
+ }
// The global reference to the AssetsProvider
jobject assets_provider_;
+ std::string debug_name_;
};
static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -170,18 +190,26 @@
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
- case FORMAT_APK:
- apk_assets = ApkAssets::Load(path.c_str(), property_flags, std::move(loader_assets));
+ case FORMAT_APK: {
+ auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
+ ZipAssetsProvider::Create(path.c_str()));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
case FORMAT_IDMAP:
apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
break;
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTable(path.c_str(), property_flags, std::move(loader_assets));
+ apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()),
+ std::move(loader_assets),
+ property_flags);
break;
- case FORMAT_DIRECTORY:
- apk_assets = ApkAssets::LoadFromDir(path.c_str(), property_flags, std::move(loader_assets));
+ case FORMAT_DIRECTORY: {
+ auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
+ DirectoryAssetsProvider::Create(path.c_str()));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
@@ -221,13 +249,17 @@
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
- case FORMAT_APK:
- apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets));
+ case FORMAT_APK: {
+ auto assets = MultiAssetsProvider::Create(
+ std::move(loader_assets),
+ ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str()));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets));
+ apk_assets = ApkAssets::LoadTable(
+ AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */),
+ std::move(loader_assets), property_flags);
break;
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
@@ -282,17 +314,20 @@
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
- case FORMAT_APK:
- apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets),
- static_cast<off64_t>(offset),
- static_cast<off64_t>(length));
+ case FORMAT_APK: {
+ auto assets = MultiAssetsProvider::Create(
+ std::move(loader_assets),
+ ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str(),
+ static_cast<off64_t>(offset), static_cast<off64_t>(length)));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
break;
+ }
case FORMAT_ARSC:
- apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
- property_flags, std::move(loader_assets),
- static_cast<off64_t>(offset),
- static_cast<off64_t>(length));
+ apk_assets = ApkAssets::LoadTable(
+ AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */,
+ static_cast<off64_t>(offset),
+ static_cast<off64_t>(length)),
+ std::move(loader_assets), property_flags);
break;
default:
const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
@@ -310,8 +345,7 @@
}
static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
- auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
- auto apk_assets = ApkAssets::LoadEmpty(flags, std::move(loader_assets));
+ auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
return reinterpret_cast<jlong>(apk_assets.release());
}
@@ -458,6 +492,8 @@
gAssetsProviderOffsets.loadAssetFd = GetMethodIDOrDie(
env, gAssetsProviderOffsets.classObject, "loadAssetFd",
"(Ljava/lang/String;I)Landroid/content/res/AssetFileDescriptor;");
+ gAssetsProviderOffsets.toString = GetMethodIDOrDie(
+ env, gAssetsProviderOffsets.classObject, "toString", "()Ljava/lang/String;");
jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 5f2dcdf..5630a1e 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -41,6 +41,10 @@
#define ENCODING_OPUS 20
#define ENCODING_PCM_24BIT_PACKED 21
#define ENCODING_PCM_32BIT 22
+#define ENCODING_MPEGH_BL_L3 23
+#define ENCODING_MPEGH_BL_L4 24
+#define ENCODING_MPEGH_LC_L3 25
+#define ENCODING_MPEGH_LC_L4 26
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -98,6 +102,14 @@
return AUDIO_FORMAT_PCM_24_BIT_PACKED;
case ENCODING_PCM_32BIT:
return AUDIO_FORMAT_PCM_32_BIT;
+ case ENCODING_MPEGH_BL_L3:
+ return AUDIO_FORMAT_MPEGH_BL_L3;
+ case ENCODING_MPEGH_BL_L4:
+ return AUDIO_FORMAT_MPEGH_BL_L4;
+ case ENCODING_MPEGH_LC_L3:
+ return AUDIO_FORMAT_MPEGH_LC_L3;
+ case ENCODING_MPEGH_LC_L4:
+ return AUDIO_FORMAT_MPEGH_LC_L4;
default:
return AUDIO_FORMAT_INVALID;
}
@@ -159,6 +171,14 @@
return ENCODING_DOLBY_MAT;
case AUDIO_FORMAT_OPUS:
return ENCODING_OPUS;
+ case AUDIO_FORMAT_MPEGH_BL_L3:
+ return ENCODING_MPEGH_BL_L3;
+ case AUDIO_FORMAT_MPEGH_BL_L4:
+ return ENCODING_MPEGH_BL_L4;
+ case AUDIO_FORMAT_MPEGH_LC_L3:
+ return ENCODING_MPEGH_LC_L3;
+ case AUDIO_FORMAT_MPEGH_LC_L4:
+ return ENCODING_MPEGH_LC_L4;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 787d348..3acbd1e 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -36,6 +36,7 @@
#include <utils/List.h>
#include <utils/KeyedVector.h>
#include <binder/Parcel.h>
+#include <binder/ParcelRef.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/threads.h>
@@ -515,8 +516,9 @@
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
- Parcel* parcel = new Parcel();
- return reinterpret_cast<jlong>(parcel);
+ sp<ParcelRef> parcelRef = ParcelRef::create();
+ parcelRef->incStrong(reinterpret_cast<const void*>(android_os_Parcel_create));
+ return reinterpret_cast<jlong>(static_cast<Parcel *>(parcelRef.get()));
}
static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -529,8 +531,8 @@
static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
{
- Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
- delete parcel;
+ ParcelRef* derivative = static_cast<ParcelRef*>(reinterpret_cast<Parcel*>(nativePtr));
+ derivative->decStrong(reinterpret_cast<const void*>(android_os_Parcel_create));
}
static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 675648a..f7b3f30 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -35,6 +35,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
+#include <binder/ParcelRef.h>
#include <binder/ProcessState.h>
#include <binder/Stability.h>
#include <binderthreadstate/CallerUtils.h>
@@ -1367,7 +1368,8 @@
}
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
- jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
+ jint code, jobject dataObj, jobject replyObj, jboolean replyObjOwnsNativeParcel,
+ jint flags) // throws RemoteException
{
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -1409,6 +1411,21 @@
status_t err = target->transact(code, *data, reply, flags);
//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
+ if (reply) {
+ if (replyObjOwnsNativeParcel) {
+ // as per Parcel java class constructor, here, "reply" MUST be a "ParcelRef"
+ // only for Parcel that contained Binder objects
+ if (reply->objectsCount() > 0) {
+ IPCThreadState::self()->createTransactionReference(static_cast<ParcelRef*>(reply));
+ }
+ } else {
+ // as per Parcel.java, if Parcel java object NOT owning native Parcel object, it will
+ // NOT destroy the native Parcel object upon GC(finalize()), so, there will be no race
+ // condtion in this case. Please refer to the java class methods: Parcel.finalize(),
+ // Parcel.destroy().
+ }
+ }
+
if (kEnableBinderSample) {
if (time_binder_calls) {
conditionally_log_binder_call(start_millis, target, code);
@@ -1535,7 +1552,7 @@
{"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder},
{"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive},
{"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
- {"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
+ {"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;ZI)Z", (void*)android_os_BinderProxy_transact},
{"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
{"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 05fcaec..8597308 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1409,16 +1409,6 @@
transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber);
}
-static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj,
- jlong nativeObject,
- jlong newParentObject) {
-
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- auto newParent = reinterpret_cast<SurfaceControl *>(newParentObject);
- auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->reparentChildren(ctrl, newParent);
-}
-
static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject,
jlong newParentObject) {
@@ -1838,8 +1828,6 @@
(void*)nativeGetProtectedContentSupport },
{"nativeDeferTransactionUntil", "(JJJJ)V",
(void*)nativeDeferTransactionUntil },
- {"nativeReparentChildren", "(JJJ)V",
- (void*)nativeReparentChildren } ,
{"nativeReparent", "(JJJ)V",
(void*)nativeReparent },
{"nativeCaptureDisplay",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c5a9559..3d1c38d 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -339,6 +339,7 @@
GWP_ASAN_LEVEL_NEVER = 0 << 21,
GWP_ASAN_LEVEL_LOTTERY = 1 << 21,
GWP_ASAN_LEVEL_ALWAYS = 2 << 21,
+ NATIVE_HEAP_ZERO_INIT = 1 << 23,
};
enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -1778,15 +1779,20 @@
}
mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level);
+ // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
+ runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
+
// Avoid heap zero initialization for applications without MTE. Zero init may
// cause app compat problems, use more memory, or reduce performance. While it
// would be nice to have them for apps, we will have to wait until they are
// proven out, have more efficient hardware, and/or apply them only to new
// applications.
- mallopt(M_BIONIC_ZERO_INIT, 0);
+ if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT)) {
+ mallopt(M_BIONIC_ZERO_INIT, 0);
+ }
// Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
- runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
+ runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT;
bool forceEnableGwpAsan = false;
switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 748b4b4..99fd215 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -16,6 +16,7 @@
jjaggi@google.com
roosa@google.com
per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
# Biometrics
kchyn@google.com
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index e683306..bb39ea8 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -110,6 +110,8 @@
optional int32 network_security_config_res = 17;
optional int32 category = 18;
optional int32 enable_gwp_asan = 19;
+ optional int32 enable_memtag = 20;
+ optional bool native_heap_zero_init = 21;
}
optional Detail detail = 17;
}
diff --git a/core/proto/android/server/apphibernationservice.proto b/core/proto/android/server/apphibernationservice.proto
new file mode 100644
index 0000000..d341c4b
--- /dev/null
+++ b/core/proto/android/server/apphibernationservice.proto
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package com.android.server.apphibernation;
+
+option java_multiple_files = true;
+
+// Proto for hibernation states for all packages for a user.
+message UserLevelHibernationStatesProto {
+ repeated UserLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.UserLevelState.
+message UserLevelHibernationStateProto {
+ optional string package_name = 1;
+ optional bool hibernated = 2;
+}
+
+// Proto for global hibernation states for all packages.
+message GlobalLevelHibernationStatesProto {
+ repeated GlobalLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.GlobalLevelState
+message GlobalLevelHibernationStateProto {
+ optional string package_name = 1;
+ optional bool hibernated = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index 0c5a360..4b1ee02 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -191,6 +191,14 @@
optional string name = 4;
}
+message EnergyConsumerAttributionProto {
+ /** Android ID / Linux UID, the accumulated energy should be attributed to. */
+ optional int32 uid = 1;
+
+ /** Accumulated energy since boot in microwatt-seconds (uWs) for this AID. */
+ optional int64 energy_uws = 2;
+}
+
/**
* Energy consumer result:
* An estimate of energy consumption since boot for the subsystem identified
@@ -205,6 +213,9 @@
/** Accumulated energy since device boot in microwatt-seconds (uWs) */
optional int64 energy_uws = 3;
+
+ /** Optional attribution per UID for this EnergyConsumer. */
+ repeated EnergyConsumerAttributionProto attribution = 4;
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 989e25d..827bf7b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -93,6 +93,7 @@
<protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
<protected-broadcast android:name="android.intent.action.USER_INITIALIZE" />
<protected-broadcast android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
+ <protected-broadcast android:name="android.intent.action.DOMAINS_NEED_VERIFICATION" />
<protected-broadcast android:name="android.intent.action.OVERLAY_ADDED" />
<protected-broadcast android:name="android.intent.action.OVERLAY_CHANGED" />
<protected-broadcast android:name="android.intent.action.OVERLAY_REMOVED" />
@@ -689,10 +690,6 @@
<!-- Made protected in S (was added in R) -->
<protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
- <!-- Added in S -->
- <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_CREATED" />
- <protected-broadcast android:name="android.app.action.PROVISIONED_MANAGED_DEVICE" />
-
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -2538,7 +2535,7 @@
<permission android:name="android.permission.REAL_GET_TASKS"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+ <!-- @TestApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
@hide -->
<permission android:name="android.permission.START_TASKS_FROM_RECENTS"
android:protectionLevel="signature|privileged|recents" />
@@ -2583,6 +2580,10 @@
<permission android:name="android.permission.CREATE_USERS"
android:protectionLevel="signature" />
+ <!-- @TestApi @hide Allows an application to query user info for all users on the device. -->
+ <permission android:name="android.permission.QUERY_USERS"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
@@ -4702,6 +4703,26 @@
<permission android:name="android.permission.BIND_INTENT_FILTER_VERIFIER"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Domain verification agent package needs to have this permission before the
+ system will trust it to verify domains.
+
+ TODO(159952358): STOPSHIP: This must be updated to the new "internal" protectionLevel
+ -->
+ <permission android:name="android.permission.DOMAIN_VERIFICATION_AGENT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Must be required by the domain verification agent's intent
+ BroadcastReceiver, to ensure that only the system can interact with it.
+ -->
+ <permission android:name="android.permission.BIND_DOMAIN_VERIFICATION_AGENT"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an app like Settings to update the user's grants to what domains
+ an app is allowed to automatically open.
+ -->
+ <permission android:name="android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows applications to access serial ports via the SerialManager.
@hide -->
<permission android:name="android.permission.SERIAL_PORT"
@@ -5406,17 +5427,18 @@
<permission android:name="android.permission.INPUT_CONSUMER"
android:protectionLevel="signature" />
- <!-- @hide Allows an application to control the system's device state managed by the
+ <!-- @hide @TestApi Allows an application to control the system's device state managed by the
{@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable
devices this would grant access to toggle between the folded and unfolded states. -->
<permission android:name="android.permission.CONTROL_DEVICE_STATE"
android:protectionLevel="signature" />
- <!-- Must be required by an {@link android.service.attestation.ImpressionAttestationService}
- to ensure that only the system can bind to it.
- @hide This is not a third-party API (intended for OEMs and system apps).
+ <!-- Must be required by a
+ {@link android.service.screenshot.ScreenshotHasherService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
-->
- <permission android:name="android.permission.BIND_IMPRESSION_ATTESTATION_SERVICE"
+ <permission android:name="android.permission.BIND_SCREENSHOT_HASHER_SERVICE"
android:protectionLevel="signature" />
<!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.
diff --git a/core/res/res/color-car/car_borderless_button_text_color.xml b/core/res/res/color-car/car_borderless_button_text_color.xml
index 1cdd6cd..0a86e40 100644
--- a/core/res/res/color-car/car_borderless_button_text_color.xml
+++ b/core/res/res/color-car/car_borderless_button_text_color.xml
@@ -16,5 +16,6 @@
<!-- Default text colors for car buttons when enabled/disabled. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@*android:color/car_grey_700" android:state_enabled="false"/>
+ <item android:color="@*android:color/car_grey_700" android:state_ux_restricted="true"/>
<item android:color="?android:attr/colorButtonNormal"/>
</selector>
diff --git a/core/res/res/color-car/car_switch_track.xml b/core/res/res/color-car/car_switch_track.xml
new file mode 100644
index 0000000..8ca67dd
--- /dev/null
+++ b/core/res/res/color-car/car_switch_track.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<!-- copy of switch_track_material, but with a ux restricted state -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="?attr/colorForeground"
+ android:alpha="?attr/disabledAlpha" />
+ <item android:state_ux_restricted="true"
+ android:color="?attr/colorForeground"
+ android:alpha="?attr/disabledAlpha" />
+ <item android:state_checked="true"
+ android:color="?attr/colorControlActivated" />
+ <item android:color="?attr/colorForeground" />
+</selector>
diff --git a/core/res/res/drawable-car/car_button_background.xml b/core/res/res/drawable-car/car_button_background.xml
index e568aeb..13b0ec1 100644
--- a/core/res/res/drawable-car/car_button_background.xml
+++ b/core/res/res/drawable-car/car_button_background.xml
@@ -25,6 +25,22 @@
android:color="#0059B3"/>
</shape>
</item>
+ <item android:state_focused="true" android:state_pressed="true" android:state_ux_restricted="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@*android:dimen/car_button_radius"/>
+ <solid android:color="@*android:color/car_grey_300"/>
+ <stroke android:width="4dp"
+ android:color="#0059B3"/>
+ </shape>
+ </item>
+ <item android:state_focused="true" android:state_ux_restricted="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@*android:dimen/car_button_radius"/>
+ <solid android:color="@*android:color/car_grey_300"/>
+ <stroke android:width="8dp"
+ android:color="#0059B3"/>
+ </shape>
+ </item>
<item android:state_focused="true" android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="@*android:dimen/car_button_radius"/>
@@ -47,6 +63,12 @@
<solid android:color="@*android:color/car_grey_300"/>
</shape>
</item>
+ <item android:state_ux_restricted="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@*android:dimen/car_button_radius"/>
+ <solid android:color="@*android:color/car_grey_300"/>
+ </shape>
+ </item>
<item>
<ripple android:color="?android:attr/colorControlHighlight">
<item>
diff --git a/core/res/res/drawable-car/car_switch_track.xml b/core/res/res/drawable-car/car_switch_track.xml
index cb0b9be..51e9f7e 100644
--- a/core/res/res/drawable-car/car_switch_track.xml
+++ b/core/res/res/drawable-car/car_switch_track.xml
@@ -41,7 +41,7 @@
android:right="@dimen/car_switch_track_margin_size">
<shape
android:shape="rectangle"
- android:tint="@color/switch_track_material">
+ android:tint="@color/car_switch_track">
<corners android:radius="7dp" />
<solid android:color="@color/white_disabled_material" />
<size android:height="14dp" />
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 39e1bbb..9e1692f 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -22,7 +22,8 @@
android:importantForAccessibility="noHideDescendants"
android:clickable="true">
- <ImageView android:id="@+id/work_widget_app_icon"
+ <com.android.internal.widget.DisableImageView
+ android:id="@+id/work_widget_app_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 26fb8b8..14df775 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -87,6 +87,14 @@
theme does not set this value, meaning it is based on whether the
window is floating. -->
<attr name="backgroundDimEnabled" format="boolean" />
+ <!-- When windowBlurBehindEnabled is set, this is the amount of blur to apply
+ behind the window. The range is from 0, which means no blur, to 150. -->
+ <attr name="windowBlurBehindRadius" format="dimension"/>
+ <!-- If set, everything behind the window will be blurred with radius
+ windowBackgroundBlurRadius. -->
+ <attr name="windowBlurBehindEnabled" format="boolean" />
+
+
<!-- Color of background imagery used for popup windows. -->
<attr name="colorPopupBackground" format="color" />
<!-- Color used for list divider. -->
@@ -1964,6 +1972,8 @@
<attr name="textColor" />
<attr name="backgroundDimEnabled" />
<attr name="backgroundDimAmount" />
+ <attr name="windowBlurBehindEnabled" />
+ <attr name="windowBlurBehindRadius" />
<attr name="windowActionBar" />
<attr name="windowActionModeOverlay" />
<attr name="windowActionBarOverlay" />
@@ -2181,10 +2191,6 @@
the decor view. -->
<attr name="windowLightNavigationBar" format="boolean" />
- <!-- @hide -->
- <attr name="windowBackgroundBlurRadius" format="dimension"/>
-
-
<!-- Controls how the window is laid out if there is a {@code DisplayCutout}.
<p>
Defaults to {@code default}.
@@ -4062,6 +4068,12 @@
<attr name="hand_hour" format="reference"/>
<attr name="hand_minute" format="reference"/>
<attr name="hand_second" format="reference"/>
+ <!-- Specifies the time zone to use. When this attribute is specified, the
+ TextClock will ignore the time zone of the system. To use the user's
+ time zone, do not specify this attribute. The default value is the
+ user's time zone. Please refer to {@link java.util.TimeZone} for more
+ information about time zone ids. -->
+ <attr name="timeZone" format="string"/>
</declare-styleable>
<declare-styleable name="Button">
</declare-styleable>
@@ -8428,6 +8440,9 @@
<attr name="errorColor" format="color|reference" />
<!-- The success color -->
<attr name="successColor" format="color|reference"/>
+ <!-- The dot color -->
+ <attr name="dotColor" format="color|reference"/>
+
</declare-styleable>
<!-- =============================== -->
@@ -8485,6 +8500,9 @@
interaction requests from an Activity. This flag is new in
{@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->
<attr name="supportsLocalInteraction" format="boolean" />
+ <!-- The service that provides {@link android.service.voice.HotwordDetectionService}.
+ @hide @SystemApi -->
+ <attr name="hotwordDetectionService" format="string" />
</declare-styleable>
<!-- Use <code>voice-enrollment-application</code>
@@ -9186,6 +9204,9 @@
<!-- @hide From Theme.colorBackground, used for the TaskDescription background
color. -->
<attr name="colorBackground" />
+ <!-- @hide From Theme.colorBackgroundFloating, used for the TaskDescription background
+ color floating. -->
+ <attr name="colorBackgroundFloating" />
<!-- @hide From Theme.statusBarColor, used for the TaskDescription status bar color. -->
<attr name="statusBarColor"/>
<!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar
diff --git a/core/res/res/values/attrs_car.xml b/core/res/res/values/attrs_car.xml
new file mode 100644
index 0000000..6bfea97
--- /dev/null
+++ b/core/res/res/values/attrs_car.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Formatting note: terminate all comments with a period, to avoid breaking
+ the documentation output. To suppress comment lines from the documentation
+ output, insert an eat-comment element after the comment lines.
+-->
+
+<resources>
+ <attr name="state_ux_restricted" format="boolean"/>
+</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c16588c..b4e580a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1580,6 +1580,13 @@
<enum name="always" value="1" />
</attr>
+ <attr name="memtagMode">
+ <enum name="default" value="-1" />
+ <enum name="off" value="0" />
+ <enum name="async" value="1" />
+ <enum name="sync" value="2" />
+ </attr>
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -1847,6 +1854,10 @@
<attr name="gwpAsanMode" />
+ <attr name="memtagMode" />
+
+ <attr name="nativeHeapZeroInit" format="boolean" />
+
<!-- @hide no longer used, kept to preserve padding -->
<attr name="allowAutoRevokePermissionsExemption" format="boolean" />
@@ -2417,6 +2428,8 @@
<!-- Required name of the process that is allowed -->
<attr name="process" />
<attr name="gwpAsanMode" />
+ <attr name="memtagMode" />
+ <attr name="nativeHeapZeroInit" />
</declare-styleable>
<!-- The <code>deny-permission</code> tag specifies that a permission is to be denied
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 4f920da..20a5d37 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -255,7 +255,7 @@
<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">#777777</color>
+ <color name="system_main_500">#757575</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">#5e5e5e</color>
@@ -292,7 +292,7 @@
<color name="system_accent_400">#1fa293</color>
<!-- Shade of the accent system color at 50% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_500">#00877a</color>
+ <color name="system_accent_500">#008377</color>
<!-- Shade of the accent system color at 40% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent_600">#006d61</color>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 6e9eb82..c8cccc4 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -17,9 +17,9 @@
<!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
overlaying new theme colors. -->
<resources>
- <color name="primary_device_default_dark">@color/system_main_900</color>
+ <color name="primary_device_default_dark">@color/system_main_800</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">@color/system_main_800</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>
@@ -37,17 +37,24 @@
<color name="accent_device_default_dark">@color/system_accent_200</color>
<color name="accent_device_default">@color/accent_device_default_light</color>
- <color name="background_device_default_dark">@color/system_main_900</color>
+ <color name="background_device_default_dark">@color/system_main_800</color>
<color name="background_device_default_light">@color/system_main_50</color>
- <color name="background_floating_device_default_dark">@color/system_main_800</color>
+ <color name="background_floating_device_default_dark">@color/system_main_900</color>
<color name="background_floating_device_default_light">@color/system_main_100</color>
+ <color name="text_color_primary_device_default_light">@color/system_main_900</color>
+ <color name="text_color_primary_device_default_dark">@color/system_main_50</color>
+ <color name="text_color_secondary_device_default_light">@color/system_main_700</color>
+ <color name="text_color_secondary_device_default_dark">@color/system_main_200</color>
+ <color name="text_color_tertiary_device_default_light">@color/system_main_500</color>
+ <color name="text_color_tertiary_device_default_dark">@color/system_main_400</color>
+
<!-- Error color -->
<color name="error_color_device_default_dark">@color/error_color_material_dark</color>
<color name="error_color_device_default_light">@color/error_color_material_light</color>
- <color name="list_divider_color_light">#ffdadce0</color>
- <color name="list_divider_color_dark">#85ffffff</color>
+ <color name="list_divider_color_light">@color/system_main_500</color>
+ <color name="list_divider_color_dark">@color/system_main_400</color>
<color name="list_divider_opacity_device_default_light">@android:color/white</color>
<color name="list_divider_opacity_device_default_dark">@android:color/white</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5d14328..3790aa4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -663,9 +663,15 @@
The default is false. -->
<bool name="config_lidControlsSleep">false</bool>
- <!-- The device state (supplied by DeviceStateManager) that should be treated as folded by the
- display fold controller. Default is DeviceStateManager.INVALID_DEVICE_STATE. -->
- <integer name="config_foldedDeviceState">-1</integer>
+ <!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
+ display fold controller. Default is empty. -->
+ <integer-array name="config_foldedDeviceStates">
+ <!-- Example:
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ -->
+ </integer-array>
<!-- Indicate the display area rect for foldable devices in folded state. -->
<string name="config_foldedArea"></string>
@@ -825,28 +831,21 @@
<!-- B y-intercept --> <item>-0.198650895</item>
</string-array>
- <string-array name="config_reduceBrightColorsCoefficientsNative">
- <!-- R a-coefficient --> <item>-0.691218457</item>
- <!-- R b-coefficient --> <item>0.050135153</item>
- <!-- R y-intercept --> <item>0.917684143</item>
- <!-- G a-coefficient --> <item>-0.691218457</item>
- <!-- G b-coefficient --> <item>0.050135153</item>
- <!-- G y-intercept --> <item>0.917684143</item>
- <!-- B a-coefficient --> <item>-0.691218457</item>
- <!-- B b-coefficient --> <item>0.050135153</item>
- <!-- B y-intercept --> <item>0.917684143</item>
+ <!-- Control whether bright color reduction is available. This should only be enabled on devices
+ that have a HWC implementation that can apply the matrix passed to setColorTransform
+ without impacting power, performance, and app compatibility (e.g. protected content). -->
+ <bool name="config_reduceBrightColorsAvailable">@bool/config_setColorTransformAccelerated</bool>
+
+ <string-array name="config_reduceBrightColorsCoefficientsNonlinear">
+ <!-- a-coefficient --> <item>-0.4429953456</item>
+ <!-- b-coefficient --> <item>-0.2434077725</item>
+ <!-- y-intercept --> <item>0.9809063061</item>
</string-array>
<string-array name="config_reduceBrightColorsCoefficients">
- <!-- R a-coefficient --> <item>0.00000000000000154</item>
- <!-- R b-coefficient --> <item>-1.0</item>
- <!-- R y-intercept --> <item>1.045977011</item>
- <!-- G a-coefficient --> <item>0.00000000000000224</item>
- <!-- G b-coefficient --> <item>-1.0</item>
- <!-- G y-intercept --> <item>1.045977011</item>
- <!-- B a-coefficient --> <item>0.0000000000000022</item>
- <!-- B b-coefficient --> <item>-1.0</item>
- <!-- B y-intercept --> <item>1.045977011</item>
+ <!-- a-coefficient --> <item>-0.000000000000001</item>
+ <!-- b-coefficient --> <item>-0.955555555555554</item>
+ <!-- y-intercept --> <item>1.000000000000000</item>
</string-array>
<!-- Boolean indicating whether display white balance is supported. -->
@@ -4407,6 +4406,15 @@
<!--If true, allows the device to load udfps components on older HIDL implementations -->
<bool name="allow_test_udfps" translatable="false" >false</bool>
+ <!-- The properties of a UDFPS sensor in pixels, in the order listed below: -->
+ <integer-array name="config_udfps_sensor_props" translatable="false" >
+ <!--
+ <item>sensorLocationX</item>
+ <item>sensorLocationY</item>
+ <item>sensorRadius</item>
+ -->
+ </integer-array>
+
<!-- Messages that should not be shown to the user during face auth enrollment. This should be
used to hide messages that may be too chatty or messages that the user can't do much about.
Entries are defined in android.hardware.biometrics.face@1.0 types.hal -->
@@ -4648,6 +4656,23 @@
corners of the activity won't be rounded. -->
<integer name="config_letterboxActivityCornersRadius">0</integer>
+ <!-- Corners appearance of the letterbox background.
+ 0 - Solid background using color specified in R.color.config_letterboxBackgroundColor.
+ 1 - Color specified in R.attr.colorBackground for the letterboxed application.
+ 2 - Color specified in R.attr.colorBackgroundFloating for the letterboxed application.
+ If given value is outside of this range, the option 0 will be assummed. -->
+ <integer name="config_letterboxBackgroundType">0</integer>
+
+ <!-- Color of the letterbox background if one following conditions is true
+ - Option 0 is selected for R.integer.config_letterboxBackgroundType.
+ - Option 1 is selected for R.integer.config_letterboxBackgroundType and
+ R.attr.colorBackground isn't specified for the app.
+ - Option 2 is selected for R.integer.config_letterboxBackgroundType and
+ R.attr.colorBackgroundFloating isn't specified for the app.
+ Defaults to black if not specified.
+ -->
+ <color name="config_letterboxBackgroundColor">#000</color>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
@@ -4682,4 +4707,7 @@
<!-- Whether to select voice/data/sms preference without user confirmation -->
<bool name="config_voice_data_sms_auto_fallback">false</bool>
+
+ <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
+ <bool name="config_enableOneHandedKeyguard">false</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 3ae2131..cb16af5 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -622,9 +622,9 @@
aliasing effects). This is only used on circular displays. -->
<dimen name="circular_display_mask_thickness">1px</dimen>
- <dimen name="lock_pattern_dot_line_width">3dp</dimen>
- <dimen name="lock_pattern_dot_size">12dp</dimen>
- <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
+ <dimen name="lock_pattern_dot_line_width">22dp</dimen>
+ <dimen name="lock_pattern_dot_size">14dp</dimen>
+ <dimen name="lock_pattern_dot_size_activated">30dp</dimen>
<dimen name="text_handle_min_size">40dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2381fc6..30cfb89 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3048,8 +3048,8 @@
<public name="allowClickWhenDisabled" />
<public name="windowLayoutAffinity" />
<public name="canPauseRecording" />
- <!-- @hide -->
- <public name="windowBackgroundBlurRadius"/>
+ <public name="windowBlurBehindRadius"/>
+ <public name="windowBlurBehindEnabled"/>
<public name="requireDeviceScreenOn" />
<public name="pathSuffix" />
<public name="sspSuffix" />
@@ -3057,6 +3057,10 @@
<public name="sspAdvancedPattern" />
<public name="fontProviderSystemFontFamily" />
<public name="hand_second" />
+ <public name="memtagMode" />
+ <public name="nativeHeapZeroInit" />
+ <!-- @hide @SystemApi -->
+ <public name="hotwordDetectionService" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 98b36c5..f6d1b7d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1585,6 +1585,8 @@
<!-- Template to be used to name enrolled fingerprints by default. -->
<string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
+ <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] -->
+ <string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string>
<!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
<string-array name="fingerprint_error_vendor">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 527fab5..ae5e2f5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2488,6 +2488,7 @@
<java-symbol type="string" name="fingerprint_error_lockout" />
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
<java-symbol type="string" name="fingerprint_name_template" />
+ <java-symbol type="string" name="fingerprint_dialog_default_subtitle" />
<java-symbol type="string" name="fingerprint_authenticated" />
<java-symbol type="string" name="fingerprint_error_no_fingerprints" />
<java-symbol type="string" name="fingerprint_error_hw_not_present" />
@@ -2541,6 +2542,7 @@
<java-symbol type="string" name="config_biometric_prompt_ui_package" />
<java-symbol type="array" name="config_biometric_sensors" />
<java-symbol type="bool" name="allow_test_udfps" />
+ <java-symbol type="array" name="config_udfps_sensor_props" />
<java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
<java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" />
@@ -3192,8 +3194,9 @@
<java-symbol type="integer" name="config_nightDisplayColorTemperatureMax" />
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" />
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
+ <java-symbol type="bool" name="config_reduceBrightColorsAvailable" />
<java-symbol type="array" name="config_reduceBrightColorsCoefficients" />
- <java-symbol type="array" name="config_reduceBrightColorsCoefficientsNative" />
+ <java-symbol type="array" name="config_reduceBrightColorsCoefficientsNonlinear" />
<java-symbol type="array" name="config_availableColorModes" />
<java-symbol type="array" name="config_mappedColorModes" />
<java-symbol type="string" name="config_vendorColorModesRestoreHint" />
@@ -3730,7 +3733,7 @@
<java-symbol type="string" name="config_customCountryDetector" />
<!-- For Foldables -->
- <java-symbol type="integer" name="config_foldedDeviceState" />
+ <java-symbol type="array" name="config_foldedDeviceStates" />
<java-symbol type="string" name="config_foldedArea" />
<java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
@@ -4135,6 +4138,8 @@
<java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
<java-symbol type="integer" name="config_letterboxActivityCornersRadius" />
+ <java-symbol type="integer" name="config_letterboxBackgroundType" />
+ <java-symbol type="color" name="config_letterboxBackgroundColor" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
@@ -4171,4 +4176,6 @@
<java-symbol type="bool" name="config_telephony5gNonStandalone" />
<java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
+
+ <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index dff6e87..ee17f6f 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -219,6 +219,9 @@
<item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
<item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
<item name="panelColorBackground">?attr/colorBackgroundFloating</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
</style>
<style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -230,6 +233,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -258,6 +264,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -288,6 +297,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -317,6 +329,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -363,6 +378,9 @@
<item name="colorError">@color/error_color_device_default_dark</item>
<item name="colorBackground">@color/background_device_default_dark</item>
<item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -384,6 +402,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -411,6 +432,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -439,6 +463,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -483,6 +510,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -512,6 +542,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -539,6 +572,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -568,6 +604,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -596,6 +635,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -624,6 +666,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -652,6 +697,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -680,6 +728,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -712,6 +763,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Text styles -->
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
@@ -741,6 +795,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -767,6 +824,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -949,6 +1009,9 @@
<item name="colorError">@color/error_color_device_default_light</item>
<item name="colorBackground">@color/background_device_default_light</item>
<item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
<item name="panelColorBackground">?attr/colorBackgroundFloating</item>
</style>
@@ -961,6 +1024,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -988,6 +1054,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1016,6 +1085,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1046,6 +1118,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1075,6 +1150,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1123,6 +1201,9 @@
<item name="colorError">@color/error_color_device_default_light</item>
<item name="colorBackground">@color/background_device_default_light</item>
<item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Progress bar attributes -->
<item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
@@ -1143,6 +1224,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1173,6 +1257,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1204,6 +1291,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1236,6 +1326,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -1250,6 +1343,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -1263,6 +1359,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1295,6 +1394,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1325,6 +1427,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1354,6 +1459,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1382,6 +1490,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1410,6 +1521,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1436,6 +1550,9 @@
<item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1552,6 +1669,9 @@
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_dark</item>
<item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1579,6 +1699,9 @@
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
@@ -1616,6 +1739,9 @@
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1646,6 +1772,9 @@
<item name="colorSecondary">@color/secondary_device_default_settings</item>
<item name="colorAccent">@color/accent_device_default_light</item>
<item name="colorError">@color/error_color_device_default_light</item>
+ <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
+ <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
+ <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
index e0d159b..9e6827c 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
@@ -63,7 +63,9 @@
private final List<Entry> mEntries = new ArrayList<>();
public BatteryConsumerData(Context context, BatteryStatsHelper batteryStatsHelper,
- BatteryUsageStats batteryUsageStats, String batteryConsumerId) {
+ List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) {
+ BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
+ BatteryUsageStats powerProfileModeledUsageStats = batteryUsageStatsList.get(1);
List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
BatteryStats batteryStats = batteryStatsHelper.getStats();
@@ -142,16 +144,22 @@
}
BatteryConsumer requestedBatteryConsumer = null;
- double totalModeledCpuPowerMah = 0;
for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
requestedBatteryConsumer = consumer;
}
+ }
- totalModeledCpuPowerMah += consumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
- + BatteryConsumer.POWER_COMPONENT_CPU);
+ double totalModeledCpuPowerMah = 0;
+ BatteryConsumer requestedBatteryConsumerPowerProfileModeled = null;
+ for (BatteryConsumer consumer : powerProfileModeledUsageStats.getUidBatteryConsumers()) {
+ if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
+ requestedBatteryConsumerPowerProfileModeled = consumer;
+ }
+
+ totalModeledCpuPowerMah += consumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU);
}
if (requestedBatterySipper == null) {
@@ -196,11 +204,10 @@
addEntry("CPU", EntryType.POWER,
requestedBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU),
totalCpuPowerMah);
- if (totalModeledCpuPowerMah != 0) {
+ if (requestedBatteryConsumerPowerProfileModeled != null) {
addEntry("CPU (modeled)", EntryType.POWER,
- requestedBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
- + BatteryConsumer.POWER_COMPONENT_CPU),
+ requestedBatteryConsumerPowerProfileModeled.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU),
totalModeledCpuPowerMah);
}
} else {
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
index 87a175a..4ead8ee 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
@@ -68,7 +68,7 @@
private ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult(
BatteryConsumerPickerActivity.CONTRACT, this::onApplicationSelected);
private BatteryStatsHelper mBatteryStatsHelper;
- private BatteryUsageStats mBatteryUsageStats;
+ private List<BatteryUsageStats> mBatteryUsageStats;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -188,7 +188,8 @@
}
}
- private static class BatteryUsageStatsLoader extends AsyncLoaderCompat<BatteryUsageStats> {
+ private static class BatteryUsageStatsLoader extends
+ AsyncLoaderCompat<List<BatteryUsageStats>> {
private final BatteryStatsManager mBatteryStatsManager;
BatteryUsageStatsLoader(Context context) {
@@ -197,33 +198,38 @@
}
@Override
- public BatteryUsageStats loadInBackground() {
- final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
- .includeModeled()
- .build();
- return mBatteryStatsManager.getBatteryUsageStats(query);
+ public List<BatteryUsageStats> loadInBackground() {
+ final BatteryUsageStatsQuery queryDefault =
+ new BatteryUsageStatsQuery.Builder().build();
+ final BatteryUsageStatsQuery queryPowerProfileModeledOnly =
+ new BatteryUsageStatsQuery.Builder()
+ .powerProfileModeledOnly()
+ .build();
+ return mBatteryStatsManager.getBatteryUsageStats(
+ List.of(queryDefault, queryPowerProfileModeledOnly));
}
@Override
- protected void onDiscardResult(BatteryUsageStats result) {
+ protected void onDiscardResult(List<BatteryUsageStats> result) {
}
}
- private class BatteryUsageStatsLoaderCallbacks implements LoaderCallbacks<BatteryUsageStats> {
+ private class BatteryUsageStatsLoaderCallbacks
+ implements LoaderCallbacks<List<BatteryUsageStats>> {
@NonNull
@Override
- public Loader<BatteryUsageStats> onCreateLoader(int id, Bundle args) {
+ public Loader<List<BatteryUsageStats>> onCreateLoader(int id, Bundle args) {
return new BatteryUsageStatsLoader(BatteryStatsViewerActivity.this);
}
@Override
- public void onLoadFinished(@NonNull Loader<BatteryUsageStats> loader,
- BatteryUsageStats batteryUsageStats) {
+ public void onLoadFinished(@NonNull Loader<List<BatteryUsageStats>> loader,
+ List<BatteryUsageStats> batteryUsageStats) {
onBatteryUsageStatsLoaded(batteryUsageStats);
}
@Override
- public void onLoaderReset(@NonNull Loader<BatteryUsageStats> loader) {
+ public void onLoaderReset(@NonNull Loader<List<BatteryUsageStats>> loader) {
}
}
@@ -232,7 +238,7 @@
onBatteryStatsDataLoaded();
}
- private void onBatteryUsageStatsLoaded(BatteryUsageStats batteryUsageStats) {
+ private void onBatteryUsageStatsLoaded(List<BatteryUsageStats> batteryUsageStats) {
mBatteryUsageStats = batteryUsageStats;
onBatteryStatsDataLoaded();
}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 151a320..f31233b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -35,8 +35,6 @@
android:label="@string/permlab_testDenied"
android:description="@string/permdesc_testDenied" />
- <uses-permission android:name="com.android.frameworks.coretests.permission.TEST_GRANTED" />
-
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
@@ -78,6 +76,7 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SMS"/>
+ <uses-permission android:name="android.permission.TEST_GRANTED" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
@@ -148,6 +147,9 @@
<!-- WindowMetricsTest permissions -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <!-- WindowContextTest permissions -->
+ <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+
<!-- Allow use of PendingIntent.getIntent() -->
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
@@ -1456,7 +1458,6 @@
</intent-filter>
</receiver>
<receiver android:name="android.app.activity.RemoteGrantedReceiver"
- android:process=":remoteReceiver"
android:permission="com.android.frameworks.coretests.permission.TEST_GRANTED"
android:exported="true">
<intent-filter android:priority="2">
@@ -1464,7 +1465,6 @@
</intent-filter>
</receiver>
<receiver android:name="android.app.activity.RemoteDeniedReceiver"
- android:process=":remoteReceiver"
android:permission="com.android.frameworks.coretests.permission.TEST_DENIED"
android:exported="true">
<intent-filter android:priority="2">
diff --git a/core/tests/coretests/assets/fonts/OWNERS b/core/tests/coretests/assets/fonts/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/OWNERS b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/res/font/OWNERS b/core/tests/coretests/res/font/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/res/font/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
index dcf5e02..48b58c6 100644
--- a/core/tests/coretests/src/android/app/WindowContextTest.java
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -18,29 +18,38 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.content.Intent;
import android.hardware.display.DisplayManager;
+import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerImpl;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for {@link WindowContext}
*
@@ -54,20 +63,14 @@
@SmallTest
@Presubmit
public class WindowContextTest {
+ @Rule
+ public ActivityTestRule<EmptyActivity> mActivityRule =
+ new ActivityTestRule<>(EmptyActivity.class, false /* initialTouchMode */,
+ false /* launchActivity */);
+
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final WindowContext mWindowContext = createWindowContext();
-
- @Test
- public void testWindowContextRelease_doRemoveWindowToken() throws Throwable {
- final IBinder token = mWindowContext.getWindowContextToken();
- final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-
- assertTrue("Token must be registered to WMS", wms.isWindowToken(token));
-
- mWindowContext.release();
-
- assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token));
- }
+ private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService();
@Test
public void testCreateWindowContextWindowManagerAttachClientToken() {
@@ -83,12 +86,134 @@
assertEquals(mWindowContext.getWindowContextToken(), params.mWindowContextToken);
}
+ /**
+ * Test the {@link WindowContext} life cycle behavior to add a new window token:
+ * <ul>
+ * <li>The window token is created before adding the first view.</li>
+ * <li>The window token is registered after adding the first view.</li>
+ * <li>The window token is removed after {@link WindowContext}'s release.</li>
+ * </ul>
+ */
+ @Test
+ public void testCreateWindowContextNewTokenFromClient() throws Throwable {
+ final IBinder token = mWindowContext.getWindowContextToken();
+
+ // Test that the window token is not created yet.
+ assertFalse("Token must not be registered until adding the first window",
+ mWms.isWindowToken(token));
+
+ final WindowManager.LayoutParams params =
+ new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ final View testView = new View(mWindowContext);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ testView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ latch.countDown();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {}
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+ assertEquals(token, params.mWindowContextToken);
+ });
+
+
+ assertTrue(latch.await(4, TimeUnit.SECONDS));
+
+
+ // Verify that the window token of the window context is created after first addView().
+ assertTrue("Token must exist after adding the first view.",
+ mWms.isWindowToken(token));
+
+ mWindowContext.release();
+
+ // After the window context's release, the window token is also removed.
+ assertFalse("Token must be removed after release.", mWms.isWindowToken(token));
+ }
+
+ /**
+ * Verifies the behavior when window context attaches an {@link Activity} by override
+ * {@link WindowManager.LayoutParams#token}.
+ *
+ * The window context token should be overridden to
+ * {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must
+ * not be removed regardless of the release of window context.
+ */
+ @Test
+ public void testCreateWindowContext_AttachActivity_TokenNotRemovedAfterRelease()
+ throws Throwable {
+ mActivityRule.launchActivity(new Intent());
+ final Activity activity = mActivityRule.getActivity();
+ final WindowManager.LayoutParams params = activity.getWindow().getAttributes();
+
+ final WindowContext windowContext = createWindowContext(params.type);
+ final IBinder token = windowContext.getWindowContextToken();
+
+ final View testView = new View(windowContext);
+
+ mInstrumentation.runOnMainSync(() -> {
+ windowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+ assertEquals(token, params.mWindowContextToken);
+ });
+ windowContext.release();
+
+ // Even if the window context is released, the activity should still exist.
+ assertTrue("Token must exist even if the window context is released.",
+ mWms.isWindowToken(activity.getActivityToken()));
+ }
+
+ /**
+ * Verifies the behavior when window context attaches an existing token by override
+ * {@link WindowManager.LayoutParams#token}.
+ *
+ * The window context token should be overridden to
+ * {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must not be
+ * removed regardless of release of window context.
+ */
+ @Test
+ public void testCreateWindowContext_AttachWindowToken_TokenNotRemovedAfterRelease()
+ throws Throwable {
+ final WindowContext windowContext = createWindowContext(TYPE_INPUT_METHOD);
+ final IBinder token = windowContext.getWindowContextToken();
+
+ final IBinder existingToken = new Binder();
+ mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId(),
+ null /* options */);
+
+ final WindowManager.LayoutParams params =
+ new WindowManager.LayoutParams(TYPE_INPUT_METHOD);
+ params.token = existingToken;
+ final View testView = new View(windowContext);
+
+ mInstrumentation.runOnMainSync(() -> {
+ windowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+ assertEquals(token, params.mWindowContextToken);
+ });
+ windowContext.release();
+
+ // Even if the window context is released, the existing token should still exist.
+ assertTrue("Token must exist even if the window context is released.",
+ mWms.isWindowToken(existingToken));
+
+ mWms.removeWindowToken(existingToken, DEFAULT_DISPLAY);
+ }
+
private WindowContext createWindowContext() {
+ return createWindowContext(TYPE_APPLICATION_OVERLAY);
+ }
+
+ private WindowContext createWindowContext(@WindowType int type) {
final Context instContext = mInstrumentation.getTargetContext();
final Display display = instContext.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY);
- final Context context = instContext.createDisplayContext(display);
- return new WindowContext(context, TYPE_APPLICATION_OVERLAY,
- null /* options */);
+ return (WindowContext) instContext.createWindowContext(display, type, null /* options */);
}
}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
index 0c351d1..81eb213 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -132,7 +132,8 @@
true, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_RESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
TaskDescription td2 = new TaskDescription();
@@ -155,7 +156,8 @@
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
TaskDescription td2 = new TaskDescription(
@@ -169,7 +171,8 @@
true, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_RESIZEABLE, // resizeMode
102, // minWidth
- 202 // minHeight
+ 202, // minHeight
+ 0 // colorBackgroundFloating
);
// Must overwrite all public and hidden fields, since other has all fields set.
@@ -198,7 +201,8 @@
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
// Normal parceling should keep everything the same.
@@ -219,7 +223,8 @@
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
10, // minWidth
- 20 // minHeight
+ 20, // minHeight
+ 0 // colorBackgroundFloating
);
// Recycled bitmap will be ignored while parceling.
tdParcelled = new TaskDescription(parcelingRoundTrip(tdBitmapRecycled));
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index d79c2fe..0f81896 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -56,8 +56,6 @@
"com.android.frameworks.coretests.activity.BROADCAST_MULTI";
public static final String BROADCAST_ABORT =
"com.android.frameworks.coretests.activity.BROADCAST_ABORT";
- public static final String BROADCAST_RESULT =
- "com.android.frameworks.coretests.activity.BROADCAST_RESULT";
public static final String BROADCAST_STICKY1 =
"com.android.frameworks.coretests.activity.BROADCAST_STICKY1";
@@ -108,14 +106,7 @@
}
public Intent makeBroadcastIntent(String action) {
- return makeBroadcastIntent(action, false);
- }
-
- public Intent makeBroadcastIntent(String action, boolean makeImplicit) {
Intent intent = new Intent(action, null);
- if (makeImplicit) {
- intent.addFlags(intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- }
intent.putExtra("caller", mCallTarget);
return intent;
}
@@ -286,7 +277,7 @@
map.putString("foo", "you");
map.putString("remove", "me");
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_RESULT, true),
+ new Intent("com.android.frameworks.coretests.activity.BROADCAST_RESULT"),
null, broadcastReceiver, null, 1, "foo", map);
while (!broadcastReceiver.mHaveResult) {
try {
@@ -433,13 +424,10 @@
public void testLocalReceivePermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED, true));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- /*
- // TODO: multi-package test b/c self-target broadcasts are always allowed
- // even when gated on ungranted permissions
public void testLocalReceivePermissionDenied() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_RESULTS});
@@ -450,17 +438,16 @@
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_LOCAL_DENIED, true),
+ makeBroadcastIntent(BROADCAST_LOCAL_DENIED),
null, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- */
public void testLocalBroadcastPermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
getContext().sendBroadcast(
- makeBroadcastIntent(BROADCAST_LOCAL, true),
+ makeBroadcastIntent(BROADCAST_LOCAL),
PERMISSION_GRANTED);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
@@ -475,7 +462,7 @@
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_LOCAL, true),
+ makeBroadcastIntent(BROADCAST_LOCAL),
PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
@@ -483,13 +470,10 @@
public void testRemoteReceivePermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED, true));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- /*
- // TODO: multi-package test b/c self-target broadcasts are always allowed
- // even when gated on ungranted permissions
public void testRemoteReceivePermissionDenied() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_RESULTS});
@@ -500,17 +484,16 @@
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_REMOTE_DENIED, true),
+ makeBroadcastIntent(BROADCAST_REMOTE_DENIED),
null, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- */
public void testRemoteBroadcastPermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
getContext().sendBroadcast(
- makeBroadcastIntent(BROADCAST_REMOTE, true),
+ makeBroadcastIntent(BROADCAST_REMOTE),
PERMISSION_GRANTED);
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
@@ -525,7 +508,7 @@
};
getContext().sendOrderedBroadcast(
- makeBroadcastIntent(BROADCAST_REMOTE, true),
+ makeBroadcastIntent(BROADCAST_REMOTE),
PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
null, null);
waitForResultOrThrow(BROADCAST_TIMEOUT);
@@ -533,13 +516,13 @@
public void testReceiverCanNotRegister() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER, true));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
public void testReceiverCanNotBind() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND, true));
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND));
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
diff --git a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
index 0b21fa9..7662456 100644
--- a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
+++ b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
@@ -253,16 +253,16 @@
sendBroadcast(makeBroadcastIntent(BROADCAST_REGISTERED));
} else if (BROADCAST_LOCAL.equals(action)) {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
- sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL, true));
+ sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL));
} else if (BROADCAST_REMOTE.equals(action)) {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
- sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE, true));
+ sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE));
} else if (BROADCAST_ALL.equals(action)) {
setExpectedReceivers(new String[]{
RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL});
registerMyReceiver(new IntentFilter(BROADCAST_ALL));
sCallingTest.addIntermediate("after-register");
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL, true), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
} else if (BROADCAST_MULTI.equals(action)) {
setExpectedReceivers(new String[]{
RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
@@ -277,26 +277,23 @@
RECEIVER_REMOTE, RECEIVER_LOCAL});
registerMyReceiver(new IntentFilter(BROADCAST_ALL));
sCallingTest.addIntermediate("after-register");
- final Intent allIntent = makeBroadcastIntent(BROADCAST_ALL, true);
- final Intent localIntent = makeBroadcastIntent(BROADCAST_LOCAL, true);
- final Intent remoteIntent = makeBroadcastIntent(BROADCAST_REMOTE, true);
- sendOrderedBroadcast(allIntent, null);
- sendOrderedBroadcast(allIntent, null);
- sendOrderedBroadcast(allIntent, null);
- sendOrderedBroadcast(localIntent, null);
- sendOrderedBroadcast(remoteIntent, null);
- sendOrderedBroadcast(localIntent, null);
- sendOrderedBroadcast(remoteIntent, null);
- sendOrderedBroadcast(allIntent, null);
- sendOrderedBroadcast(allIntent, null);
- sendOrderedBroadcast(allIntent, null);
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT, true), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT), null);
} else if (BROADCAST_ABORT.equals(action)) {
setExpectedReceivers(new String[]{
RECEIVER_REMOTE, RECEIVER_ABORT});
registerMyReceiver(new IntentFilter(BROADCAST_ABORT));
sCallingTest.addIntermediate("after-register");
- sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT, true), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT), null);
} else if (BROADCAST_STICKY1.equals(action)) {
setExpectedReceivers(new String[]{RECEIVER_REG});
setExpectedData(new String[]{DATA_1});
@@ -439,14 +436,7 @@
}
private Intent makeBroadcastIntent(String action) {
- return makeBroadcastIntent(action, false);
- }
-
- private Intent makeBroadcastIntent(String action, boolean makeImplicit) {
Intent intent = new Intent(action, null);
- if (makeImplicit) {
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- }
intent.putExtra("caller", mCallTarget);
return intent;
}
diff --git a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
index 16ea73f..2120a1d 100644
--- a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
@@ -23,7 +23,7 @@
import android.os.IBinder;
import android.os.Parcel;
-public class LocalDeniedReceiver extends BroadcastReceiver {
+class LocalDeniedReceiver extends BroadcastReceiver {
public LocalDeniedReceiver() {
}
diff --git a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
index 5c1ded9..7c89346 100644
--- a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
@@ -23,7 +23,7 @@
import android.os.IBinder;
import android.os.Parcel;
-public class RemoteDeniedReceiver extends BroadcastReceiver {
+class RemoteDeniedReceiver extends BroadcastReceiver {
public RemoteDeniedReceiver() {
}
diff --git a/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
new file mode 100644
index 0000000..be38260
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ContentValues;
+import android.os.Parcel;
+import android.provider.SimPhonebookContract.SimRecords.NameValidationResult;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SimPhonebookContractTest {
+
+ @Test
+ public void getContentUri_invalidEfType_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getContentUri(1, 100)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getContentUri(1, -1)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getContentUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_UNKNOWN)
+ );
+ }
+
+ @Test
+ public void getItemUri_invalidEfType_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1, 100, 1)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1, -1, 1)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_UNKNOWN, 1)
+ );
+ }
+
+ @Test
+ public void getItemUri_invalidRecordIndex_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_ADN, 0)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_ADN, -1)
+ );
+ }
+
+ @Test
+ public void nameValidationResult_isValid_validNames() {
+ assertThat(new NameValidationResult("", "", 0, 1).isValid()).isTrue();
+ assertThat(new NameValidationResult("a", "a", 1, 1).isValid()).isTrue();
+ assertThat(new NameValidationResult("First Last", "First Last", 10, 10).isValid()).isTrue();
+ assertThat(
+ new NameValidationResult("First Last", "First Last", 10, 100).isValid()).isTrue();
+ }
+
+ @Test
+ public void nameValidationResult_isValid_invalidNames() {
+ assertThat(new NameValidationResult("", "", 0, 0).isValid()).isFalse();
+ assertThat(new NameValidationResult("ab", "ab", 2, 1).isValid()).isFalse();
+ NameValidationResult unsupportedCharactersResult = new NameValidationResult("A_b_c",
+ "A b c", 5, 5);
+ assertThat(unsupportedCharactersResult.isValid()).isFalse();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(0)).isTrue();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(1)).isFalse();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(2)).isTrue();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(3)).isFalse();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(4)).isTrue();
+ }
+
+ @Test
+ public void nameValidationResult_parcel() {
+ ContentValues values = new ContentValues();
+ values.put("name", "Name");
+ values.put("phone_number", "123");
+
+ NameValidationResult result;
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeParcelable(new NameValidationResult("name", "sanitized name", 1, 2), 0);
+ parcel.setDataPosition(0);
+ result = parcel.readParcelable(NameValidationResult.class.getClassLoader());
+ } finally {
+ parcel.recycle();
+ }
+
+ assertThat(result.getName()).isEqualTo("name");
+ assertThat(result.getSanitizedName()).isEqualTo("sanitized name");
+ assertThat(result.getEncodedLength()).isEqualTo(1);
+ assertThat(result.getMaxEncodedLength()).isEqualTo(2);
+ }
+}
+
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 9705284..cfe3803 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -27,6 +27,10 @@
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
@@ -427,6 +431,27 @@
cutout.getBoundingRectBottom());
}
+ @Test
+ public void testCalculateRelativeRoundedCorners() {
+ mState.setDisplayFrame(new Rect(0, 0, 200, 400));
+ mState.setRoundedCorners(new RoundedCorners(
+ new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10),
+ new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10),
+ new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
+ new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
+ WindowInsets windowInsets = mState.calculateInsets(new Rect(1, 2, 197, 396), null, false,
+ false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
+ WINDOWING_MODE_UNDEFINED, new SparseIntArray());
+ assertEquals(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8),
+ windowInsets.getRoundedCorner(POSITION_TOP_LEFT));
+ assertEquals(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8),
+ windowInsets.getRoundedCorner(POSITION_TOP_RIGHT));
+ assertEquals(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 179, 378),
+ windowInsets.getRoundedCorner(POSITION_BOTTOM_RIGHT));
+ assertEquals(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 19, 378),
+ windowInsets.getRoundedCorner(POSITION_BOTTOM_LEFT));
+ }
+
private void assertEqualsAndHashCode() {
assertEquals(mState, mState2);
assertEquals(mState.hashCode(), mState2.hashCode());
diff --git a/core/tests/coretests/src/android/view/RoundedCornerTest.java b/core/tests/coretests/src/android/view/RoundedCornerTest.java
new file mode 100644
index 0000000..8eb13bc
--- /dev/null
+++ b/core/tests/coretests/src/android/view/RoundedCornerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Point;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RoundedCornerTest {
+
+ @Test
+ public void testGetPosition() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner.getPosition(), is(RoundedCorner.POSITION_BOTTOM_LEFT));
+ }
+
+ @Test
+ public void testGetRadius() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner.getRadius(), is(2));
+ }
+
+ @Test
+ public void testGetCenter() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner.getCenter(), equalTo(new Point(3, 4)));
+ }
+
+ @Test
+ public void testIsEmpty() {
+ RoundedCorner roundedCorner = new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+ assertThat(roundedCorner.isEmpty(), is(true));
+ }
+
+ @Test
+ public void testEquals() {
+ RoundedCorner roundedCorner = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ RoundedCorner roundedCorner2 = new RoundedCorner(
+ RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
+ assertThat(roundedCorner, equalTo(roundedCorner2));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/RoundedCornersTest.java b/core/tests/coretests/src/android/view/RoundedCornersTest.java
new file mode 100644
index 0000000..07ef33a
--- /dev/null
+++ b/core/tests/coretests/src/android/view/RoundedCornersTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RoundedCornersTest {
+
+ final RoundedCorners mRoundedCorners = new RoundedCorners(
+ new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10),
+ new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10),
+ new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
+ new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380));
+
+ @Test
+ public void testGetRoundedCorner() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
+ }
+
+ @Test
+ public void testGetRoundedCorner_noRoundedCorners() {
+ RoundedCorners roundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT), nullValue());
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT), nullValue());
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT), nullValue());
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), nullValue());
+ }
+
+ @Test
+ public void testHashCode() {
+ assertThat(mRoundedCorners.hashCode(),
+ equalTo(RoundedCorners.fromRadii(new Pair<>(10, 20), 200, 400).hashCode()));
+ assertThat(mRoundedCorners.hashCode(),
+ not(equalTo(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400).hashCode())));
+ }
+
+ @Test
+ public void testEquals() {
+ assertThat(mRoundedCorners,
+ equalTo(RoundedCorners.fromRadii(new Pair<>(10, 20), 200, 400)));
+
+ assertThat(mRoundedCorners,
+ not(equalTo(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400))));
+ }
+
+ @Test
+ public void testSetRoundedCorner() {
+ RoundedCorner roundedCorner = new RoundedCorner(POSITION_BOTTOM_LEFT, 5, 6, 7);
+ mRoundedCorners.setRoundedCorner(POSITION_BOTTOM_LEFT, roundedCorner);
+
+ assertThat(mRoundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), equalTo(roundedCorner));
+ }
+
+ @Test
+ public void testSetRoundedCorner_null() {
+ mRoundedCorners.setRoundedCorner(POSITION_BOTTOM_LEFT, null);
+
+ assertThat(mRoundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT],
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT)));
+ assertThat(mRoundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), nullValue());
+ }
+
+ @Test
+ public void testInsetRoundedCorners_partialOverlap() {
+ RoundedCorners roundedCorners = mRoundedCorners.inset(1, 2, 3, 4);
+
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_LEFT],
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8)));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_RIGHT],
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8)));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_RIGHT],
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 179, 378)));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT],
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 19, 378)));
+ }
+
+ @Test
+ public void testInsetRoundedCorners_noOverlap() {
+ RoundedCorners roundedCorners = mRoundedCorners.inset(20, 20, 20, 20);
+
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_LEFT].isEmpty(), is(true));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_RIGHT].isEmpty(), is(true));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_RIGHT].isEmpty(), is(true));
+ assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT].isEmpty(), is(true));
+ }
+
+ @Test
+ public void testRotateRoundedCorners_90() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400)
+ .rotate(ROTATION_90, 200, 400);
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 20, 380, 20)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 380, 180)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 10, 10, 190)));
+ }
+
+ @Test
+ public void testRotateRoundedCorners_270() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400)
+ .rotate(ROTATION_270, 200, 400);
+
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
+ equalTo(new RoundedCorner(POSITION_TOP_LEFT, 20, 20, 20)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
+ equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 390, 10)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 10, 390, 190)));
+ assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
+ equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 180)));
+ }
+
+ @Test
+ public void testFromRadius_cache() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(radius, 200, 400), sameInstance(cached));
+ }
+
+ @Test
+ public void testFromRadius_wontCacheIfRadiusChanged() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400),
+ not(sameInstance(cached)));
+ }
+
+ @Test
+ public void testFromRadius_wontCacheIfDisplayWidthChanged() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(radius, 100, 400),
+ not(sameInstance(cached)));
+ }
+
+ @Test
+ public void testFromRadius_wontCacheIfDisplayHeightChanged() {
+ final Pair<Integer, Integer> radius = new Pair<>(10, 20);
+ RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
+
+ assertThat(RoundedCorners.fromRadii(radius, 200, 300),
+ not(sameInstance(cached)));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 8e4e735..b80837f 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -61,7 +61,7 @@
WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0));
WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0));
WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, false, null,
- systemBars(), true /* compatIgnoreVisibility */);
+ null, systemBars(), true /* compatIgnoreVisibility */);
assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets());
}
}
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index a4284a0..47ce2d8 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -278,29 +278,29 @@
@Test
@UiThreadTest
- public void setSetImeTemporarilyConsumesInput_recoveryToVisible() {
+ public void setSetImeConsumesInput_recoveryToVisible() {
mTextView = new TextView(mActivity);
mTextView.setCursorVisible(true);
assertTrue(mTextView.isCursorVisible());
- mTextView.setImeTemporarilyConsumesInput(true);
+ mTextView.setImeConsumesInput(true);
assertFalse(mTextView.isCursorVisible());
- mTextView.setImeTemporarilyConsumesInput(false);
+ mTextView.setImeConsumesInput(false);
assertTrue(mTextView.isCursorVisible());
}
@Test
@UiThreadTest
- public void setSetImeTemporarilyConsumesInput_recoveryToInvisible() {
+ public void setSetImeConsumesInput_recoveryToInvisible() {
mTextView = new TextView(mActivity);
mTextView.setCursorVisible(false);
assertFalse(mTextView.isCursorVisible());
- mTextView.setImeTemporarilyConsumesInput(true);
+ mTextView.setImeConsumesInput(true);
assertFalse(mTextView.isCursorVisible());
- mTextView.setImeTemporarilyConsumesInput(false);
+ mTextView.setImeConsumesInput(false);
assertFalse(mTextView.isCursorVisible());
}
diff --git a/core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java b/core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java
deleted file mode 100644
index 127ecfb..0000000
--- a/core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.listeners;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import junit.framework.TestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicReference;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ListenerTransportMultiplexerTest extends TestCase {
-
- TestMultiplexer mMultiplexer;
-
- @Before
- public void setUp() {
- mMultiplexer = new TestMultiplexer();
- }
-
- @Test
- public void testAdd() {
- Runnable runnable = mock(Runnable.class);
-
- mMultiplexer.addListener(0, runnable, Runnable::run);
- assertThat(mMultiplexer.mRegistered).isTrue();
- assertThat(mMultiplexer.mMergedRequest).isEqualTo(0);
-
- mMultiplexer.notifyListeners();
- verify(runnable, times(1)).run();
- }
-
- @Test
- public void testAdd_Multiple() {
- Runnable runnable1 = mock(Runnable.class);
- Runnable runnable2 = mock(Runnable.class);
-
- mMultiplexer.addListener(0, runnable1, Runnable::run);
- mMultiplexer.addListener(0, runnable2, Runnable::run);
-
- mMultiplexer.notifyListeners();
- verify(runnable1).run();
- verify(runnable2).run();
- }
-
- @Test
- public void testRemove() {
- Runnable runnable = mock(Runnable.class);
-
- mMultiplexer.addListener(0, runnable, Runnable::run);
- mMultiplexer.removeListener(runnable);
- assertThat(mMultiplexer.mRegistered).isFalse();
-
- mMultiplexer.notifyListeners();
- verify(runnable, never()).run();
- }
-
- @Test
- public void testRemove_Multiple() {
- Runnable runnable1 = mock(Runnable.class);
- Runnable runnable2 = mock(Runnable.class);
-
- mMultiplexer.addListener(0, runnable1, Runnable::run);
- mMultiplexer.addListener(1, runnable2, Runnable::run);
- mMultiplexer.removeListener(runnable1);
-
- mMultiplexer.notifyListeners();
- verify(runnable1, never()).run();
- verify(runnable2).run();
- }
-
- @Test
- public void testMergeMultiple() {
- Runnable runnable1 = mock(Runnable.class);
- Runnable runnable2 = mock(Runnable.class);
- Runnable runnable3 = mock(Runnable.class);
-
- mMultiplexer.addListener(0, runnable1, Runnable::run);
- mMultiplexer.addListener(1, runnable2, Runnable::run);
- assertThat(mMultiplexer.mMergedRequest).isEqualTo(1);
-
- mMultiplexer.notifyListeners();
- verify(runnable1, times(1)).run();
- verify(runnable2, times(1)).run();
- verify(runnable3, times(0)).run();
-
- mMultiplexer.addListener(0, runnable3, Runnable::run);
- assertThat(mMultiplexer.mMergedRequest).isEqualTo(1);
-
- mMultiplexer.notifyListeners();
- verify(runnable1, times(2)).run();
- verify(runnable2, times(2)).run();
- verify(runnable3, times(1)).run();
-
- mMultiplexer.removeListener(runnable2);
- assertThat(mMultiplexer.mMergedRequest).isEqualTo(0);
-
- mMultiplexer.notifyListeners();
- verify(runnable1, times(3)).run();
- verify(runnable2, times(2)).run();
- verify(runnable3, times(2)).run();
-
- mMultiplexer.removeListener(runnable1);
- mMultiplexer.removeListener(runnable3);
- mMultiplexer.notifyListeners();
- verify(runnable1, times(3)).run();
- verify(runnable2, times(2)).run();
- verify(runnable3, times(2)).run();
- }
-
- @Test(timeout = 5000)
- public void testReentrancy() {
- AtomicReference<Runnable> runnable = new AtomicReference<>();
- runnable.set(() -> mMultiplexer.removeListener(runnable.get()));
-
- mMultiplexer.addListener(0, runnable.get(), command -> {
- CountDownLatch latch = new CountDownLatch(1);
- new Handler(Looper.getMainLooper()).post(() -> {
- command.run();
- latch.countDown();
- });
- try {
- latch.await();
- } catch (InterruptedException e) {
- throw new AssertionError(e);
- }
- });
-
- mMultiplexer.notifyListeners();
- assertThat(mMultiplexer.mRegistered).isFalse();
- }
-
- private static class TestMultiplexer extends ListenerTransportMultiplexer<Integer, Runnable> {
-
- boolean mRegistered;
- int mMergedRequest;
-
- TestMultiplexer() {
- }
-
- public void notifyListeners() {
- deliverToListeners(Runnable::run);
- }
-
- @Override
- protected void registerWithServer(Integer mergedRequest) {
- mRegistered = true;
- mMergedRequest = mergedRequest;
- }
-
- @Override
- protected void unregisterWithServer() {
- mRegistered = false;
- }
-
- @Override
- protected Integer mergeRequests(Collection<Integer> requests) {
- return requests.stream().max(Comparator.naturalOrder()).get();
- }
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 59534e4..2c71287 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -114,7 +114,7 @@
}
void apply(PowerCalculator calculator) {
- BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0, false);
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0);
SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats();
for (int i = 0; i < uidStats.size(); i++) {
builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 96e9c4a..018a810 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -66,21 +66,17 @@
final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000);
- final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1, true);
+ final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1);
builder.setConsumedPower(100);
builder.setDischargePercentage(20);
final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo");
- uidBatteryConsumerBuilder.setConsumedPower(200);
uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300);
uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500);
- uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
- + BatteryConsumer.POWER_COMPONENT_CPU, 510);
uidBatteryConsumerBuilder.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, 600);
uidBatteryConsumerBuilder.setUsageDurationMillis(
BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700);
@@ -90,13 +86,9 @@
final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
builder.getOrCreateSystemBatteryConsumerBuilder(
SystemBatteryConsumer.DRAIN_TYPE_CAMERA);
- systemBatteryConsumerBuilder.setConsumedPower(10000);
systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100);
systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
- systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
- + BatteryConsumer.POWER_COMPONENT_CPU, 10210);
systemBatteryConsumerBuilder.setUsageDurationMillis(
BatteryConsumer.TIME_COMPONENT_CPU, 10300);
systemBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis(
@@ -114,22 +106,19 @@
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
if (uidBatteryConsumer.getUid() == 2000) {
assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo");
- assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(200);
assertThat(uidBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300);
assertThat(uidBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(400);
assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(500);
- assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
- + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(510);
assertThat(uidBatteryConsumer.getUsageDurationMillis(
BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(600);
assertThat(uidBatteryConsumer.getUsageDurationMillis(
BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700);
assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800);
+ assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1710);
} else {
fail("Unexpected UID " + uidBatteryConsumer.getUid());
}
@@ -139,18 +128,15 @@
batteryUsageStats.getSystemBatteryConsumers();
for (SystemBatteryConsumer systemBatteryConsumer : systemBatteryConsumers) {
if (systemBatteryConsumer.getDrainType() == SystemBatteryConsumer.DRAIN_TYPE_CAMERA) {
- assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(10000);
assertThat(systemBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200);
- assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID
- + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10210);
assertThat(systemBatteryConsumer.getUsageDurationMillis(
BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300);
assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400);
+ assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(30510);
} else {
fail("Unexpected drain type " + systemBatteryConsumer.getDrainType());
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index bf74c8d..1687a78 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -47,9 +47,10 @@
setExternalStatsSyncLocked(new DummyExternalStatsSync());
- final boolean[] supportedBuckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
- Arrays.fill(supportedBuckets, true);
- mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedBuckets);
+ final boolean[] supportedStandardBuckets
+ = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS];
+ Arrays.fill(supportedStandardBuckets, true);
+ mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedStandardBuckets, 2);
// A no-op handler.
mHandler = new Handler(Looper.getMainLooper()) {
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index c9c81ac..b9908f4 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -21,10 +21,11 @@
import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE;
import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON;
import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER;
-import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS;
+import static com.android.internal.power.MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
@@ -47,60 +48,77 @@
@Test
public void testConstruction() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (supportedEnergyBuckets[i]) {
- assertTrue(stats.isEnergyBucketSupported(i));
- assertEquals(0L, stats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ if (supportedStandardBuckets[i]) {
+ assertTrue(stats.isStandardBucketSupported(i));
+ assertEquals(0L, stats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertFalse(stats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(i));
+ assertFalse(stats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketEnergy(i));
}
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(0L, stats.getAccumulatedCustomBucketEnergy(i));
+ }
}
@Test
public void testCreateFromTemplate() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (supportedEnergyBuckets[i]) {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(0, newStats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ if (supportedStandardBuckets[i]) {
+ assertTrue(newStats.isStandardBucketSupported(i));
+ assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertFalse(newStats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
+ assertFalse(newStats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(i));
+ }
}
@Test
public void testReadWriteParcel() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
final Parcel parcel = Parcel.obtain();
stats.writeToParcel(parcel);
@@ -108,94 +126,127 @@
parcel.setDataPosition(0);
MeasuredEnergyStats newStats = new MeasuredEnergyStats(parcel);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(i),
+ newStats.getAccumulatedCustomBucketEnergy(i));
+ }
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1));
parcel.recycle();
}
@Test
public void testCreateAndReadSummaryFromParcel() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
parcel.setDataPosition(0);
MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- assertEquals(stats.isEnergyBucketSupported(i),
- newStats.isEnergyBucketSupported(i));
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ assertEquals(stats.isStandardBucketSupported(i),
+ newStats.isStandardBucketSupported(i));
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(i),
+ newStats.getAccumulatedCustomBucketEnergy(i));
+ }
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1));
parcel.recycle();
}
@Test
public void testCreateAndReadSummaryFromParcel_existingTemplate() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats template = new MeasuredEnergyStats(supportedEnergyBuckets);
- template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- template.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- template.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats template
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ template.updateCustomBucket(0, 50, true);
final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 7, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
+ stats.updateCustomBucket(0, 315, true);
+ stats.updateCustomBucket(1, 316, true);
final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
- final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched from false to true
- newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true to false
- final MeasuredEnergyStats newTemplate = new MeasuredEnergyStats(newSupportedEnergyBuckets);
+ final boolean[] newsupportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched false > true
+ newsupportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true > false
+ final MeasuredEnergyStats newTemplate
+ = new MeasuredEnergyStats(newsupportedStandardBuckets, numCustomBuckets);
parcel.setDataPosition(0);
final MeasuredEnergyStats newStats =
MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (!newSupportedEnergyBuckets[i]) {
- assertFalse(newStats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
- } else if (!supportedEnergyBuckets[i]) {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ if (!newsupportedStandardBuckets[i]) {
+ assertFalse(newStats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedStandardBucketEnergy(i));
+ } else if (!supportedStandardBuckets[i]) {
+ assertTrue(newStats.isStandardBucketSupported(i));
+ assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ assertTrue(newStats.isStandardBucketSupported(i));
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(i),
+ newStats.getAccumulatedCustomBucketEnergy(i));
+ }
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedCustomBucketEnergy(numCustomBuckets + 1));
parcel.recycle();
}
@Test
public void testCreateAndReadSummaryFromParcel_skipZero() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- Arrays.fill(supportedEnergyBuckets, true);
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ Arrays.fill(supportedStandardBuckets, true);
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- // Accumulate energy in one bucket, the rest should be zero
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ // Accumulate energy in one bucket and one custom bucket, the rest should be zero
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+ stats.updateCustomBucket(1, 60, true);
+ // Let's try parcelling with including zeros
final Parcel includeZerosParcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false);
includeZerosParcel.setDataPosition(0);
@@ -203,90 +254,180 @@
MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
includeZerosParcel);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
if (i == ENERGY_BUCKET_SCREEN_ON) {
- assertEquals(stats.isEnergyBucketSupported(i),
- newStats.isEnergyBucketSupported(i));
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ assertEquals(stats.isStandardBucketSupported(i),
+ newStats.isStandardBucketSupported(i));
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
+ assertTrue(newStats.isStandardBucketSupported(i));
+ assertEquals(0L, newStats.getAccumulatedStandardBucketEnergy(i));
}
}
+ assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0));
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(1),
+ newStats.getAccumulatedCustomBucketEnergy(1));
includeZerosParcel.recycle();
+ // Now let's try parcelling with skipping zeros
final Parcel skipZerosParcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true);
skipZerosParcel.setDataPosition(0);
newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel);
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
if (i == ENERGY_BUCKET_SCREEN_ON) {
- assertEquals(stats.isEnergyBucketSupported(i),
- newStats.isEnergyBucketSupported(i));
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
+ assertEquals(stats.isStandardBucketSupported(i),
+ newStats.isStandardBucketSupported(i));
+ assertEquals(stats.getAccumulatedStandardBucketEnergy(i),
+ newStats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertFalse(newStats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
+ assertFalse(newStats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE,
+ newStats.getAccumulatedStandardBucketEnergy(i));
}
}
+ assertEquals(0L, newStats.getAccumulatedCustomBucketEnergy(0));
+ assertEquals(stats.getAccumulatedCustomBucketEnergy(1),
+ newStats.getAccumulatedCustomBucketEnergy(1));
skipZerosParcel.recycle();
}
@Test
+ public void testCreateAndReadSummaryFromParcel_nullTemplate() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
+
+ final Parcel parcel = Parcel.obtain();
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
+ parcel.setDataPosition(0);
+
+ MeasuredEnergyStats newStats
+ = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, null);
+ assertNull(newStats);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testCreateAndReadSummaryFromParcel_boring() {
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+
+ final MeasuredEnergyStats template
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ template.updateCustomBucket(0, 50, true);
+
+ final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L, true);
+
+ final Parcel parcel = Parcel.obtain();
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
+
+ final boolean[] newSupportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched false > true
+ newSupportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true > false
+ final MeasuredEnergyStats newTemplate
+ = new MeasuredEnergyStats(newSupportedStandardBuckets, numCustomBuckets);
+ parcel.setDataPosition(0);
+
+ final MeasuredEnergyStats newStats =
+ MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel, newTemplate);
+ // The only non-0 entry in stats is no longer supported, so now there's no interesting data.
+ assertNull(newStats);
+ assertEquals("Parcel was not properly consumed", 0, parcel.dataAvail());
+ parcel.recycle();
+ }
+
+ @Test
public void testUpdateBucket() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- assertEquals(15, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
+ stats.updateCustomBucket(0, 3, true);
+
+ assertEquals(15, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
assertEquals(ENERGY_DATA_UNAVAILABLE,
- stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE));
- assertEquals(40, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER));
+ stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE));
+ assertEquals(40, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_OTHER));
+ assertEquals(50 + 3, stats.getAccumulatedCustomBucketEnergy(0));
+ assertEquals(60, stats.getAccumulatedCustomBucketEnergy(1));
}
@Test
public void testReset() {
- final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
- supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
+ final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
+ final int numCustomBuckets = 2;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
+ supportedStandardBuckets[ENERGY_BUCKET_SCREEN_OTHER] = true;
- final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
MeasuredEnergyStats.resetIfNotNull(stats);
// All energy should be reset to 0
- for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (supportedEnergyBuckets[i]) {
- assertTrue(stats.isEnergyBucketSupported(i));
- assertEquals(0, stats.getAccumulatedBucketEnergy(i));
+ for (int i = 0; i < NUMBER_STANDARD_ENERGY_BUCKETS; i++) {
+ if (supportedStandardBuckets[i]) {
+ assertTrue(stats.isStandardBucketSupported(i));
+ assertEquals(0, stats.getAccumulatedStandardBucketEnergy(i));
} else {
- assertFalse(stats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedBucketEnergy(i));
+ assertFalse(stats.isStandardBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketEnergy(i));
}
}
+ for (int i = 0; i < numCustomBuckets; i++) {
+ assertEquals(0, stats.getAccumulatedCustomBucketEnergy(i));
+ }
// Values should increase as usual.
- stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 70, true);
- assertEquals(70L, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70, true);
+ assertEquals(70L, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
+
+ stats.updateCustomBucket(1, 12, true);
+ assertEquals(12L, stats.getAccumulatedCustomBucketEnergy(1));
}
/** Test that states are mapped to the expected energy buckets. Beware of mapping changes. */
@Test
- public void testEnergyBucketMapping() {
+ public void testStandardBucketMapping() {
int exp;
exp = ENERGY_BUCKET_SCREEN_ON;
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 36f01f9..e7fdfb8 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -17,8 +17,11 @@
package android.hardware.devicestate;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import android.annotation.Nullable;
+import android.os.IBinder;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
@@ -30,6 +33,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -41,6 +45,9 @@
@RunWith(JUnit4.class)
@SmallTest
public final class DeviceStateManagerGlobalTest {
+ private static final int DEFAULT_DEVICE_STATE = 0;
+ private static final int OTHER_DEVICE_STATE = 1;
+
private TestDeviceStateManagerService mService;
private DeviceStateManagerGlobal mDeviceStateManagerGlobal;
@@ -52,7 +59,7 @@
@Test
public void registerListener() {
- mService.setDeviceState(0);
+ mService.setBaseState(DEFAULT_DEVICE_STATE);
TestDeviceStateListener listener1 = new TestDeviceStateListener();
TestDeviceStateListener listener2 = new TestDeviceStateListener();
@@ -61,28 +68,58 @@
ConcurrentUtils.DIRECT_EXECUTOR);
mDeviceStateManagerGlobal.registerDeviceStateListener(listener2,
ConcurrentUtils.DIRECT_EXECUTOR);
- assertEquals(0, listener1.getLastReportedState().intValue());
- assertEquals(0, listener2.getLastReportedState().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue());
- mService.setDeviceState(1);
- assertEquals(1, listener1.getLastReportedState().intValue());
- assertEquals(1, listener2.getLastReportedState().intValue());
+ mService.setBaseState(OTHER_DEVICE_STATE);
+ assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue());
+ assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue());
}
@Test
public void unregisterListener() {
- mService.setDeviceState(0);
+ mService.setBaseState(DEFAULT_DEVICE_STATE);
TestDeviceStateListener listener = new TestDeviceStateListener();
mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
ConcurrentUtils.DIRECT_EXECUTOR);
- assertEquals(0, listener.getLastReportedState().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener);
- mService.setDeviceState(1);
- assertEquals(0, listener.getLastReportedState().intValue());
+ mService.setBaseState(OTHER_DEVICE_STATE);
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+ }
+
+ @Test
+ public void submittingRequestRegisteredCallback() {
+ assertTrue(mService.mCallbacks.isEmpty());
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+ assertFalse(mService.mCallbacks.isEmpty());
+ }
+
+ @Test
+ public void submitRequest() {
+ mService.setBaseState(DEFAULT_DEVICE_STATE);
+
+ TestDeviceStateListener listener = new TestDeviceStateListener();
+ mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
+ ConcurrentUtils.DIRECT_EXECUTOR);
+
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+ assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue());
+
+ mDeviceStateManagerGlobal.cancelRequest(request);
+
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
}
private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener {
@@ -100,8 +137,23 @@
}
}
- private final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
- private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
+ private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
+ public static final class Request {
+ public final IBinder token;
+ public final int state;
+ public final int flags;
+
+ private Request(IBinder token, int state, int flags) {
+ this.token = token;
+ this.state = state;
+ this.flags = flags;
+ }
+ }
+
+ private int mBaseState = DEFAULT_DEVICE_STATE;
+ private int mMergedState = DEFAULT_DEVICE_STATE;
+ private ArrayList<Request> mRequests = new ArrayList<>();
+
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
@Override
@@ -112,19 +164,86 @@
mCallbacks.add(callback);
try {
- callback.onDeviceStateChanged(mDeviceState);
+ callback.onDeviceStateChanged(mMergedState);
} catch (RemoteException e) {
// Do nothing. Should never happen.
}
}
- public void setDeviceState(int deviceState) {
- boolean stateChanged = mDeviceState != deviceState;
- mDeviceState = deviceState;
- if (stateChanged) {
+ @Override
+ public int[] getSupportedDeviceStates() {
+ return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
+ }
+
+ @Override
+ public void requestState(IBinder token, int state, int flags) {
+ if (!mRequests.isEmpty()) {
+ final Request topRequest = mRequests.get(mRequests.size() - 1);
for (IDeviceStateManagerCallback callback : mCallbacks) {
try {
- callback.onDeviceStateChanged(mDeviceState);
+ callback.onRequestSuspended(topRequest.token);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ }
+
+ final Request request = new Request(token, state, flags);
+ mRequests.add(request);
+ notifyStateChangedIfNeeded();
+
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestActive(token);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ }
+
+ @Override
+ public void cancelRequest(IBinder token) {
+ int index = -1;
+ for (int i = 0; i < mRequests.size(); i++) {
+ if (mRequests.get(i).token.equals(token)) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ throw new IllegalArgumentException("Unknown request: " + token);
+ }
+
+ mRequests.remove(index);
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestCanceled(token);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ notifyStateChangedIfNeeded();
+ }
+
+ public void setBaseState(int state) {
+ mBaseState = state;
+ notifyStateChangedIfNeeded();
+ }
+
+ private void notifyStateChangedIfNeeded() {
+ final int originalMergedState = mMergedState;
+
+ if (!mRequests.isEmpty()) {
+ mMergedState = mRequests.get(mRequests.size() - 1).state;
+ } else {
+ mMergedState = mBaseState;
+ }
+
+ if (mMergedState != originalMergedState) {
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onDeviceStateChanged(mMergedState);
} catch (RemoteException e) {
// Do nothing. Should never happen.
}
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
deleted file mode 100644
index 5a3ea35..0000000
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
-import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Consumer;
-
-/**
- * Tests for {@link Display}.
- *
- * <p>Build/Install/Run:
- *
- * atest FrameworksMockingCoreTests:android.view.DisplayTests
- */
-@RunWith(AndroidJUnit4.class)
-public class DisplayTests {
-
- private static final int APP_WIDTH = 272;
- private static final int APP_HEIGHT = 700;
- // Tablet size device, ROTATION_0 corresponds to portrait.
- private static final int LOGICAL_WIDTH = 700;
- private static final int LOGICAL_HEIGHT = 1800;
-
- // Bounds of the app when the device is in portrait mode.
- private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
- private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
-
- private StaticMockitoSession mMockitoSession;
-
- private DisplayManagerGlobal mDisplayManagerGlobal;
- private Context mApplicationContext;
- private DisplayInfo mDisplayInfo = new DisplayInfo();
-
- @Before
- public void setupTests() {
- mMockitoSession = mockitoSession()
- .mockStatic(DisplayManagerGlobal.class)
- .strictness(Strictness.LENIENT)
- .startMocking();
-
- // Ensure no adjustments are set before each test.
- mApplicationContext = ApplicationProvider.getApplicationContext();
- DisplayAdjustments displayAdjustments =
- mApplicationContext.getResources().getDisplayAdjustments();
- displayAdjustments.setFixedRotationAdjustments(null);
- mApplicationContext.getResources().overrideDisplayAdjustments(null);
- mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
- null);
- mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
- null);
- mDisplayInfo.rotation = ROTATION_0;
-
- mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
- doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
- }
-
- @After
- public void teardownTests() {
- if (mMockitoSession != null) {
- mMockitoSession.finishMocking();
- }
- Mockito.framework().clearInlineMocks();
- }
-
- @Test
- public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- assertThat(display.getDisplayAdjustments()).isEqualTo(
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- DisplayInfo actualDisplayInfo = new DisplayInfo();
- display.getDisplayInfo(actualDisplayInfo);
- verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
- }
-
- @Test
- public void testConstructor_defaultResources_matchesDisplayInfo() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- assertThat(display.getDisplayAdjustments()).isEqualTo(
- mApplicationContext.getResources().getDisplayAdjustments());
- DisplayInfo actualDisplayInfo = new DisplayInfo();
- display.getDisplayInfo(actualDisplayInfo);
- verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
- }
-
- @Test
- public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
- // GIVEN display is constructed with display adjustments.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- displayAdjustments);
- // THEN rotation is not adjusted since no override was set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN rotation is not adjusted since no override is set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN rotation is adjusted since an override is set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_90);
- }
-
- @Test
- public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsPortrait);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app bounds.
- verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
- }
-
- @Test
- public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsLandscape);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app bounds.
- verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
- }
-
- @Test
- public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated with an override.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsPortrait);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app bounds.
- verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
- }
-
- @Test
- public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsLandscape);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app bounds.
- verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
- }
-
- // Given rotated display dimensions, calculate the letterboxed app bounds.
- private static Rect buildAppBounds(int displayWidth, int displayHeight) {
- final int midWidth = displayWidth / 2;
- final int left = midWidth - (APP_WIDTH / 2);
- final int right = midWidth + (APP_WIDTH / 2);
- final int midHeight = displayHeight / 2;
- // Coordinate system starts at top left.
- final int top = midHeight - (APP_HEIGHT / 2);
- final int bottom = midHeight + (APP_HEIGHT / 2);
- return new Rect(left, top, right, bottom);
- }
-
- private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
- displayInfo.rotation = ROTATION_90;
- // Flip width & height assignment since the device is rotated.
- displayInfo.logicalWidth = LOGICAL_HEIGHT;
- displayInfo.logicalHeight = LOGICAL_WIDTH;
- }
-
- private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
- displayInfo.rotation = ROTATION_0;
- displayInfo.logicalWidth = LOGICAL_WIDTH;
- displayInfo.logicalHeight = LOGICAL_HEIGHT;
- }
-
- /**
- * Set max bounds to be sandboxed to the app bounds, indicating the app is in
- * size compat mode or letterbox.
- */
- private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
- resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
- }
-
- /**
- * Do not compare entire display info, since it is updated to match display the test is run on.
- */
- private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
- assertThat(actual.displayId).isEqualTo(expected.displayId);
- assertThat(actual.rotation).isEqualTo(expected.rotation);
- assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
- assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
- }
-
- private static void verifyRealSizeIsLandscape(Display display) {
- Point size = new Point();
- display.getRealSize(size);
- // Flip the width and height check since the device is rotated.
- assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
- }
-
- private static void verifyRealMetricsIsLandscape(Display display) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- // Flip the width and height check since the device is rotated.
- assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
- assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
- }
-
- private static void verifyRealSizeIsPortrait(Display display) {
- Point size = new Point();
- display.getRealSize(size);
- assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
- }
-
- private static void verifyRealMetricsIsPortrait(Display display) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
- assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
- }
-
- private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
- Point size = new Point();
- display.getRealSize(size);
- assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
- }
-
- private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
- assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
- }
-
- private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
- Resources resources, @Surface.Rotation int rotation) {
- FixedRotationAdjustments fixedRotationAdjustments =
- setFixedRotationAdjustments(resources, rotation);
- resources.overrideDisplayAdjustments(
- buildOverrideRotationAdjustments(fixedRotationAdjustments));
- return fixedRotationAdjustments;
- }
-
- private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
- @Surface.Rotation int rotation) {
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
- return fixedRotationAdjustments;
- }
-
- private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
- FixedRotationAdjustments fixedRotationAdjustments) {
- return consumedDisplayAdjustments
- -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
- }
-}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index b0ae9b9..ea42246 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -165,6 +165,7 @@
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" />
+ <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="audioserver" />
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index ae8e3ce..cdc61a1 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -468,6 +468,7 @@
<!-- Permission required for CTS test - CallLogTest -->
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
+ <permission name="android.permission.MODIFY_QUIET_MODE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ac8a296..222c9bd 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1903,6 +1903,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "123161180": {
+ "message": "SEVER CHILDREN",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
"140319294": {
"message": "IME target changed within ActivityRecord",
"level": "DEBUG",
@@ -2137,12 +2143,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "332390227": {
- "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2623,12 +2623,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
},
- "910200295": {
- "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"913494177": {
"message": "removeAllWindowsIfPossible: removing win=%s",
"level": "WARN",
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index a7d3f798..5b79d76 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -175,6 +175,39 @@
public static final int Y16 = 0x20363159;
/**
+ * <p>Android YUV P010 format.</p>
+ *
+ * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane
+ * followed immediately by a Wx(H/2) CbCr plane. Each sample is
+ * represented by a 16-bit little-endian value, with the lower 6 bits set
+ * to zero.
+ *
+ * <p>This format assumes
+ * <ul>
+ * <li>an even height</li>
+ * <li>a vertical stride equal to the height</li>
+ * </ul>
+ * </p>
+ *
+ * <pre> stride_in_bytes = stride * 2 </pre>
+ * <pre> y_size = stride_in_bytes * height </pre>
+ * <pre> cbcr_size = stride_in_bytes * (height / 2) </pre>
+ * <pre> cb_offset = y_size </pre>
+ * <pre> cr_offset = cb_offset + 2 </pre>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.hardware.camera2.CameraDevice}
+ * through a {@link android.media.ImageReader} object if this format is
+ * supported by {@link android.hardware.camera2.CameraDevice}.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.camera2.CameraDevice
+ *
+ */
+ public static final int YCBCR_P010 = 0x36;
+
+ /**
* YCbCr format, used for video.
*
* <p>For the {@link android.hardware.camera2} API, the {@link #YUV_420_888} format is
@@ -807,6 +840,8 @@
case RAW_DEPTH:
case RAW_SENSOR:
return 16;
+ case YCBCR_P010:
+ return 20;
case RAW_DEPTH10:
case RAW10:
return 10;
@@ -839,6 +874,7 @@
case YUV_420_888:
case YUV_422_888:
case YUV_444_888:
+ case YCBCR_P010:
case FLEX_RGB_888:
case FLEX_RGBA_8888:
case RAW_SENSOR:
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 005a726..35e6b859 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -42,6 +42,7 @@
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.FontConfig;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.LongSparseArray;
import android.util.LruCache;
@@ -67,7 +68,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -147,7 +147,7 @@
*/
@GuardedBy("SYSTEM_FONT_MAP_LOCK")
@UnsupportedAppUsage(trackingBug = 123769347)
- static final Map<String, Typeface> sSystemFontMap = new HashMap<>();
+ static final Map<String, Typeface> sSystemFontMap = new ArrayMap<>();
// DirectByteBuffer object to hold sSystemFontMap's backing memory mapping.
static ByteBuffer sSystemFontMapBuffer = null;
@@ -1231,7 +1231,7 @@
/** @hide */
@VisibleForTesting
public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException {
- Map<String, Typeface> fontMap = new HashMap<>();
+ Map<String, Typeface> fontMap = new ArrayMap<>();
int typefacesBytesCount = buffer.getInt();
long[] nativePtrs = nativeReadTypefaces(buffer.slice());
if (nativePtrs == null) {
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index fea756c..9214ff1 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -486,7 +486,8 @@
long ptr;
int fontIdentifier;
if (mFont == null) {
- ptr = nBuild(builderPtr, readonlyBuffer, filePath, mWeight, italic, mTtcIndex);
+ ptr = nBuild(builderPtr, readonlyBuffer, filePath, mLocaleList, mWeight, italic,
+ mTtcIndex);
long fontBufferPtr = nGetFontBufferAddress(ptr);
synchronized (SOURCE_ID_LOCK) {
long id = FONT_SOURCE_ID_MAP.get(fontBufferPtr, -1);
@@ -513,8 +514,8 @@
@CriticalNative
private static native void nAddAxis(long builderPtr, int tag, float value);
private static native long nBuild(
- long builderPtr, @NonNull ByteBuffer buffer, @NonNull String filePath, int weight,
- boolean italic, int ttcIndex);
+ long builderPtr, @NonNull ByteBuffer buffer, @NonNull String filePath,
+ @NonNull String localeList, int weight, boolean italic, int ttcIndex);
@CriticalNative
private static native long nGetReleaseNativeFont();
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index c29c194..77f86fe 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -20,15 +20,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.text.FontConfig;
+import android.util.SparseIntArray;
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
import java.util.ArrayList;
-import java.util.HashSet;
/**
* A font family class can be used for creating Typeface.
@@ -68,7 +69,9 @@
nGetReleaseNativeFamily());
private final ArrayList<Font> mFonts = new ArrayList<>();
- private final HashSet<Integer> mStyleHashSet = new HashSet<>();
+ // Most FontFamily only has regular, bold, italic, bold-italic. Thus 4 should be good for
+ // initial capacity.
+ private final SparseIntArray mStyles = new SparseIntArray(4);
/**
* Constructs a builder.
@@ -77,7 +80,7 @@
*/
public Builder(@NonNull Font font) {
Preconditions.checkNotNull(font, "font can not be null");
- mStyleHashSet.add(makeStyleIdentifier(font));
+ mStyles.append(makeStyleIdentifier(font), 0);
mFonts.add(font);
}
@@ -97,9 +100,11 @@
*/
public @NonNull Builder addFont(@NonNull Font font) {
Preconditions.checkNotNull(font, "font can not be null");
- if (!mStyleHashSet.add(makeStyleIdentifier(font))) {
+ int key = makeStyleIdentifier(font);
+ if (mStyles.indexOfKey(key) >= 0) {
throw new IllegalArgumentException(font + " has already been added");
}
+ mStyles.append(key, 0);
mFonts.add(font);
return this;
}
@@ -120,7 +125,7 @@
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
- final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr);
+ final FontFamily family = new FontFamily(mFonts, ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
}
@@ -139,15 +144,11 @@
}
private final ArrayList<Font> mFonts;
- private final String mLangTags;
- private final int mVariant;
private final long mNativePtr;
// Use Builder instead.
- private FontFamily(@NonNull ArrayList<Font> fonts, String langTags, int variant, long ptr) {
+ private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
mFonts = fonts;
- mLangTags = langTags;
- mVariant = variant;
mNativePtr = ptr;
}
@@ -157,7 +158,7 @@
* @return a BCP-47 compliant language tag.
*/
public @Nullable String getLangTags() {
- return mLangTags;
+ return nGetLangTags(mNativePtr);
}
/**
@@ -165,7 +166,7 @@
* @return a family variant
*/
public int getVariant() {
- return mVariant;
+ return nGetVariant(mNativePtr);
}
/**
@@ -191,4 +192,10 @@
public long getNativePtr() {
return mNativePtr;
}
+
+ @FastNative
+ private static native String nGetLangTags(long family);
+
+ @CriticalNative
+ private static native int nGetVariant(long family);
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index c166e12..904085f 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -22,6 +22,7 @@
import android.graphics.Typeface;
import android.text.FontConfig;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -36,8 +37,6 @@
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -76,7 +75,7 @@
if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
sAvailableFonts = collectAllFonts();
} else {
- Set<Font> set = new HashSet<>();
+ Set<Font> set = new ArraySet<>();
for (FontFamily[] items : sFamilyMap.values()) {
for (FontFamily family : items) {
for (int i = 0; i < family.getSize(); ++i) {
@@ -96,7 +95,7 @@
FontConfig fontConfig = getSystemPreinstalledFontConfig();
Map<String, FontFamily[]> map = buildSystemFallback(fontConfig);
- Set<Font> res = new HashSet<>();
+ Set<Font> res = new ArraySet<>();
for (FontFamily[] families : map.values()) {
for (FontFamily family : families) {
for (int i = 0; i < family.getSize(); ++i) {
@@ -218,7 +217,7 @@
}
private static void appendNamedFamily(@NonNull FontConfig.FontFamily xmlFamily,
- @NonNull HashMap<String, ByteBuffer> bufferCache,
+ @NonNull ArrayMap<String, ByteBuffer> bufferCache,
@NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) {
final String familyName = xmlFamily.getName();
final FontFamily family = createFontFamily(
@@ -284,8 +283,8 @@
*/
@VisibleForTesting
public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) {
- final Map<String, FontFamily[]> fallbackMap = new HashMap<>();
- final HashMap<String, ByteBuffer> bufferCache = new HashMap<>();
+ final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
@@ -326,7 +325,7 @@
public static Map<String, Typeface> buildSystemTypefaces(
FontConfig fontConfig,
Map<String, FontFamily[]> fallbackMap) {
- final HashMap<String, Typeface> result = new HashMap<>();
+ final ArrayMap<String, Typeface> result = new ArrayMap<>();
Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result);
return result;
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index b1b6306..16bf546 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -23,7 +23,6 @@
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
-import android.sysprop.Keystore2Properties;
import java.io.IOException;
import java.security.KeyFactory;
@@ -117,8 +116,6 @@
putSecretKeyFactoryImpl("HmacSHA512");
}
- private static boolean sKeystore2Enabled;
-
/**
* This function indicates whether or not Keystore 2.0 is enabled. Some parts of the
* Keystore SPI must behave subtly differently when Keystore 2.0 is enabled. However,
@@ -133,10 +130,9 @@
* @hide
*/
public static boolean isKeystore2Enabled() {
- return sKeystore2Enabled;
+ return android.security.keystore2.AndroidKeyStoreProvider.isInstalled();
}
-
/**
* Installs a new instance of this provider (and the
* {@link AndroidKeyStoreBCWorkaroundProvider}).
@@ -164,11 +160,6 @@
// priority.
Security.addProvider(workaroundProvider);
}
-
- // {@code install()} is run by zygote when this property is still accessible. We store its
- // value so that the Keystore SPI can act accordingly without having to access an internal
- // property.
- sKeystore2Enabled = Keystore2Properties.keystore2_enabled().orElse(false);
}
private void putSecretKeyFactoryImpl(String algorithm) {
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index fa4f8b1..3ebca6a 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.os.Process;
import android.security.KeyStore;
import android.security.keymaster.KeymasterDefs;
@@ -874,9 +876,18 @@
* which it must be configured in SEPolicy.
* @hide
*/
+ @SystemApi
public static final int NAMESPACE_APPLICATION = -1;
/**
+ * The namespace identifier for the WIFI Keystore namespace.
+ * This must be kept in sync with system/sepolicy/private/keystore2_key_contexts
+ * @hide
+ */
+ @SystemApi
+ public static final int NAMESPACE_WIFI = 102;
+
+ /**
* For legacy support, translate namespaces into known UIDs.
* @hide
*/
@@ -884,6 +895,8 @@
switch (namespace) {
case NAMESPACE_APPLICATION:
return KeyStore.UID_SELF;
+ case NAMESPACE_WIFI:
+ return Process.WIFI_UID;
// TODO Translate WIFI and VPN UIDs once the namespaces are defined.
// b/171305388 and b/171305607
default:
@@ -900,6 +913,8 @@
switch (uid) {
case KeyStore.UID_SELF:
return NAMESPACE_APPLICATION;
+ case Process.WIFI_UID:
+ return NAMESPACE_WIFI;
// TODO Translate WIFI and VPN UIDs once the namespaces are defined.
// b/171305388 and b/171305607
default:
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 75ac61a..e101115 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -352,14 +352,17 @@
try {
response = keyStore.getKeyEntry(descriptor);
} catch (android.security.KeyStoreException e) {
- if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) {
- throw new KeyPermanentlyInvalidatedException(
- "User changed or deleted their auth credentials",
- e);
- } else {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about key")
- .initCause(e);
+ switch (e.getErrorCode()) {
+ case ResponseCode.KEY_NOT_FOUND:
+ return null;
+ case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+ throw new KeyPermanentlyInvalidatedException(
+ "User changed or deleted their auth credentials",
+ e);
+ default:
+ throw (UnrecoverableKeyException)
+ new UnrecoverableKeyException("Failed to obtain information about key")
+ .initCause(e);
}
}
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 7fbbb61..4612ba2 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// Sidecar
android_library_import {
name: "window-sidecar",
aars: ["window-sidecar-release.aar"],
@@ -20,7 +21,7 @@
java_library {
name: "androidx.window.sidecar",
- srcs: ["src/**/*.java"],
+ srcs: ["src/androidx/window/sidecar/**/*.java", "src/androidx/window/util/**/*.java"],
static_libs: ["window-sidecar"],
installable: true,
sdk_version: "core_platform",
@@ -36,3 +37,31 @@
src: "androidx.window.sidecar.xml",
filename_from_src: true,
}
+
+// Extensions
+// NOTE: This module is still under active development and must not
+// be used in production. Use 'androidx.window.sidecar' instead.
+android_library_import {
+ name: "window-extensions",
+ aars: ["window-extensions-release.aar"],
+ sdk_version: "current",
+}
+
+java_library {
+ name: "androidx.window.extensions",
+ srcs: ["src/androidx/window/extensions/**/*.java", "src/androidx/window/util/**/*.java"],
+ static_libs: ["window-extensions"],
+ installable: true,
+ sdk_version: "core_platform",
+ system_ext_specific: true,
+ libs: ["framework", "androidx.annotation_annotation",],
+ required: ["androidx.window.extensions.xml",],
+}
+
+prebuilt_etc {
+ name: "androidx.window.extensions.xml",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+ src: "androidx.window.extensions.xml",
+ filename_from_src: true,
+}
diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
new file mode 100644
index 0000000..34264aa
--- /dev/null
+++ b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<permissions>
+ <library
+ name="androidx.window.extensions"
+ file="/system_ext/framework/androidx.window.extensions.jar"/>
+</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
new file mode 100644
index 0000000..b7a6039
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import android.content.Context;
+
+/**
+ * Provider class that will instantiate the library implementation. It must be included in the
+ * vendor library, and the vendor implementation must match the signature of this class.
+ */
+public class ExtensionProvider {
+ /**
+ * Provides a simple implementation of {@link ExtensionInterface} that can be replaced by
+ * an OEM by overriding this method.
+ */
+ public static ExtensionInterface getExtensionImpl(Context context) {
+ return new SampleExtensionImpl(context);
+ }
+
+ /**
+ * The support library will use this method to check API version compatibility.
+ * @return API version string in MAJOR.MINOR.PATCH-description format.
+ */
+ public static String getApiVersion() {
+ return "1.0.0-settings_sample";
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
new file mode 100644
index 0000000..0bf6965
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -0,0 +1,109 @@
+/*
+ * 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 androidx.window.extensions;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.extensions OEM interface for use with
+ * WindowManager Jetpack.
+ *
+ * NOTE: This version is a work in progress and under active development. It MUST NOT be used in
+ * production builds since the interface can still change before reaching stable version.
+ * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
+ */
+class SampleExtensionImpl extends StubExtension implements
+ SettingsConfigProvider.StateChangeCallback {
+ private static final String TAG = "SampleExtension";
+
+ private final SettingsConfigProvider mConfigProvider;
+
+ SampleExtensionImpl(Context context) {
+ mConfigProvider = new SettingsConfigProvider(context, this);
+ }
+
+ @Override
+ public void onDevicePostureChanged() {
+ updateDeviceState(new ExtensionDeviceState(mConfigProvider.getDeviceState()));
+ }
+
+ @Override
+ public void onDisplayFeaturesChanged() {
+ for (Activity activity : getActivitiesListeningForLayoutChanges()) {
+ ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
+ updateWindowLayout(activity, newLayout);
+ }
+ }
+
+ @NonNull
+ private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
+ List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
+ return new ExtensionWindowLayoutInfo(displayFeatures);
+ }
+
+ private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+ List<ExtensionDisplayFeature> features = new ArrayList<>();
+ int displayId = activity.getDisplayId();
+ if (displayId != DEFAULT_DISPLAY) {
+ Log.w(TAG, "This sample doesn't support display features on secondary displays");
+ return features;
+ }
+
+ if (activity.isInMultiWindowMode()) {
+ // It is recommended not to report any display features in multi-window mode, since it
+ // won't be possible to synchronize the display feature positions with window movement.
+ return features;
+ }
+
+ List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
+ for (BaseDisplayFeature baseFeature : storedFeatures) {
+ Rect featureRect = baseFeature.getRect();
+ rotateRectToDisplayRotation(displayId, featureRect);
+ transformToWindowSpaceRect(activity, featureRect);
+ features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
+ baseFeature.getState()));
+ }
+ return features;
+ }
+
+ @Override
+ protected void onListenersChanged() {
+ if (hasListeners()) {
+ mConfigProvider.registerObserversIfNeeded();
+ } else {
+ mConfigProvider.unregisterObserversIfNeeded();
+ }
+
+ onDevicePostureChanged();
+ onDisplayFeaturesChanged();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
new file mode 100644
index 0000000..b0895ef
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
@@ -0,0 +1,86 @@
+/*
+ * 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 androidx.window.extensions;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Basic implementation of the {@link ExtensionInterface}. An OEM can choose to use it as the base
+ * class for their implementation.
+ */
+abstract class StubExtension implements ExtensionInterface {
+
+ private ExtensionCallback mExtensionCallback;
+ private final Set<Activity> mWindowLayoutChangeListenerActivities = new HashSet<>();
+ private boolean mDeviceStateChangeListenerRegistered;
+
+ StubExtension() {
+ }
+
+ @Override
+ public void setExtensionCallback(@NonNull ExtensionCallback extensionCallback) {
+ this.mExtensionCallback = extensionCallback;
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
+ this.mWindowLayoutChangeListenerActivities.add(activity);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
+ this.mWindowLayoutChangeListenerActivities.remove(activity);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onDeviceStateListenersChanged(boolean isEmpty) {
+ this.mDeviceStateChangeListenerRegistered = !isEmpty;
+ this.onListenersChanged();
+ }
+
+ void updateDeviceState(ExtensionDeviceState newState) {
+ if (this.mExtensionCallback != null) {
+ mExtensionCallback.onDeviceStateChanged(newState);
+ }
+ }
+
+ void updateWindowLayout(@NonNull Activity activity,
+ @NonNull ExtensionWindowLayoutInfo newLayout) {
+ if (this.mExtensionCallback != null) {
+ mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
+ }
+ }
+
+ @NonNull
+ Set<Activity> getActivitiesListeningForLayoutChanges() {
+ return mWindowLayoutChangeListenerActivities;
+ }
+
+ protected boolean hasListeners() {
+ return !mWindowLayoutChangeListenerActivities.isEmpty()
+ || mDeviceStateChangeListenerRegistered;
+ }
+
+ protected abstract void onListenersChanged();
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
new file mode 100644
index 0000000..1094a0e
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.sidecar;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.sidecar OEM interface for use with
+ * WindowManager Jetpack.
+ */
+class SampleSidecarImpl extends StubSidecar implements
+ SettingsConfigProvider.StateChangeCallback {
+ private static final String TAG = "SampleSidecar";
+
+ private final SettingsConfigProvider mConfigProvider;
+
+ SampleSidecarImpl(Context context) {
+ mConfigProvider = new SettingsConfigProvider(context, this);
+ }
+
+ @Override
+ public void onDevicePostureChanged() {
+ updateDeviceState(getDeviceState());
+ }
+
+ @Override
+ public void onDisplayFeaturesChanged() {
+ for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
+ SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
+ updateWindowLayout(windowToken, newLayout);
+ }
+ }
+
+ @NonNull
+ @Override
+ public SidecarDeviceState getDeviceState() {
+ SidecarDeviceState deviceState = new SidecarDeviceState();
+ deviceState.posture = mConfigProvider.getDeviceState();
+ return deviceState;
+ }
+
+ @NonNull
+ @Override
+ public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+ Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
+ SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
+ if (activity == null) {
+ return windowLayoutInfo;
+ }
+ windowLayoutInfo.displayFeatures = getDisplayFeatures(activity);
+ return windowLayoutInfo;
+ }
+
+ private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+ List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
+ int displayId = activity.getDisplayId();
+ if (displayId != DEFAULT_DISPLAY) {
+ Log.w(TAG, "This sample doesn't support display features on secondary displays");
+ return features;
+ }
+
+ if (activity.isInMultiWindowMode()) {
+ // It is recommended not to report any display features in multi-window mode, since it
+ // won't be possible to synchronize the display feature positions with window movement.
+ return features;
+ }
+
+ List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
+ for (BaseDisplayFeature baseFeature : storedFeatures) {
+ SidecarDisplayFeature feature = new SidecarDisplayFeature();
+ Rect featureRect = baseFeature.getRect();
+ rotateRectToDisplayRotation(displayId, featureRect);
+ transformToWindowSpaceRect(activity, featureRect);
+ feature.setRect(featureRect);
+ feature.setType(baseFeature.getType());
+ features.add(feature);
+ }
+ return features;
+ }
+
+ @Override
+ protected void onListenersChanged() {
+ if (hasListeners()) {
+ mConfigProvider.registerObserversIfNeeded();
+ } else {
+ mConfigProvider.unregisterObserversIfNeeded();
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
deleted file mode 100644
index 5397302..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.sidecar;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static androidx.window.sidecar.SidecarHelper.getWindowDisplay;
-import static androidx.window.sidecar.SidecarHelper.isInMultiWindow;
-import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation;
-import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-class SettingsSidecarImpl extends StubSidecar {
- private static final String TAG = "SettingsSidecar";
-
- private static final String DEVICE_POSTURE = "device_posture";
- private static final String DISPLAY_FEATURES = "display_features";
-
- private static final Pattern FEATURE_PATTERN =
- Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
-
- private static final String FEATURE_TYPE_FOLD = "fold";
- private static final String FEATURE_TYPE_HINGE = "hinge";
-
- private Context mContext;
- private SettingsObserver mSettingsObserver;
-
- final class SettingsObserver extends ContentObserver {
- private final Uri mDevicePostureUri =
- Settings.Global.getUriFor(DEVICE_POSTURE);
- private final Uri mDisplayFeaturesUri =
- Settings.Global.getUriFor(DISPLAY_FEATURES);
- private final ContentResolver mResolver = mContext.getContentResolver();
- private boolean mRegisteredObservers;
-
-
- private SettingsObserver() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- private void registerObserversIfNeeded() {
- if (mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = true;
- mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */,
- this /* ContentObserver */);
- mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */,
- this /* ContentObserver */);
- }
-
- private void unregisterObserversIfNeeded() {
- if (!mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = false;
- mResolver.unregisterContentObserver(this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (uri == null) {
- return;
- }
-
- if (mDevicePostureUri.equals(uri)) {
- updateDevicePosture();
- return;
- }
- if (mDisplayFeaturesUri.equals(uri)) {
- updateDisplayFeatures();
- return;
- }
- }
- }
-
- SettingsSidecarImpl(Context context) {
- mContext = context;
- mSettingsObserver = new SettingsObserver();
- }
-
- private void updateDevicePosture() {
- updateDeviceState(getDeviceState());
- }
-
- /** Update display features with values read from settings. */
- private void updateDisplayFeatures() {
- for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
- SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
- updateWindowLayout(windowToken, newLayout);
- }
- }
-
- @NonNull
- @Override
- public SidecarDeviceState getDeviceState() {
- ContentResolver resolver = mContext.getContentResolver();
- int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE,
- SidecarDeviceState.POSTURE_UNKNOWN);
- SidecarDeviceState deviceState = new SidecarDeviceState();
- deviceState.posture = posture;
- return deviceState;
- }
-
- @NonNull
- @Override
- public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
- List<SidecarDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
- SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
- windowLayoutInfo.displayFeatures = displayFeatures;
- return windowLayoutInfo;
- }
-
- private List<SidecarDisplayFeature> readDisplayFeatures(IBinder windowToken) {
- List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
- int displayId = getWindowDisplay(windowToken);
- if (displayId != DEFAULT_DISPLAY) {
- Log.w(TAG, "This sample doesn't support display features on secondary displays");
- return features;
- }
-
- if (isInMultiWindow(windowToken)) {
- // It is recommended not to report any display features in multi-window mode, since it
- // won't be possible to synchronize the display feature positions with window movement.
- return features;
- }
-
- ContentResolver resolver = mContext.getContentResolver();
- String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES);
- if (TextUtils.isEmpty(displayFeaturesString)) {
- displayFeaturesString = mContext.getResources().getString(
- R.string.config_display_features);
- }
- if (TextUtils.isEmpty(displayFeaturesString)) {
- return features;
- }
-
- String[] featureStrings = displayFeaturesString.split(";");
- for (String featureString : featureStrings) {
- Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
- if (!featureMatcher.matches()) {
- Log.e(TAG, "Malformed feature description format: " + featureString);
- continue;
- }
- try {
- String featureType = featureMatcher.group(1);
- int type;
- switch (featureType) {
- case FEATURE_TYPE_FOLD:
- type = SidecarDisplayFeature.TYPE_FOLD;
- break;
- case FEATURE_TYPE_HINGE:
- type = SidecarDisplayFeature.TYPE_HINGE;
- break;
- default: {
- Log.e(TAG, "Malformed feature type: " + featureType);
- continue;
- }
- }
-
- int left = Integer.parseInt(featureMatcher.group(2));
- int top = Integer.parseInt(featureMatcher.group(3));
- int right = Integer.parseInt(featureMatcher.group(4));
- int bottom = Integer.parseInt(featureMatcher.group(5));
- Rect featureRect = new Rect(left, top, right, bottom);
- rotateRectToDisplayRotation(featureRect, displayId);
- transformToWindowSpaceRect(featureRect, windowToken);
- if (isNotZero(featureRect)) {
- SidecarDisplayFeature feature = new SidecarDisplayFeature();
- feature.setRect(featureRect);
- feature.setType(type);
- features.add(feature);
- } else {
- Log.w(TAG, "Failed to adjust feature to window");
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "Malformed feature description: " + featureString);
- }
- }
- return features;
- }
-
- private static boolean isNotZero(Rect rect) {
- return rect.height() > 0 || rect.width() > 0;
- }
-
- @Override
- protected void onListenersChanged() {
- if (mSettingsObserver == null) {
- return;
- }
-
- if (hasListeners()) {
- mSettingsObserver.registerObserversIfNeeded();
- } else {
- mSettingsObserver.unregisterObserversIfNeeded();
- }
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
index 0b4915e..e6f8388 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
@@ -28,7 +28,7 @@
* an OEM by overriding this method.
*/
public static SidecarInterface getSidecarImpl(Context context) {
- return new SettingsSidecarImpl(context);
+ return new SampleSidecarImpl(context);
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
new file mode 100644
index 0000000..b74a2a4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
@@ -0,0 +1,52 @@
+/*
+ * 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 androidx.window.util;
+
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+
+/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
+public class BaseDisplayFeature {
+ private final int mType;
+ private final int mState;
+ @NonNull
+ public final Rect mRect;
+
+ public BaseDisplayFeature(int type, int state, @NonNull Rect rect) {
+ this.mType = type;
+ this.mState = state;
+ if (rect.width() == 0 && rect.height() == 0) {
+ throw new IllegalArgumentException(
+ "Display feature rectangle cannot have zero width and height simultaneously.");
+ }
+ this.mRect = rect;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ @NonNull
+ public Rect getRect() {
+ return mRect;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
similarity index 62%
rename from libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
rename to libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
index e5b6cff..2a593f1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,30 +14,36 @@
* limitations under the License.
*/
-package androidx.window.sidecar;
+package androidx.window.util;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.app.Activity;
-import android.app.ActivityThread;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.IBinder;
import android.view.DisplayInfo;
import android.view.Surface;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-class SidecarHelper {
+/**
+ * Util class for both Sidecar and Extensions.
+ */
+public final class ExtensionHelper {
+
+ private ExtensionHelper() {
+ // Util class, no instances should be created.
+ }
+
/**
- * Rotate the input rectangle specified in default display orientation to the current display
+ * Rotates the input rectangle specified in default display orientation to the current display
* rotation.
*/
- static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) {
+ public static void rotateRectToDisplayRotation(int displayId, Rect inOutRect) {
DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
int rotation = displayInfo.rotation;
@@ -52,7 +58,7 @@
}
/**
- * Rotate the input rectangle within parent bounds for a given delta.
+ * Rotates the input rectangle within parent bounds for a given delta.
*/
private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight,
@Surface.Rotation int delta) {
@@ -79,9 +85,9 @@
}
}
- /** Transform rectangle from absolute coordinate space to the window coordinate space. */
- static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) {
- Rect windowRect = getWindowBounds(windowToken);
+ /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
+ public static void transformToWindowSpaceRect(Activity activity, Rect inOutRect) {
+ Rect windowRect = getWindowBounds(activity);
if (windowRect == null) {
inOutRect.setEmpty();
return;
@@ -95,32 +101,17 @@
}
/**
- * Get the current window bounds in absolute coordinates.
- * NOTE: Only works with Activity windows.
+ * Gets the current window bounds in absolute coordinates.
*/
@Nullable
- private static Rect getWindowBounds(IBinder windowToken) {
- Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- return activity != null
- ? activity.getWindowManager().getCurrentWindowMetrics().getBounds()
- : null;
+ private static Rect getWindowBounds(@NonNull Activity activity) {
+ return activity.getWindowManager().getCurrentWindowMetrics().getBounds();
}
/**
- * Check if this window is an Activity window that is in multi-window mode.
+ * Checks if both dimensions of the given rect are zero at the same time.
*/
- static boolean isInMultiWindow(IBinder windowToken) {
- Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- return activity != null && activity.isInMultiWindowMode();
- }
-
- /**
- * Get the id of the parent display for the window.
- * NOTE: Only works with Activity windows.
- */
- static int getWindowDisplay(IBinder windowToken) {
- Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- return activity != null
- ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY;
+ public static boolean isZero(@NonNull Rect rect) {
+ return rect.height() == 0 && rect.width() == 0;
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
new file mode 100644
index 0000000..6dd190c
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
@@ -0,0 +1,196 @@
+/*
+ * 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 androidx.window.util;
+
+import static androidx.window.util.ExtensionHelper.isZero;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Device and display feature state provider that uses Settings as the source.
+ */
+public final class SettingsConfigProvider extends ContentObserver {
+ private static final String TAG = "SettingsConfigProvider";
+ private static final String DEVICE_POSTURE = "device_posture";
+ private static final String DISPLAY_FEATURES = "display_features";
+
+ private static final Pattern FEATURE_PATTERN =
+ Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
+
+ private static final String FEATURE_TYPE_FOLD = "fold";
+ private static final String FEATURE_TYPE_HINGE = "hinge";
+
+ private final Uri mDevicePostureUri =
+ Settings.Global.getUriFor(DEVICE_POSTURE);
+ private final Uri mDisplayFeaturesUri =
+ Settings.Global.getUriFor(DISPLAY_FEATURES);
+ private final Context mContext;
+ private final ContentResolver mResolver;
+ private final StateChangeCallback mCallback;
+ private boolean mRegisteredObservers;
+
+ public SettingsConfigProvider(@NonNull Context context, @NonNull StateChangeCallback callback) {
+ super(new Handler(Looper.getMainLooper()));
+ mContext = context;
+ mResolver = context.getContentResolver();
+ mCallback = callback;
+ }
+
+ /**
+ * Registers the content observers for Settings keys that store device state and display feature
+ * configurations.
+ */
+ public void registerObserversIfNeeded() {
+ if (mRegisteredObservers) {
+ return;
+ }
+ mRegisteredObservers = true;
+ mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */,
+ this /* ContentObserver */);
+ mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
+ this /* ContentObserver */);
+ }
+
+ /**
+ * Unregisters the content observers that are tracking the state changes.
+ * @see #registerObserversIfNeeded()
+ */
+ public void unregisterObserversIfNeeded() {
+ if (!mRegisteredObservers) {
+ return;
+ }
+ mRegisteredObservers = false;
+ mResolver.unregisterContentObserver(this);
+ }
+
+ /**
+ * Gets the device posture int stored in Settings.
+ */
+ public int getDeviceState() {
+ return Settings.Global.getInt(mResolver, DEVICE_POSTURE,
+ 0 /* POSTURE_UNKNOWN */);
+ }
+
+ /**
+ * Gets the list of all display feature configs stored in Settings. Uses a custom
+ * {@link BaseDisplayFeature} class to report the config to be translated for actual
+ * containers in Sidecar or Extensions.
+ */
+ public List<BaseDisplayFeature> getDisplayFeatures() {
+ List<BaseDisplayFeature> features = new ArrayList<>();
+ String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ displayFeaturesString = mContext.getResources().getString(
+ R.string.config_display_features);
+ }
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ return features;
+ }
+ String[] featureStrings = displayFeaturesString.split(";");
+
+ int deviceState = getDeviceState();
+
+ for (String featureString : featureStrings) {
+ Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
+ if (!featureMatcher.matches()) {
+ Log.e(TAG, "Malformed feature description format: " + featureString);
+ continue;
+ }
+ try {
+ String featureType = featureMatcher.group(1);
+ int type;
+ switch (featureType) {
+ case FEATURE_TYPE_FOLD:
+ type = 1 /* TYPE_FOLD */;
+ break;
+ case FEATURE_TYPE_HINGE:
+ type = 2 /* TYPE_HINGE */;
+ break;
+ default: {
+ Log.e(TAG, "Malformed feature type: " + featureType);
+ continue;
+ }
+ }
+
+ int left = Integer.parseInt(featureMatcher.group(2));
+ int top = Integer.parseInt(featureMatcher.group(3));
+ int right = Integer.parseInt(featureMatcher.group(4));
+ int bottom = Integer.parseInt(featureMatcher.group(5));
+ Rect featureRect = new Rect(left, top, right, bottom);
+ if (!isZero(featureRect)) {
+ BaseDisplayFeature feature = new BaseDisplayFeature(type, deviceState,
+ featureRect);
+ features.add(feature);
+ } else {
+ Log.w(TAG, "Read empty feature");
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Malformed feature description: " + featureString);
+ }
+ }
+ return features;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri == null) {
+ return;
+ }
+
+ if (mDevicePostureUri.equals(uri)) {
+ mCallback.onDevicePostureChanged();
+ mCallback.onDisplayFeaturesChanged();
+ return;
+ }
+ if (mDisplayFeaturesUri.equals(uri)) {
+ mCallback.onDisplayFeaturesChanged();
+ }
+ }
+
+ /**
+ * Callback that notifies about device or display feature state changes.
+ */
+ public interface StateChangeCallback {
+ /**
+ * Notifies about the device state update.
+ */
+ void onDevicePostureChanged();
+
+ /**
+ * Notifies about the display feature config update.
+ */
+ void onDisplayFeaturesChanged();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
new file mode 100644
index 0000000..7b306b0
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 2cfb13e..9c3d84e 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -13,6 +13,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "-1671119352": {
+ "message": " Delegate animation for %s to %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TRANSITIONS",
+ "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
+ },
"-1501874464": {
"message": "Fullscreen Task Appeared: #%d",
"level": "VERBOSE",
@@ -49,6 +55,24 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "-1308483871": {
+ "message": " try handler %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TRANSITIONS",
+ "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+ },
+ "-1297259344": {
+ "message": " animated by %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TRANSITIONS",
+ "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+ },
+ "-1269886472": {
+ "message": "Transition %s doesn't have explicit remote, search filters for match for %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TRANSITIONS",
+ "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
+ },
"-1006733970": {
"message": "Display added: %d",
"level": "VERBOSE",
@@ -91,12 +115,24 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/apppairs\/AppPairsController.java"
},
+ "138343607": {
+ "message": " try firstHandler %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TRANSITIONS",
+ "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+ },
"157713005": {
"message": "Task info changed taskId=%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "214412327": {
+ "message": "RemoteTransition directly requested for %s: %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TRANSITIONS",
+ "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
+ },
"274140888": {
"message": "Animate alpha: from=%d to=%d",
"level": "VERBOSE",
@@ -115,6 +151,12 @@
"group": "WM_SHELL_DRAG_AND_DROP",
"at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
},
+ "410592459": {
+ "message": "Invalid root leash (%s): %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TRANSITIONS",
+ "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+ },
"473543554": {
"message": "%s onTaskAppeared Supported",
"level": "VERBOSE",
@@ -139,6 +181,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "707170340": {
+ "message": " animated by firstHandler",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TRANSITIONS",
+ "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
+ },
"900599280": {
"message": "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
"level": "ERROR",
@@ -163,6 +211,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/legacysplitscreen\/LegacySplitScreenTaskListener.java"
},
+ "990371881": {
+ "message": " Checking filter %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TRANSITIONS",
+ "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
+ },
"1070270131": {
"message": "onTransitionReady %s: %s",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 52648d9..fe97e24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -16,7 +16,7 @@
package com.android.wm.shell;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.common.ShellExecutor;
@@ -155,7 +155,7 @@
}
final int taskId = new Integer(args[2]);
final int sideStagePosition = args.length > 3
- ? new Integer(args[3]) : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ ? new Integer(args[3]) : STAGE_POSITION_BOTTOM_OR_RIGHT;
mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 7ea4689..255e4d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.animation
-import android.os.Looper
import android.util.ArrayMap
import android.util.Log
import android.view.View
@@ -847,7 +846,7 @@
* pass to [spring].
*/
data class SpringConfig internal constructor(
- internal var stiffness: Float,
+ var stiffness: Float,
internal var dampingRatio: Float,
internal var startVelocity: Float = 0f,
internal var finalPosition: Float = UNSET
@@ -879,8 +878,8 @@
*/
data class FlingConfig internal constructor(
internal var friction: Float,
- internal var min: Float,
- internal var max: Float,
+ var min: Float,
+ var max: Float,
internal var startVelocity: Float
) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index ffeabd8..40fdb97 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -39,6 +39,7 @@
import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
@@ -59,6 +60,8 @@
private static final String TAG = "Bubble";
private final String mKey;
+ @Nullable
+ private final String mGroupKey;
private final Executor mMainExecutor;
private long mLastUpdated;
@@ -165,6 +168,7 @@
mMetadataShortcutId = shortcutInfo.getId();
mShortcutInfo = shortcutInfo;
mKey = key;
+ mGroupKey = null;
mFlags = 0;
mUser = shortcutInfo.getUserHandle();
mPackageName = shortcutInfo.getPackage();
@@ -182,6 +186,7 @@
final Bubbles.PendingIntentCanceledListener intentCancelListener,
Executor mainExecutor) {
mKey = entry.getKey();
+ mGroupKey = entry.getGroupKey();
mSuppressionListener = listener;
mIntentCancelListener = intent -> {
if (mIntent != null) {
@@ -200,6 +205,14 @@
return mKey;
}
+ /**
+ * @see StatusBarNotification#getGroupKey()
+ * @return the group key for this bubble, if one exists.
+ */
+ public String getGroupKey() {
+ return mGroupKey;
+ }
+
public UserHandle getUser() {
return mUser;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 7538c8b..d73fc6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -771,7 +771,8 @@
// if the bubble is already active, there's no need to push it to overflow
return;
}
- bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble),
+ bubble.inflate(
+ (b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble),
mContext, this, mStackView, mBubbleIconFactory, true /* skipInflation */);
});
return null;
@@ -892,10 +893,7 @@
return bubbleChildren;
}
for (Bubble bubble : mBubbleData.getActiveBubbles()) {
- // TODO(178620678): Prevent calling into SysUI since this can be a part of a blocking
- // call from SysUI to Shell
- final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey());
- if (entry != null && groupKey.equals(entry.getStatusBarNotification().getGroupKey())) {
+ if (bubble.getGroupKey() != null && groupKey.equals(bubble.getGroupKey())) {
bubbleChildren.add(bubble);
}
}
@@ -1064,8 +1062,8 @@
private boolean isSummaryOfBubbles(BubbleEntry entry) {
String groupKey = entry.getStatusBarNotification().getGroupKey();
ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey);
- boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
- && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
+ boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey)
+ && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey());
boolean isSummary = entry.getStatusBarNotification().getNotification().isGroupSummary();
return (isSuppressedSummary || isSummary) && !bubbleChildren.isEmpty();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 9d196ba..53b7537 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -542,7 +542,8 @@
void overflowBubble(@DismissReason int reason, Bubble bubble) {
if (bubble.getPendingIntentCanceled()
|| !(reason == Bubbles.DISMISS_AGED
- || reason == Bubbles.DISMISS_USER_GESTURE)) {
+ || reason == Bubbles.DISMISS_USER_GESTURE
+ || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)) {
return;
}
if (DEBUG_BUBBLE_DATA) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
index 3361c4c..c88a58b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
@@ -61,7 +61,10 @@
BUBBLE_OVERFLOW_REMOVE_BLOCKED(490),
@UiEvent(doc = "User selected the overflow.")
- BUBBLE_OVERFLOW_SELECTED(600);
+ BUBBLE_OVERFLOW_SELECTED(600),
+
+ @UiEvent(doc = "Restore bubble to overflow after phone reboot.")
+ BUBBLE_OVERFLOW_RECOVER(691);
private final int mId;
@@ -112,6 +115,8 @@
log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
} else if (r == Bubbles.DISMISS_USER_GESTURE) {
log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
+ } else if (r == Bubbles.DISMISS_RELOAD_FROM_DISK) {
+ log(b, Event.BUBBLE_OVERFLOW_RECOVER);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index af421fa..d54be0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1478,6 +1478,9 @@
* Update bubble order and pointer position.
*/
public void updateBubbleOrder(List<Bubble> bubbles) {
+ if (isExpansionAnimating()) {
+ return;
+ }
final Runnable reorder = () -> {
for (int i = 0; i < bubbles.size(); i++) {
Bubble bubble = bubbles.get(i);
@@ -1662,6 +1665,7 @@
}
beforeExpandedViewAnimation();
+ updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
mBubbleContainer.setActiveController(mExpandedAnimationController);
updateOverflowVisibility();
updatePointerPosition();
@@ -1875,7 +1879,7 @@
mExpandedBubble));
}
updateOverflowVisibility();
-
+ updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */);
afterExpandedViewAnimation();
if (previouslySelected != null) {
previouslySelected.setContentVisibility(false);
@@ -2623,7 +2627,6 @@
}
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
- updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
}
/**
@@ -2733,9 +2736,13 @@
* @param action the user interaction enum.
*/
private void logBubbleEvent(@Nullable BubbleViewProvider provider, int action) {
+ final String packageName =
+ (provider != null && provider instanceof Bubble)
+ ? ((Bubble) provider).getPackageName()
+ : "null";
mBubbleData.logBubbleEvent(provider,
action,
- mContext.getApplicationInfo().packageName,
+ packageName,
getBubbleCount(),
getBubbleIndex(provider),
getNormalizedXPosition(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 6102147..6a1026b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -54,7 +54,7 @@
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
- DISMISS_NO_BUBBLE_UP})
+ DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK})
@Target({FIELD, LOCAL_VARIABLE, PARAMETER})
@interface DismissReason {}
@@ -72,6 +72,7 @@
int DISMISS_SHORTCUT_REMOVED = 12;
int DISMISS_PACKAGE_REMOVED = 13;
int DISMISS_NO_BUBBLE_UP = 14;
+ int DISMISS_RELOAD_FROM_DISK = 15;
/**
* @return {@code true} if there is a bubble associated with the provided key and if its
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index e1f831e..73371e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -62,8 +62,8 @@
private static final String TAG = "Bubbs.StackCtrl";
- /** Values to use for animating bubbles in. */
- private static final float ANIMATE_IN_STIFFNESS = 1000f;
+ /** Value to use for animating bubbles in and springing stack after fling. */
+ private static final float STACK_SPRING_STIFFNESS = 700f;
/** Values to use for animating updated bubble to top of stack. */
private static final float NEW_BUBBLE_START_SCALE = 0.5f;
@@ -80,20 +80,15 @@
private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
new PhysicsAnimator.SpringConfig(
- ANIMATE_IN_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+ STACK_SPRING_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
/**
* Friction applied to fling animations. Since the stack must land on one of the sides of the
* screen, we want less friction horizontally so that the stack has a better chance of making it
* to the side without needing a spring.
*/
- private static final float FLING_FRICTION = 2.2f;
+ private static final float FLING_FRICTION = 1.9f;
- /**
- * Values to use for the stack spring animation used to spring the stack to its final position
- * after a fling.
- */
- private static final int SPRING_AFTER_FLING_STIFFNESS = 750;
private static final float SPRING_AFTER_FLING_DAMPING_RATIO = 0.85f;
/** Sentinel value for unset position value. */
@@ -216,7 +211,7 @@
@Override
public void moveToBounds(@NonNull Rect bounds) {
- springStack(bounds.left, bounds.top, SpringForce.STIFFNESS_LOW);
+ springStack(bounds.left, bounds.top, STACK_SPRING_STIFFNESS);
}
@NonNull
@@ -341,7 +336,7 @@
* flings.
*/
public void springStackAfterFling(float destinationX, float destinationY) {
- springStack(destinationX, destinationY, SPRING_AFTER_FLING_STIFFNESS);
+ springStack(destinationX, destinationY, STACK_SPRING_STIFFNESS);
}
/**
@@ -371,7 +366,7 @@
final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
final float stiffness = Settings.Secure.getFloat(contentResolver, "bubble_stiffness",
- SPRING_AFTER_FLING_STIFFNESS /* default */);
+ STACK_SPRING_STIFFNESS /* default */);
final float dampingRatio = Settings.Secure.getFloat(contentResolver, "bubble_damping",
SPRING_AFTER_FLING_DAMPING_RATIO);
final float friction = Settings.Secure.getFloat(contentResolver, "bubble_friction",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
index 00bd9e5..59374a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
@@ -19,7 +19,6 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ITaskStackListener;
import android.content.ComponentName;
-import android.os.IBinder;
import android.window.TaskSnapshot;
import androidx.annotation.BinderThread;
@@ -85,6 +84,4 @@
default void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }
default void onActivityRotation(int displayId) { }
-
- default void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
index db34248..e94080a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
@@ -22,7 +22,6 @@
import android.app.TaskStackListener;
import android.content.ComponentName;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Message;
import android.os.Trace;
import android.util.Log;
@@ -54,13 +53,12 @@
private static final int ON_TASK_MOVED_TO_FRONT = 12;
private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 13;
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 14;
- private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 15;
- private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 16;
- private static final int ON_TASK_DISPLAY_CHANGED = 17;
- private static final int ON_TASK_LIST_UPDATED = 18;
- private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 19;
- private static final int ON_TASK_DESCRIPTION_CHANGED = 20;
- private static final int ON_ACTIVITY_ROTATION = 21;
+ private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 15;
+ private static final int ON_TASK_DISPLAY_CHANGED = 16;
+ private static final int ON_TASK_LIST_UPDATED = 17;
+ private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 18;
+ private static final int ON_TASK_DESCRIPTION_CHANGED = 19;
+ private static final int ON_ACTIVITY_ROTATION = 20;
/**
* List of {@link TaskStackListenerCallback} registered from {@link #addListener}.
@@ -264,13 +262,6 @@
}
@Override
- public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
- mMainHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId,
- 0 /* unused */,
- activityToken).sendToTarget();
- }
-
- @Override
public boolean handleMessage(Message msg) {
synchronized (mTaskStackListeners) {
switch (msg.what) {
@@ -383,13 +374,6 @@
}
break;
}
- case ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onSizeCompatModeActivityChanged(
- msg.arg1, (IBinder) msg.obj);
- }
- break;
- }
case ON_BACK_PRESSED_ON_TASK_ROOT: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onBackPressedOnTaskRoot(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 800150c..35dcdd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -34,8 +34,11 @@
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -84,8 +87,7 @@
private DragSession mSession;
public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
- this(context, ActivityTaskManager.getInstance(), splitScreen,
- new DefaultStarter(context, splitScreen));
+ this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context));
}
@VisibleForTesting
@@ -94,7 +96,7 @@
mContext = context;
mActivityTaskManager = activityTaskManager;
mSplitScreen = splitScreen;
- mStarter = starter;
+ mStarter = mSplitScreen != null ? mSplitScreen : starter;
}
/**
@@ -195,39 +197,23 @@
return;
}
- final ClipDescription description = data.getDescription();
- final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
- final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
- final Intent dragData = mSession.dragData;
final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
- final Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
- ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
- : new Bundle();
- if (target.type == TYPE_FULLSCREEN) {
- // Exit split stages if needed
- mStarter.exitSplitScreen();
- } else if (mSplitScreen != null) {
+ @SplitScreen.StageType int stage = STAGE_TYPE_UNDEFINED;
+ @SplitScreen.StagePosition int position = STAGE_POSITION_UNDEFINED;
+ if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
// Update launch options for the split side we are targeting.
- final int position = leftOrTop
- ? SIDE_STAGE_POSITION_TOP_OR_LEFT : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
if (!inSplitScreen) {
- // Update the side stage position to match where we want to launch.
- mSplitScreen.setSideStagePosition(position);
+ // Launch in the side stage if we are not in split-screen already.
+ stage = STAGE_TYPE_SIDE;
}
- mSplitScreen.updateActivityOptions(opts, position);
}
- if (isTask) {
- mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts);
- } else if (isShortcut) {
- mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME),
- dragData.getStringExtra(EXTRA_SHORTCUT_ID),
- opts, dragData.getParcelableExtra(EXTRA_USER));
- } else {
- mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
- }
+ final ClipDescription description = data.getDescription();
+ final Intent dragData = mSession.dragData;
+ mStarter.startClipDescription(description, dragData, stage, position);
}
/**
@@ -247,7 +233,6 @@
int runningTaskActType = ACTIVITY_TYPE_STANDARD;
boolean runningTaskIsResizeable;
boolean dragItemSupportsSplitscreen;
- boolean isPhone;
DragSession(Context context, ActivityTaskManager activityTaskManager,
DisplayLayout dispLayout, ClipData data) {
@@ -275,7 +260,6 @@
final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo();
dragItemSupportsSplitscreen = info == null
|| ActivityInfo.isResizeableMode(info.resizeMode);
- isPhone = mContext.getResources().getConfiguration().smallestScreenWidthDp < 600;
dragData = mInitialDragData.getItemAt(0).getIntent();
}
}
@@ -284,11 +268,33 @@
* Interface for actually committing the task launches.
*/
@VisibleForTesting
- interface Starter {
- void startTask(int taskId, Bundle activityOptions);
- void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
- UserHandle user);
- void startIntent(PendingIntent intent, Bundle activityOptions);
+ public interface Starter {
+ default void startClipDescription(ClipDescription description, Intent intent,
+ @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position) {
+ final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+ final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+ ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
+
+ if (isTask) {
+ final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+ startTask(taskId, stage, position, opts);
+ } else if (isShortcut) {
+ final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+ final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+ final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
+ startShortcut(packageName, id, stage, position, opts, user);
+ } else {
+ startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, opts);
+ }
+ }
+ void startTask(int taskId, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId,
+ @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+ @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
void exitSplitScreen();
}
@@ -299,39 +305,39 @@
*/
private static class DefaultStarter implements Starter {
private final Context mContext;
- private final SplitScreen mSplitScreen;
- public DefaultStarter(Context context, SplitScreen splitScreen) {
+ public DefaultStarter(Context context) {
mContext = context;
- mSplitScreen = splitScreen;
}
@Override
- public void startTask(int taskId, Bundle activityOptions) {
+ public void startTask(int taskId, int stage, int position,
+ @Nullable Bundle options) {
try {
- ActivityTaskManager.getService().startActivityFromRecents(taskId, activityOptions);
+ ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to launch task", e);
}
}
@Override
- public void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
- UserHandle user) {
+ public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ @Nullable Bundle options, UserHandle user) {
try {
LauncherApps launcherApps =
mContext.getSystemService(LauncherApps.class);
launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
- activityOptions, user);
+ options, user);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "Failed to launch shortcut", e);
}
}
@Override
- public void startIntent(PendingIntent intent, Bundle activityOptions) {
+ public void startIntent(PendingIntent intent, int stage, int position,
+ @Nullable Bundle options) {
try {
- intent.send(null, 0, null, null, null, null, activityOptions);
+ intent.send(null, 0, null, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
Slog.e(TAG, "Failed to launch activity", e);
}
@@ -339,14 +345,12 @@
@Override
public void enterSplitScreen(int taskId, boolean leftOrTop) {
- mSplitScreen.moveToSideStage(taskId,
- leftOrTop ? SIDE_STAGE_POSITION_TOP_OR_LEFT
- : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
+ throw new UnsupportedOperationException("enterSplitScreen not implemented by starter");
}
@Override
public void exitSplitScreen() {
- mSplitScreen.exitSplitScreen();
+ throw new UnsupportedOperationException("exitSplitScreen not implemented by starter");
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 90992fb..45aa387 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -21,6 +21,7 @@
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.app.TaskInfo;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.SurfaceControl;
@@ -99,18 +100,20 @@
@SuppressWarnings("unchecked")
@VisibleForTesting
- public PipTransitionAnimator getAnimator(SurfaceControl leash,
+ public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
Rect destinationBounds, float alphaStart, float alphaEnd) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
+ PipTransitionAnimator.ofAlpha(taskInfo, leash, destinationBounds, alphaStart,
+ alphaEnd));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
mCurrentAnimator.updateEndValue(alphaEnd);
} else {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
+ PipTransitionAnimator.ofAlpha(taskInfo, leash, destinationBounds, alphaStart,
+ alphaEnd));
}
return mCurrentAnimator;
}
@@ -131,13 +134,13 @@
* the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
*/
@VisibleForTesting
- public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect baseBounds,
- Rect startBounds, Rect endBounds, Rect sourceHintRect,
+ public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
+ Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, float startingAngle) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, startBounds, startBounds, endBounds,
- sourceHintRect, direction, 0 /* startingAngle */));
+ PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
+ endBounds, sourceHintRect, direction, 0 /* startingAngle */));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
// If we are still animating the fade into pip, then just move the surface and ensure
@@ -152,8 +155,8 @@
} else {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, baseBounds, startBounds, endBounds,
- sourceHintRect, direction, startingAngle));
+ PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
+ endBounds, sourceHintRect, direction, startingAngle));
}
return mCurrentAnimator;
}
@@ -177,18 +180,18 @@
/**
* Called when PiP animation is started.
*/
- public void onPipAnimationStart(PipTransitionAnimator animator) {}
+ public void onPipAnimationStart(TaskInfo taskInfo, PipTransitionAnimator animator) {}
/**
* Called when PiP animation is ended.
*/
- public void onPipAnimationEnd(SurfaceControl.Transaction tx,
+ public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
PipTransitionAnimator animator) {}
/**
* Called when PiP animation is cancelled.
*/
- public void onPipAnimationCancel(PipTransitionAnimator animator) {}
+ public void onPipAnimationCancel(TaskInfo taskInfo, PipTransitionAnimator animator) {}
}
/**
@@ -198,6 +201,7 @@
public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
ValueAnimator.AnimatorUpdateListener,
ValueAnimator.AnimatorListener {
+ private final TaskInfo mTaskInfo;
private final SurfaceControl mLeash;
private final @AnimationType int mAnimationType;
private final Rect mDestinationBounds = new Rect();
@@ -213,9 +217,10 @@
private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private @TransitionDirection int mTransitionDirection;
- private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
- Rect destinationBounds, T baseValue, T startValue, T endValue,
- float startingAngle) {
+ private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash,
+ @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue,
+ T endValue, float startingAngle) {
+ mTaskInfo = taskInfo;
mLeash = leash;
mAnimationType = animationType;
mDestinationBounds.set(destinationBounds);
@@ -234,7 +239,7 @@
mCurrentValue = mStartValue;
onStartTransaction(mLeash, newSurfaceControlTransaction());
if (mPipAnimationCallback != null) {
- mPipAnimationCallback.onPipAnimationStart(this);
+ mPipAnimationCallback.onPipAnimationStart(mTaskInfo, this);
}
}
@@ -250,14 +255,14 @@
final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
onEndTransaction(mLeash, tx, mTransitionDirection);
if (mPipAnimationCallback != null) {
- mPipAnimationCallback.onPipAnimationEnd(tx, this);
+ mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this);
}
}
@Override
public void onAnimationCancel(Animator animation) {
if (mPipAnimationCallback != null) {
- mPipAnimationCallback.onPipAnimationCancel(this);
+ mPipAnimationCallback.onPipAnimationCancel(mTaskInfo, this);
}
}
@@ -368,9 +373,9 @@
abstract void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction);
- static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash,
+ static PipTransitionAnimator<Float> ofAlpha(TaskInfo taskInfo, SurfaceControl leash,
Rect destinationBounds, float startValue, float endValue) {
- return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA,
+ return new PipTransitionAnimator<Float>(taskInfo, leash, ANIM_TYPE_ALPHA,
destinationBounds, startValue, startValue, endValue, 0) {
@Override
void applySurfaceControlTransaction(SurfaceControl leash,
@@ -403,7 +408,7 @@
};
}
- static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
+ static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, float startingAngle) {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
@@ -427,7 +432,7 @@
final Rect sourceInsets = new Rect(0, 0, 0, 0);
// construct new Rect instances in case they are recycled
- return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
+ return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
startingAngle) {
private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index a8961ea..ac5d14c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -19,7 +19,9 @@
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import android.annotation.NonNull;
+import android.app.PictureInPictureParams;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
@@ -27,7 +29,6 @@
import android.util.DisplayMetrics;
import android.util.Size;
import android.util.TypedValue;
-import android.view.DisplayInfo;
import android.view.Gravity;
import com.android.wm.shell.common.DisplayLayout;
@@ -142,11 +143,53 @@
true /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
}
+ /**
+ *
+ * Get the smallest/most minimal size allowed.
+ */
+ public Size getMinimalSize(ActivityInfo activityInfo) {
+ if (activityInfo == null || activityInfo.windowLayout == null) {
+ return null;
+ }
+ final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
+ // -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout>
+ // without minWidth/minHeight
+ if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
+ return new Size(windowLayout.minWidth, windowLayout.minHeight);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the source hint rect if it is valid (if provided and is contained by the current
+ * task bounds).
+ */
+ public static Rect getValidSourceHintRect(PictureInPictureParams params, Rect sourceBounds) {
+ final Rect sourceHintRect = params != null && params.hasSourceBoundsHint()
+ ? params.getSourceRectHint()
+ : null;
+ if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) {
+ return sourceHintRect;
+ }
+ return null;
+ }
+
public float getDefaultAspectRatio() {
return mDefaultAspectRatio;
}
/**
+ *
+ * Give the aspect ratio if the supplied PiP params have one, or else return default.
+ */
+ public float getAspectRatioOrDefault(
+ @android.annotation.Nullable PictureInPictureParams params) {
+ return params != null && params.hasSetAspectRatio()
+ ? params.getAspectRatio()
+ : getDefaultAspectRatio();
+ }
+
+ /**
* @return whether the given {@param aspectRatio} is valid.
*/
private boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index b112c51..cb39b4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -25,7 +25,6 @@
import android.graphics.Rect;
import android.util.Size;
import android.view.Display;
-import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.TriConsumer;
@@ -344,6 +343,16 @@
}
}
+ /**
+ * Initialize states when first entering PiP.
+ */
+ public void setBoundsStateForEntry(ComponentName componentName, float aspectRatio,
+ Size overrideMinSize) {
+ setLastPipComponentName(componentName);
+ setAspectRatio(aspectRatio);
+ setOverrideMinSize(overrideMinSize);
+ }
+
/** Returns whether the shelf is currently showing. */
public boolean isShelfShowing() {
return mIsShelfShowing;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b7958b7..fb83006 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -41,10 +41,10 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PictureInPictureParams;
+import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -54,7 +54,6 @@
import android.os.RemoteException;
import android.util.Log;
import android.util.Rational;
-import android.util.Size;
import android.view.Display;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
@@ -63,7 +62,6 @@
import android.window.WindowContainerTransactionCallback;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -71,10 +69,10 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.transition.Transitions;
+
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -132,8 +130,8 @@
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipMenuController mPipMenuController;
private final PipAnimationController mPipAnimationController;
+ private final PipTransitionController mPipTransitionController;
private final PipUiEventLogger mPipUiEventLoggerLogger;
- private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
@@ -145,7 +143,8 @@
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
new PipAnimationController.PipAnimationCallback() {
@Override
- public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) {
+ public void onPipAnimationStart(TaskInfo taskInfo,
+ PipAnimationController.PipTransitionAnimator animator) {
final int direction = animator.getTransitionDirection();
if (direction == TRANSITION_DIRECTION_TO_PIP) {
// TODO (b//169221267): Add jank listener for transactions without buffer updates.
@@ -156,7 +155,7 @@
}
@Override
- public void onPipAnimationEnd(SurfaceControl.Transaction tx,
+ public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
PipAnimationController.PipTransitionAnimator animator) {
final int direction = animator.getTransitionDirection();
finishResize(tx, animator.getDestinationBounds(), direction,
@@ -170,7 +169,8 @@
}
@Override
- public void onPipAnimationCancel(PipAnimationController.PipTransitionAnimator animator) {
+ public void onPipAnimationCancel(TaskInfo taskInfo,
+ PipAnimationController.PipTransitionAnimator animator) {
sendOnPipTransitionCancelled(animator.getTransitionDirection());
}
};
@@ -202,7 +202,9 @@
public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState,
@NonNull PipBoundsAlgorithm boundsHandler,
@NonNull PipMenuController pipMenuController,
+ @NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
+ @NonNull PipTransitionController pipTransitionController,
Optional<LegacySplitScreen> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@@ -211,10 +213,11 @@
mPipBoundsState = pipBoundsState;
mPipBoundsAlgorithm = boundsHandler;
mPipMenuController = pipMenuController;
+ mPipTransitionController = pipTransitionController;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = surfaceTransactionHelper;
- mPipAnimationController = new PipAnimationController(mSurfaceTransactionHelper);
+ mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mSplitScreenOptional = splitScreenOptional;
@@ -246,13 +249,6 @@
}
/**
- * Registers {@link PipTransitionCallback} to receive transition callbacks.
- */
- public void registerPipTransitionCallback(PipTransitionCallback callback) {
- mPipTransitionCallbacks.add(callback);
- }
-
- /**
* Registers a callback when a display change has been detected when we enter PiP.
*/
public void registerOnDisplayIdChangeCallback(IntConsumer onDisplayIdChangeCallback) {
@@ -275,7 +271,7 @@
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams) {
mInSwipePipToHomeTransition = true;
- sendOnPipTransitionStarted(componentName, TRANSITION_DIRECTION_TO_PIP);
+ sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP);
setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo);
// disable the conflicting transaction from fixed rotation, see also
// onFixedRotationStarted and onFixedRotationFinished
@@ -296,9 +292,9 @@
private void setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params,
ActivityInfo activityInfo) {
- mPipBoundsState.setLastPipComponentName(componentName);
- mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(params));
- mPipBoundsState.setOverrideMinSize(getMinimalSize(activityInfo));
+ mPipBoundsState.setBoundsStateForEntry(componentName,
+ mPipBoundsAlgorithm.getAspectRatioOrDefault(params),
+ mPipBoundsAlgorithm.getMinimalSize(activityInfo));
}
/**
@@ -362,8 +358,8 @@
t.apply();
// Make sure to grab the latest source hint rect as it could have been
// updated right after applying the windowing mode change.
- final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams,
- destinationBounds);
+ final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+ mPictureInPictureParams, destinationBounds);
scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
0 /* startingAngle */, sourceHintRect, direction,
animationDurationMs, null /* updateBoundsCallback */);
@@ -398,7 +394,7 @@
// removePipImmediately is expected when the following animation finishes.
mPipAnimationController
- .getAnimator(mLeash, mPipBoundsState.getBounds(), 1f, 0f)
+ .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), 1f, 0f)
.setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
@@ -470,10 +466,17 @@
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+ mPipMenuController.attach(mLeash);
+ }
+ return;
+ }
+
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
mPipMenuController.attach(mLeash);
- final Rect sourceHintRect = getValidSourceHintRect(info.pictureInPictureParams,
- currentBounds);
+ final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+ info.pictureInPictureParams, currentBounds);
scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */,
sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
null /* updateBoundsCallback */);
@@ -486,21 +489,6 @@
}
}
- /**
- * Returns the source hint rect if it is valid (if provided and is contained by the current
- * task bounds).
- */
- private Rect getValidSourceHintRect(PictureInPictureParams params, Rect sourceBounds) {
- final Rect sourceHintRect = params != null
- && params.hasSourceBoundsHint()
- ? params.getSourceRectHint()
- : null;
- if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) {
- return sourceHintRect;
- }
- return null;
- }
-
@VisibleForTesting
void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) {
// If we are fading the PIP in, then we should move the pip to the final location as
@@ -512,7 +500,7 @@
tx.apply();
applyEnterPipSyncTransaction(destinationBounds, () -> {
mPipAnimationController
- .getAnimator(mLeash, destinationBounds, 0f, 1f)
+ .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
@@ -547,19 +535,10 @@
private void sendOnPipTransitionStarted(
@PipAnimationController.TransitionDirection int direction) {
- sendOnPipTransitionStarted(mTaskInfo.baseActivity, direction);
- }
-
- private void sendOnPipTransitionStarted(ComponentName componentName,
- @PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) {
mState = State.ENTERING_PIP;
}
- final Rect pipBounds = mPipBoundsState.getBounds();
- for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
- final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
- callback.onPipTransitionStarted(componentName, direction, pipBounds);
- }
+ mPipTransitionController.sendOnPipTransitionStarted(direction);
}
private void sendOnPipTransitionFinished(
@@ -567,18 +546,12 @@
if (direction == TRANSITION_DIRECTION_TO_PIP) {
mState = State.ENTERED_PIP;
}
- for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
- final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
- callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction);
- }
+ mPipTransitionController.sendOnPipTransitionFinished(direction);
}
private void sendOnPipTransitionCancelled(
@PipAnimationController.TransitionDirection int direction) {
- for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
- final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
- callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction);
- }
+ mPipTransitionController.sendOnPipTransitionCancelled(direction);
}
/**
@@ -616,7 +589,8 @@
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
mPipBoundsState.setLastPipComponentName(info.topActivity);
- mPipBoundsState.setOverrideMinSize(getMinimalSize(info.topActivityInfo));
+ mPipBoundsState.setOverrideMinSize(
+ mPipBoundsAlgorithm.getMinimalSize(info.topActivityInfo));
final PictureInPictureParams newParams = info.pictureInPictureParams;
if (newParams == null || !applyPictureInPictureParams(newParams)) {
Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
@@ -1081,33 +1055,14 @@
Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
? mPipBoundsState.getBounds() : currentBounds;
mPipAnimationController
- .getAnimator(mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect,
- direction, startingAngle)
+ .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
+ sourceHintRect, direction, startingAngle)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
.start();
}
- private Size getMinimalSize(ActivityInfo activityInfo) {
- if (activityInfo == null || activityInfo.windowLayout == null) {
- return null;
- }
- final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
- // -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout>
- // without minWidth/minHeight
- if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
- return new Size(windowLayout.minWidth, windowLayout.minHeight);
- }
- return null;
- }
-
- private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
- return params == null || !params.hasSetAspectRatio()
- ? mPipBoundsAlgorithm.getDefaultAspectRatio()
- : params.getAspectRatio();
- }
-
/**
* Sync with {@link LegacySplitScreen} on destination bounds if PiP is going to split screen.
*
@@ -1157,24 +1112,4 @@
public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_PIP);
}
-
- /**
- * Callback interface for PiP transitions (both from and to PiP mode)
- */
- public interface PipTransitionCallback {
- /**
- * Callback when the pip transition is started.
- */
- void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds);
-
- /**
- * Callback when the pip transition is finished.
- */
- void onPipTransitionFinished(ComponentName activity, int direction);
-
- /**
- * Callback when the pip transition is cancelled.
- */
- void onPipTransitionCanceled(ComponentName activity, int direction);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
new file mode 100644
index 0000000..91e8c99
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -0,0 +1,162 @@
+/*
+ * 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.wm.shell.pip;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
+import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
+import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+
+import android.app.TaskInfo;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.transition.Transitions;
+
+/**
+ * Implementation of transitions for PiP on phone. Responsible for enter (alpha, bounds) and
+ * exit animation.
+ */
+public class PipTransition extends PipTransitionController {
+
+ private final int mEnterExitAnimationDuration;
+ private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+ private Transitions.TransitionFinishCallback mFinishCallback;
+
+ public PipTransition(Context context,
+ PipBoundsState pipBoundsState, PipMenuController pipMenuController,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipAnimationController pipAnimationController,
+ Transitions transitions,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer) {
+ super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
+ pipAnimationController, transitions, shellTaskOrganizer);
+ mEnterExitAnimationDuration = context.getResources()
+ .getInteger(R.integer.config_pipResizeAnimationDuration);
+ }
+
+ @Override
+ public boolean startAnimation(@android.annotation.NonNull IBinder transition,
+ @android.annotation.NonNull TransitionInfo info,
+ @android.annotation.NonNull SurfaceControl.Transaction t,
+ @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_PINNED) {
+ mFinishCallback = finishCallback;
+ return startEnterAnimation(change.getTaskInfo(), change.getLeash(), t);
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
+ @PipAnimationController.TransitionDirection int direction,
+ SurfaceControl.Transaction tx) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareFinishResizeTransaction(taskInfo, destinationBounds,
+ direction, tx, wct);
+ mFinishCallback.onTransitionFinished(wct, null);
+ finishResizeForMenu(destinationBounds);
+ }
+
+ private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
+ final SurfaceControl.Transaction t) {
+ setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
+ taskInfo.topActivityInfo);
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+ final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
+ PipAnimationController.PipTransitionAnimator animator;
+ if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+ final Rect sourceHintRect =
+ PipBoundsAlgorithm.getValidSourceHintRect(
+ taskInfo.pictureInPictureParams, currentBounds);
+ animator = mPipAnimationController.getAnimator(taskInfo, leash,
+ currentBounds, currentBounds, destinationBounds, sourceHintRect,
+ TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */);
+ } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ t.setAlpha(leash, 0f);
+ t.apply();
+ animator = mPipAnimationController.getAnimator(taskInfo, leash,
+ destinationBounds, 0f, 1f);
+ mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+ } else {
+ throw new RuntimeException("Unrecognized animation type: "
+ + mOneShotAnimationType);
+ }
+ animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(mEnterExitAnimationDuration)
+ .start();
+ return true;
+ }
+
+ private void finishResizeForMenu(Rect destinationBounds) {
+ mPipMenuController.movePipMenu(null, null, destinationBounds);
+ mPipMenuController.updateMenuBounds(destinationBounds);
+ }
+
+ private void prepareFinishResizeTransaction(TaskInfo taskInfo, Rect destinationBounds,
+ @PipAnimationController.TransitionDirection int direction,
+ SurfaceControl.Transaction tx,
+ WindowContainerTransaction wct) {
+ Rect taskBounds = null;
+ if (isInPipDirection(direction)) {
+ // If we are animating from fullscreen using a bounds animation, then reset the
+ // activity windowing mode set by WM, and set the task bounds to the final bounds
+ taskBounds = destinationBounds;
+ wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+ wct.scheduleFinishEnterPip(taskInfo.token, destinationBounds);
+ } else if (isOutPipDirection(direction)) {
+ // If we are animating to fullscreen, then we need to reset the override bounds
+ // on the task to ensure that the task "matches" the parent's bounds.
+ taskBounds = (direction == TRANSITION_DIRECTION_LEAVE_PIP)
+ ? null : destinationBounds;
+ wct.setWindowingMode(taskInfo.token, getOutPipWindowingMode());
+ // Simply reset the activity mode set prior to the animation running.
+ wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+ }
+
+ wct.setBounds(taskInfo.token, taskBounds);
+ wct.setBoundsChangeTransaction(taskInfo.token, tx);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
new file mode 100644
index 0000000..d801c91
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -0,0 +1,184 @@
+/*
+ * 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.wm.shell.pip;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+
+import android.app.PictureInPictureParams;
+import android.app.TaskInfo;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible supplying PiP Transitions.
+ */
+public abstract class PipTransitionController implements Transitions.TransitionHandler {
+
+ protected final PipAnimationController mPipAnimationController;
+ protected final PipBoundsAlgorithm mPipBoundsAlgorithm;
+ protected final PipBoundsState mPipBoundsState;
+ protected final ShellTaskOrganizer mShellTaskOrganizer;
+ protected final PipMenuController mPipMenuController;
+ private final Handler mMainHandler;
+ private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
+
+ protected final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
+ new PipAnimationController.PipAnimationCallback() {
+ @Override
+ public void onPipAnimationStart(TaskInfo taskInfo,
+ PipAnimationController.PipTransitionAnimator animator) {
+ final int direction = animator.getTransitionDirection();
+ if (direction == TRANSITION_DIRECTION_TO_PIP) {
+ // TODO (b//169221267): Add jank listener for transactions without buffer
+ // updates.
+ //InteractionJankMonitor.getInstance().begin(
+ // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000);
+ }
+ sendOnPipTransitionStarted(direction);
+ }
+
+ @Override
+ public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
+ PipAnimationController.PipTransitionAnimator animator) {
+ final int direction = animator.getTransitionDirection();
+ mPipBoundsState.setBounds(animator.getDestinationBounds());
+ if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
+ return;
+ }
+ onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx);
+ sendOnPipTransitionFinished(direction);
+ if (direction == TRANSITION_DIRECTION_TO_PIP) {
+ // TODO (b//169221267): Add jank listener for transactions without buffer
+ // updates.
+ //InteractionJankMonitor.getInstance().end(
+ // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
+ }
+ }
+
+ @Override
+ public void onPipAnimationCancel(TaskInfo taskInfo,
+ PipAnimationController.PipTransitionAnimator animator) {
+ sendOnPipTransitionCancelled(animator.getTransitionDirection());
+ }
+ };
+
+ /**
+ * Called when transition is about to finish. This is usually for performing tasks such as
+ * applying WindowContainerTransaction to finalize the PiP bounds and send to the framework.
+ */
+ public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
+ @PipAnimationController.TransitionDirection int direction,
+ SurfaceControl.Transaction tx) {
+ }
+
+ public PipTransitionController(PipBoundsState pipBoundsState,
+ PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipAnimationController pipAnimationController, Transitions transitions,
+ @android.annotation.NonNull ShellTaskOrganizer shellTaskOrganizer) {
+ mPipBoundsState = pipBoundsState;
+ mPipMenuController = pipMenuController;
+ mShellTaskOrganizer = shellTaskOrganizer;
+ mPipBoundsAlgorithm = pipBoundsAlgorithm;
+ mPipAnimationController = pipAnimationController;
+ mMainHandler = new Handler(Looper.getMainLooper());
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ transitions.addHandler(this);
+ }
+ }
+
+ /**
+ * Registers {@link PipTransitionCallback} to receive transition callbacks.
+ */
+ public void registerPipTransitionCallback(PipTransitionCallback callback) {
+ mPipTransitionCallbacks.add(callback);
+ }
+
+ protected void sendOnPipTransitionStarted(
+ @PipAnimationController.TransitionDirection int direction) {
+ final Rect pipBounds = mPipBoundsState.getBounds();
+ for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+ callback.onPipTransitionStarted(direction, pipBounds);
+ }
+ }
+
+ protected void sendOnPipTransitionFinished(
+ @PipAnimationController.TransitionDirection int direction) {
+ for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+ callback.onPipTransitionFinished(direction);
+ }
+ }
+
+ protected void sendOnPipTransitionCancelled(
+ @PipAnimationController.TransitionDirection int direction) {
+ for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+ callback.onPipTransitionCanceled(direction);
+ }
+ }
+
+ /**
+ * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined
+ * and can be overridden to restore to an alternate windowing mode.
+ */
+ public int getOutPipWindowingMode() {
+ // By default, simply reset the windowing mode to undefined.
+ return WINDOWING_MODE_UNDEFINED;
+ }
+
+ protected void setBoundsStateForEntry(ComponentName componentName,
+ PictureInPictureParams params,
+ ActivityInfo activityInfo) {
+ mPipBoundsState.setBoundsStateForEntry(componentName,
+ mPipBoundsAlgorithm.getAspectRatioOrDefault(params),
+ mPipBoundsAlgorithm.getMinimalSize(activityInfo));
+ }
+
+ /**
+ * Callback interface for PiP transitions (both from and to PiP mode)
+ */
+ public interface PipTransitionCallback {
+ /**
+ * Callback when the pip transition is started.
+ */
+ void onPipTransitionStarted(int direction, Rect pipBounds);
+
+ /**
+ * Callback when the pip transition is finished.
+ */
+ void onPipTransitionFinished(int direction);
+
+ /**
+ * Callback when the pip transition is cancelled.
+ */
+ void onPipTransitionCanceled(int direction);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index c06f9d0..c3970e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -61,6 +61,7 @@
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipUtils;
import java.io.PrintWriter;
@@ -69,7 +70,7 @@
/**
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
-public class PipController implements PipTaskOrganizer.PipTransitionCallback {
+public class PipController implements PipTransitionController.PipTransitionCallback {
private static final String TAG = "PipController";
private Context mContext;
@@ -82,6 +83,7 @@
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private PipBoundsState mPipBoundsState;
private PipTouchHandler mTouchHandler;
+ private PipTransitionController mPipTransitionController;
protected final PipImpl mImpl = new PipImpl();
private final Rect mTmpInsetBounds = new Rect();
@@ -214,7 +216,6 @@
}
}
-
/**
* Instantiates {@link PipController}, returns {@code null} if the feature not supported.
*/
@@ -223,7 +224,8 @@
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
- PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
+ PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
+ WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
Slog.w(TAG, "Device doesn't support Pip feature");
@@ -232,7 +234,8 @@
return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer,
- pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor)
+ pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
+ taskStackListener, mainExecutor)
.mImpl;
}
@@ -245,6 +248,7 @@
PhonePipMenuController phonePipMenuController,
PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler,
+ PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
ShellExecutor mainExecutor
@@ -266,9 +270,10 @@
mMenuController = phonePipMenuController;
mTouchHandler = pipTouchHandler;
mAppOpsListener = pipAppOpsListener;
+ mPipTransitionController = pipTransitionController;
mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
INPUT_CONSUMER_PIP, mainExecutor);
- mPipTaskOrganizer.registerPipTransitionCallback(this);
+ mPipTransitionController.registerPipTransitionCallback(this);
mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
mPipBoundsState.setDisplayId(displayId);
onDisplayChanged(displayController.getDisplayLayout(displayId),
@@ -489,7 +494,7 @@
}
@Override
- public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
+ public void onPipTransitionStarted(int direction, Rect pipBounds) {
if (isOutPipDirection(direction)) {
// Exiting PIP, save the reentry state to restore to when re-entering.
saveReentryState(pipBounds);
@@ -514,12 +519,12 @@
}
@Override
- public void onPipTransitionFinished(ComponentName activity, int direction) {
+ public void onPipTransitionFinished(int direction) {
onPipTransitionFinishedOrCanceled(direction);
}
@Override
- public void onPipTransitionCanceled(ComponentName activity, int direction) {
+ public void onPipTransitionCanceled(int direction) {
onPipTransitionFinishedOrCanceled(direction);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 2df3e2e..962c467 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -231,7 +231,6 @@
&& (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
mAllowTouches = !disallowTouchesUntilAnimationEnd;
cancelDelayedHide();
- updateActionViews(stackBounds);
if (mMenuContainerAnimator != null) {
mMenuContainerAnimator.cancel();
}
@@ -280,6 +279,7 @@
setVisibility(VISIBLE);
mMenuContainerAnimator.start();
}
+ updateActionViews(stackBounds);
} else {
// If we are already visible, then just start the delayed dismiss and unregister any
// existing input consumers from the previous drag
@@ -395,7 +395,7 @@
return true;
});
- if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
+ if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE || mMenuState == MENU_STATE_NONE) {
actionsContainer.setVisibility(View.INVISIBLE);
} else {
actionsContainer.setVisibility(View.VISIBLE);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index fd4ea61..b19dcae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -44,6 +43,7 @@
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
import java.util.function.Consumer;
@@ -152,13 +152,13 @@
*/
private Runnable mPostPipTransitionCallback;
- private final PipTaskOrganizer.PipTransitionCallback mPipTransitionCallback =
- new PipTaskOrganizer.PipTransitionCallback() {
+ private final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
+ new PipTransitionController.PipTransitionCallback() {
@Override
- public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {}
+ public void onPipTransitionStarted(int direction, Rect pipBounds) {}
@Override
- public void onPipTransitionFinished(ComponentName activity, int direction) {
+ public void onPipTransitionFinished(int direction) {
if (mPostPipTransitionCallback != null) {
mPostPipTransitionCallback.run();
mPostPipTransitionCallback = null;
@@ -166,20 +166,20 @@
}
@Override
- public void onPipTransitionCanceled(ComponentName activity, int direction) {}
+ public void onPipTransitionCanceled(int direction) {}
};
public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController,
- PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator,
- ShellExecutor mainExecutor) {
+ PipSnapAlgorithm snapAlgorithm, PipTransitionController pipTransitionController,
+ FloatingContentCoordinator floatingContentCoordinator, ShellExecutor mainExecutor) {
mContext = context;
mPipTaskOrganizer = pipTaskOrganizer;
mPipBoundsState = pipBoundsState;
mMenuController = menuController;
mSnapAlgorithm = snapAlgorithm;
mFloatingContentCoordinator = floatingContentCoordinator;
- mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
+ pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
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 8fb358a..b91ba34 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
@@ -64,6 +64,7 @@
private static final String TAG = "PipResizeGestureHandler";
private static final int PINCH_RESIZE_SNAP_DURATION = 250;
private static final int PINCH_RESIZE_MAX_ANGLE_ROTATION = 45;
+ private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f;
private final Context mContext;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -539,6 +540,11 @@
// position correctly. Drag-resize does not need to move, so just finalize resize.
if (mUsingPinchToZoom) {
final Rect startBounds = new Rect(mLastResizeBounds);
+ // If user resize is pretty close to max size, just auto resize to max.
+ if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
+ || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
+ mLastResizeBounds.set(0, 0, mMaxSize.x, mMaxSize.y);
+ }
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds,
mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()));
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
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 3cb3ae8..e69c6f2 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
@@ -51,6 +51,7 @@
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipUiEventLogger;
import java.io.PrintWriter;
@@ -156,6 +157,7 @@
PipBoundsAlgorithm pipBoundsAlgorithm,
@NonNull PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer,
+ PipTransitionController pipTransitionController,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
ShellExecutor mainExecutor) {
@@ -168,7 +170,7 @@
mMenuController.addListener(new PipMenuListener());
mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer,
- mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(),
+ mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(), pipTransitionController,
floatingContentCoordinator, mainExecutor);
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 75fc9f5..56f183f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -46,6 +46,7 @@
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,7 +54,7 @@
/**
* Manages the picture-in-picture (PIP) UI and states.
*/
-public class TvPipController implements PipTaskOrganizer.PipTransitionCallback,
+public class TvPipController implements PipTransitionController.PipTransitionCallback,
TvPipMenuController.Delegate, TvPipNotificationController.Delegate {
private static final String TAG = "TvPipController";
static final boolean DEBUG = true;
@@ -105,6 +106,7 @@
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
+ PipTransitionController pipTransitionController,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
TvPipNotificationController pipNotificationController,
@@ -116,6 +118,7 @@
pipBoundsState,
pipBoundsAlgorithm,
pipTaskOrganizer,
+ pipTransitionController,
tvPipMenuController,
pipMediaController,
pipNotificationController,
@@ -129,6 +132,7 @@
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
+ PipTransitionController pipTransitionController,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
TvPipNotificationController pipNotificationController,
@@ -152,7 +156,7 @@
mTvPipMenuController.setDelegate(this);
mPipTaskOrganizer = pipTaskOrganizer;
- mPipTaskOrganizer.registerPipTransitionCallback(this);
+ pipTransitionController.registerPipTransitionCallback(this);
loadConfigurations();
@@ -302,17 +306,17 @@
}
@Override
- public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
+ public void onPipTransitionStarted(int direction, Rect pipBounds) {
if (DEBUG) Log.d(TAG, "onPipTransition_Started(), state=" + stateToName(mState));
}
@Override
- public void onPipTransitionCanceled(ComponentName activity, int direction) {
+ public void onPipTransitionCanceled(int direction) {
if (DEBUG) Log.d(TAG, "onPipTransition_Canceled(), state=" + stateToName(mState));
}
@Override
- public void onPipTransitionFinished(ComponentName activity, int direction) {
+ public void onPipTransitionFinished(int direction) {
if (DEBUG) Log.d(TAG, "onPipTransition_Finished(), state=" + stateToName(mState));
if (mState == STATE_PIP_MENU) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
new file mode 100644
index 0000000..b7caf72
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -0,0 +1,72 @@
+/*
+ * 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.wm.shell.pip.tv;
+
+import android.app.TaskInfo;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.transition.Transitions;
+
+/**
+ * PiP Transition for TV.
+ * TODO: Implement animation once TV is using Transitions.
+ */
+public class TvPipTransition extends PipTransitionController {
+ public TvPipTransition(PipBoundsState pipBoundsState,
+ PipMenuController pipMenuController,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipAnimationController pipAnimationController,
+ Transitions transitions,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer) {
+ super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController,
+ transitions, shellTaskOrganizer);
+ }
+
+ @Override
+ public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, int direction,
+ SurfaceControl.Transaction tx) {
+
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ return null;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index 8cb16c8..286c3b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -91,7 +91,6 @@
}
}
- // TODO move from SizeCompatModeActivityController from system UI.
@Override
public void onDisplayRemoved(int displayId) {
mDisplayContextCache.remove(displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 7c1b9d8..2c68092 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -18,12 +18,16 @@
import android.annotation.IntDef;
import android.app.ActivityManager;
+import android.app.PendingIntent;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import java.io.PrintWriter;
@@ -31,46 +35,93 @@
* Interface to engage split-screen feature.
*/
@ExternalThread
-public interface SplitScreen {
+public interface SplitScreen extends DragAndDropPolicy.Starter {
/**
- * Specifies that the side-stage is positioned at the top half of the screen if
+ * Stage position isn't specified normally meaning to use what ever it is currently set to.
+ */
+ int STAGE_POSITION_UNDEFINED = -1;
+ /**
+ * Specifies that a stage is positioned at the top half of the screen if
* in portrait mode or at the left half of the screen if in landscape mode.
*/
- int SIDE_STAGE_POSITION_TOP_OR_LEFT = 0;
+ int STAGE_POSITION_TOP_OR_LEFT = 0;
/**
- * Specifies that the side-stage is positioned at the bottom half of the screen if
+ * Specifies that a stage is positioned at the bottom half of the screen if
* in portrait mode or at the right half of the screen if in landscape mode.
*/
- int SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
+ int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
- @IntDef(prefix = { "SIDE_STAGE_POSITION_" }, value = {
- SIDE_STAGE_POSITION_TOP_OR_LEFT,
- SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT
+ @IntDef(prefix = { "STAGE_POSITION_" }, value = {
+ STAGE_POSITION_UNDEFINED,
+ STAGE_POSITION_TOP_OR_LEFT,
+ STAGE_POSITION_BOTTOM_OR_RIGHT
})
- @interface SideStagePosition {}
+ @interface StagePosition {}
+
+ /**
+ * Stage type isn't specified normally meaning to use what ever the default is.
+ * E.g. exit split-screen and launch the app in fullscreen.
+ */
+ int STAGE_TYPE_UNDEFINED = -1;
+ /**
+ * The main stage type.
+ * @see MainStage
+ */
+ int STAGE_TYPE_MAIN = 0;
+
+ /**
+ * The side stage type.
+ * @see SideStage
+ */
+ int STAGE_TYPE_SIDE = 1;
+
+ @IntDef(prefix = { "STAGE_TYPE_" }, value = {
+ STAGE_TYPE_UNDEFINED,
+ STAGE_TYPE_MAIN,
+ STAGE_TYPE_SIDE
+ })
+ @interface StageType {}
+
+ /** Callback interface for listening to changes in a split-screen stage. */
+ interface SplitScreenListener {
+ void onStagePositionChanged(@StageType int stage, @StagePosition int position);
+ void onTaskStageChanged(int taskId, @StageType int stage);
+ }
/** @return {@code true} if split-screen is currently visible. */
boolean isSplitScreenVisible();
/** Moves a task in the side-stage of split-screen. */
- boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition);
+ boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition);
/** Moves a task in the side-stage of split-screen. */
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SideStagePosition int sideStagePosition);
+ @StagePosition int sideStagePosition);
/** Removes a task from the side-stage of split-screen. */
boolean removeFromSideStage(int taskId);
/** Sets the position of the side-stage. */
- void setSideStagePosition(@SideStagePosition int sideStagePosition);
+ void setSideStagePosition(@StagePosition int sideStagePosition);
/** Hides the side-stage if it is currently visible. */
void setSideStageVisibility(boolean visible);
+ default void enterSplitScreen(int taskId, boolean leftOrTop) {
+ moveToSideStage(taskId,
+ leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
+ }
/** Removes the split-screen stages. */
void exitSplitScreen();
/** Gets the stage bounds. */
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
- /** Updates the launch activity options for the split position we want to launch it in. */
- void updateActivityOptions(Bundle opts, @SideStagePosition int position);
/** Dumps current status of split-screen. */
void dump(@NonNull PrintWriter pw, String prefix);
/** Called when the shell organizer has been registered. */
void onOrganizerRegistered();
+
+ void registerSplitScreenListener(SplitScreenListener listener);
+ void unregisterSplitScreenListener(SplitScreenListener listener);
+
+ void startTask(int taskId,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId, @StageType int stage,
+ @StagePosition int position, @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 27d3b81..18dd53b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -19,11 +19,19 @@
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.pm.LauncherApps;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -69,7 +77,7 @@
}
@Override
- public boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition) {
+ public boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
@@ -79,7 +87,7 @@
@Override
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SideStagePosition int sideStagePosition) {
+ @StagePosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
@@ -89,7 +97,7 @@
}
@Override
- public void setSideStagePosition(@SideStagePosition int sideStagePosition) {
+ public void setSideStagePosition(@StagePosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition);
}
@@ -109,8 +117,103 @@
}
@Override
- public void updateActivityOptions(Bundle opts, @SideStagePosition int position) {
- mStageCoordinator.updateActivityOptions(opts, position);
+ public void registerSplitScreenListener(SplitScreenListener listener) {
+ mStageCoordinator.registerSplitScreenListener(listener);
+ }
+
+ @Override
+ public void unregisterSplitScreenListener(SplitScreenListener listener) {
+ mStageCoordinator.unregisterSplitScreenListener(listener);
+ }
+
+ @Override
+ public void startTask(int taskId,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ options = resolveStartStage(stage, position, options);
+
+ try {
+ ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to launch task", e);
+ }
+ }
+
+ @Override
+ public void startShortcut(String packageName, String shortcutId, @StageType int stage,
+ @StagePosition int position, @Nullable Bundle options, UserHandle user) {
+ options = resolveStartStage(stage, position, options);
+
+ try {
+ LauncherApps launcherApps =
+ mContext.getSystemService(LauncherApps.class);
+ launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
+ options, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "Failed to launch shortcut", e);
+ }
+ }
+
+ @Override
+ public void startIntent(PendingIntent intent,
+ @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ options = resolveStartStage(stage, position, options);
+
+ try {
+ intent.send(null, 0, null, null, null, null, options);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "Failed to launch activity", e);
+ }
+ }
+
+ private Bundle resolveStartStage(@StageType int stage, @StagePosition int position,
+ @Nullable Bundle options) {
+ switch (stage) {
+ case STAGE_TYPE_UNDEFINED: {
+ // Use the stage of the specified position is valid.
+ if (position != STAGE_POSITION_UNDEFINED) {
+ if (position == mStageCoordinator.getSideStagePosition()) {
+ options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
+ } else {
+ options = resolveStartStage(STAGE_TYPE_MAIN, position, options);
+ }
+ } else {
+ // Exit split-screen and launch fullscreen since stage wasn't specified.
+ mStageCoordinator.exitSplitScreen();
+ }
+ break;
+ }
+ case STAGE_TYPE_SIDE: {
+ if (position != STAGE_POSITION_UNDEFINED) {
+ mStageCoordinator.setSideStagePosition(position);
+ } else {
+ position = mStageCoordinator.getSideStagePosition();
+ }
+ if (options == null) {
+ options = new Bundle();
+ }
+ mStageCoordinator.updateActivityOptions(options, position);
+ break;
+ }
+ case STAGE_TYPE_MAIN: {
+ if (position != STAGE_POSITION_UNDEFINED) {
+ // Set the side stage opposite of what we want to the main stage.
+ final int sideStagePosition = position == STAGE_POSITION_TOP_OR_LEFT
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+ mStageCoordinator.setSideStagePosition(sideStagePosition);
+ } else {
+ position = mStageCoordinator.getMainStagePosition();
+ }
+ if (options == null) {
+ options = new Bundle();
+ }
+ mStageCoordinator.updateActivityOptions(options, position);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown stage=" + stage);
+ }
+
+ return options;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index d571e75..176852b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -17,12 +17,14 @@
package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.app.ActivityManager;
import android.content.Context;
@@ -41,6 +43,8 @@
import com.android.wm.shell.common.split.SplitLayout;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -64,8 +68,7 @@
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
private final SideStage mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
- private @SplitScreen.SideStagePosition int mSideStagePosition =
- SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ private @SplitScreen.StagePosition int mSideStagePosition = STAGE_POSITION_BOTTOM_OR_RIGHT;
private final int mDisplayId;
private SplitLayout mSplitLayout;
@@ -75,6 +78,7 @@
private final ShellTaskOrganizer mTaskOrganizer;
private DisplayAreaInfo mDisplayAreaInfo;
private final Context mContext;
+ private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) {
@@ -107,7 +111,7 @@
}
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitScreen.SideStagePosition int sideStagePosition) {
+ @SplitScreen.StagePosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSideStagePosition = sideStagePosition;
mMainStage.activate(getMainStageBounds(), wct);
@@ -130,7 +134,16 @@
return result;
}
- void setSideStagePosition(@SplitScreen.SideStagePosition int sideStagePosition) {
+ @SplitScreen.StagePosition int getSideStagePosition() {
+ return mSideStagePosition;
+ }
+
+ @SplitScreen.StagePosition int getMainStagePosition() {
+ return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+ }
+
+ void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
mSideStagePosition = sideStagePosition;
if (mSideStageListener.mVisible) {
onStageVisibilityChanged(mSideStageListener);
@@ -163,7 +176,7 @@
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
}
- void updateActivityOptions(Bundle opts, @SplitScreen.SideStagePosition int position) {
+ void updateActivityOptions(Bundle opts, @SplitScreen.StagePosition int position) {
final StageTaskListener stage = position == mSideStagePosition ? mSideStage : mMainStage;
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
@@ -176,6 +189,35 @@
}
}
+ void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
+ if (mListeners.contains(listener)) return;
+ mListeners.add(listener);
+ listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
+ listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
+ mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
+ mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
+ }
+
+ void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
+ mListeners.remove(listener);
+ }
+
+ private void onStageChildTaskStatusChanged(
+ StageListenerImpl stageListener, int taskId, boolean present) {
+
+ int stage;
+ if (present) {
+ stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ } else {
+ // No longer on any stage
+ stage = STAGE_TYPE_UNDEFINED;
+ }
+
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onTaskStageChanged(taskId, stage);
+ }
+ }
+
private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -299,7 +341,7 @@
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
final boolean mainStageToTop = bottomOrRight
- && mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+ && mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
exitSplitScreen(mainStageToTop ? mMainStage : mSideStage);
}
@@ -326,8 +368,8 @@
@Override
public void onDoubleTappedDivider() {
- setSideStagePosition(mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
- ? SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT : SIDE_STAGE_POSITION_TOP_OR_LEFT);
+ setSideStagePosition(mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT);
}
@Override
@@ -380,12 +422,12 @@
}
private Rect getSideStageBounds() {
- return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
+ return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
}
private Rect getMainStageBounds() {
- return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
+ return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
}
@@ -429,6 +471,11 @@
}
@Override
+ public void onChildTaskStatusChanged(int taskId, boolean present) {
+ StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present);
+ }
+
+ @Override
public void onRootTaskVanished() {
reset();
StageCoordinator.this.onStageRootTaskVanished(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 1aa7552..6532993 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -58,6 +58,7 @@
public interface StageListenerCallbacks {
void onRootTaskAppeared();
void onStatusChanged(boolean visible, boolean hasChildren);
+ void onChildTaskStatusChanged(int taskId, boolean present);
void onRootTaskVanished();
}
private final StageListenerCallbacks mCallbacks;
@@ -83,9 +84,11 @@
mRootTaskInfo = taskInfo;
mCallbacks.onRootTaskAppeared();
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
- mChildrenLeashes.put(taskInfo.taskId, leash);
- mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
+ final int taskId = taskInfo.taskId;
+ mChildrenLeashes.put(taskId, leash);
+ mChildrenTaskInfo.put(taskId, taskInfo);
updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
+ mCallbacks.onChildTaskStatusChanged(taskId, true /* present */);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -120,6 +123,7 @@
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
sendStatusChanged();
+ mCallbacks.onChildTaskStatusChanged(taskId, false /* present */);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -134,6 +138,13 @@
wct.reorder(mRootTaskInfo.token, visible /* onTop */);
}
+ void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
+ @SplitScreen.StageType int stage) {
+ for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+ listener.onTaskStageChanged(mChildrenTaskInfo.keyAt(i), stage);
+ }
+ }
+
private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl leash, boolean firstAppeared) {
final Point taskPositionInParent = taskInfo.positionInParent;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 3198725..b5e1896 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -220,9 +220,8 @@
final InputChannel tmpInputChannel = new InputChannel();
mainExecutor.execute(() -> {
try {
- final int res = session.addToDisplay(window, layoutParams, View.GONE,
- displayId, mTmpInsetsState, tmpFrames.frame, tmpInputChannel,
- mTmpInsetsState, mTempControls);
+ final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
+ mTmpInsetsState, tmpInputChannel, mTmpInsetsState, mTempControls);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 8271b06..ac93a17 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -31,7 +31,9 @@
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
@@ -71,14 +73,20 @@
@NonNull Transitions.TransitionFinishCallback finishCallback) {
IRemoteTransition pendingRemote = mPendingRemotes.remove(transition);
if (pendingRemote == null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
+ + "explicit remote, search filters for match for %s", transition, info);
// If no explicit remote, search filters until one matches
for (int i = mFilters.size() - 1; i >= 0; --i) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Checking filter %s",
+ mFilters.get(i));
if (mFilters.get(i).first.matches(info)) {
pendingRemote = mFilters.get(i).second;
break;
}
}
}
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for %s to %s",
+ transition, pendingRemote);
if (pendingRemote == null) return false;
@@ -121,6 +129,8 @@
IRemoteTransition remote = request.getRemoteTransition();
if (remote == null) return null;
mPendingRemotes.put(transition, remote);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
+ + " for %s: %s", transition, remote);
return new WindowContainerTransaction();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 2ab4e0b..d8687bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -232,6 +232,8 @@
if (!info.getRootLeash().isValid()) {
// Invalid root-leash implies that the transition is empty/no-op, so just do
// housekeeping and return.
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
+ transitionToken, info);
t.apply();
onFinish(transitionToken, null /* wct */, null /* wctCB */);
return;
@@ -241,14 +243,22 @@
final TransitionFinishCallback finishCb = (wct, cb) -> onFinish(transitionToken, wct, cb);
// If a handler chose to uniquely run this animation, try delegating to it.
- if (active.mFirstHandler != null && active.mFirstHandler.startAnimation(
- transitionToken, info, t, finishCb)) {
- return;
+ if (active.mFirstHandler != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
+ active.mFirstHandler);
+ if (active.mFirstHandler.startAnimation(transitionToken, info, t, finishCb)) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
+ return;
+ }
}
// Otherwise give every other handler a chance (in order)
for (int i = mHandlers.size() - 1; i >= 0; --i) {
if (mHandlers.get(i) == active.mFirstHandler) continue;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
+ mHandlers.get(i));
if (mHandlers.get(i).startAnimation(transitionToken, info, t, finishCb)) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
+ mHandlers.get(i));
return;
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 5374bd9..2c29220 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -18,6 +18,7 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
+import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
@@ -47,6 +48,7 @@
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+// @FlakyTest(bugId = 179116910)
class EnterSplitScreenDockActivity(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
@@ -92,7 +94,9 @@
}
return FlickerTestRunnerFactory.getInstance().buildTest(
instrumentation, defaultTransitionSetup, testSpec,
- repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+ )
}
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index d750403..903971e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -18,6 +18,7 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
+import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
@@ -96,7 +97,9 @@
}
return FlickerTestRunnerFactory.getInstance().buildTest(
instrumentation, defaultTransitionSetup, testSpec,
- repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842
+ )
}
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 6aed83f..4933665 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -18,6 +18,7 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
+import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
@@ -78,14 +79,17 @@
statusBarWindowIsAlwaysVisible()
visibleWindowsShownMoreThanOneConsecutiveEntry(
listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
+ secondaryApp.defaultWindowName),
+ bugId = 178447631
)
}
}
}
return FlickerTestRunnerFactory.getInstance().buildTest(
instrumentation, defaultTransitionSetup, testSpec,
- repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842
+ )
}
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index 59f6aaf..ff3a979 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.os.Bundle
+import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
@@ -87,7 +88,9 @@
}
return FlickerTestRunnerFactory.getInstance().buildTest(
instrumentation, defaultTransitionSetup, testSpec,
- repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
index 42c7b7c..f2a7cda 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
@@ -86,7 +86,8 @@
listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
LETTERBOX_NAME, TOAST_NAME,
splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName)
+ nonResizeableApp.defaultWindowName),
+ bugId = 178447631
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
index 5b8ec1e..421ecff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
@@ -75,7 +75,8 @@
LAUNCHER_PACKAGE_NAME,
LETTERBOX_NAME,
nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
+ splitScreenApp.defaultWindowName),
+ bugId = 178447631
)
}
windowManagerTrace {
@@ -86,7 +87,8 @@
LAUNCHER_PACKAGE_NAME,
LETTERBOX_NAME,
nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
+ splitScreenApp.defaultWindowName),
+ bugId = 178447631
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index c802ffe..7edef93 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -18,6 +18,7 @@
import android.os.Bundle
import android.platform.test.annotations.Presubmit
+import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
@@ -93,7 +94,9 @@
}
return FlickerTestRunnerFactory.getInstance().buildTest(
instrumentation, defaultTransitionSetup, testSpec,
- repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index dca2732..c0ab20d 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -15,7 +15,10 @@
android_test {
name: "WMShellUnitTests",
- srcs: ["**/*.java"],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
static_libs: [
"WindowManager-Shell",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
index 4bd9bed..17ed396 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
@@ -26,7 +26,7 @@
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringForce
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.animation.PhysicsAnimator.EndListener
import com.android.wm.shell.animation.PhysicsAnimator.UpdateListener
import com.android.wm.shell.animation.PhysicsAnimatorTestUtils.clearAnimationUpdateFrames
@@ -54,8 +54,7 @@
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
-@Ignore("Blocking presubmits - investigating in b/158697054")
-class PhysicsAnimatorTest : SysuiTestCase() {
+class PhysicsAnimatorTest : ShellTestCase() {
private lateinit var viewGroup: ViewGroup
private lateinit var testView: View
private lateinit var testView2: View
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 4fab9a5..dd1a6a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -20,12 +20,12 @@
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.util.mockito.eq
import com.android.wm.shell.ShellTestCase
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
index 495be41..21bc32c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
@@ -27,7 +27,6 @@
import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
@@ -234,14 +233,6 @@
verify(mOtherCallback).onActivityRotation(eq(123));
}
- @Test
- public void testOnSizeCompatModeActivityChanged() {
- IBinder b = mock(IBinder.class);
- mImpl.onSizeCompatModeActivityChanged(123, b);
- verify(mCallback).onSizeCompatModeActivityChanged(eq(123), eq(b));
- verify(mOtherCallback).onSizeCompatModeActivityChanged(eq(123), eq(b));
- }
-
/**
* Handler that synchronously calls TaskStackListenerImpl#handleMessage() when it receives a
* message.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
index fe53641..9f1ee6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
@@ -21,8 +21,8 @@
import android.view.View
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.animation.PhysicsAnimatorTestUtils
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -43,7 +43,7 @@
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
-class MagnetizedObjectTest : SysuiTestCase() {
+class MagnetizedObjectTest : ShellTestCase() {
/** Incrementing value for fake MotionEvent timestamps. */
private var time = 0L
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 79bdaf4..2572106 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -29,6 +29,10 @@
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -86,11 +90,9 @@
@Mock
private ActivityTaskManager mActivityTaskManager;
+ // Both the split-screen and start interface.
@Mock
- private SplitScreen mSplitScreen;
-
- @Mock
- private DragAndDropPolicy.Starter mStarter;
+ private SplitScreen mSplitScreenStarter;
private DisplayLayout mLandscapeDisplayLayout;
private DisplayLayout mPortraitDisplayLayout;
@@ -126,7 +128,7 @@
mInsets = Insets.of(0, 0, 0, 0);
mPolicy = new DragAndDropPolicy(
- mContext, mActivityTaskManager, mSplitScreen, mStarter);
+ mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter);
mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -191,7 +193,7 @@
}
private void setInSplitScreen(boolean inSplitscreen) {
- doReturn(inSplitscreen).when(mSplitScreen).isSplitScreenVisible();
+ doReturn(inSplitscreen).when(mSplitScreenStarter).isSplitScreenVisible();
}
@Test
@@ -202,7 +204,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
}
@Test
@@ -213,12 +216,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).exitSplitScreen();
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
@@ -229,12 +233,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).exitSplitScreen();
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
@@ -245,7 +250,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
}
@Test
@@ -256,7 +262,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
}
@Test
@@ -268,12 +275,14 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
@@ -285,12 +294,14 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
- reset(mStarter);
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mStarter).startIntent(any(), any());
+ verify(mSplitScreenStarter).startClipDescription(any(), any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index c565a4c..0087d91 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
+import android.app.TaskInfo;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -54,6 +55,9 @@
private SurfaceControl mLeash;
@Mock
+ private TaskInfo mTaskInfo;
+
+ @Mock
private PipAnimationController.PipAnimationCallback mPipAnimationCallback;
@Before
@@ -70,7 +74,7 @@
@Test
public void getAnimator_withAlpha_returnFloatAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, new Rect(), 0f, 1f);
+ .getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f);
assertEquals("Expect ANIM_TYPE_ALPHA animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA);
@@ -79,7 +83,7 @@
@Test
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, new Rect(), new Rect(), new Rect(), null,
+ .getAnimator(mTaskInfo, mLeash, new Rect(), new Rect(), new Rect(), null,
TRANSITION_DIRECTION_TO_PIP, 0);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
@@ -93,13 +97,13 @@
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
- .getAnimator(mLeash, baseValue, startValue, endValue1, null,
+ .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP, 0);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
- .getAnimator(mLeash, baseValue, startValue, endValue2, null,
+ .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null,
TRANSITION_DIRECTION_TO_PIP, 0);
assertEquals("getAnimator with same type returns same animator",
@@ -111,13 +115,13 @@
@Test
public void getAnimator_setTransitionDirection() {
PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, new Rect(), 0f, 1f)
+ .getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f)
.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP);
assertEquals("Transition to PiP mode",
animator.getTransitionDirection(), TRANSITION_DIRECTION_TO_PIP);
animator = mPipAnimationController
- .getAnimator(mLeash, new Rect(), 0f, 1f)
+ .getAnimator(mTaskInfo, mLeash, new Rect(), 0f, 1f)
.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP);
assertEquals("Transition to fullscreen mode",
animator.getTransitionDirection(), TRANSITION_DIRECTION_LEAVE_PIP);
@@ -131,7 +135,7 @@
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, baseValue, startValue, endValue1, null,
+ .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP, 0);
animator.updateEndValue(endValue2);
@@ -145,7 +149,7 @@
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, baseValue, startValue, endValue, null,
+ .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
TRANSITION_DIRECTION_TO_PIP, 0);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
@@ -153,16 +157,16 @@
// onAnimationStart triggers onPipAnimationStart
animator.onAnimationStart(animator);
- verify(mPipAnimationCallback).onPipAnimationStart(animator);
+ verify(mPipAnimationCallback).onPipAnimationStart(mTaskInfo, animator);
// onAnimationCancel triggers onPipAnimationCancel
animator.onAnimationCancel(animator);
- verify(mPipAnimationCallback).onPipAnimationCancel(animator);
+ verify(mPipAnimationCallback).onPipAnimationCancel(mTaskInfo, animator);
// onAnimationEnd triggers onPipAnimationEnd
animator.onAnimationEnd(animator);
- verify(mPipAnimationCallback).onPipAnimationEnd(any(SurfaceControl.Transaction.class),
- eq(animator));
+ verify(mPipAnimationCallback).onPipAnimationEnd(eq(mTaskInfo),
+ any(SurfaceControl.Transaction.class), eq(animator));
}
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 7a810a1..9430af9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -18,27 +18,23 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
-import android.graphics.Rect;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Rational;
import android.util.Size;
-import android.view.Display;
import android.view.DisplayInfo;
import android.window.WindowContainerToken;
@@ -47,9 +43,8 @@
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.pip.phone.PhonePipMenuController;
import org.junit.Before;
import org.junit.Test;
@@ -69,14 +64,17 @@
private PipTaskOrganizer mSpiedPipTaskOrganizer;
@Mock private DisplayController mMockdDisplayController;
- @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
+
@Mock private PhonePipMenuController mMockPhonePipMenuController;
+ @Mock private PipAnimationController mMockPipAnimationController;
+ @Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
@Mock private Optional<LegacySplitScreen> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
+ private PipBoundsAlgorithm mPipBoundsAlgorithm;
private ComponentName mComponent1;
private ComponentName mComponent2;
@@ -87,10 +85,12 @@
mComponent1 = new ComponentName(mContext, "component1");
mComponent2 = new ComponentName(mContext, "component2");
mPipBoundsState = new PipBoundsState(mContext);
+ mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState);
mMainExecutor = new TestShellExecutor();
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState,
- mMockPipBoundsAlgorithm, mMockPhonePipMenuController,
- mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController,
+ mPipBoundsAlgorithm, mMockPhonePipMenuController,
+ mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
+ mMockPipTransitionController, mMockOptionalSplitScreen, mMockdDisplayController,
mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
mMainExecutor.flushAll();
preparePipTaskOrg();
@@ -117,7 +117,7 @@
@Test
public void startSwipePipToHome_updatesLastPipComponentName() {
- mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, null);
+ mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(null));
assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
}
@@ -126,7 +126,8 @@
public void startSwipePipToHome_updatesOverrideMinSize() {
final Size minSize = new Size(100, 80);
- mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize), null);
+ mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
+ createPipParams(null));
assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
}
@@ -200,9 +201,6 @@
final DisplayInfo info = new DisplayInfo();
mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
mContext.getResources(), true, true));
- when(mMockPipBoundsAlgorithm.getEntryDestinationBounds()).thenReturn(new Rect());
- when(mMockPipBoundsAlgorithm.getAdjustedDestinationBounds(any(), anyFloat()))
- .thenReturn(new Rect());
mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 62ffac4..cfe8463 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -46,6 +46,7 @@
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
import org.junit.Before;
import org.junit.Test;
@@ -68,6 +69,7 @@
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
@Mock private PipMediaController mMockPipMediaController;
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
+ @Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipTouchHandler mMockPipTouchHandler;
@Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
@Mock private PipBoundsState mMockPipBoundsState;
@@ -80,8 +82,8 @@
mPipController = new PipController(mContext, mMockDisplayController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
- mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener,
- mMockExecutor);
+ mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
+ mMockTaskStackListener, mMockExecutor);
doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
@@ -90,7 +92,7 @@
@Test
public void instantiatePipController_registersPipTransitionCallback() {
- verify(mMockPipTaskOrganizer).registerPipTransitionCallback(any());
+ verify(mMockPipTransitionController).registerPipTransitionCallback(any());
}
@Test
@@ -113,8 +115,8 @@
assertNull(PipController.create(spyContext, mMockDisplayController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
- mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener,
- mMockExecutor));
+ mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
+ mMockTaskStackListener, mMockExecutor));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index b4cfbc2..449ad88 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -38,6 +38,7 @@
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipUiEventLogger;
import org.junit.Before;
@@ -67,6 +68,9 @@
private PipTaskOrganizer mPipTaskOrganizer;
@Mock
+ private PipTransitionController mMockPipTransitionController;
+
+ @Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
@Mock
@@ -98,7 +102,8 @@
mPipSnapAlgorithm = new PipSnapAlgorithm();
mPipTouchHandler = new PipTouchHandler(mContext, mPhonePipMenuController,
mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer,
- mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
+ mMockPipTransitionController, mFloatingContentCoordinator, mPipUiEventLogger,
+ mMainExecutor);
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
mPipTouchHandler.setPipMotionHelper(mMotionHelper);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 168e0df..d2d1812 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -19,7 +19,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -71,7 +71,7 @@
public void testMoveToSideStage() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
- mStageCoordinator.moveToSideStage(task, SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
+ mStageCoordinator.moveToSideStage(task, STAGE_POSITION_BOTTOM_OR_RIGHT);
verify(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class));
verify(mSideStage).addTask(eq(task), any(Rect.class),
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index b54f7d8..4b4284a 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -41,6 +41,7 @@
"AssetDir.cpp",
"AssetManager.cpp",
"AssetManager2.cpp",
+ "AssetsProvider.cpp",
"AttributeResolution.cpp",
"ChunkIterator.cpp",
"ConfigDescription.cpp",
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 011a0de..ca5981c0 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -16,533 +16,145 @@
#include "androidfw/ApkAssets.h"
-#include <algorithm>
-
#include "android-base/errors.h"
-#include "android-base/file.h"
#include "android-base/logging.h"
-#include "android-base/stringprintf.h"
-#include "android-base/unique_fd.h"
-#include "android-base/utf8.h"
-#include "utils/Compat.h"
-#include "ziparchive/zip_archive.h"
-
-#include "androidfw/Asset.h"
-#include "androidfw/Idmap.h"
-#include "androidfw/misc.h"
-#include "androidfw/Util.h"
namespace android {
using base::SystemErrorCodeToString;
using base::unique_fd;
-static const std::string kResourcesArsc("resources.arsc");
+constexpr const char* kResourcesArsc = "resources.arsc";
-ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
- std::string path,
- time_t last_mod_time,
- package_property_t property_flags)
- : assets_provider_(std::move(assets_provider)),
- path_(std::move(path)),
- last_mod_time_(last_mod_time),
- property_flags_(property_flags) {
+ApkAssets::ApkAssets(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<LoadedArsc> loaded_arsc,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap)
+ : resources_asset_(std::move(resources_asset)),
+ loaded_arsc_(std::move(loaded_arsc)),
+ assets_provider_(std::move(assets)),
+ property_flags_(property_flags),
+ idmap_asset_(std::move(idmap_asset)),
+ loaded_idmap_(std::move(loaded_idmap)) {}
+
+std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path, package_property_t flags) {
+ return Load(ZipAssetsProvider::Create(path), flags);
}
-// Provides asset files from a zip file.
-class ZipAssetsProvider : public AssetsProvider {
- public:
- ~ZipAssetsProvider() override = default;
-
- static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
- ::ZipArchiveHandle unmanaged_handle;
- const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
- if (result != 0) {
- LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
- ::CloseArchive(unmanaged_handle);
- return {};
- }
-
- return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle));
- }
-
- static std::unique_ptr<const AssetsProvider> Create(
- unique_fd fd, const std::string& friendly_name, const off64_t offset = 0,
- const off64_t length = ApkAssets::kUnknownLength) {
-
- ::ZipArchiveHandle unmanaged_handle;
- const int32_t result = (length == ApkAssets::kUnknownLength)
- ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle)
- : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length,
- offset);
-
- if (result != 0) {
- LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
- << " and length " << length << ": " << ::ErrorCodeString(result);
- ::CloseArchive(unmanaged_handle);
- return {};
- }
-
- return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name,
- unmanaged_handle));
- }
-
- // Iterate over all files and directories within the zip. The order of iteration is not
- // guaranteed to be the same as the order of elements in the central directory but is stable for a
- // given zip file.
- bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override {
- // If this is a resource loader from an .arsc, there will be no zip handle
- if (zip_handle_ == nullptr) {
- return false;
- }
-
- std::string root_path_full = root_path;
- if (root_path_full.back() != '/') {
- root_path_full += '/';
- }
-
- void* cookie;
- if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
- return false;
- }
-
- std::string name;
- ::ZipEntry entry{};
-
- // We need to hold back directories because many paths will contain them and we want to only
- // surface one.
- std::set<std::string> dirs{};
-
- int32_t result;
- while ((result = ::Next(cookie, &entry, &name)) == 0) {
- StringPiece full_file_path(name);
- StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
-
- if (!leaf_file_path.empty()) {
- auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
- if (iter != leaf_file_path.end()) {
- std::string dir =
- leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
- dirs.insert(std::move(dir));
- } else {
- f(leaf_file_path, kFileTypeRegular);
- }
- }
- }
- ::EndIteration(cookie);
-
- // Now present the unique directories.
- for (const std::string& dir : dirs) {
- f(dir, kFileTypeDirectory);
- }
-
- // -1 is end of iteration, anything else is an error.
- return result == -1;
- }
-
- protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
- if (file_exists) {
- *file_exists = false;
- }
-
- ::ZipEntry entry;
- int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
- if (result != 0) {
- return {};
- }
-
- if (file_exists) {
- *file_exists = true;
- }
-
- const int fd = ::GetFileDescriptor(zip_handle_.get());
- const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
- incfs::IncFsFileMap asset_map;
- if (entry.method == kCompressDeflated) {
- if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
-
- std::unique_ptr<Asset> asset =
- Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- return asset;
- }
-
- if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
-
- unique_fd ufd;
- if (!GetPath()) {
- // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
- // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
- // to create new file descriptors.
- ufd = unique_fd(dup(fd));
- if (!ufd.ok()) {
- LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- }
-
- auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- return asset;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider);
-
- explicit ZipAssetsProvider(std::string path,
- std::string friendly_name,
- ZipArchiveHandle unmanaged_handle)
- : zip_handle_(unmanaged_handle, ::CloseArchive),
- path_(std::move(path)),
- friendly_name_(std::move(friendly_name)) { }
-
- const char* GetPath() const {
- return path_.empty() ? nullptr : path_.c_str();
- }
-
- using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
- ZipArchivePtr zip_handle_;
- std::string path_;
- std::string friendly_name_;
-};
-
-class DirectoryAssetsProvider : AssetsProvider {
- public:
- ~DirectoryAssetsProvider() override = default;
-
- static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
- struct stat sb{};
- const int result = stat(path.c_str(), &sb);
- if (result == -1) {
- LOG(ERROR) << "Failed to find directory '" << path << "'.";
- return nullptr;
- }
-
- if (!S_ISDIR(sb.st_mode)) {
- LOG(ERROR) << "Path '" << path << "' is not a directory.";
- return nullptr;
- }
-
- return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path));
- }
-
- protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override {
- const std::string resolved_path = ResolvePath(path);
- if (file_exists) {
- struct stat sb{};
- const int result = stat(resolved_path.c_str(), &sb);
- *file_exists = result != -1 && S_ISREG(sb.st_mode);
- }
-
- return ApkAssets::CreateAssetFromFile(resolved_path);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider);
-
- explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { }
-
- inline std::string ResolvePath(const std::string& path) const {
- return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str());
- }
-
- const std::string path_;
-};
-
-// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty.
-class EmptyAssetsProvider : public AssetsProvider {
- public:
- EmptyAssetsProvider() = default;
- ~EmptyAssetsProvider() override = default;
-
- protected:
- std::unique_ptr<Asset> OpenInternal(const std::string& /*path */,
- Asset::AccessMode /* mode */,
- bool* file_exists) const override {
- if (file_exists) {
- *file_exists = false;
- }
- return nullptr;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider);
-};
-
-// AssetProvider implementation
-class MultiAssetsProvider : public AssetsProvider {
- public:
- ~MultiAssetsProvider() override = default;
-
- static std::unique_ptr<const AssetsProvider> Create(
- std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) {
- CHECK(parent != nullptr) << "parent provider must not be null";
- return (!child) ? std::move(parent)
- : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider(
- std::move(child), std::move(parent)));
- }
-
- bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override {
- // TODO: Only call the function once for files defined in the parent and child
- return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f);
- }
-
- protected:
- std::unique_ptr<Asset> OpenInternal(
- const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
- auto asset = child_->Open(path, mode, file_exists);
- return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider);
-
- MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child,
- std::unique_ptr<const AssetsProvider> parent)
- : child_(std::move(child)), parent_(std::move(parent)) { }
-
- std::unique_ptr<const AssetsProvider> child_;
- std::unique_ptr<const AssetsProvider> parent_;
-};
-
-// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the
-// file.
-std::unique_ptr<const ApkAssets> ApkAssets::Load(
- const std::string& path, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset) {
- auto assets = ZipAssetsProvider::Create(path);
- return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
- : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::LoadFromFd(base::unique_fd fd,
+ const std::string& debug_name,
+ package_property_t flags,
+ off64_t offset,
+ off64_t len) {
+ return Load(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags);
}
-// Opens the archive using the file file descriptor with the specified file offset and read length.
-// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file.
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(
- unique_fd fd, const std::string& friendly_name, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
- const off64_t length) {
- CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
- CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
- << kUnknownLength;
-
- auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length);
- return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset))
- : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::Load(std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags) {
+ return LoadImpl(std::move(assets), flags, nullptr /* idmap_asset */, nullptr /* loaded_idmap */);
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(
- const std::string& path, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset) {
-
- auto assets = CreateAssetFromFile(path);
- return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset))
- : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::LoadTable(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags) {
+ if (resources_asset == nullptr) {
+ return {};
+ }
+ return LoadImpl(std::move(resources_asset), std::move(assets), flags, nullptr /* idmap_asset */,
+ nullptr /* loaded_idmap */);
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(
- unique_fd fd, const std::string& friendly_name, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
- const off64_t length) {
-
- auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
- return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags,
- std::move(override_asset))
- : nullptr;
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
- const package_property_t flags) {
+std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
+ package_property_t flags) {
CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
- std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
+ auto idmap_asset = AssetsProvider::CreateAssetFromFile(idmap_path);
if (idmap_asset == nullptr) {
+ LOG(ERROR) << "failed to read IDMAP " << idmap_path;
return {};
}
- const StringPiece idmap_data(
- reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
- static_cast<size_t>(idmap_asset->getLength()));
- std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
+ StringPiece idmap_data(reinterpret_cast<const char*>(idmap_asset->getBuffer(true /* aligned */)),
+ static_cast<size_t>(idmap_asset->getLength()));
+ auto loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
if (loaded_idmap == nullptr) {
LOG(ERROR) << "failed to load IDMAP " << idmap_path;
return {};
}
-
- auto overlay_path = std::string(loaded_idmap->OverlayApkPath());
- auto assets = ZipAssetsProvider::Create(overlay_path);
- return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY,
- nullptr /* override_asset */, std::move(idmap_asset),
- std::move(loaded_idmap))
- : nullptr;
-}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(
- const std::string& path, const package_property_t flags,
- std::unique_ptr<const AssetsProvider> override_asset) {
-
- auto assets = DirectoryAssetsProvider::Create(path);
- return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
- : nullptr;
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(
- const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) {
-
- auto assets = (override_asset) ? std::move(override_asset)
- : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider());
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */,
- -1 /* last_mod-time */, flags));
- loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
- // Need to force a move for mingw32.
- return std::move(loaded_apk);
-}
-
-std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
- unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
- if (!fd.ok()) {
- LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
+ const std::string overlay_path(loaded_idmap->OverlayApkPath());
+ auto overlay_assets = ZipAssetsProvider::Create(overlay_path);
+ if (overlay_assets == nullptr) {
return {};
}
- return CreateAssetFromFd(std::move(fd), path.c_str());
+ return LoadImpl(std::move(overlay_assets), flags | PROPERTY_OVERLAY, std::move(idmap_asset),
+ std::move(loaded_idmap));
}
-std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
- const char* path,
- off64_t offset,
- off64_t length) {
- CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
- CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
- << kUnknownLength;
- if (length == kUnknownLength) {
- length = lseek64(fd, 0, SEEK_END);
- if (length < 0) {
- LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
- << SystemErrorCodeToString(errno);
- return {};
- }
- }
-
- incfs::IncFsFileMap file_map;
- if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
- LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
- << SystemErrorCodeToString(errno);
+std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap) {
+ if (assets == nullptr) {
return {};
}
- // If `path` is set, do not pass ownership of the `fd` to the new Asset since
- // Asset::openFileDescriptor can use `path` to create new file descriptors.
- return Asset::createFromUncompressedMap(std::move(file_map),
- Asset::AccessMode::ACCESS_RANDOM,
- (path) ? base::unique_fd(-1) : std::move(fd));
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
- std::unique_ptr<const AssetsProvider> assets, const std::string& path,
- package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets,
- std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) {
-
- const time_t last_mod_time = getFileModDate(path.c_str());
-
// Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
bool resources_asset_exists = false;
- auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
- &resources_asset_exists);
-
- assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets));
-
- // Wrap the handle in a unique_ptr so it gets automatically closed.
- std::unique_ptr<ApkAssets>
- loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
-
- if (!resources_asset_exists) {
- loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
- return std::move(loaded_apk);
- }
-
- loaded_apk->resources_asset_ = std::move(resources_asset_);
- if (!loaded_apk->resources_asset_) {
- LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
+ auto resources_asset = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
+ &resources_asset_exists);
+ if (resources_asset == nullptr && resources_asset_exists) {
+ LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << assets->GetDebugName()
+ << "'.";
return {};
}
- // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
- loaded_apk->idmap_asset_ = std::move(idmap_asset);
- loaded_apk->loaded_idmap_ = std::move(idmap);
-
- const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
- const size_t length = loaded_apk->resources_asset_->getLength();
- if (!data || length == 0) {
- LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'.";
- return {};
- }
-
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(),
- property_flags);
- if (!loaded_apk->loaded_arsc_) {
- LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
- return {};
- }
-
- // Need to force a move for mingw32.
- return std::move(loaded_apk);
+ return LoadImpl(std::move(resources_asset), std::move(assets), property_flags,
+ std::move(idmap_asset), std::move(loaded_idmap));
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(
- std::unique_ptr<Asset> resources_asset, const std::string& path,
- package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) {
-
- const time_t last_mod_time = getFileModDate(path.c_str());
-
- auto assets = (override_assets) ? std::move(override_assets)
- : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider());
-
- std::unique_ptr<ApkAssets> loaded_apk(
- new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
- loaded_apk->resources_asset_ = std::move(resources_asset);
-
- const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
- const size_t length = loaded_apk->resources_asset_->getLength();
- if (!data || length == 0) {
- LOG(ERROR) << "Failed to read resources table data in '" << path << "'.";
+std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap) {
+ if (assets == nullptr ) {
return {};
}
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */,
- property_flags);
- if (loaded_apk->loaded_arsc_ == nullptr) {
- LOG(ERROR) << "Failed to read resources table in '" << path << "'.";
+ std::unique_ptr<LoadedArsc> loaded_arsc;
+ if (resources_asset != nullptr) {
+ const auto data = resources_asset->getIncFsBuffer(true /* aligned */);
+ const size_t length = resources_asset->getLength();
+ if (!data || length == 0) {
+ LOG(ERROR) << "Failed to read resources table in APK '" << assets->GetDebugName() << "'.";
+ return {};
+ }
+ loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags);
+ } else {
+ loaded_arsc = LoadedArsc::CreateEmpty();
+ }
+
+ if (loaded_arsc == nullptr) {
+ LOG(ERROR) << "Failed to load resources table in APK '" << assets->GetDebugName() << "'.";
return {};
}
- // Need to force a move for mingw32.
- return std::move(loaded_apk);
+ return std::unique_ptr<ApkAssets>(new ApkAssets(std::move(resources_asset),
+ std::move(loaded_arsc), std::move(assets),
+ property_flags, std::move(idmap_asset),
+ std::move(loaded_idmap)));
+}
+
+const std::string& ApkAssets::GetPath() const {
+ return assets_provider_->GetDebugName();
}
bool ApkAssets::IsUpToDate() const {
- if (IsLoader()) {
- // Loaders are invalidated by the app, not the system, so assume they are up to date.
- return true;
- }
- return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) &&
- last_mod_time_ == getFileModDate(path_.c_str());
+ // Loaders are invalidated by the app, not the system, so assume they are up to date.
+ return IsLoader() || ((!loaded_idmap_ || loaded_idmap_->IsUpToDate())
+ && assets_provider_->IsUpToDate());
}
-
} // namespace android
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
new file mode 100644
index 0000000..23cacf8
--- /dev/null
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -0,0 +1,398 @@
+/*
+ * 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.
+ */
+
+#include "androidfw/AssetsProvider.h"
+
+#include <sys/stat.h>
+
+#include <android-base/errors.h>
+#include <android-base/stringprintf.h>
+#include <android-base/utf8.h>
+#include <ziparchive/zip_archive.h>
+
+namespace android {
+namespace {
+constexpr const char* kEmptyDebugString = "<empty>";
+} // namespace
+
+std::unique_ptr<Asset> AssetsProvider::Open(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const {
+ return OpenInternal(path, mode, file_exists);
+}
+
+std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFile(const std::string& path) {
+ base::unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ if (!fd.ok()) {
+ LOG(ERROR) << "Failed to open file '" << path << "': " << base::SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ return CreateAssetFromFd(std::move(fd), path.c_str());
+}
+
+std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset,
+ off64_t length) {
+ CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
+ CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
+ << kUnknownLength;
+ if (length == kUnknownLength) {
+ length = lseek64(fd, 0, SEEK_END);
+ if (length < 0) {
+ LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
+ << base::SystemErrorCodeToString(errno);
+ return {};
+ }
+ }
+
+ incfs::IncFsFileMap file_map;
+ if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
+ LOG(ERROR) << "Failed to mmap file '" << ((path != nullptr) ? path : "anon") << "': "
+ << base::SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ // If `path` is set, do not pass ownership of the `fd` to the new Asset since
+ // Asset::openFileDescriptor can use `path` to create new file descriptors.
+ return Asset::createFromUncompressedMap(std::move(file_map),
+ Asset::AccessMode::ACCESS_RANDOM,
+ (path != nullptr) ? base::unique_fd(-1) : std::move(fd));
+}
+
+ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path)
+ : value_(std::forward<std::string>(value)), is_path_(is_path) {}
+
+const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const {
+ return is_path_ ? &value_ : nullptr;
+}
+
+const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
+ return value_;
+}
+
+ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path,
+ time_t last_mod_time)
+ : zip_handle_(handle, ::CloseArchive),
+ name_(std::forward<PathOrDebugName>(path)),
+ last_mod_time_(last_mod_time) {}
+
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) {
+ ZipArchiveHandle handle;
+ if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
+ CloseArchive(handle);
+ return {};
+ }
+
+ struct stat sb{.st_mtime = -1};
+ if (stat(path.c_str(), &sb) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to stat file '" << path << "': "
+ << base::SystemErrorCodeToString(errno);
+ }
+
+ return std::unique_ptr<ZipAssetsProvider>(
+ new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
+ true /* is_path */}, sb.st_mtime));
+}
+
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
+ std::string friendly_name,
+ off64_t offset,
+ off64_t len) {
+ ZipArchiveHandle handle;
+ const int released_fd = fd.release();
+ const int32_t result = (len == AssetsProvider::kUnknownLength)
+ ? ::OpenArchiveFd(released_fd, friendly_name.c_str(), &handle)
+ : ::OpenArchiveFdRange(released_fd, friendly_name.c_str(), &handle, len, offset);
+
+ if (result != 0) {
+ LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
+ << " and length " << len << ": " << ::ErrorCodeString(result);
+ CloseArchive(handle);
+ return {};
+ }
+
+ struct stat sb{.st_mtime = -1};
+ if (fstat(released_fd, &sb) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': "
+ << base::SystemErrorCodeToString(errno);
+ }
+
+ return std::unique_ptr<ZipAssetsProvider>(
+ new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
+ false /* is_path */}, sb.st_mtime));
+}
+
+std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const {
+ if (file_exists != nullptr) {
+ *file_exists = false;
+ }
+
+ ZipEntry entry;
+ if (FindEntry(zip_handle_.get(), path, &entry) != 0) {
+ return {};
+ }
+
+ if (file_exists != nullptr) {
+ *file_exists = true;
+ }
+
+ const int fd = GetFileDescriptor(zip_handle_.get());
+ const off64_t fd_offset = GetFileDescriptorOffset(zip_handle_.get());
+ incfs::IncFsFileMap asset_map;
+ if (entry.method == kCompressDeflated) {
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length,
+ name_.GetDebugName().c_str())) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName()
+ << "'";
+ return {};
+ }
+
+ std::unique_ptr<Asset> asset =
+ Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << name_.GetDebugName()
+ << "'";
+ return {};
+ }
+ return asset;
+ }
+
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length,
+ name_.GetDebugName().c_str())) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
+ return {};
+ }
+
+ base::unique_fd ufd;
+ if (name_.GetPath() == nullptr) {
+ // If the zip name does not represent a path, create a new `fd` for the new Asset to own in
+ // order to create new file descriptors using Asset::openFileDescriptor. If the zip name is a
+ // path, it will be used to create new file descriptors.
+ ufd = base::unique_fd(dup(fd));
+ if (!ufd.ok()) {
+ LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << name_.GetDebugName() << "'";
+ return {};
+ }
+ }
+
+ auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
+ return {};
+ }
+ return asset;
+}
+
+bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f)
+ const {
+ std::string root_path_full = root_path;
+ if (root_path_full.back() != '/') {
+ root_path_full += '/';
+ }
+
+ void* cookie;
+ if (StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
+ return false;
+ }
+
+ std::string name;
+ ::ZipEntry entry{};
+
+ // We need to hold back directories because many paths will contain them and we want to only
+ // surface one.
+ std::set<std::string> dirs{};
+
+ int32_t result;
+ while ((result = Next(cookie, &entry, &name)) == 0) {
+ StringPiece full_file_path(name);
+ StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
+
+ if (!leaf_file_path.empty()) {
+ auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
+ if (iter != leaf_file_path.end()) {
+ std::string dir =
+ leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+ dirs.insert(std::move(dir));
+ } else {
+ f(leaf_file_path, kFileTypeRegular);
+ }
+ }
+ }
+ EndIteration(cookie);
+
+ // Now present the unique directories.
+ for (const std::string& dir : dirs) {
+ f(dir, kFileTypeDirectory);
+ }
+
+ // -1 is end of iteration, anything else is an error.
+ return result == -1;
+}
+
+const std::string& ZipAssetsProvider::GetDebugName() const {
+ return name_.GetDebugName();
+}
+
+bool ZipAssetsProvider::IsUpToDate() const {
+ struct stat sb{};
+ if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
+ // If fstat fails on the zip archive, return true so the zip archive the resource system does
+ // attempt to refresh the ApkAsset.
+ return true;
+ }
+ return last_mod_time_ == sb.st_mtime;
+}
+
+DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
+ : dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}
+
+std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
+ struct stat sb{};
+ const int result = stat(path.c_str(), &sb);
+ if (result == -1) {
+ LOG(ERROR) << "Failed to find directory '" << path << "'.";
+ return nullptr;
+ }
+
+ if (!S_ISDIR(sb.st_mode)) {
+ LOG(ERROR) << "Path '" << path << "' is not a directory.";
+ return nullptr;
+ }
+
+ if (path[path.size() - 1] != OS_PATH_SEPARATOR) {
+ path += OS_PATH_SEPARATOR;
+ }
+
+ return std::unique_ptr<DirectoryAssetsProvider>(new DirectoryAssetsProvider(std::move(path),
+ sb.st_mtime));
+}
+
+std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
+ Asset::AccessMode /* mode */,
+ bool* file_exists) const {
+ const std::string resolved_path = dir_ + path;
+ if (file_exists != nullptr) {
+ struct stat sb{};
+ *file_exists = (stat(resolved_path.c_str(), &sb) != -1) && S_ISREG(sb.st_mode);
+ }
+
+ return CreateAssetFromFile(resolved_path);
+}
+
+bool DirectoryAssetsProvider::ForEachFile(
+ const std::string& /* root_path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */)
+ const {
+ return true;
+}
+
+const std::string& DirectoryAssetsProvider::GetDebugName() const {
+ return dir_;
+}
+
+bool DirectoryAssetsProvider::IsUpToDate() const {
+ struct stat sb{};
+ if (stat(dir_.c_str(), &sb) < 0) {
+ // If stat fails on the zip archive, return true so the zip archive the resource system does
+ // attempt to refresh the ApkAsset.
+ return true;
+ }
+ return last_mod_time_ == sb.st_mtime;
+}
+
+MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
+ std::unique_ptr<AssetsProvider>&& secondary)
+ : primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
+ secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) {
+ if (primary_->GetDebugName() == kEmptyDebugString) {
+ debug_name_ = secondary_->GetDebugName();
+ } else if (secondary_->GetDebugName() == kEmptyDebugString) {
+ debug_name_ = primary_->GetDebugName();
+ } else {
+ debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
+ }
+}
+
+std::unique_ptr<AssetsProvider> MultiAssetsProvider::Create(
+ std::unique_ptr<AssetsProvider>&& primary, std::unique_ptr<AssetsProvider>&& secondary) {
+ if (primary == nullptr || secondary == nullptr) {
+ return nullptr;
+ }
+ return std::unique_ptr<MultiAssetsProvider>(new MultiAssetsProvider(std::move(primary),
+ std::move(secondary)));
+}
+
+std::unique_ptr<Asset> MultiAssetsProvider::OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const {
+ auto asset = primary_->Open(path, mode, file_exists);
+ return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists);
+}
+
+bool MultiAssetsProvider::ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f)
+ const {
+ return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f);
+}
+
+const std::string& MultiAssetsProvider::GetDebugName() const {
+ return debug_name_;
+}
+
+bool MultiAssetsProvider::IsUpToDate() const {
+ return primary_->IsUpToDate() && secondary_->IsUpToDate();
+}
+
+std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create() {
+ return std::make_unique<EmptyAssetsProvider>();
+}
+
+std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* path */,
+ Asset::AccessMode /* mode */,
+ bool* file_exists) const {
+ if (file_exists) {
+ *file_exists = false;
+ }
+ return nullptr;
+}
+
+bool EmptyAssetsProvider::ForEachFile(
+ const std::string& /* root_path */,
+ const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ return true;
+}
+
+const std::string& EmptyAssetsProvider::GetDebugName() const {
+ const static std::string kEmpty = kEmptyDebugString;
+ return kEmpty;
+}
+
+bool EmptyAssetsProvider::IsUpToDate() const {
+ return true;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index adb383f95..f216f55 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -257,8 +257,8 @@
target_apk_path_(target_apk_path),
idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
-std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data) {
+std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
+ const StringPiece& idmap_data) {
ATRACE_CALL();
size_t data_size = idmap_data.size();
auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 996b424..2a70f0d 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -767,10 +767,10 @@
return true;
}
-std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
- const size_t length,
- const LoadedIdmap* loaded_idmap,
- const package_property_t property_flags) {
+std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
+ const size_t length,
+ const LoadedIdmap* loaded_idmap,
+ const package_property_t property_flags) {
ATRACE_NAME("LoadedArsc::Load");
// Not using make_unique because the constructor is private.
@@ -799,11 +799,10 @@
}
}
- // Need to force a move for mingw32.
- return std::move(loaded_arsc);
+ return loaded_arsc;
}
-std::unique_ptr<const LoadedArsc> LoadedArsc::CreateEmpty() {
+std::unique_ptr<LoadedArsc> LoadedArsc::CreateEmpty() {
return std::unique_ptr<LoadedArsc>(new LoadedArsc());
}
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index e57490a..d0019ed 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -24,104 +24,49 @@
#include "android-base/unique_fd.h"
#include "androidfw/Asset.h"
+#include "androidfw/AssetsProvider.h"
#include "androidfw/Idmap.h"
#include "androidfw/LoadedArsc.h"
#include "androidfw/misc.h"
-struct ZipArchive;
-typedef ZipArchive* ZipArchiveHandle;
-
namespace android {
-class LoadedIdmap;
-
-// Interface for retrieving assets provided by an ApkAssets.
-class AssetsProvider {
- public:
- virtual ~AssetsProvider() = default;
-
- // Opens a file for reading.
- std::unique_ptr<Asset> Open(const std::string& path,
- Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
- bool* file_exists = nullptr) const {
- return OpenInternal(path, mode, file_exists);
- }
-
- // Iterate over all files and directories provided by the zip. The order of iteration is stable.
- virtual bool ForEachFile(const std::string& /* path */,
- const std::function<void(const StringPiece&, FileType)>& /* f */) const {
- return true;
- }
-
- protected:
- AssetsProvider() = default;
-
- virtual std::unique_ptr<Asset> OpenInternal(const std::string& path,
- Asset::AccessMode mode,
- bool* file_exists) const = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AssetsProvider);
-};
-
-class ZipAssetsProvider;
-
// Holds an APK.
class ApkAssets {
public:
- // This means the data extends to the end of the file.
- static constexpr off64_t kUnknownLength = -1;
- // Creates an ApkAssets.
- // If `system` is true, the package is marked as a system package, and allows some functions to
- // filter out this package when computing what configurations/resources are available.
- static std::unique_ptr<const ApkAssets> Load(
- const std::string& path, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+ // Creates an ApkAssets from a path on device.
+ static std::unique_ptr<ApkAssets> Load(const std::string& path,
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the given file descriptor, and takes ownership of the file
- // descriptor. The `friendly_name` is some name that will be used to identify the source of
- // this ApkAssets in log messages and other debug scenarios.
- // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read
- // using the `offset` into the file descriptor and will be `length` bytes long.
- static std::unique_ptr<const ApkAssets> LoadFromFd(
- base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
- off64_t length = kUnknownLength);
+ // Creates an ApkAssets from an open file descriptor.
+ static std::unique_ptr<ApkAssets> LoadFromFd(base::unique_fd fd,
+ const std::string& debug_name,
+ package_property_t flags = 0U,
+ off64_t offset = 0,
+ off64_t len = AssetsProvider::kUnknownLength);
- // Creates an ApkAssets from the given path which points to a resources.arsc.
- static std::unique_ptr<const ApkAssets> LoadTable(
- const std::string& path, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+ // Creates an ApkAssets from an AssetProvider.
+ // The ApkAssets will take care of destroying the AssetsProvider when it is destroyed.
+ static std::unique_ptr<ApkAssets> Load(std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and
- // takes ownership of the file descriptor.
- // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read
- // using the `offset` into the file descriptor and will be `length` bytes long.
- static std::unique_ptr<const ApkAssets> LoadTableFromFd(
- base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
- off64_t length = kUnknownLength);
+ // Creates an ApkAssets from the given asset file representing a resources.arsc.
+ static std::unique_ptr<ApkAssets> LoadTable(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t flags = 0U);
// Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay
// data.
- static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path,
- package_property_t flags = 0U);
+ static std::unique_ptr<ApkAssets> LoadOverlay(const std::string& idmap_path,
+ package_property_t flags = 0U);
- // Creates an ApkAssets from the directory path. File-based resources are read within the
- // directory as if the directory is an APK.
- static std::unique_ptr<const ApkAssets> LoadFromDir(
- const std::string& path, package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
-
- // Creates a totally empty ApkAssets with no resources table and no file entries.
- static std::unique_ptr<const ApkAssets> LoadEmpty(
- package_property_t flags = 0U,
- std::unique_ptr<const AssetsProvider> override_asset = nullptr);
-
- const std::string& GetPath() const {
- return path_;
- }
+ // TODO(177101983): Remove all uses of GetPath for checking whether two ApkAssets are the same.
+ // With the introduction of ResourcesProviders, not all ApkAssets have paths. This could cause
+ // bugs when path is used for comparison because multiple ApkAssets could have the same "firendly
+ // name". Use pointer equality instead. ResourceManager caches and reuses ApkAssets so the
+ // same asset should have the same pointer.
+ const std::string& GetPath() const;
const AssetsProvider* GetAssetsProvider() const {
return assets_provider_.get();
@@ -146,53 +91,40 @@
// Returns whether the resources.arsc is allocated in RAM (not mmapped).
bool IsTableAllocated() const {
- return resources_asset_ && resources_asset_->isAllocated();
+ return resources_asset_ != nullptr && resources_asset_->isAllocated();
}
bool IsUpToDate() const;
- // Creates an Asset from a file on disk.
- static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
-
- // Creates an Asset from a file descriptor.
- //
- // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
- // must equal 0; otherwise, the asset data will be read using the `offset` into the file
- // descriptor and will be `length` bytes long.
- static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
- const char* path,
- off64_t offset = 0,
- off64_t length = kUnknownLength);
private:
- DISALLOW_COPY_AND_ASSIGN(ApkAssets);
+ static std::unique_ptr<ApkAssets> LoadImpl(std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap);
- static std::unique_ptr<const ApkAssets> LoadImpl(
- std::unique_ptr<const AssetsProvider> assets, const std::string& path,
- package_property_t property_flags,
- std::unique_ptr<const AssetsProvider> override_assets = nullptr,
- std::unique_ptr<Asset> idmap_asset = nullptr,
- std::unique_ptr<const LoadedIdmap> idmap = nullptr);
+ static std::unique_ptr<ApkAssets> LoadImpl(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap);
- static std::unique_ptr<const ApkAssets> LoadTableImpl(
- std::unique_ptr<Asset> resources_asset, const std::string& path,
- package_property_t property_flags,
- std::unique_ptr<const AssetsProvider> override_assets = nullptr);
+ ApkAssets(std::unique_ptr<Asset> resources_asset,
+ std::unique_ptr<LoadedArsc> loaded_arsc,
+ std::unique_ptr<AssetsProvider> assets,
+ package_property_t property_flags,
+ std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<LoadedIdmap> loaded_idmap);
- ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
- std::string path,
- time_t last_mod_time,
- package_property_t property_flags);
-
- std::unique_ptr<const AssetsProvider> assets_provider_;
- const std::string path_;
- time_t last_mod_time_;
- package_property_t property_flags_ = 0U;
std::unique_ptr<Asset> resources_asset_;
+ std::unique_ptr<LoadedArsc> loaded_arsc_;
+
+ std::unique_ptr<AssetsProvider> assets_provider_;
+ package_property_t property_flags_ = 0U;
+
std::unique_ptr<Asset> idmap_asset_;
- std::unique_ptr<const LoadedArsc> loaded_arsc_;
- std::unique_ptr<const LoadedIdmap> loaded_idmap_;
+ std::unique_ptr<LoadedIdmap> loaded_idmap_;
};
-} // namespace android
+} // namespace android
-#endif /* APKASSETS_H_ */
+#endif // APKASSETS_H_
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 80bae20..40c91a6 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -167,8 +167,8 @@
private:
/* AssetManager needs access to our "create" functions */
friend class AssetManager;
- friend class ApkAssets;
- friend class ZipAssetsProvider;
+ friend struct ZipAssetsProvider;
+ friend struct AssetsProvider;
/*
* Create the asset from a named file on disk.
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
new file mode 100644
index 0000000..7b06947
--- /dev/null
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROIDFW_ASSETSPROVIDER_H
+#define ANDROIDFW_ASSETSPROVIDER_H
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "android-base/unique_fd.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/Idmap.h"
+#include "androidfw/LoadedArsc.h"
+#include "androidfw/misc.h"
+
+struct ZipArchive;
+
+namespace android {
+
+// Interface responsible for opening and iterating through asset files.
+struct AssetsProvider {
+ static constexpr off64_t kUnknownLength = -1;
+
+ // Opens a file for reading. If `file_exists` is not null, it will be set to `true` if the file
+ // exists. This is useful for determining if the file exists but was unable to be opened due to
+ // an I/O error.
+ std::unique_ptr<Asset> Open(const std::string& path,
+ Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
+ bool* file_exists = nullptr) const;
+
+ // Iterate over all files and directories provided by the interface. The order of iteration is
+ // stable.
+ virtual bool ForEachFile(const std::string& path,
+ const std::function<void(const StringPiece&, FileType)>& f) const = 0;
+
+ // Retrieves a name that represents the interface. This may or may not be the path of the
+ // interface source.
+ WARN_UNUSED virtual const std::string& GetDebugName() const = 0;
+
+ // Returns whether the interface provides the most recent version of its files.
+ WARN_UNUSED virtual bool IsUpToDate() const = 0;
+
+ // Creates an Asset from a file on disk.
+ static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+
+ // Creates an Asset from a file descriptor.
+ //
+ // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
+ // must equal 0; otherwise, the asset data will be read using the `offset` into the file
+ // descriptor and will be `length` bytes long.
+ static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
+ const char* path,
+ off64_t offset = 0,
+ off64_t length = AssetsProvider::kUnknownLength);
+
+ virtual ~AssetsProvider() = default;
+ protected:
+ virtual std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const = 0;
+};
+
+// Supplies assets from a zip archive.
+struct ZipAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<ZipAssetsProvider> Create(std::string path);
+ static std::unique_ptr<ZipAssetsProvider> Create(base::unique_fd fd,
+ std::string friendly_name,
+ off64_t offset = 0,
+ off64_t len = kUnknownLength);
+
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~ZipAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const override;
+
+ private:
+ struct PathOrDebugName;
+ ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, time_t last_mod_time);
+
+ struct PathOrDebugName {
+ PathOrDebugName(std::string&& value, bool is_path);
+
+ // Retrieves the path or null if this class represents a debug name.
+ WARN_UNUSED const std::string* GetPath() const;
+
+ // Retrieves a name that represents the interface. This may or may not represent a path.
+ WARN_UNUSED const std::string& GetDebugName() const;
+
+ private:
+ std::string value_;
+ bool is_path_;
+ };
+
+ std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
+ PathOrDebugName name_;
+ time_t last_mod_time_;
+};
+
+// Supplies assets from a root directory.
+struct DirectoryAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<DirectoryAssetsProvider> Create(std::string root_dir);
+
+ bool ForEachFile(const std::string& path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~DirectoryAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path,
+ Asset::AccessMode mode,
+ bool* file_exists) const override;
+
+ private:
+ explicit DirectoryAssetsProvider(std::string&& path, time_t last_mod_time);
+ std::string dir_;
+ time_t last_mod_time_;
+};
+
+// Supplies assets from a `primary` asset provider and falls back to supplying assets from the
+// `secondary` asset provider if the asset cannot be found in the `primary`.
+struct MultiAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<AssetsProvider> Create(std::unique_ptr<AssetsProvider>&& primary,
+ std::unique_ptr<AssetsProvider>&& secondary);
+
+ bool ForEachFile(const std::string& root_path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~MultiAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(
+ const std::string& path, Asset::AccessMode mode, bool* file_exists) const override;
+
+ private:
+ MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
+ std::unique_ptr<AssetsProvider>&& secondary);
+
+ std::unique_ptr<AssetsProvider> primary_;
+ std::unique_ptr<AssetsProvider> secondary_;
+ std::string debug_name_;
+};
+
+// Does not provide any assets.
+struct EmptyAssetsProvider : public AssetsProvider {
+ static std::unique_ptr<AssetsProvider> Create();
+
+ bool ForEachFile(const std::string& path,
+ const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+ WARN_UNUSED const std::string& GetDebugName() const override;
+ WARN_UNUSED bool IsUpToDate() const override;
+
+ ~EmptyAssetsProvider() override = default;
+ protected:
+ std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+ bool* file_exists) const override;
+};
+
+} // namespace android
+
+#endif /* ANDROIDFW_ASSETSPROVIDER_H */
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index fd9a8d13..0ded793 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -149,8 +149,8 @@
class LoadedIdmap {
public:
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
- static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data);
+ static std::unique_ptr<LoadedIdmap> Load(const StringPiece& idmap_path,
+ const StringPiece& idmap_data);
// Returns the path to the IDMAP.
std::string_view IdmapPath() const {
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 891fb90..d9225cd 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -300,17 +300,14 @@
public:
// Load a resource table from memory pointed to by `data` of size `len`.
// The lifetime of `data` must out-live the LoadedArsc returned from this method.
- // If `system` is set to true, the LoadedArsc is considered as a system provided resource.
- // If `load_as_shared_library` is set to true, the application package (0x7f) is treated
- // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an
- // ID.
- static std::unique_ptr<const LoadedArsc> Load(incfs::map_ptr<void> data,
- size_t length,
- const LoadedIdmap* loaded_idmap = nullptr,
- package_property_t property_flags = 0U);
+
+ static std::unique_ptr<LoadedArsc> Load(incfs::map_ptr<void> data,
+ size_t length,
+ const LoadedIdmap* loaded_idmap = nullptr,
+ package_property_t property_flags = 0U);
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
- static std::unique_ptr<const LoadedArsc> CreateEmpty();
+ static std::unique_ptr<LoadedArsc> CreateEmpty();
// Returns the string pool where all string resource values
// (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 3f0c7cb..b434915 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -27,6 +27,8 @@
#include "data/overlayable/R.h"
#include "data/system/R.h"
+using ::testing::NotNull;
+
namespace overlay = com::android::overlay;
namespace overlayable = com::android::overlayable;
@@ -195,7 +197,11 @@
}
TEST_F(IdmapTest, OverlayLoaderInterop) {
- auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER);
+ auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+ ASSERT_THAT(asset, NotNull());
+
+ auto loader_assets = ApkAssets::LoadTable(std::move(asset), EmptyAssetsProvider::Create(),
+ PROPERTY_LOADER);
AssetManager2 asset_manager;
asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
overlay_assets_.get()});
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 9aa3634..f356c8130 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -339,10 +339,8 @@
}
TEST(LoadedArscTest, LoadCustomLoader) {
- std::string contents;
-
- std::unique_ptr<Asset>
- asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+ auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+ ASSERT_THAT(asset, NotNull());
const StringPiece data(
reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 65f4e8c..e798f2a 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -129,8 +129,9 @@
runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false);
- defaultRenderAhead = std::max(-1, std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD,
- render_ahead().value_or(0))));
+ defaultRenderAhead = std::max(
+ -1,
+ std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD, render_ahead().value_or(-1))));
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index b944310..b769d40 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -79,7 +79,8 @@
// Regular JNI
static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer,
- jstring filePath, jint weight, jboolean italic, jint ttcIndex) {
+ jstring filePath, jstring langTags, jint weight, jboolean italic,
+ jint ttcIndex) {
NPE_CHECK_RETURN_ZERO(env, buffer);
std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
const void* fontPtr = env->GetDirectBufferAddress(buffer);
@@ -94,6 +95,7 @@
return 0;
}
ScopedUtfChars fontPath(env, filePath);
+ ScopedUtfChars langTagStr(env, langTags);
jobject fontRef = MakeGlobalRefOrDie(env, buffer);
sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
release_global_ref, reinterpret_cast<void*>(fontRef)));
@@ -105,8 +107,13 @@
"Failed to create internal object. maybe invalid font data.");
return 0;
}
- std::shared_ptr<minikin::Font> font = minikin::Font::Builder(minikinFont).setWeight(weight)
- .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
+ uint32_t localeListId = minikin::registerLocaleList(langTagStr.c_str());
+ std::shared_ptr<minikin::Font> font =
+ minikin::Font::Builder(minikinFont)
+ .setWeight(weight)
+ .setSlant(static_cast<minikin::FontStyle::Slant>(italic))
+ .setLocaleListId(localeListId)
+ .build();
return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
}
@@ -302,11 +309,12 @@
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontBuilderMethods[] = {
- { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
- { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
- { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
- { "nClone", "(JJIZI)J", (void*) Font_Builder_clone },
- { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
+ {"nInitBuilder", "()J", (void*)Font_Builder_initBuilder},
+ {"nAddAxis", "(JIF)V", (void*)Font_Builder_addAxis},
+ {"nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;Ljava/lang/String;IZI)J",
+ (void*)Font_Builder_build},
+ {"nClone", "(JJIZI)J", (void*)Font_Builder_clone},
+ {"nGetReleaseNativeFont", "()J", (void*)Font_Builder_getReleaseNativeFont},
};
static const JNINativeMethod gFontMethods[] = {
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index 37e5276..8fe6da318 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -83,6 +83,23 @@
return reinterpret_cast<jlong>(releaseFontFamily);
}
+// FastNative
+static jstring FontFamily_getLangTags(JNIEnv* env, jobject, jlong familyPtr) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+ uint32_t localeListId = family->family->localeListId();
+ if (localeListId == 0) {
+ return nullptr;
+ }
+ std::string langTags = minikin::getLocaleString(localeListId);
+ return env->NewStringUTF(langTags.c_str());
+}
+
+// CriticalNative
+static jint FontFamily_getVariant(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+ return static_cast<jint>(family->family->variant());
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontFamilyBuilderMethods[] = {
@@ -93,9 +110,16 @@
{ "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
};
+static const JNINativeMethod gFontFamilyMethods[] = {
+ {"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags},
+ {"nGetVariant", "(J)I", (void*)FontFamily_getVariant},
+};
+
int register_android_graphics_fonts_FontFamily(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder",
- gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods));
+ gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)) +
+ RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily", gFontFamilyMethods,
+ NELEM(gFontFamilyMethods));
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 633f21c..37a6ee7 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -157,12 +157,14 @@
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
ATRACE_CALL();
- if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
- mFixedRenderAhead = false;
- mRenderAheadCapacity = 1;
- } else {
- mFixedRenderAhead = true;
+ if (mFixedRenderAhead) {
mRenderAheadCapacity = mRenderAheadDepth;
+ } else {
+ if (DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
+ mRenderAheadCapacity = 1;
+ } else {
+ mRenderAheadCapacity = 0;
+ }
}
if (window) {
@@ -764,11 +766,16 @@
}
void CanvasContext::setRenderAheadDepth(int renderAhead) {
- if (renderAhead > 2 || renderAhead < 0 || mNativeSurface) {
+ if (renderAhead > 2 || renderAhead < -1 || mNativeSurface) {
return;
}
- mFixedRenderAhead = true;
- mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
+ if (renderAhead == -1) {
+ mFixedRenderAhead = false;
+ mRenderAheadDepth = 0;
+ } else {
+ mFixedRenderAhead = true;
+ mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
+ }
}
SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index ce92661..40d8615 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -16,7 +16,7 @@
package android.location;
-import android.location.LocationResult;
+import android.location.Location;
import android.os.IRemoteCallback;
/**
@@ -24,7 +24,7 @@
*/
oneway interface ILocationListener
{
- void onLocationChanged(in LocationResult locationResult, in @nullable IRemoteCallback onCompleteCallback);
+ void onLocationChanged(in List<Location> locations, in @nullable IRemoteCallback onCompleteCallback);
void onProviderEnabledChanged(String provider, boolean enabled);
void onFlushComplete(int requestCode);
}
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 523117b..35a4091 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.os.Bundle;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -48,15 +49,18 @@
/**
* Called when the location has changed and locations are being delivered in batches. The
* default implementation calls through to ({@link #onLocationChanged(Location)} with all
- * locations in the batch, from earliest to latest.
+ * locations in the batch. The list of locations is always guaranteed to be non-empty, and is
+ * always guaranteed to be ordered from earliest location to latest location (so that the
+ * earliest location in the batch is at index 0 in the list, and the latest location in the
+ * batch is at index size-1 in the list).
*
* @see LocationRequest#getMaxUpdateDelayMillis()
- * @param locationResult the location result list
+ * @param locations the location list
*/
- default void onLocationChanged(@NonNull LocationResult locationResult) {
- final int size = locationResult.size();
- for (int i = 0; i < size; ++i) {
- onLocationChanged(locationResult.get(i));
+ default void onLocationChanged(@NonNull List<Location> locations) {
+ final int size = locations.size();
+ for (int i = 0; i < size; i++) {
+ onLocationChanged(locations.get(i));
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index fdb044d..d569482 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.LOCATION_HARDWARE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.location.GpsStatus.GPS_EVENT_STARTED;
import static android.location.LocationRequest.createFromDeprecatedCriteria;
import static android.location.LocationRequest.createFromDeprecatedProvider;
@@ -43,7 +44,6 @@
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -60,16 +60,17 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.listeners.ListenerExecutor;
-import com.android.internal.listeners.ListenerTransportMultiplexer;
+import com.android.internal.listeners.ListenerTransport;
+import com.android.internal.listeners.ListenerTransportManager;
import com.android.internal.util.Preconditions;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -231,19 +232,26 @@
/**
* Key used for an extra holding a {@link Location} value when a location change is sent using
- * a PendingIntent.
+ * a PendingIntent. If the location change includes a list of batched locations via
+ * {@link #KEY_LOCATIONS} then this key will still be present, and will hold the last location
+ * in the batch. Use {@link Intent#getParcelableExtra(String)} to retrieve the location.
*
* @see #requestLocationUpdates(String, LocationRequest, PendingIntent)
*/
public static final String KEY_LOCATION_CHANGED = "location";
/**
- * Key used for an extra holding a {@link LocationResult} value when a location change is sent
- * using a PendingIntent.
+ * Key used for an extra holding a array of {@link Location}s when a location change is sent
+ * using a PendingIntent. This key will only be present if the location change includes
+ * multiple (ie, batched) locations, otherwise only {@link #KEY_LOCATION_CHANGED} will be
+ * present. Use {@link Intent#getParcelableArrayExtra(String)} to retrieve the locations.
+ *
+ * <p>The array of locations will never be empty, and will ordered from earliest location to
+ * latest location, the same as with {@link LocationListener#onLocationChanged(List)}.
*
* @see #requestLocationUpdates(String, LocationRequest, PendingIntent)
*/
- public static final String KEY_LOCATION_RESULT = "locationResult";
+ public static final String KEY_LOCATIONS = "locations";
/**
* Key used for an extra holding an integer request code when location flush completion is sent
@@ -398,20 +406,40 @@
private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
+ private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
+ "cache_key.location_enabled";
+
+ private static ILocationManager getService() throws RemoteException {
+ try {
+ return ILocationManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE));
+ } catch (ServiceManager.ServiceNotFoundException e) {
+ throw new RemoteException(e);
+ }
+ }
+
@GuardedBy("sLocationListeners")
private static final WeakHashMap<LocationListener, WeakReference<LocationListenerTransport>>
sLocationListeners = new WeakHashMap<>();
- final Context mContext;
+ // allows lazy instantiation since most processes do not use GNSS APIs
+ private static class GnssLazyLoader {
+ static final GnssStatusTransportManager sGnssStatusListeners =
+ new GnssStatusTransportManager();
+ static final GnssNmeaTransportManager sGnssNmeaListeners =
+ new GnssNmeaTransportManager();
+ static final GnssMeasurementsTransportManager sGnssMeasurementsListeners =
+ new GnssMeasurementsTransportManager();
+ static final GnssAntennaTransportManager sGnssAntennaInfoListeners =
+ new GnssAntennaTransportManager();
+ static final GnssNavigationTransportManager sGnssNavigationListeners =
+ new GnssNavigationTransportManager();
+ }
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
- + "LocationManager}")
- final ILocationManager mService;
+ private final Context mContext;
+ private final ILocationManager mService;
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache =
+ private volatile PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache =
new PropertyInvalidatedCache<Integer, Boolean>(
4,
CACHE_KEY_LOCATION_ENABLED_PROPERTY) {
@@ -425,68 +453,12 @@
}
};
- @GuardedBy("mLock")
- @Nullable private GnssStatusTransportMultiplexer mGnssStatusTransportMultiplexer;
- @GuardedBy("mLock")
- @Nullable private GnssNmeaTransportMultiplexer mGnssNmeaTransportMultiplexer;
- @GuardedBy("mLock")
- @Nullable private GnssMeasurementsTransportMultiplexer mGnssMeasurementsTransportMultiplexer;
- @GuardedBy("mLock")
- @Nullable private GnssNavigationTransportMultiplexer mGnssNavigationTransportMultiplexer;
- @GuardedBy("mLock")
- @Nullable private GnssAntennaInfoTransportMultiplexer mGnssAntennaInfoTransportMultiplexer;
-
/**
* @hide
*/
public LocationManager(@NonNull Context context, @NonNull ILocationManager service) {
- mService = service;
- mContext = context;
- }
-
- private GnssStatusTransportMultiplexer getGnssStatusTransportMultiplexer() {
- synchronized (mLock) {
- if (mGnssStatusTransportMultiplexer == null) {
- mGnssStatusTransportMultiplexer = new GnssStatusTransportMultiplexer();
- }
- return mGnssStatusTransportMultiplexer;
- }
- }
-
- private GnssNmeaTransportMultiplexer getGnssNmeaTransportMultiplexer() {
- synchronized (mLock) {
- if (mGnssNmeaTransportMultiplexer == null) {
- mGnssNmeaTransportMultiplexer = new GnssNmeaTransportMultiplexer();
- }
- return mGnssNmeaTransportMultiplexer;
- }
- }
-
- private GnssMeasurementsTransportMultiplexer getGnssMeasurementsTransportMultiplexer() {
- synchronized (mLock) {
- if (mGnssMeasurementsTransportMultiplexer == null) {
- mGnssMeasurementsTransportMultiplexer = new GnssMeasurementsTransportMultiplexer();
- }
- return mGnssMeasurementsTransportMultiplexer;
- }
- }
-
- private GnssNavigationTransportMultiplexer getGnssNavigationTransportMultiplexer() {
- synchronized (mLock) {
- if (mGnssNavigationTransportMultiplexer == null) {
- mGnssNavigationTransportMultiplexer = new GnssNavigationTransportMultiplexer();
- }
- return mGnssNavigationTransportMultiplexer;
- }
- }
-
- private GnssAntennaInfoTransportMultiplexer getGnssAntennaInfoTransportMultiplexer() {
- synchronized (mLock) {
- if (mGnssAntennaInfoTransportMultiplexer == null) {
- mGnssAntennaInfoTransportMultiplexer = new GnssAntennaInfoTransportMultiplexer();
- }
- return mGnssAntennaInfoTransportMultiplexer;
- }
+ mContext = Objects.requireNonNull(context);
+ mService = Objects.requireNonNull(service);
}
/**
@@ -627,10 +599,9 @@
*/
@SystemApi
public boolean isLocationEnabledForUser(@NonNull UserHandle userHandle) {
- synchronized (mLock) {
- if (mLocationEnabledCache != null) {
- return mLocationEnabledCache.query(userHandle.getIdentifier());
- }
+ PropertyInvalidatedCache<Integer, Boolean> cache = mLocationEnabledCache;
+ if (cache != null) {
+ return cache.query(userHandle.getIdentifier());
}
// fallback if cache is disabled
@@ -1905,6 +1876,7 @@
@Deprecated
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
@Nullable
+ @SuppressWarnings("NullableCollection")
public List<String> getProviderPackages(@NonNull String provider) {
try {
return mService.getProviderPackages(provider);
@@ -2264,9 +2236,8 @@
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
}
- GnssStatusTransportMultiplexer multiplexer = getGnssStatusTransportMultiplexer();
- GnssStatus gnssStatus = multiplexer.getGnssStatus();
- int ttff = multiplexer.getTtff();
+ GnssStatus gnssStatus = GpsStatusTransport.sGnssStatus;
+ int ttff = GpsStatusTransport.sTtff;
if (gnssStatus != null) {
if (status == null) {
status = GpsStatus.create(gnssStatus, ttff);
@@ -2299,8 +2270,8 @@
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
}
- getGnssStatusTransportMultiplexer().addListener(listener,
- new HandlerExecutor(new Handler()));
+ GnssLazyLoader.sGnssStatusListeners.addListener(listener,
+ new GpsStatusTransport(new HandlerExecutor(new Handler()), mContext, listener));
return true;
}
@@ -2319,7 +2290,7 @@
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
}
- getGnssStatusTransportMultiplexer().removeListener(listener);
+ GnssLazyLoader.sGnssStatusListeners.removeListener(listener);
}
/**
@@ -2382,7 +2353,8 @@
public boolean registerGnssStatusCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull GnssStatus.Callback callback) {
- getGnssStatusTransportMultiplexer().addListener(callback, executor);
+ GnssLazyLoader.sGnssStatusListeners.addListener(callback,
+ new GnssStatusTransport(executor, mContext, callback));
return true;
}
@@ -2392,7 +2364,7 @@
* @param callback GNSS status callback object to remove
*/
public void unregisterGnssStatusCallback(@NonNull GnssStatus.Callback callback) {
- getGnssStatusTransportMultiplexer().removeListener(callback);
+ GnssLazyLoader.sGnssStatusListeners.removeListener(callback);
}
/**
@@ -2472,7 +2444,8 @@
public boolean addNmeaListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnNmeaMessageListener listener) {
- getGnssNmeaTransportMultiplexer().addListener(listener, executor);
+ GnssLazyLoader.sGnssNmeaListeners.addListener(listener,
+ new GnssNmeaTransport(executor, mContext, listener));
return true;
}
@@ -2482,7 +2455,7 @@
* @param listener a {@link OnNmeaMessageListener} object to remove
*/
public void removeNmeaListener(@NonNull OnNmeaMessageListener listener) {
- getGnssNmeaTransportMultiplexer().removeListener(listener);
+ GnssLazyLoader.sGnssNmeaListeners.removeListener(listener);
}
/**
@@ -2598,10 +2571,8 @@
@NonNull GnssRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull GnssMeasurementsEvent.Callback callback) {
- Preconditions.checkArgument(request != null, "invalid null request");
- getGnssMeasurementsTransportMultiplexer().addListener(request.toGnssMeasurementRequest(),
- callback, executor);
- return true;
+ return registerGnssMeasurementsCallback(request.toGnssMeasurementRequest(), executor,
+ callback);
}
/**
@@ -2623,8 +2594,8 @@
@NonNull GnssMeasurementRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull GnssMeasurementsEvent.Callback callback) {
- Preconditions.checkArgument(request != null, "invalid null request");
- getGnssMeasurementsTransportMultiplexer().addListener(request, callback, executor);
+ GnssLazyLoader.sGnssMeasurementsListeners.addListener(callback,
+ new GnssMeasurementsTransport(executor, mContext, request, callback));
return true;
}
@@ -2656,7 +2627,7 @@
*/
public void unregisterGnssMeasurementsCallback(
@NonNull GnssMeasurementsEvent.Callback callback) {
- getGnssMeasurementsTransportMultiplexer().removeListener(callback);
+ GnssLazyLoader.sGnssMeasurementsListeners.removeListener(callback);
}
/**
@@ -2681,7 +2652,8 @@
public boolean registerAntennaInfoListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull GnssAntennaInfo.Listener listener) {
- getGnssAntennaInfoTransportMultiplexer().addListener(listener, executor);
+ GnssLazyLoader.sGnssAntennaInfoListeners.addListener(listener,
+ new GnssAntennaInfoTransport(executor, mContext, listener));
return true;
}
@@ -2694,7 +2666,7 @@
*/
@Deprecated
public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) {
- getGnssAntennaInfoTransportMultiplexer().removeListener(listener);
+ GnssLazyLoader.sGnssAntennaInfoListeners.removeListener(listener);
}
/**
@@ -2784,7 +2756,8 @@
public boolean registerGnssNavigationMessageCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull GnssNavigationMessage.Callback callback) {
- getGnssNavigationTransportMultiplexer().addListener(callback, executor);
+ GnssLazyLoader.sGnssNavigationListeners.addListener(callback,
+ new GnssNavigationTransport(executor, mContext, callback));
return true;
}
@@ -2795,7 +2768,7 @@
*/
public void unregisterGnssNavigationMessageCallback(
@NonNull GnssNavigationMessage.Callback callback) {
- getGnssNavigationTransportMultiplexer().removeListener(callback);
+ GnssLazyLoader.sGnssNavigationListeners.removeListener(callback);
}
/**
@@ -2904,6 +2877,89 @@
}
}
+ private static class GnssStatusTransportManager extends
+ ListenerTransportManager<GnssStatusTransport> {
+
+ @Override
+ protected void registerTransport(GnssStatusTransport transport)
+ throws RemoteException {
+ getService().registerGnssStatusCallback(transport, transport.getPackage(),
+ transport.getAttributionTag());
+ }
+
+ @Override
+ protected void unregisterTransport(GnssStatusTransport transport)
+ throws RemoteException {
+ getService().unregisterGnssStatusCallback(transport);
+ }
+ }
+
+ private static class GnssNmeaTransportManager extends
+ ListenerTransportManager<GnssNmeaTransport> {
+
+ @Override
+ protected void registerTransport(GnssNmeaTransport transport)
+ throws RemoteException {
+ getService().registerGnssNmeaCallback(transport, transport.getPackage(),
+ transport.getAttributionTag());
+ }
+
+ @Override
+ protected void unregisterTransport(GnssNmeaTransport transport)
+ throws RemoteException {
+ getService().unregisterGnssNmeaCallback(transport);
+ }
+ }
+
+ private static class GnssMeasurementsTransportManager extends
+ ListenerTransportManager<GnssMeasurementsTransport> {
+
+ @Override
+ protected void registerTransport(GnssMeasurementsTransport transport)
+ throws RemoteException {
+ getService().addGnssMeasurementsListener(transport.getRequest(), transport,
+ transport.getPackage(), transport.getAttributionTag());
+ }
+
+ @Override
+ protected void unregisterTransport(GnssMeasurementsTransport transport)
+ throws RemoteException {
+ getService().removeGnssMeasurementsListener(transport);
+ }
+ }
+
+ private static class GnssAntennaTransportManager extends
+ ListenerTransportManager<GnssAntennaInfoTransport> {
+
+ @Override
+ protected void registerTransport(GnssAntennaInfoTransport transport) {
+ transport.getContext().registerReceiver(transport,
+ new IntentFilter(ACTION_GNSS_ANTENNA_INFOS_CHANGED));
+ }
+
+ @Override
+ protected void unregisterTransport(GnssAntennaInfoTransport transport) {
+ transport.getContext().unregisterReceiver(transport);
+ }
+ }
+
+ private static class GnssNavigationTransportManager extends
+ ListenerTransportManager<GnssNavigationTransport> {
+
+ @Override
+ protected void registerTransport(GnssNavigationTransport transport)
+ throws RemoteException {
+ getService().addGnssNavigationMessageListener(transport,
+ transport.getPackage(), transport.getAttributionTag());
+ }
+
+ @Override
+ protected void unregisterTransport(GnssNavigationTransport transport)
+ throws RemoteException {
+ getService().removeGnssNavigationMessageListener(transport);
+ }
+ }
+
private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements
ListenerExecutor, CancellationSignal.OnCancelListener {
@@ -2969,12 +3025,12 @@
}
@Override
- public void onLocationChanged(LocationResult locationResult,
+ public void onLocationChanged(List<Location> locations,
@Nullable IRemoteCallback onCompleteCallback) {
executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
@Override
public void operate(LocationListener listener) {
- listener.onLocationChanged(locationResult);
+ listener.onLocationChanged(locations);
}
@Override
@@ -3018,7 +3074,7 @@
@Override
public void onStarted() {
- mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
+ mGpsListener.onGpsStatusChanged(GPS_EVENT_STARTED);
}
@Override
@@ -3037,273 +3093,269 @@
}
}
- private class GnssStatusTransportMultiplexer extends
- ListenerTransportMultiplexer<Void, GnssStatus.Callback> {
+ private static class GnssStatusTransport extends IGnssStatusListener.Stub implements
+ ListenerTransport<GnssStatus.Callback> {
- private @Nullable IGnssStatusListener mListenerTransport;
+ private final Executor mExecutor;
+ private final String mPackageName;
+ private final String mAttributionTag;
- volatile @Nullable GnssStatus mGnssStatus;
- volatile int mTtff;
+ private volatile @Nullable GnssStatus.Callback mListener;
- GnssStatusTransportMultiplexer() {}
-
- public GnssStatus getGnssStatus() {
- return mGnssStatus;
+ GnssStatusTransport(Executor executor, Context context, GnssStatus.Callback listener) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ Preconditions.checkArgument(listener != null, "invalid null callback");
+ mExecutor = executor;
+ mPackageName = context.getPackageName();
+ mAttributionTag = context.getAttributionTag();
+ mListener = listener;
}
- public int getTtff() {
- return mTtff;
+ public String getPackage() {
+ return mPackageName;
}
- public void addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor) {
- addListener(listener, null, new GpsAdapter(listener), executor);
+ public String getAttributionTag() {
+ return mAttributionTag;
}
@Override
- protected void registerWithServer(Void ignored) throws RemoteException {
- IGnssStatusListener transport = mListenerTransport;
- if (transport == null) {
- transport = new GnssStatusListener();
- }
-
- // if a remote exception is thrown the transport should not be set
- mListenerTransport = null;
- mService.registerGnssStatusCallback(transport, mContext.getPackageName(),
- mContext.getAttributionTag());
- mListenerTransport = transport;
+ public void unregister() {
+ mListener = null;
}
@Override
- protected void unregisterWithServer() throws RemoteException {
- if (mListenerTransport != null) {
- IGnssStatusListener transport = mListenerTransport;
- mListenerTransport = null;
- mService.unregisterGnssStatusCallback(transport);
- }
+ public @Nullable GnssStatus.Callback getListener() {
+ return mListener;
}
- private class GnssStatusListener extends IGnssStatusListener.Stub {
+ @Override
+ public void onGnssStarted() {
+ execute(mExecutor, GnssStatus.Callback::onStarted);
+ }
- GnssStatusListener() {}
+ @Override
+ public void onGnssStopped() {
+ execute(mExecutor, GnssStatus.Callback::onStopped);
+ }
- @Override
- public void onGnssStarted() {
- deliverToListeners(GnssStatus.Callback::onStarted);
- }
+ @Override
+ public void onFirstFix(int ttff) {
+ execute(mExecutor, listener -> listener.onFirstFix(ttff));
- @Override
- public void onGnssStopped() {
- deliverToListeners(GnssStatus.Callback::onStopped);
- }
+ }
- @Override
- public void onFirstFix(int ttff) {
- mTtff = ttff;
- deliverToListeners(callback -> callback.onFirstFix(ttff));
- }
+ @Override
+ public void onSvStatusChanged(GnssStatus gnssStatus) {
+ execute(mExecutor, listener -> listener.onSatelliteStatusChanged(gnssStatus));
+ }
+ }
- @Override
- public void onSvStatusChanged(GnssStatus gnssStatus) {
- mGnssStatus = gnssStatus;
- deliverToListeners(callback -> callback.onSatelliteStatusChanged(gnssStatus));
+ private static class GpsStatusTransport extends GnssStatusTransport {
+
+ static volatile int sTtff;
+ static volatile GnssStatus sGnssStatus;
+
+ GpsStatusTransport(Executor executor, Context context, GpsStatus.Listener listener) {
+ super(executor, context, new GpsAdapter(listener));
+ }
+
+ @Override
+ public void onFirstFix(int ttff) {
+ sTtff = ttff;
+ super.onFirstFix(ttff);
+ }
+
+ @Override
+ public void onSvStatusChanged(GnssStatus gnssStatus) {
+ sGnssStatus = gnssStatus;
+ super.onSvStatusChanged(gnssStatus);
+ }
+ }
+
+ private static class GnssNmeaTransport extends IGnssNmeaListener.Stub implements
+ ListenerTransport<OnNmeaMessageListener> {
+
+ private final Executor mExecutor;
+ private final String mPackageName;
+ private final String mAttributionTag;
+
+ private volatile @Nullable OnNmeaMessageListener mListener;
+
+ GnssNmeaTransport(Executor executor, Context context, OnNmeaMessageListener listener) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+ mExecutor = executor;
+ mPackageName = context.getPackageName();
+ mAttributionTag = context.getAttributionTag();
+ mListener = listener;
+ }
+
+ public String getPackage() {
+ return mPackageName;
+ }
+
+ public String getAttributionTag() {
+ return mAttributionTag;
+ }
+
+ @Override
+ public void unregister() {
+ mListener = null;
+ }
+
+ @Override
+ public @Nullable OnNmeaMessageListener getListener() {
+ return mListener;
+ }
+
+ @Override
+ public void onNmeaReceived(long timestamp, String nmea) {
+ execute(mExecutor, callback -> callback.onNmeaMessage(nmea, timestamp));
+ }
+ }
+
+ private static class GnssMeasurementsTransport extends IGnssMeasurementsListener.Stub implements
+ ListenerTransport<GnssMeasurementsEvent.Callback> {
+
+ private final Executor mExecutor;
+ private final String mPackageName;
+ private final String mAttributionTag;
+ private final GnssMeasurementRequest mRequest;
+
+ private volatile @Nullable GnssMeasurementsEvent.Callback mListener;
+
+ GnssMeasurementsTransport(Executor executor, Context context,
+ GnssMeasurementRequest request, GnssMeasurementsEvent.Callback listener) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ Preconditions.checkArgument(listener != null, "invalid null callback");
+ Preconditions.checkArgument(request != null, "invalid null request");
+ mExecutor = executor;
+ mPackageName = context.getPackageName();
+ mAttributionTag = context.getAttributionTag();
+ mRequest = request;
+ mListener = listener;
+ }
+
+ public String getPackage() {
+ return mPackageName;
+ }
+
+ public String getAttributionTag() {
+ return mAttributionTag;
+ }
+
+ public GnssMeasurementRequest getRequest() {
+ return mRequest;
+ }
+
+ @Override
+ public void unregister() {
+ mListener = null;
+ }
+
+ @Override
+ public @Nullable GnssMeasurementsEvent.Callback getListener() {
+ return mListener;
+ }
+
+ @Override
+ public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
+ execute(mExecutor, callback -> callback.onGnssMeasurementsReceived(event));
+ }
+
+ @Override
+ public void onStatusChanged(int status) {
+ execute(mExecutor, callback -> callback.onStatusChanged(status));
+ }
+ }
+
+ private static class GnssAntennaInfoTransport extends BroadcastReceiver implements
+ ListenerTransport<GnssAntennaInfo.Listener> {
+
+ private final Executor mExecutor;
+ private final Context mContext;
+
+ private volatile @Nullable GnssAntennaInfo.Listener mListener;
+
+ GnssAntennaInfoTransport(Executor executor, Context context,
+ GnssAntennaInfo.Listener listener) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+ mExecutor = executor;
+ mContext = context;
+ mListener = listener;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public void unregister() {
+ mListener = null;
+ }
+
+ @Override
+ public @Nullable GnssAntennaInfo.Listener getListener() {
+ return mListener;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ArrayList<GnssAntennaInfo> infos = intent.getParcelableArrayListExtra(
+ EXTRA_GNSS_ANTENNA_INFOS);
+ if (infos != null) {
+ execute(mExecutor, callback -> callback.onGnssAntennaInfoReceived(infos));
}
}
}
- private class GnssNmeaTransportMultiplexer extends
- ListenerTransportMultiplexer<Void, OnNmeaMessageListener> {
+ private static class GnssNavigationTransport extends IGnssNavigationMessageListener.Stub
+ implements ListenerTransport<GnssNavigationMessage.Callback> {
- private @Nullable IGnssNmeaListener mListenerTransport;
+ private final Executor mExecutor;
+ private final String mPackageName;
+ private final String mAttributionTag;
- GnssNmeaTransportMultiplexer() {}
+ private volatile @Nullable GnssNavigationMessage.Callback mListener;
- public void addListener(@NonNull OnNmeaMessageListener listener,
- @NonNull Executor executor) {
- addListener(listener, null, listener, executor);
+ GnssNavigationTransport(Executor executor, Context context,
+ GnssNavigationMessage.Callback listener) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ Preconditions.checkArgument(listener != null, "invalid null callback");
+ mExecutor = executor;
+ mPackageName = context.getPackageName();
+ mAttributionTag = context.getAttributionTag();
+ mListener = listener;
+ }
+
+ public String getPackage() {
+ return mPackageName;
+ }
+
+ public String getAttributionTag() {
+ return mAttributionTag;
}
@Override
- protected void registerWithServer(Void ignored) throws RemoteException {
- IGnssNmeaListener transport = mListenerTransport;
- if (transport == null) {
- transport = new GnssNmeaListener();
- }
-
- // if a remote exception is thrown the transport should not be set
- mListenerTransport = null;
- mService.registerGnssNmeaCallback(transport, mContext.getPackageName(),
- mContext.getAttributionTag());
- mListenerTransport = transport;
+ public void unregister() {
+ mListener = null;
}
@Override
- protected void unregisterWithServer() throws RemoteException {
- if (mListenerTransport != null) {
- IGnssNmeaListener transport = mListenerTransport;
- mListenerTransport = null;
- mService.unregisterGnssNmeaCallback(transport);
- }
- }
-
- private class GnssNmeaListener extends IGnssNmeaListener.Stub {
-
- GnssNmeaListener() {}
-
- @Override
- public void onNmeaReceived(long timestamp, String nmea) {
- deliverToListeners(callback -> callback.onNmeaMessage(nmea, timestamp));
- }
- }
- }
-
- private class GnssMeasurementsTransportMultiplexer extends
- ListenerTransportMultiplexer<GnssMeasurementRequest, GnssMeasurementsEvent.Callback> {
-
- private @Nullable IGnssMeasurementsListener mListenerTransport;
-
- GnssMeasurementsTransportMultiplexer() {}
-
- @Override
- protected void registerWithServer(GnssMeasurementRequest request) throws RemoteException {
- IGnssMeasurementsListener transport = mListenerTransport;
- if (transport == null) {
- transport = new GnssMeasurementsListener();
- }
-
- // if a remote exception is thrown the transport should not be set
- mListenerTransport = null;
- mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
- mContext.getAttributionTag());
- mListenerTransport = transport;
+ public @Nullable GnssNavigationMessage.Callback getListener() {
+ return mListener;
}
@Override
- protected void unregisterWithServer() throws RemoteException {
- if (mListenerTransport != null) {
- IGnssMeasurementsListener transport = mListenerTransport;
- mListenerTransport = null;
- mService.removeGnssMeasurementsListener(transport);
- }
+ public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
+ execute(mExecutor, listener -> listener.onGnssNavigationMessageReceived(event));
}
@Override
- protected GnssMeasurementRequest mergeRequests(
- Collection<GnssMeasurementRequest> requests) {
- GnssMeasurementRequest.Builder builder = new GnssMeasurementRequest.Builder();
- for (GnssMeasurementRequest request : requests) {
- if (request.isFullTracking()) {
- builder.setFullTracking(true);
- }
- if (request.isCorrelationVectorOutputsEnabled()) {
- builder.setCorrelationVectorOutputsEnabled(true);
- }
- }
-
- return builder.build();
- }
-
- private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub {
-
- GnssMeasurementsListener() {}
-
- @Override
- public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
- deliverToListeners(callback -> callback.onGnssMeasurementsReceived(event));
- }
-
- @Override
- public void onStatusChanged(int status) {
- deliverToListeners(callback -> callback.onStatusChanged(status));
- }
- }
- }
-
- private class GnssNavigationTransportMultiplexer extends
- ListenerTransportMultiplexer<Void, GnssNavigationMessage.Callback> {
-
- @Nullable
- private IGnssNavigationMessageListener mListenerTransport;
-
- GnssNavigationTransportMultiplexer() {}
-
- @Override
- protected void registerWithServer(Void ignored) throws RemoteException {
- IGnssNavigationMessageListener transport = mListenerTransport;
- if (transport == null) {
- transport = new GnssNavigationMessageListener();
- }
-
- // if a remote exception is thrown the transport should not be set
- mListenerTransport = null;
- mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(),
- mContext.getAttributionTag());
- mListenerTransport = transport;
- }
-
- @Override
- protected void unregisterWithServer() throws RemoteException {
- if (mListenerTransport != null) {
- IGnssNavigationMessageListener transport = mListenerTransport;
- mListenerTransport = null;
- mService.removeGnssNavigationMessageListener(transport);
- }
- }
-
- private class GnssNavigationMessageListener extends IGnssNavigationMessageListener.Stub {
-
- GnssNavigationMessageListener() {}
-
- @Override
- public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
- deliverToListeners(listener -> listener.onGnssNavigationMessageReceived(event));
- }
-
- @Override
- public void onStatusChanged(int status) {
- deliverToListeners(listener -> listener.onStatusChanged(status));
- }
- }
- }
-
- private class GnssAntennaInfoTransportMultiplexer extends
- ListenerTransportMultiplexer<Void, GnssAntennaInfo.Listener> {
-
- private @Nullable BroadcastReceiver mListenerTransport;
-
- GnssAntennaInfoTransportMultiplexer() {}
-
- @Override
- protected void registerWithServer(Void ignored) {
- if (mListenerTransport == null) {
- // if an exception is thrown the transport should not be set
- BroadcastReceiver transport = new GnssAntennaInfoReceiver();
- mContext.registerReceiver(transport,
- new IntentFilter(ACTION_GNSS_ANTENNA_INFOS_CHANGED));
- mListenerTransport = transport;
- }
- }
-
- @Override
- protected void unregisterWithServer() {
- if (mListenerTransport != null) {
- BroadcastReceiver transport = mListenerTransport;
- mListenerTransport = null;
- mContext.unregisterReceiver(transport);
- }
- }
-
- private class GnssAntennaInfoReceiver extends BroadcastReceiver {
-
- GnssAntennaInfoReceiver() {}
-
- @Override
- public void onReceive(Context context, Intent intent) {
- ArrayList<GnssAntennaInfo> infos = intent.getParcelableArrayListExtra(
- EXTRA_GNSS_ANTENNA_INFOS);
- if (infos != null) {
- deliverToListeners(callback -> callback.onGnssAntennaInfoReceived(infos));
- }
- }
+ public void onStatusChanged(int status) {
+ execute(mExecutor, listener -> listener.onStatusChanged(status));
}
}
@@ -3321,8 +3373,8 @@
}
@Override
- public void onLocationChanged(@NonNull LocationResult locationResult) {
- mCallback.onLocationBatch(locationResult.asList());
+ public void onLocationChanged(@NonNull List<Location> locations) {
+ mCallback.onLocationBatch(locations);
}
}
@@ -3336,12 +3388,6 @@
/**
* @hide
*/
- private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
- "cache_key.location_enabled";
-
- /**
- * @hide
- */
public static void invalidateLocalLocationEnabledCaches() {
PropertyInvalidatedCache.invalidateCache(CACHE_KEY_LOCATION_ENABLED_PROPERTY);
}
@@ -3350,8 +3396,6 @@
* @hide
*/
public void disableLocalLocationEnabledCaches() {
- synchronized (mLock) {
- mLocationEnabledCache = null;
- }
+ mLocationEnabledCache = null;
}
}
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index 60b251e..26cf018 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -23,7 +23,9 @@
* Information about the properties of a location provider.
*
* @deprecated This class is incapable of representing unknown provider properties and may return
- * incorrect results when the properties are unknown.
+ * incorrect results on the rare occasion when a provider's properties are unknown. Prefer using
+ * {@link LocationManager#getProviderProperties(String)} to retrieve {@link ProviderProperties}
+ * instead.
*/
@Deprecated
public class LocationProvider {
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 323e740..cb56ee5 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -605,7 +605,7 @@
* When available, batching can provide substantial power savings to the device, and clients are
* encouraged to take advantage where appropriate for the use case.
*
- * @see LocationListener#onLocationChanged(LocationResult)
+ * @see LocationListener#onLocationChanged(java.util.List)
* @return the maximum time by which a location update may be delayed
*/
public @IntRange(from = 0) long getMaxUpdateDelayMillis() {
diff --git a/location/java/android/location/LocationResult.java b/location/java/android/location/LocationResult.java
index 79a000c..8423000 100644
--- a/location/java/android/location/LocationResult.java
+++ b/location/java/android/location/LocationResult.java
@@ -19,7 +19,6 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,21 +33,14 @@
/**
* A location result representing a list of locations, ordered from earliest to latest.
+ *
+ * @hide
*/
public final class LocationResult implements Parcelable {
/**
- * Creates a new LocationResult from the given location.
- */
- public static @NonNull LocationResult create(@NonNull Location location) {
- ArrayList<Location> locations = new ArrayList<>(1);
- locations.add(new Location(Objects.requireNonNull(location)));
- return new LocationResult(locations);
- }
-
- /**
- * Creates a new LocationResult from the given locations. Locations must be ordered in the same
- * order they were derived (earliest to latest).
+ * Creates a new LocationResult from the given locations, making a copy of each location.
+ * Locations must be ordered in the same order they were derived (earliest to latest).
*/
public static @NonNull LocationResult create(@NonNull List<Location> locations) {
Preconditions.checkArgument(!locations.isEmpty());
@@ -60,16 +52,40 @@
}
/**
- * Creates a new LocationResult that takes ownership of the given location without copying it.
- * Callers must ensure the given location is never mutated after this method is called.
- *
- * @hide
+ * Creates a new LocationResult from the given locations, making a copy of each location.
+ * Locations must be ordered in the same order they were derived (earliest to latest).
*/
- @SystemApi
- public static @NonNull LocationResult wrap(@NonNull Location location) {
- ArrayList<Location> locations = new ArrayList<>(1);
- locations.add(Objects.requireNonNull(location));
- return new LocationResult(locations);
+ public static @NonNull LocationResult create(@NonNull Location... locations) {
+ Preconditions.checkArgument(locations.length > 0);
+ ArrayList<Location> locationsCopy = new ArrayList<>(locations.length);
+ for (Location location : locations) {
+ locationsCopy.add(new Location(Objects.requireNonNull(location)));
+ }
+ return new LocationResult(locationsCopy);
+ }
+
+ /**
+ * Creates a new LocationResult that takes ownership of the given locations without copying
+ * them. Callers must ensure the given locations are never mutated after this method is called.
+ * Locations must be ordered in the same order they were derived (earliest to latest).
+ */
+ public static @NonNull LocationResult wrap(@NonNull List<Location> locations) {
+ Preconditions.checkArgument(!locations.isEmpty());
+ return new LocationResult(new ArrayList<>(locations));
+ }
+
+ /**
+ * Creates a new LocationResult that takes ownership of the given locations without copying
+ * them. Callers must ensure the given locations are never mutated after this method is called.
+ * Locations must be ordered in the same order they were derived (earliest to latest).
+ */
+ public static @NonNull LocationResult wrap(@NonNull Location... locations) {
+ Preconditions.checkArgument(locations.length > 0);
+ ArrayList<Location> newLocations = new ArrayList<>(locations.length);
+ for (Location location : locations) {
+ newLocations.add(Objects.requireNonNull(location));
+ }
+ return new LocationResult(newLocations);
}
private final ArrayList<Location> mLocations;
@@ -112,7 +128,7 @@
}
/**
- * Returns the numer of locations in this location result.
+ * Returns the number of locations in this location result.
*/
public @IntRange(from = 1) int size() {
return mLocations.size();
@@ -139,9 +155,9 @@
* @hide
*/
public @NonNull LocationResult deepCopy() {
- ArrayList<Location> copy = new ArrayList<>(mLocations.size());
final int size = mLocations.size();
- for (int i = 0; i < size; ++i) {
+ ArrayList<Location> copy = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
copy.add(new Location(mLocations.get(i)));
}
return new LocationResult(copy);
@@ -164,7 +180,7 @@
* Returns a LocationResult with only locations that pass the given predicate. This
* implementation will avoid allocations when no locations are filtered out. The predicate is
* guaranteed to be invoked once per location, in order from earliest to latest. If all
- * locations are filtered out a null value is returned instead of an empty LocationResult.
+ * locations are filtered out a null value is returned.
*
* @hide
*/
diff --git a/location/java/android/location/provider/ILocationProviderManager.aidl b/location/java/android/location/provider/ILocationProviderManager.aidl
index e3f51d9..50ed046 100644
--- a/location/java/android/location/provider/ILocationProviderManager.aidl
+++ b/location/java/android/location/provider/ILocationProviderManager.aidl
@@ -16,7 +16,7 @@
package android.location.provider;
-import android.location.LocationResult;
+import android.location.Location;
import android.location.provider.ProviderProperties;
/**
@@ -28,6 +28,7 @@
void onSetAllowed(boolean allowed);
void onSetProperties(in ProviderProperties properties);
- void onReportLocation(in LocationResult locationResult);
+ void onReportLocation(in Location location);
+ void onReportLocations(in List<Location> locations);
void onFlushComplete();
}
diff --git a/location/java/android/location/provider/LocationProviderBase.java b/location/java/android/location/provider/LocationProviderBase.java
index 8f455cd..ae6395d 100644
--- a/location/java/android/location/provider/LocationProviderBase.java
+++ b/location/java/android/location/provider/LocationProviderBase.java
@@ -25,12 +25,13 @@
import android.content.Context;
import android.content.Intent;
import android.location.Location;
-import android.location.LocationResult;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -200,35 +201,29 @@
* Reports a new location from this provider.
*/
public void reportLocation(@NonNull Location location) {
- reportLocation(LocationResult.create(location));
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onReportLocation(stripExtras(location));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
}
/**
- * Reports a new location result from this provider.
- *
- * <p>May only be used from Android S onwards.
+ * Reports a new batch of locations from this provider. Locations must be ordered in the list
+ * from earliest first to latest last.
*/
- public void reportLocation(@NonNull LocationResult locationResult) {
+ public void reportLocations(@NonNull List<Location> locations) {
ILocationProviderManager manager = mManager;
if (manager != null) {
- locationResult = locationResult.map(location -> {
- // remove deprecated extras to save on serialization costs
- Bundle extras = location.getExtras();
- if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
- || extras.containsKey("coarseLocation"))) {
- location = new Location(location);
- extras = location.getExtras();
- extras.remove(EXTRA_NO_GPS_LOCATION);
- extras.remove("coarseLocation");
- if (extras.isEmpty()) {
- location.setExtras(null);
- }
- }
- return location;
- });
+
try {
- manager.onReportLocation(locationResult);
+ manager.onReportLocations(stripExtras(locations));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (RuntimeException e) {
@@ -246,9 +241,9 @@
/**
* Requests a flush of any pending batched locations. The callback must always be invoked once
- * per invocation, and should be invoked after {@link #reportLocation(LocationResult)} has been
- * invoked with any flushed locations. The callback may be invoked immediately if no locations
- * are flushed.
+ * per invocation, and should be invoked after {@link #reportLocation(Location)} or
+ * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may
+ * be invoked immediately if no locations are flushed.
*/
public abstract void onFlush(@NonNull OnFlushCompleteCallback callback);
@@ -259,6 +254,49 @@
@SuppressLint("NullableCollection")
@Nullable Bundle extras);
+ private static Location stripExtras(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
+ || extras.containsKey("indoorProbability")
+ || extras.containsKey("coarseLocation"))) {
+ location = new Location(location);
+ extras = location.getExtras();
+ extras.remove(EXTRA_NO_GPS_LOCATION);
+ extras.remove("indoorProbability");
+ extras.remove("coarseLocation");
+ if (extras.isEmpty()) {
+ location.setExtras(null);
+ }
+ }
+ return location;
+ }
+
+ private static List<Location> stripExtras(List<Location> locations) {
+ List<Location> mapped = locations;
+ final int size = locations.size();
+ int i = 0;
+ for (Location location : locations) {
+ Location newLocation = stripExtras(location);
+ if (mapped != locations) {
+ mapped.add(newLocation);
+ } else if (newLocation != location) {
+ mapped = new ArrayList<>(size);
+ int j = 0;
+ for (Location copiedLocation : locations) {
+ if (j >= i) {
+ break;
+ }
+ mapped.add(copiedLocation);
+ j++;
+ }
+ mapped.add(newLocation);
+ }
+ i++;
+ }
+
+ return mapped;
+ }
+
private final class Service extends ILocationProvider.Stub {
Service() {}
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 338d7cc..0d81f36 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -21,8 +21,8 @@
method @Deprecated protected void onInit();
method @Deprecated protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle);
method @Deprecated protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
- method @Deprecated public void reportLocation(android.location.Location);
- method @Deprecated public void reportLocation(android.location.LocationResult);
+ method @Deprecated public void reportLocation(@NonNull android.location.Location);
+ method @Deprecated public void reportLocations(@NonNull java.util.List<android.location.Location>);
method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setAdditionalProviderPackages(java.util.List<java.lang.String>);
method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.R) public void setAllowed(boolean);
method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setEnabled(boolean);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index aea93ce..7f1cf6d 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,13 +16,13 @@
package com.android.location.provider;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.location.LocationResult;
import android.location.provider.ILocationProvider;
import android.location.provider.ILocationProviderManager;
import android.location.provider.ProviderProperties;
@@ -39,6 +39,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -94,10 +95,6 @@
*/
public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
- private static final String EXTRA_KEY_COARSE_LOCATION = "coarseLocation";
- private static final String EXTRA_KEY_NO_GPS_LOCATION = "noGPSLocation";
- private static final String EXTRA_KEY_INDOOR_PROB = "indoorProbability";
-
final String mTag;
@Nullable final String mPackageName;
@Nullable final String mAttributionTag;
@@ -254,20 +251,11 @@
/**
* Reports a new location from this provider.
*/
- public void reportLocation(Location location) {
- reportLocation(LocationResult.create(location));
- }
-
- /**
- * Reports a new location from this provider.
- */
- public void reportLocation(LocationResult locationResult) {
+ public void reportLocation(@NonNull Location location) {
ILocationProviderManager manager = mManager;
if (manager != null) {
- locationResult = locationResult.map(this::cleanUpExtras);
-
try {
- manager.onReportLocation(locationResult);
+ manager.onReportLocation(stripExtras(location));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (RuntimeException e) {
@@ -277,30 +265,20 @@
}
/**
- * Remove deprecated/unnecessary extras to save on serialization costs.
- *
- * {@link #EXTRA_KEY_NO_GPS_LOCATION} and {@link #EXTRA_KEY_COARSE_LOCATION} are deprecated.
- *
- * {@link #EXTRA_KEY_INDOOR_PROB} should only be used in the framework.
+ * Reports a new batch of locations from this provider. Locations must be ordered in the list
+ * from earliest first to latest last.
*/
- private Location cleanUpExtras(Location location) {
- Bundle extras = location.getExtras();
- if (extras == null) {
- return location;
- }
- if (extras.containsKey(EXTRA_KEY_NO_GPS_LOCATION)
- || extras.containsKey(EXTRA_KEY_COARSE_LOCATION)
- || extras.containsKey(EXTRA_KEY_INDOOR_PROB)) {
- location = new Location(location);
- extras = location.getExtras();
- extras.remove(EXTRA_KEY_NO_GPS_LOCATION);
- extras.remove(EXTRA_KEY_COARSE_LOCATION);
- extras.remove(EXTRA_KEY_INDOOR_PROB);
- if (extras.isEmpty()) {
- location.setExtras(null);
+ public void reportLocations(@NonNull List<Location> locations) {
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onReportLocations(stripExtras(locations));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
+ Log.w(mTag, e);
}
}
- return location;
}
protected void onInit() {
@@ -336,9 +314,9 @@
/**
* Requests a flush of any pending batched locations. The callback must always be invoked once
- * per invocation, and should be invoked after {@link #reportLocation(LocationResult)} has been
- * invoked with any flushed locations. The callback may be invoked immediately if no locations
- * are flushed.
+ * per invocation, and should be invoked after {@link #reportLocation(Location)} or
+ * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may
+ * be invoked immediately if no locations are flushed.
*/
protected void onFlush(OnFlushCompleteCallback callback) {
callback.onFlushComplete();
@@ -433,4 +411,47 @@
onSendExtraCommand(command, extras);
}
}
+
+ private static Location stripExtras(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
+ || extras.containsKey("indoorProbability")
+ || extras.containsKey("coarseLocation"))) {
+ location = new Location(location);
+ extras = location.getExtras();
+ extras.remove(EXTRA_NO_GPS_LOCATION);
+ extras.remove("indoorProbability");
+ extras.remove("coarseLocation");
+ if (extras.isEmpty()) {
+ location.setExtras(null);
+ }
+ }
+ return location;
+ }
+
+ private static List<Location> stripExtras(List<Location> locations) {
+ List<Location> mapped = locations;
+ final int size = locations.size();
+ int i = 0;
+ for (Location location : locations) {
+ Location newLocation = stripExtras(location);
+ if (mapped != locations) {
+ mapped.add(newLocation);
+ } else if (newLocation != location) {
+ mapped = new ArrayList<>(size);
+ int j = 0;
+ for (Location copiedLocation : locations) {
+ if (j >= i) {
+ break;
+ }
+ mapped.add(copiedLocation);
+ j++;
+ }
+ mapped.add(newLocation);
+ }
+ i++;
+ }
+
+ return mapped;
+ }
}
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 255c15c..eedb996 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -316,6 +316,15 @@
* Not guaranteed to be supported by devices, may be emulated if not supported. */
public static final int ENCODING_PCM_32BIT = 22;
+ /** Audio data format: MPEG-H baseline profile, level 3 */
+ public static final int ENCODING_MPEGH_BL_L3 = 23;
+ /** Audio data format: MPEG-H baseline profile, level 4 */
+ public static final int ENCODING_MPEGH_BL_L4 = 24;
+ /** Audio data format: MPEG-H low complexity profile, level 3 */
+ public static final int ENCODING_MPEGH_LC_L3 = 25;
+ /** Audio data format: MPEG-H low complexity profile, level 4 */
+ public static final int ENCODING_MPEGH_LC_L4 = 26;
+
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
switch(enc) {
@@ -363,6 +372,14 @@
return "ENCODING_PCM_24BIT_PACKED";
case ENCODING_PCM_32BIT:
return "ENCODING_PCM_32BIT";
+ case ENCODING_MPEGH_BL_L3:
+ return "ENCODING_MPEGH_BL_L3";
+ case ENCODING_MPEGH_BL_L4:
+ return "ENCODING_MPEGH_BL_L4";
+ case ENCODING_MPEGH_LC_L3:
+ return "ENCODING_MPEGH_LC_L3";
+ case ENCODING_MPEGH_LC_L4:
+ return "ENCODING_MPEGH_LC_L4";
default :
return "invalid encoding " + enc;
}
@@ -594,6 +611,10 @@
case ENCODING_OPUS:
case ENCODING_PCM_24BIT_PACKED:
case ENCODING_PCM_32BIT:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return true;
default:
return false;
@@ -625,6 +646,10 @@
case ENCODING_OPUS:
case ENCODING_PCM_24BIT_PACKED:
case ENCODING_PCM_32BIT:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return true;
default:
return false;
@@ -659,6 +684,10 @@
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
case ENCODING_OPUS:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return false;
case ENCODING_INVALID:
default:
@@ -693,6 +722,10 @@
case ENCODING_E_AC3_JOC:
case ENCODING_DOLBY_MAT:
case ENCODING_OPUS:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
return false;
case ENCODING_INVALID:
default:
@@ -975,6 +1008,10 @@
case ENCODING_OPUS:
case ENCODING_PCM_24BIT_PACKED:
case ENCODING_PCM_32BIT:
+ case ENCODING_MPEGH_BL_L3:
+ case ENCODING_MPEGH_BL_L4:
+ case ENCODING_MPEGH_LC_L3:
+ case ENCODING_MPEGH_LC_L4:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1197,7 +1234,11 @@
ENCODING_DOLBY_MAT,
ENCODING_OPUS,
ENCODING_PCM_24BIT_PACKED,
- ENCODING_PCM_32BIT }
+ ENCODING_PCM_32BIT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
@@ -1213,6 +1254,10 @@
ENCODING_AC4,
ENCODING_E_AC3_JOC,
ENCODING_DOLBY_MAT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4,
};
/** @hide */
@@ -1225,7 +1270,11 @@
ENCODING_DOLBY_TRUEHD,
ENCODING_AC4,
ENCODING_E_AC3_JOC,
- ENCODING_DOLBY_MAT }
+ ENCODING_DOLBY_MAT,
+ ENCODING_MPEGH_BL_L3,
+ ENCODING_MPEGH_BL_L4,
+ ENCODING_MPEGH_LC_L3,
+ ENCODING_MPEGH_LC_L4 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface SurroundSoundEncoding {}
@@ -1259,6 +1308,14 @@
return "Dolby Atmos in Dolby Digital Plus";
case ENCODING_DOLBY_MAT:
return "Dolby MAT";
+ case ENCODING_MPEGH_BL_L3:
+ return "MPEG-H 3D Audio baseline profile level 3";
+ case ENCODING_MPEGH_BL_L4:
+ return "MPEG-H 3D Audio baseline profile level 4";
+ case ENCODING_MPEGH_LC_L3:
+ return "MPEG-H 3D Audio low complexity profile level 3";
+ case ENCODING_MPEGH_LC_L4:
+ return "MPEG-H 3D Audio low complexity profile level 4";
default:
return "Unknown surround sound format";
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 08deb15..8d090f8 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1,5 +1,4 @@
/*
-/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 812dac9..27f72687 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -20,6 +20,7 @@
import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -195,6 +196,8 @@
private int mDeviceId;
+ private int mSessionId;
+
/**
* Never use without initializing parameters afterwards
*/
@@ -207,7 +210,10 @@
* @hide
*/
public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) {
- if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); }
+ if (DEBUG) {
+ Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer
+ + " sessionId=" + pic.mSessionId);
+ }
mPlayerIId = piid;
mPlayerType = pic.mPlayerType;
mClientUid = uid;
@@ -220,6 +226,7 @@
} else {
mIPlayerShell = null;
}
+ mSessionId = pic.mSessionId;
}
/**
@@ -259,6 +266,7 @@
anonymCopy.mClientUid = PLAYER_UPID_INVALID;
anonymCopy.mClientPid = PLAYER_UPID_INVALID;
anonymCopy.mIPlayerShell = null;
+ anonymCopy.mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
return anonymCopy;
}
@@ -303,6 +311,17 @@
/**
* @hide
+ * Return the audio session ID associated with this player.
+ * See {@link AudioManager#generateAudioSessionId()}.
+ * @return an audio session ID
+ */
+ @SystemApi
+ public @IntRange(from = 0) int getSessionId() {
+ return mSessionId;
+ }
+
+ /**
+ * @hide
* Return the type of player linked to this configuration.
* <br>Note that player types not exposed in the system API will be represented as
* {@link #PLAYER_TYPE_UNKNOWN}.
@@ -381,6 +400,17 @@
/**
* @hide
+ * Handle a change of audio session Id
+ * @param sessionId the audio session ID
+ */
+ public boolean handleSessionIdEvent(int sessionId) {
+ final boolean changed = sessionId != mSessionId;
+ mSessionId = sessionId;
+ return changed;
+ }
+
+ /**
+ * @hide
* Handle a player state change
* @param event
* @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID}
@@ -476,7 +506,8 @@
@Override
public int hashCode() {
- return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid);
+ return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid,
+ mSessionId);
}
@Override
@@ -498,6 +529,7 @@
ips = mIPlayerShell;
}
dest.writeStrongInterface(ips == null ? null : ips.getIPlayer());
+ dest.writeInt(mSessionId);
}
private AudioPlaybackConfiguration(Parcel in) {
@@ -510,6 +542,7 @@
mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in);
final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder());
mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p);
+ mSessionId = in.readInt();
}
@Override
@@ -523,7 +556,8 @@
&& (mDeviceId == that.mDeviceId)
&& (mPlayerType == that.mPlayerType)
&& (mClientUid == that.mClientUid)
- && (mClientPid == that.mClientPid));
+ && (mClientPid == that.mClientPid))
+ && (mSessionId == that.mSessionId);
}
@Override
@@ -533,7 +567,8 @@
+ " type:" + toLogFriendlyPlayerType(mPlayerType)
+ " u/pid:" + mClientUid + "/" + mClientPid
+ " state:" + toLogFriendlyPlayerState(mPlayerState)
- + " attr:" + mPlayerAttr;
+ + " attr:" + mPlayerAttr
+ + " sessionId:" + mSessionId;
}
//=====================================================================
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index c2ce7d3..b196437 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
@@ -67,7 +68,14 @@
* fill with the new audio data. The size of this buffer, specified during the construction,
* determines how long an AudioRecord can record before "over-running" data that has not
* been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
- * the total recording buffer size.
+ * the total recording buffer size.</p>
+ * <p>
+ * Applications creating an AudioRecord instance need
+ * {@link android.Manifest.permission#RECORD_AUDIO} or the Builder will throw
+ * {@link java.lang.UnsupportedOperationException} on
+ * {@link android.media.AudioRecord.Builder#build build()},
+ * and the constructor will return an instance in state
+ * {@link #STATE_UNINITIALIZED}.</p>
*/
public class AudioRecord implements AudioRouting, MicrophoneDirection,
AudioRecordingMonitor, AudioRecordingMonitorClient
@@ -297,6 +305,7 @@
* smaller than getMinBufferSize() will result in an initialization failure.
* @throws java.lang.IllegalArgumentException
*/
+ @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes)
throws IllegalArgumentException {
@@ -334,6 +343,7 @@
* @throws IllegalArgumentException
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int sessionId) throws IllegalArgumentException {
mRecordingState = RECORDSTATE_STOPPED;
@@ -718,6 +728,7 @@
* were incompatible, or if they are not supported by the device,
* or if the device was not available.
*/
+ @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
public AudioRecord build() throws UnsupportedOperationException {
if (mAudioPlaybackCaptureConfiguration != null) {
return buildAudioPlaybackCaptureRecord();
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 175d36f..e056d43 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -836,7 +836,7 @@
mState = STATE_INITIALIZED;
}
- baseRegisterPlayer();
+ baseRegisterPlayer(mSessionId);
}
/**
@@ -866,7 +866,7 @@
// other initialization...
if (nativeTrackInJavaObj != 0) {
- baseRegisterPlayer();
+ baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
deferred_connect(nativeTrackInJavaObj);
} else {
mState = STATE_UNINITIALIZED;
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index bbf632a..e339ae8 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -54,7 +54,7 @@
Preconditions.checkArgument(device.isSource(), "Requires a source device");
mAudioDeviceInfo = device;
mAudioAttributes = attributes;
- baseRegisterPlayer();
+ baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
}
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index dd42aab..71ee57e 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -73,6 +73,8 @@
oneway void releaseRecorder(in int riid);
+ oneway void playerSessionId(in int piid, in int sessionId);
+
// Java-only methods below.
oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index d248f61..7837d7e 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -45,6 +45,7 @@
case ImageFormat.YV12:
case ImageFormat.YUV_420_888:
case ImageFormat.NV21:
+ case ImageFormat.YCBCR_P010:
return 3;
case ImageFormat.NV16:
return 2;
@@ -225,6 +226,7 @@
case ImageFormat.RAW_SENSOR:
case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown
case ImageFormat.DEPTH16:
+ case ImageFormat.YCBCR_P010:
estimatedBytePerPixel = 2.0;
break;
case PixelFormat.RGB_888:
@@ -244,6 +246,7 @@
private static Size getEffectivePlaneSizeForImage(Image image, int planeIdx) {
switch (image.getFormat()) {
+ case ImageFormat.YCBCR_P010:
case ImageFormat.YV12:
case ImageFormat.YUV_420_888:
case ImageFormat.NV21:
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index dd0bc61..ca0d29f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -663,6 +663,10 @@
* result in an exception.</p>
*/
public MediaPlayer() {
+ this(AudioSystem.AUDIO_SESSION_ALLOCATE);
+ }
+
+ private MediaPlayer(int sessionId) {
super(new AudioAttributes.Builder().build(),
AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);
@@ -684,7 +688,7 @@
native_setup(new WeakReference<MediaPlayer>(this),
getCurrentOpPackageName());
- baseRegisterPlayer();
+ baseRegisterPlayer(sessionId);
}
/*
@@ -913,7 +917,7 @@
AudioAttributes audioAttributes, int audioSessionId) {
try {
- MediaPlayer mp = new MediaPlayer();
+ MediaPlayer mp = new MediaPlayer(audioSessionId);
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
mp.setAudioAttributes(aa);
@@ -978,7 +982,7 @@
AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
if (afd == null) return null;
- MediaPlayer mp = new MediaPlayer();
+ MediaPlayer mp = new MediaPlayer(audioSessionId);
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
@@ -2365,7 +2369,13 @@
* This method must be called before one of the overloaded <code> setDataSource </code> methods.
* @throws IllegalStateException if it is called in an invalid state
*/
- public native void setAudioSessionId(int sessionId) throws IllegalArgumentException, IllegalStateException;
+ public void setAudioSessionId(int sessionId)
+ throws IllegalArgumentException, IllegalStateException {
+ native_setAudioSessionId(sessionId);
+ baseUpdateSessionId(sessionId);
+ }
+
+ private native void native_setAudioSessionId(int sessionId);
/**
* Returns the audio session ID.
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 58ae279..4407efa 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -97,6 +97,7 @@
* Constructor. Must be given audio attributes, as they are required for AppOps.
* @param attr non-null audio attributes
* @param class non-null class of the implementation of this abstract class
+ * @param sessionId the audio session Id
*/
PlayerBase(@NonNull AudioAttributes attr, int implType) {
if (attr == null) {
@@ -110,7 +111,7 @@
/**
* Call from derived class when instantiation / initialization is successful
*/
- protected void baseRegisterPlayer() {
+ protected void baseRegisterPlayer(int sessionId) {
if (!USE_AUDIOFLINGER_MUTING_FOR_OP) {
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
mAppOps = IAppOpsService.Stub.asInterface(b);
@@ -128,7 +129,8 @@
}
try {
mPlayerIId = getService().trackPlayer(
- new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
+ new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this),
+ sessionId));
} catch (RemoteException e) {
Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
}
@@ -145,7 +147,7 @@
try {
getService().playerAttributes(mPlayerIId, attr);
} catch (RemoteException e) {
- Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
+ Log.e(TAG, "Error talking to audio service, audio attributes will not be updated", e);
}
synchronized (mLock) {
boolean attributesChanged = (mAttributes != attr);
@@ -154,6 +156,18 @@
}
}
+ /**
+ * To be called whenever the session ID of the player changes
+ * @param sessionId, the new session Id
+ */
+ void baseUpdateSessionId(int sessionId) {
+ try {
+ getService().playerSessionId(mPlayerIId, sessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to audio service, the session ID will not be updated", e);
+ }
+ }
+
void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) {
int deviceId = 0;
if (deviceInfo != null) {
@@ -566,16 +580,19 @@
public static final int AUDIO_ATTRIBUTES_DEFINED = 1;
public final AudioAttributes mAttributes;
public final IPlayer mIPlayer;
+ public final int mSessionId;
- PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) {
+ PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer,
+ int sessionId) {
mPlayerType = type;
mAttributes = attr;
mIPlayer = iplayer;
+ mSessionId = sessionId;
}
@Override
public int hashCode() {
- return Objects.hash(mPlayerType);
+ return Objects.hash(mPlayerType, mSessionId);
}
@Override
@@ -588,6 +605,7 @@
dest.writeInt(mPlayerType);
mAttributes.writeToParcel(dest, 0);
dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder());
+ dest.writeInt(mSessionId);
}
public static final @android.annotation.NonNull Parcelable.Creator<PlayerIdCard> CREATOR
@@ -611,6 +629,7 @@
// IPlayer can be null if unmarshalling a Parcel coming from who knows where
final IBinder b = in.readStrongBinder();
mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b));
+ mSessionId = in.readInt();
}
@Override
@@ -621,7 +640,8 @@
PlayerIdCard that = (PlayerIdCard) o;
// FIXME change to the binder player interface once supported as a member
- return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes));
+ return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes)
+ && (mSessionId == that.mSessionId));
}
}
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 797caf3..32413dc 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -155,7 +155,8 @@
}
mAttributes = attributes;
- baseRegisterPlayer();
+ // FIXME: b/174876164 implement session id for soundpool
+ baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
}
/**
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 8525e99..ee0be01 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -65,6 +65,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -245,6 +246,8 @@
private static final int MSG_ON_FILTER_STATUS = 3;
private static final int MSG_ON_LNB_EVENT = 4;
+ private static final int FILTER_CLEANUP_THRESHOLD = 256;
+
/** @hide */
@IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK})
@Retention(RetentionPolicy.SOURCE)
@@ -1208,6 +1211,15 @@
synchronized (mFilters) {
WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter);
mFilters.add(weakFilter);
+ if (mFilters.size() > FILTER_CLEANUP_THRESHOLD) {
+ Iterator<WeakReference<Filter>> iterator = mFilters.iterator();
+ while (iterator.hasNext()) {
+ WeakReference<Filter> wFilter = iterator.next();
+ if (wFilter.get() == null) {
+ iterator.remove();
+ }
+ }
+ }
}
}
return filter;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 67a2c49..65b64d7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -31,8 +31,8 @@
],
shared_libs: [
- "audioclient-types-aidl-unstable-cpp",
- "av-types-aidl-unstable-cpp",
+ "audioclient-types-aidl-cpp",
+ "av-types-aidl-cpp",
"libandroid_runtime",
"libaudioclient",
"libnativehelper",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index bd8d2e9..98ac5b9 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1409,7 +1409,7 @@
{"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
- {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
+ {"native_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
{"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index b6c47fca..ecd9cc1 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -69,6 +69,7 @@
case HAL_PIXEL_FORMAT_RAW_OPAQUE:
case HAL_PIXEL_FORMAT_BLOB:
case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ case HAL_PIXEL_FORMAT_YCBCR_P010:
return false;
case HAL_PIXEL_FORMAT_YV12:
@@ -261,6 +262,32 @@
pStride = 1;
rStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
break;
+ case HAL_PIXEL_FORMAT_YCBCR_P010:
+ if (buffer->height % 2 != 0) {
+ ALOGE("YCBCR_P010: height (%d) should be a multiple of 2", buffer->height);
+ return BAD_VALUE;
+ }
+
+ if (buffer->width <= 0) {
+ ALOGE("YCBCR_P010: width (%d) should be a > 0", buffer->width);
+ return BAD_VALUE;
+ }
+
+ if (buffer->height <= 0) {
+ ALOGE("YCBCR_P010: height (%d) should be a > 0", buffer->height);
+ return BAD_VALUE;
+ }
+
+ ySize = (buffer->stride * 2) * buffer->height;
+ cSize = ySize / 2;
+ pStride = (idx == 0) ? 2 : 4;
+ cb = buffer->data + ySize;
+ cr = cb + 2;
+
+ pData = (idx == 0) ? buffer->data : (idx == 1) ? cb : cr;
+ dataSize = (idx == 0) ? ySize : cSize;
+ rStride = buffer->stride * 2;
+ break;
case HAL_PIXEL_FORMAT_Y8:
// Single plane, 8bpp.
LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 9ec84d9..eee9f1e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3786,7 +3786,7 @@
jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
return -1;
}
- int realReadSize = filterClient->read(reinterpret_cast<uint8_t*>(dst) + offset, size);
+ int realReadSize = filterClient->read(reinterpret_cast<int8_t*>(dst) + offset, size);
env->ReleaseByteArrayElements(buffer, dst, 0);
return (jint) realReadSize;
}
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 1a2f8c0..748d458 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -88,12 +88,19 @@
}
sp<TimeFilterClient> DemuxClient::openTimeFilter() {
- // TODO: pending aidl interface
+ if (mTunerDemux != NULL) {
+ shared_ptr<ITunerTimeFilter> tunerTimeFilter;
+ Status s = mTunerDemux->openTimeFilter(&tunerTimeFilter);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ return new TimeFilterClient(tunerTimeFilter);
+ }
if (mDemux != NULL) {
sp<ITimeFilter> hidlTimeFilter = openHidlTimeFilter();
if (hidlTimeFilter != NULL) {
- sp<TimeFilterClient> timeFilterClient = new TimeFilterClient();
+ sp<TimeFilterClient> timeFilterClient = new TimeFilterClient(NULL);
timeFilterClient->setHidlTimeFilter(hidlTimeFilter);
return timeFilterClient;
}
@@ -103,7 +110,14 @@
}
int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ int hwId;
+ Status s = mTunerDemux->getAvSyncHwId(filterClient->getAidlFilter(), &hwId);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return INVALID_AV_SYNC_HW_ID;
+ }
+ return hwId;
+ }
if (mDemux != NULL) {
uint32_t avSyncHwId;
@@ -119,11 +133,18 @@
}
}
- return -1;
+ return INVALID_AV_SYNC_HW_ID;
}
long DemuxClient::getAvSyncTime(int avSyncHwId) {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ int64_t time;
+ Status s = mTunerDemux->getAvSyncTime(avSyncHwId, &time);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return INVALID_AV_SYNC_TIME;
+ }
+ return time;
+ }
if (mDemux != NULL) {
uint64_t time;
@@ -138,7 +159,7 @@
}
}
- return -1;
+ return INVALID_AV_SYNC_TIME;
}
sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) {
@@ -167,7 +188,10 @@
}
Result DemuxClient::connectCiCam(int ciCamId) {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ Status s = mTunerDemux->connectCiCam(ciCamId);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDemux != NULL) {
return mDemux->connectCiCam(static_cast<uint32_t>(ciCamId));
@@ -177,7 +201,10 @@
}
Result DemuxClient::disconnectCiCam() {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ Status s = mTunerDemux->disconnectCiCam();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDemux != NULL) {
return mDemux->disconnectCiCam();
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index 463944a..31eb35a 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -31,7 +31,9 @@
using Status = ::ndk::ScopedAStatus;
using ::aidl::android::media::tv::tuner::ITunerDemux;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using ::android::hardware::tv::tuner::V1_0::IDemux;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
using ::android::hardware::tv::tuner::V1_0::DvrType;
using ::android::hardware::tv::tuner::V1_0::IDemux;
@@ -39,6 +41,9 @@
using namespace std;
+const int64_t INVALID_AV_SYNC_TIME = -1;
+const int INVALID_AV_SYNC_HW_ID = -1;
+
namespace android {
struct DemuxClient : public RefBase {
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 6b78817..8b4ca37 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -18,6 +18,7 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
+#include <fmq/ConvertMQDescriptors.h>
#include <utils/Log.h>
#include "FilterClient.h"
@@ -34,7 +35,7 @@
using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
using ::aidl::android::media::tv::tuner::TunerFilterTlvConfiguration;
using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration;
-
+using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
@@ -68,18 +69,12 @@
mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter);
}
-int FilterClient::read(uint8_t* buffer, int size) {
- // TODO: pending aidl interface
-
- if (mFilter != NULL) {
- Result res = getFilterMq();
- if (res != Result::SUCCESS) {
- return -1;
- }
- return copyData(buffer, size);
+int FilterClient::read(int8_t* buffer, int size) {
+ Result res = getFilterMq();
+ if (res != Result::SUCCESS) {
+ return -1;
}
-
- return -1;
+ return copyData(buffer, size);
}
SharedHandleInfo FilterClient::getAvSharedHandleInfo() {
@@ -106,7 +101,10 @@
}
Result FilterClient::configureMonitorEvent(int monitorEventType) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->configureMonitorEvent(monitorEventType);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter_1_1 != NULL) {
return mFilter_1_1->configureMonitorEvent(monitorEventType);
@@ -116,7 +114,10 @@
}
Result FilterClient::configureIpFilterContextId(int cid) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->configureIpFilterContextId(cid);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter_1_1 != NULL) {
return mFilter_1_1->configureIpCid(cid);
@@ -126,7 +127,19 @@
}
Result FilterClient::configureAvStreamType(AvStreamType avStreamType) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ int type;
+ switch (avStreamType.getDiscriminator()) {
+ case AvStreamType::hidl_discriminator::audio:
+ type = (int)avStreamType.audio();
+ break;
+ case AvStreamType::hidl_discriminator::video:
+ type = (int)avStreamType.video();
+ break;
+ }
+ Status s = mTunerFilter->configureAvStreamType(type);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter_1_1 != NULL) {
return mFilter_1_1->configureAvStreamType(avStreamType);
@@ -228,7 +241,10 @@
}
Result FilterClient::setDataSource(sp<FilterClient> filterClient){
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter());
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter != NULL) {
sp<IFilter> sourceFilter = filterClient->getHalFilter();
@@ -687,10 +703,10 @@
void TunerFilterCallback::getHidlMediaEvent(
const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
hidl_handle handle = hidl_handle(makeFromAidl(filterEvents[i]
.get<TunerFilterEvent::media>().avMemory));
- event.events.resize(i + 1);
event.events[i].media({
.avMemory = handle,
.streamId = static_cast<DemuxStreamId>(filterEvents[i]
@@ -736,9 +752,9 @@
void TunerFilterCallback::getHidlSectionEvent(
const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto section = filterEvents[i].get<TunerFilterEvent::section>();
- event.events.resize(i + 1);
event.events[i].section({
.tableId = static_cast<uint16_t>(section.tableId),
.version = static_cast<uint16_t>(section.version),
@@ -750,9 +766,9 @@
void TunerFilterCallback::getHidlPesEvent(
const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto pes = filterEvents[i].get<TunerFilterEvent::pes>();
- event.events.resize(i + 1);
event.events[i].pes({
.streamId = static_cast<DemuxStreamId>(pes.streamId),
.dataLength = static_cast<uint16_t>(pes.dataLength),
@@ -763,9 +779,10 @@
void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ event.events.resize(filterEvents.size());
+ eventExt.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto ts = filterEvents[i].get<TunerFilterEvent::tsRecord>();
- event.events.resize(i + 1);
event.events[i].tsRecord({
.tsIndexMask = static_cast<uint32_t>(ts.tsIndexMask),
.byteNumber = static_cast<uint64_t>(ts.byteNumber),
@@ -787,7 +804,6 @@
break;
}
- eventExt.events.resize(i + 1);
if (ts.isExtended) {
eventExt.events[i].tsRecord({
.pts = static_cast<uint64_t>(ts.pts),
@@ -801,15 +817,15 @@
void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ event.events.resize(filterEvents.size());
+ eventExt.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto mmtp = filterEvents[i].get<TunerFilterEvent::mmtpRecord>();
- event.events.resize(i + 1);
event.events[i].mmtpRecord({
.scHevcIndexMask = static_cast<uint32_t>(mmtp.scHevcIndexMask),
.byteNumber = static_cast<uint64_t>(mmtp.byteNumber),
});
- eventExt.events.resize(i + 1);
if (mmtp.isExtended) {
eventExt.events[i].mmtpRecord({
.pts = static_cast<uint64_t>(mmtp.pts),
@@ -825,9 +841,9 @@
void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto download = filterEvents[i].get<TunerFilterEvent::download>();
- event.events.resize(i + 1);
event.events[i].download({
.itemId = static_cast<uint32_t>(download.itemId),
.mpuSequenceNumber = static_cast<uint32_t>(download.mpuSequenceNumber),
@@ -840,9 +856,9 @@
void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto ip = filterEvents[i].get<TunerFilterEvent::ipPayload>();
- event.events.resize(i + 1);
event.events[i].ipPayload({
.dataLength = static_cast<uint16_t>(ip.dataLength),
});
@@ -851,15 +867,15 @@
void TunerFilterCallback::getHidlTemiEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto temi = filterEvents[i].get<TunerFilterEvent::temi>();
- event.events.resize(i + 1);
event.events[i].temi({
.pts = static_cast<uint64_t>(temi.pts),
.descrTag = static_cast<uint8_t>(temi.descrTag),
});
- vector<uint8_t> descrData(temi.descrData.size());
- copy(temi.descrData.begin(), temi.descrData.end(), descrData.begin());
+ hidl_vec<uint8_t> descrData(temi.descrData.begin(), temi.descrData.end());
+ event.events[i].temi().descrData = descrData;
}
}
@@ -891,29 +907,43 @@
}
Result FilterClient::getFilterMq() {
- if (mFilter == NULL) {
- return Result::INVALID_STATE;
- }
-
if (mFilterMQ != NULL) {
return Result::SUCCESS;
}
- Result getQueueDescResult = Result::UNKNOWN_ERROR;
- MQDescriptorSync<uint8_t> filterMQDesc;
- mFilter->getQueueDesc(
- [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
- filterMQDesc = desc;
- getQueueDescResult = r;
- });
- if (getQueueDescResult == Result::SUCCESS) {
- mFilterMQ = std::make_unique<MQ>(filterMQDesc, true);
- EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+ AidlMQDesc aidlMqDesc;
+ Result res = Result::UNAVAILABLE;
+
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->getQueueDesc(&aidlMqDesc);
+ res = ClientHelper::getServiceSpecificErrorCode(s);
+ if (res == Result::SUCCESS) {
+ mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc);
+ EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+ }
+ return res;
}
- return getQueueDescResult;
+
+ if (mFilter != NULL) {
+ MQDescriptorSync<uint8_t> filterMQDesc;
+ mFilter->getQueueDesc(
+ [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
+ filterMQDesc = desc;
+ res = r;
+ });
+ if (res == Result::SUCCESS) {
+ AidlMQDesc aidlMQDesc;
+ unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
+ filterMQDesc, &aidlMQDesc);
+ mFilterMQ = new (nothrow) AidlMessageQueue(aidlMQDesc);
+ EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+ }
+ }
+
+ return res;
}
-int FilterClient::copyData(uint8_t* buffer, int size) {
+int FilterClient::copyData(int8_t* buffer, int size) {
if (mFilter == NULL || mFilterMQ == NULL || mFilterMQEventFlag == NULL) {
return -1;
}
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 21919ac..bbabc28 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -25,12 +25,14 @@
#include <android/hardware/tv/tuner/1.1/IFilter.h>
#include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
#include <android/hardware/tv/tuner/1.1/types.h>
+#include <fmq/AidlMessageQueue.h>
#include <fmq/MessageQueue.h>
#include "ClientHelper.h"
#include "FilterClientCallback.h"
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
using ::aidl::android::media::tv::tuner::ITunerFilter;
using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress;
@@ -69,10 +71,13 @@
using namespace std;
-using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-
namespace android {
+using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using MQDesc = MQDescriptorSync<uint8_t>;
+using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
+
struct SharedHandleInfo {
native_handle_t* sharedHandle;
uint64_t size;
@@ -139,7 +144,7 @@
*
* @return the actual reading size. -1 if failed to read.
*/
- int read(uint8_t* buffer, int size);
+ int read(int8_t* buffer, int size);
/**
* Get the a/v shared memory handle information
@@ -234,7 +239,7 @@
void getAidlIpAddress(DemuxIpAddress ipAddr,
TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress);
Result getFilterMq();
- int copyData(uint8_t* buffer, int size);
+ int copyData(int8_t* buffer, int size);
void checkIsMediaFilter(DemuxFilterType type);
void handleAvShareMemory();
void closeAvSharedMemory();
@@ -259,7 +264,7 @@
*/
sp<::android::hardware::tv::tuner::V1_1::IFilter> mFilter_1_1;
- unique_ptr<MQ> mFilterMQ;
+ AidlMQ* mFilterMQ;
EventFlag* mFilterMQEventFlag;
sp<FilterClientCallback> mCallback;
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 0540aac..f454907 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -21,28 +21,49 @@
#include "FrontendClient.h"
-using ::aidl::android::media::tv::tuner::TunerFrontendDvbtSettings;
using ::aidl::android::media::tv::tuner::TunerFrontendScanAtsc3PlpInfo;
+using ::aidl::android::media::tv::tuner::TunerFrontendUnionSettings;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth;
using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation;
using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
-using ::android::hardware::tv::tuner::V1_0::FrontendType;
+using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode;
using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendType;
namespace android {
@@ -86,14 +107,13 @@
Result FrontendClient::tune(const FrontendSettings& settings,
const FrontendSettingsExt1_1& settingsExt1_1) {
if (mTunerFrontend != NULL) {
- // TODO: aidl frontend settings to include Tuner HAL 1.1 settings
TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1);
Status s = mTunerFrontend->tune(tunerFeSettings);
return ClientHelper::getServiceSpecificErrorCode(s);
}
Result result;
- if (mFrontend_1_1 != NULL) {
+ if (mFrontend_1_1 != NULL && validateExtendedSettings(settingsExt1_1)) {
result = mFrontend_1_1->tune_1_1(settings, settingsExt1_1);
return result;
}
@@ -123,14 +143,13 @@
Result FrontendClient::scan(const FrontendSettings& settings, FrontendScanType type,
const FrontendSettingsExt1_1& settingsExt1_1) {
if (mTunerFrontend != NULL) {
- // TODO: aidl frontend settings to include Tuner HAL 1.1 settings
TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1);
Status s = mTunerFrontend->scan(tunerFeSettings, (int)type);
return ClientHelper::getServiceSpecificErrorCode(s);
}
Result result;
- if (mFrontend_1_1 != NULL) {
+ if (mFrontend_1_1 != NULL && validateExtendedSettings(settingsExt1_1)) {
result = mFrontend_1_1->scan_1_1(settings, type, settingsExt1_1);
return result;
}
@@ -161,9 +180,16 @@
vector<FrontendStatus> status;
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*status = mTunerFrontend->getStatus(statusTypes);
- return status;*/
+ vector<TunerFrontendStatus> aidlStatus;
+ vector<int> types;
+ for (auto t : statusTypes) {
+ types.push_back((int)t);
+ }
+ Status s = mTunerFrontend->getStatus(types, &aidlStatus);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return status;
+ }
+ return getHidlStatus(aidlStatus);
}
if (mFrontend != NULL && statusTypes.size() > 0) {
@@ -181,14 +207,22 @@
return status;
}
+
vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1(
vector<FrontendStatusTypeExt1_1> statusTypes) {
vector<FrontendStatusExt1_1> status;
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*status = mTunerFrontend->getStatusExtended_1_1(statusTypes);
- return status;*/
+ vector<TunerFrontendStatus> aidlStatus;
+ vector<int> types;
+ for (auto t : statusTypes) {
+ types.push_back((int)t);
+ }
+ Status s = mTunerFrontend->getStatusExtended_1_1(types, &aidlStatus);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return status;
+ }
+ return getHidlStatusExt(aidlStatus);
}
if (mFrontend_1_1 != NULL && statusTypes.size() > 0) {
@@ -293,6 +327,8 @@
return Result::INVALID_STATE;
}
+/////////////// TunerFrontend Helper Methods ///////////////////////
+
shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
return mTunerFrontend;
}
@@ -301,53 +337,454 @@
return mId;
}
+vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>& aidlStatus) {
+ vector<FrontendStatus> hidlStatus;
+ for (TunerFrontendStatus s : aidlStatus) {
+ FrontendStatus status;
+ switch (s.getTag()) {
+ case TunerFrontendStatus::isDemodLocked: {
+ status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::snr: {
+ status.snr(s.get<TunerFrontendStatus::snr>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::ber: {
+ status.ber((uint32_t)s.get<TunerFrontendStatus::ber>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::per: {
+ status.per((uint32_t)s.get<TunerFrontendStatus::per>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::preBer: {
+ status.preBer((uint32_t)s.get<TunerFrontendStatus::preBer>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::signalQuality: {
+ status.signalQuality((uint32_t)s.get<TunerFrontendStatus::signalQuality>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::signalStrength: {
+ status.signalStrength(s.get<TunerFrontendStatus::signalStrength>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::symbolRate: {
+ status.symbolRate((uint32_t)s.get<TunerFrontendStatus::symbolRate>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::innerFec: {
+ status.innerFec(static_cast<FrontendInnerFec>(
+ s.get<TunerFrontendStatus::innerFec>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::modulation: {
+ auto aidlMod = s.get<TunerFrontendStatus::modulation>();
+ switch (mType) {
+ case (int)FrontendType::DVBC:
+ status.modulation().dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DVBS:
+ status.modulation().dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS:
+ status.modulation().isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS3:
+ status.modulation().isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.modulation().isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::inversion: {
+ status.inversion(static_cast<FrontendDvbcSpectralInversion>(
+ s.get<TunerFrontendStatus::inversion>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::lnbVoltage: {
+ status.lnbVoltage(static_cast<LnbVoltage>(
+ s.get<TunerFrontendStatus::lnbVoltage>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::plpId: {
+ status.plpId((uint8_t)s.get<TunerFrontendStatus::plpId>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isEWBS: {
+ status.isEWBS(s.get<TunerFrontendStatus::isEWBS>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::agc: {
+ status.agc((uint8_t)s.get<TunerFrontendStatus::agc>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isLnaOn: {
+ status.isLnaOn(s.get<TunerFrontendStatus::isLnaOn>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isLayerError: {
+ auto aidlE = s.get<TunerFrontendStatus::isLayerError>();
+ hidl_vec<bool> e(aidlE.begin(), aidlE.end());
+ status.isLayerError(e);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::mer: {
+ status.mer(s.get<TunerFrontendStatus::mer>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::freqOffset: {
+ status.freqOffset(s.get<TunerFrontendStatus::freqOffset>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::hierarchy: {
+ status.hierarchy(static_cast<FrontendDvbtHierarchy>(
+ s.get<TunerFrontendStatus::freqOffset>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isRfLocked: {
+ status.isRfLocked(s.get<TunerFrontendStatus::isRfLocked>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::plpInfo: {
+ int size = s.get<TunerFrontendStatus::plpInfo>().size();
+ status.plpInfo().resize(size);
+ for (int i = 0; i < size; i++) {
+ auto aidlInfo = s.get<TunerFrontendStatus::plpInfo>()[i];
+ status.plpInfo()[i] = {
+ .plpId = (uint8_t)aidlInfo.plpId,
+ .isLocked = aidlInfo.isLocked,
+ .uec = (uint32_t)aidlInfo.uec,
+ };
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return hidlStatus;
+}
+
+vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
+ vector<TunerFrontendStatus>& aidlStatus) {
+ vector<FrontendStatusExt1_1> hidlStatus;
+ for (TunerFrontendStatus s : aidlStatus) {
+ FrontendStatusExt1_1 status;
+ switch (s.getTag()) {
+ case TunerFrontendStatus::modulations: {
+ for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) {
+ int size = status.modulations().size();
+ status.modulations().resize(size + 1);
+ switch (mType) {
+ case (int)FrontendType::DVBC:
+ status.modulations()[size].dvbc(
+ static_cast<FrontendDvbcModulation>(aidlMod));
+ break;
+ case (int)FrontendType::DVBS:
+ status.modulations()[size].dvbs(
+ static_cast<FrontendDvbsModulation>(aidlMod));
+ break;
+ case (int)FrontendType::DVBT:
+ status.modulations()[size].dvbt(
+ static_cast<FrontendDvbtConstellation>(aidlMod));
+ break;
+ case (int)FrontendType::ISDBS:
+ status.modulations()[size].isdbs(
+ static_cast<FrontendIsdbsModulation>(aidlMod));
+ break;
+ case (int)FrontendType::ISDBS3:
+ status.modulations()[size].isdbs3(
+ static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ break;
+ case (int)FrontendType::ISDBT:
+ status.modulations()[size].isdbt(
+ static_cast<FrontendIsdbtModulation>(aidlMod));
+ break;
+ case (int)FrontendType::ATSC:
+ status.modulations()[size].atsc(
+ static_cast<FrontendAtscModulation>(aidlMod));
+ break;
+ case (int)FrontendType::ATSC3:
+ status.modulations()[size].atsc3(
+ static_cast<FrontendAtsc3Modulation>(aidlMod));
+ break;
+ case (int)FrontendType::DTMB:
+ status.modulations()[size].dtmb(
+ static_cast<FrontendDtmbModulation>(aidlMod));
+ break;
+ default:
+ status.modulations().resize(size);
+ break;
+ }
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::bers: {
+ auto aidlB = s.get<TunerFrontendStatus::bers>();
+ hidl_vec<uint32_t> b(aidlB.begin(), aidlB.end());
+ status.bers(b);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::codeRates: {
+ int size = s.get<TunerFrontendStatus::codeRates>().size();
+ status.codeRates().resize(size);
+ for (int i = 0; i < size; i++) {
+ auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i];
+ status.codeRates()[i] =
+ static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate);
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::bandwidth: {
+ auto aidlBand = s.get<TunerFrontendStatus::bandwidth>();
+ switch (mType) {
+ case (int)FrontendType::ATSC3:
+ status.bandwidth().atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DVBC:
+ status.bandwidth().dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DVBT:
+ status.bandwidth().dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.bandwidth().isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DTMB:
+ status.bandwidth().dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::interval: {
+ auto aidlInter = s.get<TunerFrontendStatus::interval>();
+ switch (mType) {
+ case (int)FrontendType::DVBT:
+ status.interval().dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.interval().isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DTMB:
+ status.interval().dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::transmissionMode: {
+ auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>();
+ switch (mType) {
+ case (int)FrontendType::DVBT:
+ status.transmissionMode().dvbt(
+ static_cast<FrontendDvbtTransmissionMode>(aidlTran));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.transmissionMode().isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DTMB:
+ status.transmissionMode().dtmb(
+ static_cast<FrontendDtmbTransmissionMode>(aidlTran));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::uec: {
+ status.uec((uint32_t)s.get<TunerFrontendStatus::uec>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::systemId: {
+ status.systemId((uint16_t)s.get<TunerFrontendStatus::systemId>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::interleaving: {
+ for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) {
+ int size = status.interleaving().size();
+ status.interleaving().resize(size + 1);
+ switch (mType) {
+ case (int)FrontendType::DVBC:
+ status.interleaving()[size].dvbc(
+ static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
+ break;
+ case (int)FrontendType::ATSC3:
+ status.interleaving()[size].atsc3(
+ static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
+ break;
+ case (int)FrontendType::DTMB:
+ status.interleaving()[size].dtmb(
+ static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
+ break;
+ default:
+ status.interleaving().resize(size);
+ break;
+ }
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isdbtSegment: {
+ auto aidlSeg = s.get<TunerFrontendStatus::isdbtSegment>();
+ hidl_vec<uint8_t> s(aidlSeg.begin(), aidlSeg.end());
+ status.isdbtSegment(s);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::tsDataRate: {
+ auto aidlTs = s.get<TunerFrontendStatus::tsDataRate>();
+ hidl_vec<uint32_t> ts(aidlTs.begin(), aidlTs.end());
+ status.tsDataRate(ts);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::rollOff: {
+ auto aidlRoll = s.get<TunerFrontendStatus::rollOff>();
+ switch (mType) {
+ case (int)FrontendType::DVBS:
+ status.rollOff().dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS:
+ status.rollOff().isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS3:
+ status.rollOff().isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::isMiso: {
+ status.isMiso(s.get<TunerFrontendStatus::isMiso>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isLinear: {
+ status.isLinear(s.get<TunerFrontendStatus::isLinear>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isShortFrames: {
+ status.isShortFrames(s.get<TunerFrontendStatus::isShortFrames>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return hidlStatus;
+}
+
TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSettings& settings,
- const FrontendSettingsExt1_1& /*settingsExt1_1*/) {
- // TODO: complete hidl to aidl frontend settings conversion
- TunerFrontendSettings s;
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ bool isExtended = validateExtendedSettings(settingsExt1_1);
+ TunerFrontendSettings s{
+ .isExtended = isExtended,
+ .endFrequency = (int) settingsExt1_1.endFrequency,
+ .inversion = (int) settingsExt1_1.inversion,
+ };
+
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dtmb) {
+ s.settings.set<TunerFrontendUnionSettings::dtmb>(getAidlDtmbSettings(settingsExt1_1));
+ return s;
+ }
+
switch (settings.getDiscriminator()) {
case FrontendSettings::hidl_discriminator::analog: {
+ s.settings.set<TunerFrontendUnionSettings::analog>(
+ getAidlAnalogSettings(settings, settingsExt1_1));
break;
}
case FrontendSettings::hidl_discriminator::atsc: {
+ s.settings.set<TunerFrontendUnionSettings::atsc>(getAidlAtscSettings(settings));
break;
}
case FrontendSettings::hidl_discriminator::atsc3: {
+ s.settings.set<TunerFrontendUnionSettings::atsc3>(getAidlAtsc3Settings(settings));
break;
}
case FrontendSettings::hidl_discriminator::dvbs: {
+ s.settings.set<TunerFrontendUnionSettings::dvbs>(
+ getAidlDvbsSettings(settings, settingsExt1_1));
break;
}
case FrontendSettings::hidl_discriminator::dvbc: {
+ s.settings.set<TunerFrontendUnionSettings::cable>(
+ getAidlCableSettings(settings, settingsExt1_1));
break;
}
case FrontendSettings::hidl_discriminator::dvbt: {
- TunerFrontendDvbtSettings dvbtSettings{
- .frequency = (int)settings.dvbt().frequency,
- .transmissionMode = (int)settings.dvbt().transmissionMode,
- .bandwidth = (int)settings.dvbt().bandwidth,
- .constellation = (int)settings.dvbt().constellation,
- .hierarchy = (int)settings.dvbt().hierarchy,
- .hpCodeRate = (int)settings.dvbt().hpCoderate,
- .lpCodeRate = (int)settings.dvbt().lpCoderate,
- .guardInterval = (int)settings.dvbt().guardInterval,
- .isHighPriority = settings.dvbt().isHighPriority,
- .standard = (int)settings.dvbt().standard,
- .isMiso = settings.dvbt().isMiso,
- .plpMode = (int)settings.dvbt().plpMode,
- .plpId = (int)settings.dvbt().plpId,
- .plpGroupId = (int)settings.dvbt().plpGroupId,
- };
- s.set<TunerFrontendSettings::dvbt>(dvbtSettings);
+ s.settings.set<TunerFrontendUnionSettings::dvbt>(
+ getAidlDvbtSettings(settings, settingsExt1_1));
break;
}
case FrontendSettings::hidl_discriminator::isdbs: {
+ s.settings.set<TunerFrontendUnionSettings::isdbs>(getAidlIsdbsSettings(settings));
break;
}
case FrontendSettings::hidl_discriminator::isdbs3: {
+ s.settings.set<TunerFrontendUnionSettings::isdbs3>(getAidlIsdbs3Settings(settings));
break;
}
case FrontendSettings::hidl_discriminator::isdbt: {
+ s.settings.set<TunerFrontendUnionSettings::isdbt>(getAidlIsdbtSettings(settings));
break;
}
default:
@@ -356,6 +793,192 @@
return s;
}
+TunerFrontendAnalogSettings FrontendClient::getAidlAnalogSettings(const FrontendSettings& settings,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendAnalogSettings analogSettings{
+ .frequency = (int)settings.analog().frequency,
+ .signalType = (int)settings.analog().type,
+ .sifStandard = (int)settings.analog().sifStandard,
+ };
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::analog) {
+ analogSettings.isExtended = true;
+ analogSettings.aftFlag = (int)settingsExt1_1.settingExt.analog().aftFlag;
+ } else {
+ analogSettings.isExtended = false;
+ }
+ return analogSettings;
+}
+
+TunerFrontendDvbsSettings FrontendClient::getAidlDvbsSettings(const FrontendSettings& settings,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendDvbsSettings dvbsSettings{
+ .frequency = (int)settings.dvbs().frequency,
+ .modulation = (int)settings.dvbs().modulation,
+ .codeRate = {
+ .fec = (long)settings.dvbs().coderate.fec,
+ .isLinear = settings.dvbs().coderate.isLinear,
+ .isShortFrames = settings.dvbs().coderate.isShortFrames,
+ .bitsPer1000Symbol = (int)settings.dvbs().coderate.bitsPer1000Symbol,
+ },
+ .symbolRate = (int)settings.dvbs().symbolRate,
+ .rolloff = (int)settings.dvbs().rolloff,
+ .pilot = (int)settings.dvbs().pilot,
+ .inputStreamId = (int)settings.dvbs().inputStreamId,
+ .standard = (int)settings.dvbs().standard,
+ .vcm = (int)settings.dvbs().vcmMode,
+ };
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbs) {
+ dvbsSettings.isExtended = true;
+ dvbsSettings.scanType = (int)settingsExt1_1.settingExt.dvbs().scanType;
+ dvbsSettings.isDiseqcRxMessage = settingsExt1_1.settingExt.dvbs().isDiseqcRxMessage;
+ } else {
+ dvbsSettings.isExtended = false;
+ }
+ return dvbsSettings;
+}
+
+TunerFrontendCableSettings FrontendClient::getAidlCableSettings(const FrontendSettings& settings,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendCableSettings cableSettings{
+ .frequency = (int)settings.dvbc().frequency,
+ .modulation = (int)settings.dvbc().modulation,
+ .innerFec = (long)settings.dvbc().fec,
+ .symbolRate = (int)settings.dvbc().symbolRate,
+ .outerFec = (int)settings.dvbc().outerFec,
+ .annex = (int)settings.dvbc().annex,
+ .spectralInversion = (int)settings.dvbc().spectralInversion,
+ };
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbc) {
+ cableSettings.isExtended = true;
+ cableSettings.interleaveMode = (int)settingsExt1_1.settingExt.dvbc().interleaveMode;
+ cableSettings.bandwidth = (int)settingsExt1_1.settingExt.dvbc().bandwidth;
+ } else {
+ cableSettings.isExtended = false;
+ }
+ return cableSettings;
+}
+
+TunerFrontendDvbtSettings FrontendClient::getAidlDvbtSettings(const FrontendSettings& settings,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendDvbtSettings dvbtSettings{
+ .frequency = (int)settings.dvbt().frequency,
+ .transmissionMode = (int)settings.dvbt().transmissionMode,
+ .bandwidth = (int)settings.dvbt().bandwidth,
+ .constellation = (int)settings.dvbt().constellation,
+ .hierarchy = (int)settings.dvbt().hierarchy,
+ .hpCodeRate = (int)settings.dvbt().hpCoderate,
+ .lpCodeRate = (int)settings.dvbt().lpCoderate,
+ .guardInterval = (int)settings.dvbt().guardInterval,
+ .isHighPriority = settings.dvbt().isHighPriority,
+ .standard = (int)settings.dvbt().standard,
+ .isMiso = settings.dvbt().isMiso,
+ .plpMode = (int)settings.dvbt().plpMode,
+ .plpId = (int)settings.dvbt().plpId,
+ .plpGroupId = (int)settings.dvbt().plpGroupId,
+ };
+ if (settingsExt1_1.settingExt.getDiscriminator()
+ == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbt) {
+ dvbtSettings.isExtended = true;
+ dvbtSettings.constellation = (int)settingsExt1_1.settingExt.dvbt().constellation;
+ dvbtSettings.transmissionMode =
+ (int)settingsExt1_1.settingExt.dvbt().transmissionMode;
+ } else {
+ dvbtSettings.isExtended = false;
+ }
+ return dvbtSettings;
+}
+
+TunerFrontendDtmbSettings FrontendClient::getAidlDtmbSettings(
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ TunerFrontendDtmbSettings dtmbSettings{
+ .frequency = (int)settingsExt1_1.settingExt.dtmb().frequency,
+ .transmissionMode = (int)settingsExt1_1.settingExt.dtmb().transmissionMode,
+ .bandwidth = (int)settingsExt1_1.settingExt.dtmb().bandwidth,
+ .modulation = (int)settingsExt1_1.settingExt.dtmb().modulation,
+ .codeRate = (int)settingsExt1_1.settingExt.dtmb().codeRate,
+ .guardInterval = (int)settingsExt1_1.settingExt.dtmb().guardInterval,
+ .interleaveMode = (int)settingsExt1_1.settingExt.dtmb().interleaveMode,
+ };
+ return dtmbSettings;
+}
+
+TunerFrontendAtscSettings FrontendClient::getAidlAtscSettings(const FrontendSettings& settings) {
+ TunerFrontendAtscSettings atscSettings{
+ .frequency = (int)settings.atsc().frequency,
+ .modulation = (int)settings.atsc().modulation,
+ };
+ return atscSettings;
+}
+
+TunerFrontendAtsc3Settings FrontendClient::getAidlAtsc3Settings(const FrontendSettings& settings) {
+ TunerFrontendAtsc3Settings atsc3Settings{
+ .frequency = (int)settings.atsc3().frequency,
+ .bandwidth = (int)settings.atsc3().bandwidth,
+ .demodOutputFormat = (int)settings.atsc3().demodOutputFormat,
+ };
+ atsc3Settings.plpSettings.resize(settings.atsc3().plpSettings.size());
+ for (auto plpSetting : settings.atsc3().plpSettings) {
+ atsc3Settings.plpSettings.push_back({
+ .plpId = (int)plpSetting.plpId,
+ .modulation = (int)plpSetting.modulation,
+ .interleaveMode = (int)plpSetting.interleaveMode,
+ .codeRate = (int)plpSetting.codeRate,
+ .fec = (int)plpSetting.fec,
+ });
+ }
+ return atsc3Settings;
+}
+
+TunerFrontendIsdbsSettings FrontendClient::getAidlIsdbsSettings(const FrontendSettings& settings) {
+ TunerFrontendIsdbsSettings isdbsSettings{
+ .frequency = (int)settings.isdbs().frequency,
+ .streamId = (int)settings.isdbs().streamId,
+ .streamIdType = (int)settings.isdbs().streamIdType,
+ .modulation = (int)settings.isdbs().modulation,
+ .codeRate = (int)settings.isdbs().coderate,
+ .symbolRate = (int)settings.isdbs().symbolRate,
+ .rolloff = (int)settings.isdbs().rolloff,
+ };
+ return isdbsSettings;
+}
+
+TunerFrontendIsdbs3Settings FrontendClient::getAidlIsdbs3Settings(
+ const FrontendSettings& settings) {
+ TunerFrontendIsdbs3Settings isdbs3Settings{
+ .frequency = (int)settings.isdbs3().frequency,
+ .streamId = (int)settings.isdbs3().streamId,
+ .streamIdType = (int)settings.isdbs3().streamIdType,
+ .modulation = (int)settings.isdbs3().modulation,
+ .codeRate = (int)settings.isdbs3().coderate,
+ .symbolRate = (int)settings.isdbs3().symbolRate,
+ .rolloff = (int)settings.isdbs3().rolloff,
+ };
+ return isdbs3Settings;
+}
+
+TunerFrontendIsdbtSettings FrontendClient::getAidlIsdbtSettings(const FrontendSettings& settings) {
+ TunerFrontendIsdbtSettings isdbtSettings{
+ .frequency = (int)settings.isdbt().frequency,
+ .modulation = (int)settings.isdbt().modulation,
+ .bandwidth = (int)settings.isdbt().bandwidth,
+ .mode = (int)settings.isdbt().mode,
+ .codeRate = (int)settings.isdbt().coderate,
+ .guardInterval = (int)settings.isdbt().guardInterval,
+ .serviceAreaId = (int)settings.isdbt().serviceAreaId,
+ };
+ return isdbtSettings;
+}
+
+bool FrontendClient::validateExtendedSettings(const FrontendSettingsExt1_1& settingsExt1_1) {
+ return settingsExt1_1.endFrequency != (uint32_t)Constant::INVALID_FRONTEND_SETTING_FREQUENCY
+ || settingsExt1_1.inversion != FrontendSpectralInversion::UNDEFINED
+ || settingsExt1_1.settingExt.getDiscriminator()
+ != FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::noinit;
+}
+
/////////////// TunerFrontendCallback ///////////////////////
TunerFrontendCallback::TunerFrontendCallback(sp<FrontendClientCallback> frontendClientCallback)
@@ -492,14 +1115,15 @@
vector<TunerFrontendScanAtsc3PlpInfo> plp =
message.get<TunerFrontendScanMessage::atsc3PlpInfos>();
hidl_vec<FrontendScanAtsc3PlpInfo> plpInfo;
- for (TunerFrontendScanAtsc3PlpInfo info : plp) {
+ int size = plp.size();
+ plpInfo.resize(size);
+ for (int i = 0; i < size; i++) {
+ auto info = message.get<TunerFrontendScanMessage::atsc3PlpInfos>()[i];
FrontendScanAtsc3PlpInfo p{
.plpId = static_cast<uint8_t>(info.plpId),
.bLlsFlag = info.llsFlag,
};
- int size = plpInfo.size();
- plpInfo.resize(size + 1);
- plpInfo[size] = p;
+ plpInfo[i] = p;
}
scanMessage.atsc3PlpInfos(plpInfo);
break;
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 17fd583..298b397 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -31,8 +31,19 @@
using ::aidl::android::media::tv::tuner::BnTunerFrontendCallback;
using ::aidl::android::media::tv::tuner::ITunerFrontend;
+using ::aidl::android::media::tv::tuner::TunerFrontendAnalogSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendAtscSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendAtsc3Settings;
+using ::aidl::android::media::tv::tuner::TunerFrontendCableSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDvbsSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDvbtSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendDtmbSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbsSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbs3Settings;
+using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtSettings;
using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage;
using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendStatus;
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -172,8 +183,27 @@
int getId();
private:
- TunerFrontendSettings getAidlFrontendSettings(const FrontendSettings& settings,
- const FrontendSettingsExt1_1& settingsExt1_1);
+ vector<FrontendStatus> getHidlStatus(vector<TunerFrontendStatus>& aidlStatus);
+ vector<FrontendStatusExt1_1> getHidlStatusExt(vector<TunerFrontendStatus>& aidlStatus);
+
+ TunerFrontendSettings getAidlFrontendSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendAnalogSettings getAidlAnalogSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendDvbsSettings getAidlDvbsSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendCableSettings getAidlCableSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendDvbtSettings getAidlDvbtSettings(
+ const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendDtmbSettings getAidlDtmbSettings(const FrontendSettingsExt1_1& settingsExt1_1);
+ TunerFrontendAtscSettings getAidlAtscSettings(const FrontendSettings& settings);
+ TunerFrontendAtsc3Settings getAidlAtsc3Settings(const FrontendSettings& settings);
+ TunerFrontendIsdbsSettings getAidlIsdbsSettings(const FrontendSettings& settings);
+ TunerFrontendIsdbs3Settings getAidlIsdbs3Settings(const FrontendSettings& settings);
+ TunerFrontendIsdbtSettings getAidlIsdbtSettings(const FrontendSettings& settings);
+
+ bool validateExtendedSettings(const FrontendSettingsExt1_1& settingsExt1_1);
/**
* An AIDL Tuner Frontend Singleton assigned at the first time when the Tuner Client
diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp
index 27ea6e5..432238d 100644
--- a/media/jni/tuner/TimeFilterClient.cpp
+++ b/media/jni/tuner/TimeFilterClient.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <utils/Log.h>
+#include "ClientHelper.h"
#include "TimeFilterClient.h"
using ::android::hardware::tv::tuner::V1_0::Result;
@@ -28,13 +29,12 @@
/////////////// TimeFilterClient ///////////////////////
-// TODO: pending aidl interface
-TimeFilterClient::TimeFilterClient() {
- //mTunerTimeFilter = tunerTimeFilter;
+TimeFilterClient::TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter) {
+ mTunerTimeFilter = tunerTimeFilter;
}
TimeFilterClient::~TimeFilterClient() {
- //mTunerTimeFilter = NULL;
+ mTunerTimeFilter = NULL;
mTimeFilter = NULL;
}
@@ -44,7 +44,10 @@
}
Result TimeFilterClient::setTimeStamp(long timeStamp) {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ Status s = mTunerTimeFilter->setTimeStamp(timeStamp);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mTimeFilter != NULL) {
return mTimeFilter->setTimeStamp(timeStamp);
@@ -54,7 +57,10 @@
}
Result TimeFilterClient::clearTimeStamp() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ Status s = mTunerTimeFilter->clearTimeStamp();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mTimeFilter != NULL) {
return mTimeFilter->clearTimeStamp();
@@ -64,7 +70,14 @@
}
long TimeFilterClient::getTimeStamp() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ int64_t timeStamp;
+ Status s = mTunerTimeFilter->getTimeStamp(&timeStamp);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ }
+ return timeStamp;
+ }
if (mTimeFilter != NULL) {
Result res;
@@ -84,27 +97,37 @@
}
long TimeFilterClient::getSourceTime() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ int64_t sourceTime;
+ Status s = mTunerTimeFilter->getTimeStamp(&sourceTime);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ }
+ return sourceTime;
+ }
if (mTimeFilter != NULL) {
Result res;
- long timestamp;
+ long sourceTime;
mTimeFilter->getSourceTime(
[&](Result r, uint64_t t) {
res = r;
- timestamp = t;
+ sourceTime = t;
});
if (res != Result::SUCCESS) {
return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
}
- return timestamp;
+ return sourceTime;
}
return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
}
Result TimeFilterClient::close() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ Status s = mTunerTimeFilter->close();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mTimeFilter != NULL) {
return mTimeFilter->close();
diff --git a/media/jni/tuner/TimeFilterClient.h b/media/jni/tuner/TimeFilterClient.h
index 9a9d172..56ddd68 100644
--- a/media/jni/tuner/TimeFilterClient.h
+++ b/media/jni/tuner/TimeFilterClient.h
@@ -17,12 +17,13 @@
#ifndef _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
#define _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
-//#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
+#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
#include <android/hardware/tv/tuner/1.1/types.h>
-//using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using Status = ::ndk::ScopedAStatus;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
@@ -36,8 +37,7 @@
struct TimeFilterClient : public RefBase {
public:
- // TODO: add TunerTimeFilter as parameter.
- TimeFilterClient();
+ TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter);
~TimeFilterClient();
// TODO: remove after migration to Tuner Service is done.
@@ -73,8 +73,7 @@
* An AIDL Tuner TimeFilter Singleton assigned at the first time the Tuner Client
* opens an TimeFilter. Default null when time filter is not opened.
*/
- // TODO: pending on aidl interface
- //shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
+ shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
/**
* A TimeFilter HAL interface that is ready before migrating to the TunerTimeFilter.
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 14393a1..a604490d 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -200,7 +200,14 @@
}
shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() {
- // pending aidl interface
+ if (mTunerService != NULL) {
+ TunerDemuxCapabilities aidlCaps;
+ Status s = mTunerService->getDemuxCaps(&aidlCaps);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ return make_shared<DemuxCapabilities>(getHidlDemuxCaps(aidlCaps));
+ }
if (mTuner != NULL) {
Result res;
@@ -459,6 +466,26 @@
return descrambler;
}
+DemuxCapabilities TunerClient::getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps) {
+ DemuxCapabilities caps{
+ .numDemux = (uint32_t)aidlCaps.numDemux,
+ .numRecord = (uint32_t)aidlCaps.numRecord,
+ .numPlayback = (uint32_t)aidlCaps.numPlayback,
+ .numTsFilter = (uint32_t)aidlCaps.numTsFilter,
+ .numSectionFilter = (uint32_t)aidlCaps.numSectionFilter,
+ .numAudioFilter = (uint32_t)aidlCaps.numAudioFilter,
+ .numVideoFilter = (uint32_t)aidlCaps.numVideoFilter,
+ .numPesFilter = (uint32_t)aidlCaps.numPesFilter,
+ .numPcrFilter = (uint32_t)aidlCaps.numPcrFilter,
+ .numBytesInSectionFilter = (uint32_t)aidlCaps.numBytesInSectionFilter,
+ .filterCaps = (uint32_t)aidlCaps.filterCaps,
+ .bTimeFilter = aidlCaps.bTimeFilter,
+ };
+ caps.linkCaps.resize(aidlCaps.linkCaps.size());
+ copy(aidlCaps.linkCaps.begin(), aidlCaps.linkCaps.end(), caps.linkCaps.begin());
+ return caps;
+}
+
FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
FrontendInfo hidlFrontendInfo {
.type = static_cast<FrontendType>(aidlFrontendInfo.type),
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 6ce6661..acd018e 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -32,6 +32,7 @@
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::tv::tuner::TunerDemuxCapabilities;
using ::aidl::android::media::tv::tuner::ITunerService;
using ::aidl::android::media::tv::tuner::TunerFrontendInfo;
using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager;
@@ -145,6 +146,7 @@
sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId);
sp<IDescrambler> openHidlDescrambler();
vector<int> getLnbHandles();
+ DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
void updateTunerResources();
void updateFrontendResources();
diff --git a/native/android/OWNERS b/native/android/OWNERS
index ac5a895..d414ed4 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -1,3 +1,4 @@
per-file libandroid_net.map.txt, net.c = set noparent
per-file libandroid_net.map.txt, net.c = codewiz@google.com, jchalard@google.com, junyulai@google.com
per-file libandroid_net.map.txt, net.c = lorenzo@google.com, reminv@google.com, satk@google.com
+per-file system_fonts.cpp = file:/graphics/java/android/graphics/fonts/OWNERS
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 45f42f1b5..48d7380 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -264,7 +264,7 @@
static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant),
1 /* maxRun */);
- const minikin::Font* font = runs[0].fakedFont.font;
+ const std::shared_ptr<minikin::Font>& font = runs[0].fakedFont.font;
std::unique_ptr<AFont> result = std::make_unique<AFont>();
const android::MinikinFontSkia* minikinFontSkia =
reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get());
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 620c7ae..72589e3 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -18,6 +18,7 @@
import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
import static android.text.TextUtils.emptyIfNull;
+import static android.text.TextUtils.isEmpty;
import static android.text.TextUtils.withoutPrefix;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -67,7 +68,13 @@
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
String deviceProfile = getRequest().getDeviceProfile();
- String profileName = getDeviceProfileName(deviceProfile);
+ String profilePrivacyDisclaimer = emptyIfNull(getRequest()
+ .getDeviceProfilePrivilegesDescription())
+ .replace("APP_NAME", getCallingAppName());
+ boolean useDeviceProfile = deviceProfile != null && !isEmpty(profilePrivacyDisclaimer);
+ String profileName = useDeviceProfile
+ ? getDeviceProfileName(deviceProfile)
+ : getString(R.string.profile_name_generic);
if (getRequest().isSingleDevice()) {
setContentView(R.layout.device_confirmation);
@@ -110,15 +117,12 @@
TextView profileSummary = findViewById(R.id.profile_summary);
- if (deviceProfile != null) {
- String privacyDisclaimer = emptyIfNull(getRequest()
- .getDeviceProfilePrivilegesDescription())
- .replace("APP_NAME", getCallingAppName());
+ if (useDeviceProfile) {
profileSummary.setVisibility(View.VISIBLE);
profileSummary.setText(getString(R.string.profile_summary,
getCallingAppName(),
profileName,
- privacyDisclaimer));
+ profilePrivacyDisclaimer));
} else {
profileSummary.setVisibility(View.GONE);
}
@@ -142,7 +146,7 @@
return getString(R.string.profile_name_watch);
}
default: {
- Log.wtf(LOG_TAG,
+ Log.w(LOG_TAG,
"No localized profile name found for device profile: " + deviceProfile);
return withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile)
.toLowerCase()
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 2a0a74e..3b054e9 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3231,32 +3231,6 @@
}
}
- /** {@hide} - returns the factory serial number */
- @UnsupportedAppUsage
- @RequiresPermission(anyOf = {
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_FACTORY})
- public int registerNetworkFactory(Messenger messenger, String name) {
- try {
- return mService.registerNetworkFactory(messenger, name);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** {@hide} */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- @RequiresPermission(anyOf = {
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_FACTORY})
- public void unregisterNetworkFactory(Messenger messenger) {
- try {
- mService.unregisterNetworkFactory(messenger);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
/**
* Registers the specified {@link NetworkProvider}.
* Each listener must only be registered once. The listener can be unregistered with
@@ -4609,7 +4583,7 @@
// Set HTTP proxy system properties to match network.
// TODO: Deprecate this static method and replace it with a non-static version.
try {
- Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
+ Proxy.setHttpProxyConfiguration(getInstance().getDefaultProxy());
} catch (SecurityException e) {
// The process doesn't have ACCESS_NETWORK_STATE, so we can't fetch the proxy.
Log.e(TAG, "Can't set proxy properties", e);
@@ -4871,9 +4845,13 @@
}
}
- private void setOemNetworkPreference(@NonNull OemNetworkPreferences preference) {
- Log.d(TAG, "setOemNetworkPreference called with preference: "
- + preference.toString());
+ private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+ try {
+ mService.setOemNetworkPreference(preference);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString());
+ throw e.rethrowFromSystemServer();
+ }
}
@NonNull
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 1b4d2e4..e2672c4 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -29,6 +29,7 @@
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
+import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.UidRange;
import android.net.QosSocketInfo;
@@ -156,9 +157,6 @@
boolean requestBandwidthUpdate(in Network network);
- int registerNetworkFactory(in Messenger messenger, in String name);
- void unregisterNetworkFactory(in Messenger messenger);
-
int registerNetworkProvider(in Messenger messenger, in String name);
void unregisterNetworkProvider(in Messenger messenger);
@@ -243,4 +241,6 @@
void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
void unregisterQosCallback(in IQosCallback callback);
+
+ void setOemNetworkPreference(in OemNetworkPreferences preference);
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index d22d82d..27aa15d 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -775,7 +776,8 @@
* @param underlyingNetworks the new list of underlying networks.
* @see {@link VpnService.Builder#setUnderlyingNetworks(Network[])}
*/
- public final void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) {
+ public final void setUnderlyingNetworks(
+ @SuppressLint("NullableCollection") @Nullable List<Network> underlyingNetworks) {
final ArrayList<Network> underlyingArray = (underlyingNetworks != null)
? new ArrayList<>(underlyingNetworks) : null;
queueOrSendMessage(reg -> reg.sendUnderlyingNetworks(underlyingArray));
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 8bfa77a..55b2c3c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -1786,6 +1786,15 @@
return 0;
}
+ private <T extends Parcelable> void writeParcelableArraySet(Parcel in,
+ @Nullable ArraySet<T> val, int flags) {
+ final int size = (val != null) ? val.size() : -1;
+ in.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ in.writeParcelable(val.valueAt(i), flags);
+ }
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mNetworkCapabilities);
@@ -1796,7 +1805,7 @@
dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
dest.writeParcelable((Parcelable) mTransportInfo, flags);
dest.writeInt(mSignalStrength);
- dest.writeArraySet(mUids);
+ writeParcelableArraySet(dest, mUids, flags);
dest.writeString(mSSID);
dest.writeBoolean(mPrivateDnsBroken);
dest.writeIntArray(getAdministratorUids());
@@ -1819,8 +1828,7 @@
netCap.mNetworkSpecifier = in.readParcelable(null);
netCap.mTransportInfo = in.readParcelable(null);
netCap.mSignalStrength = in.readInt();
- netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
- null /* ClassLoader, null for default */);
+ netCap.mUids = readParcelableArraySet(in, null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
netCap.mPrivateDnsBroken = in.readBoolean();
netCap.setAdministratorUids(in.createIntArray());
@@ -1833,6 +1841,20 @@
public NetworkCapabilities[] newArray(int size) {
return new NetworkCapabilities[size];
}
+
+ private @Nullable <T extends Parcelable> ArraySet<T> readParcelableArraySet(Parcel in,
+ @Nullable ClassLoader loader) {
+ final int size = in.readInt();
+ if (size < 0) {
+ return null;
+ }
+ final ArraySet<T> result = new ArraySet<>(size);
+ for (int i = 0; i < size; i++) {
+ final T value = in.readParcelable(loader);
+ result.append(value);
+ }
+ return result;
+ }
};
@Override
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index c4d1b09..b9ef4c2 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -435,25 +435,7 @@
* @hide
*/
public boolean isRequest() {
- return isForegroundRequest() || isBackgroundRequest();
- }
-
- /**
- * Returns true iff. the contained NetworkRequest is one that:
- *
- * - should be associated with at most one satisfying network
- * at a time;
- *
- * - should cause a network to be kept up and in the foreground if
- * it is the best network which can satisfy the NetworkRequest.
- *
- * For full detail of how isRequest() is used for pairing Networks with
- * NetworkRequests read rematchNetworkAndRequests().
- *
- * @hide
- */
- public boolean isForegroundRequest() {
- return type == Type.TRACK_DEFAULT || type == Type.REQUEST;
+ return type == Type.REQUEST || type == Type.BACKGROUND_REQUEST;
}
/**
diff --git a/packages/Connectivity/framework/src/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java
index 03b07e0..77c8a4f 100644
--- a/packages/Connectivity/framework/src/android/net/Proxy.java
+++ b/packages/Connectivity/framework/src/android/net/Proxy.java
@@ -16,8 +16,10 @@
package android.net;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
@@ -30,8 +32,6 @@
import java.net.ProxySelector;
import java.net.URI;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* A convenience class for accessing the user and default proxy
@@ -64,40 +64,9 @@
@Deprecated
public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
- /** @hide */
- public static final int PROXY_VALID = 0;
- /** @hide */
- public static final int PROXY_HOSTNAME_EMPTY = 1;
- /** @hide */
- public static final int PROXY_HOSTNAME_INVALID = 2;
- /** @hide */
- public static final int PROXY_PORT_EMPTY = 3;
- /** @hide */
- public static final int PROXY_PORT_INVALID = 4;
- /** @hide */
- public static final int PROXY_EXCLLIST_INVALID = 5;
-
private static ConnectivityManager sConnectivityManager = null;
- // Hostname / IP REGEX validation
- // Matches blank input, ips, and domain names
- private static final String NAME_IP_REGEX =
- "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
-
- private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
-
- private static final Pattern HOSTNAME_PATTERN;
-
- private static final String EXCL_REGEX =
- "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*";
-
- private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$";
-
- private static final Pattern EXCLLIST_PATTERN;
-
static {
- HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
- EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
sDefaultProxySelector = ProxySelector.getDefault();
}
@@ -216,36 +185,21 @@
return false;
}
- /**
- * Validate syntax of hostname, port and exclusion list entries
- * {@hide}
- */
- public static int validate(String hostname, String port, String exclList) {
- Matcher match = HOSTNAME_PATTERN.matcher(hostname);
- Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
-
- if (!match.matches()) return PROXY_HOSTNAME_INVALID;
-
- if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID;
-
- if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY;
-
- if (port.length() > 0) {
- if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY;
- int portVal = -1;
- try {
- portVal = Integer.parseInt(port);
- } catch (NumberFormatException ex) {
- return PROXY_PORT_INVALID;
- }
- if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID;
- }
- return PROXY_VALID;
- }
-
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final void setHttpProxySystemProperty(ProxyInfo p) {
+ @Deprecated
+ public static void setHttpProxySystemProperty(ProxyInfo p) {
+ setHttpProxyConfiguration(p);
+ }
+
+ /**
+ * Set HTTP proxy configuration for the process to match the provided ProxyInfo.
+ *
+ * If the provided ProxyInfo is null, the proxy configuration will be cleared.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static void setHttpProxyConfiguration(@Nullable ProxyInfo p) {
String host = null;
String port = null;
String exclList = null;
@@ -256,11 +210,11 @@
exclList = ProxyUtils.exclusionListAsString(p.getExclusionList());
pacFileUrl = p.getPacFileUrl();
}
- setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
+ setHttpProxyConfiguration(host, port, exclList, pacFileUrl);
}
/** @hide */
- public static final void setHttpProxySystemProperty(String host, String port, String exclList,
+ public static void setHttpProxyConfiguration(String host, String port, String exclList,
Uri pacFileUrl) {
if (exclList != null) exclList = exclList.replace(",", "|");
if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
index 9c9fed1..229db0d 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -23,6 +23,8 @@
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.net.module.util.ProxyUtils;
+
import java.net.InetSocketAddress;
import java.net.URLConnection;
import java.util.List;
@@ -233,7 +235,7 @@
*/
public boolean isValid() {
if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
- return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
+ return ProxyUtils.PROXY_VALID == ProxyUtils.validate(mHost == null ? "" : mHost,
mPort == 0 ? "" : Integer.toString(mPort),
mExclusionList == null ? "" : mExclusionList);
}
diff --git a/packages/Connectivity/framework/src/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java
index c87b827..1812509 100644
--- a/packages/Connectivity/framework/src/android/net/VpnManager.java
+++ b/packages/Connectivity/framework/src/android/net/VpnManager.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
@@ -28,6 +29,8 @@
import android.content.res.Resources;
import android.os.RemoteException;
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import java.io.IOException;
@@ -161,4 +164,104 @@
throw e.rethrowFromSystemServer();
}
}
-}
+
+ /**
+ * Return the VPN configuration for the given user ID.
+ * @hide
+ */
+ @Nullable
+ public VpnConfig getVpnConfig(@UserIdInt int userId) {
+ try {
+ return mService.getVpnConfig(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Prepare for a VPN application.
+ * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+ * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param oldPackage Package name of the application which currently controls VPN, which will
+ * be replaced. If there is no such application, this should should either be
+ * {@code null} or {@link VpnConfig.LEGACY_VPN}.
+ * @param newPackage Package name of the application which should gain control of VPN, or
+ * {@code null} to disable.
+ * @param userId User for whom to prepare the new VPN.
+ *
+ * @hide
+ */
+ public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+ int userId) {
+ try {
+ return mService.prepareVpn(oldPackage, newPackage, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+ * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+ * class. If the caller is not {@code userId}, {@link
+ * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param packageName The package for which authorization state should change.
+ * @param userId User for whom {@code packageName} is installed.
+ * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+ * permissions should be granted. When unauthorizing an app, {@link
+ * VpnManager.TYPE_VPN_NONE} should be used.
+ * @hide
+ */
+ public void setVpnPackageAuthorization(
+ String packageName, int userId, @VpnManager.VpnType int vpnType) {
+ try {
+ mService.setVpnPackageAuthorization(packageName, userId, vpnType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the legacy VPN information for the specified user ID.
+ * @hide
+ */
+ public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) {
+ try {
+ return mService.getLegacyVpnInfo(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Starts a legacy VPN.
+ * @hide
+ */
+ public void startLegacyVpn(VpnProfile profile) {
+ try {
+ mService.startLegacyVpn(profile);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore
+ * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn
+ * with a reload of its profile.
+ *
+ * <p>This method can only be called by the system UID
+ * @return a boolean indicating success
+ *
+ * @hide
+ */
+ public boolean updateLockdownVpn() {
+ try {
+ return mService.updateLockdownVpn();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index d472311..7cc5994 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -26,7 +26,6 @@
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationRequest;
-import android.location.LocationResult;
import android.location.provider.ILocationProvider;
import android.location.provider.ILocationProviderManager;
import android.location.provider.ProviderProperties;
@@ -49,6 +48,7 @@
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.List;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -167,10 +167,13 @@
}
@Override
- public void onReportLocation(LocationResult locationResult) {
- for (int i = 0; i < locationResult.size(); i++) {
- mLocations.add(locationResult.get(i));
- }
+ public void onReportLocation(Location location) {
+ mLocations.add(location);
+ }
+
+ @Override
+ public void onReportLocations(List<Location> locations) {
+ mLocations.addAll(locations);
}
@Override
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index 65e75cd..d411831 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -83,9 +83,9 @@
<string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak tableta honetan."</string>
<string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telebista honetan."</string>
<string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telefono honetan."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 3837743..889980a 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Gekoppel aan beperkte netwerk"</string>
<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 4ce01d6..c41e4d5 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"ከሚለካ አውታረ መረብ ጋር ተገናኝቷል"</string>
<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 2580d0f..f8d1d57 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"تم الاتصال بشبكة تفرض تكلفة استخدام."</string>
<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 b61ff50..c6078f8 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"নিৰিখ-নিৰ্দিষ্ট নেটৱৰ্কৰ সৈতে সংযোগ কৰা হৈছে"</string>
<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 d063776..d3e0a25 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Ölçülən şəbəkəyə qoşulub"</string>
<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>
@@ -313,7 +312,7 @@
<string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Adsız Bluetooth cihazları (yalnız MAC ünvanları) göstəriləcək"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Uzaqdan idarə olunan cihazlarda dözülməz yüksək səs həcmi və ya nəzarət çatışmazlığı kimi səs problemləri olduqda Bluetooth mütləq səs həcmi xüsusiyyətini deaktiv edir."</string>
<string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche funksiyasını aktiv edir."</string>
- <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Təkmilləşdirilmiş Bağlantı funksiyasını aktiv edir."</string>
+ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Qabaqcıl məlumat mübadiləsini aktiv edir."</string>
<string name="enable_terminal_title" msgid="3834790541986303654">"Yerli terminal"</string>
<string name="enable_terminal_summary" msgid="2481074834856064500">"Yerli örtük girişini təklif edən terminal tətbiqi aktiv edin"</string>
<string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP yoxlanılır"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 2976bb5..8541222 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Povezani ste na mrežu sa ograničenjem"</string>
<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 7af9e93..aab600f 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Падключана да сеткі з падлікам трафіка"</string>
<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 77b6493..92f2935 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Установена е връзка с мрежа с отчитане"</string>
<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 819625b..b40e24e 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"মিটার্ড নেটওয়ার্কের সঙ্গে কানেক্ট করা"</string>
<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 193ac60..cec2e45 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Povezani ste s mrežom s naplatom"</string>
<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 fef9bfb..6134da8 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Connectat a una xarxa d\'ús mesurat"</string>
<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 281a788..f29a3dd 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Připojeno k měřené síti"</string>
<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 69cc8d8..d92d41d 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Der er oprettet forbindelse til det forbrugsbaserede netværk"</string>
<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 737ea16..ac05b620 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Mit kostenpflichtigem Netzwerk verbunden"</string>
<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>
@@ -287,7 +286,7 @@
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Verringert den Akkuverbrauch und verbessert die Netzwerkleistung"</string>
<string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Wenn dieser Modus aktiviert ist, kann sich die MAC-Adresse dieses Geräts bei jeder Verbindung mit einem Netzwerk ändern, bei dem die MAC-Adressen randomisiert werden."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Kostenpflichtig"</string>
- <string name="wifi_unmetered_label" msgid="6174142840934095093">"Kostenlos"</string>
+ <string name="wifi_unmetered_label" msgid="6174142840934095093">"Ohne Datenlimit"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logger-Puffergrößen"</string>
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Größe pro Protokollpuffer wählen"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Speicher der dauerhaften Protokollierung löschen?"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 8e1d5e3..0b11d69 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Σύνδεση σε δίκτυο με ογκοχρέωση"</string>
<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-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index f79072f..1da8e37 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Conectado a la red de uso medido"</string>
<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>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index f99a3f1..54226ce 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Conectado a una red de uso medido"</string>
<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 fa2aa46..4c747e3 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Ühendatud mahupõhise võrguga"</string>
<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 2c4a8ee..6016cd2 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Sare neurtu batera konektatuta"</string>
<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 1c6ca76..e855570 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -36,10 +36,9 @@
<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_to_metered_access_point" msgid="9179693207918156341">"اتصال به شبکه محدود برقرار شد"</string>
<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_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده ردهبندی شبکه"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"متصل از طریق %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"در دسترس از طریق %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 04c9130..724e52a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Yhdistetty maksulliseen verkkoon"</string>
<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 aa0cd2a..74c3005 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Appareil connecté à un réseau mesuré"</string>
<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 dbdc160..c9651ce 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Connecté au réseau facturé à l\'usage"</string>
<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 90c1303..f499f58 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Estableceuse conexión coa rede de pago por consumo"</string>
<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 4caeda2..23ee1de 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"મીટર્ડ (ડેટા નિયંત્રણ) નેટવર્ક સાથે કનેક્ટેડ છે"</string>
<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 468808b..a2fc43c 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"सीमित डेटा वाले नेटवर्क से कनेक्ट किया गया"</string>
<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 932c256..396051d 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Povezano s mrežom s ograničenim prometom"</string>
<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 fbaffac..0c9bc54 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Forgalomkorlátos hálózathoz csatlakozva"</string>
<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 224d641..56c93b6 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Միացած է վճարովի թրաֆիկով ցանցի"</string>
<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 8cf13cd..0440f47 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Terhubung ke jaringan berbayar"</string>
<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 aa8893e..0bb294f 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Tengdist neti með mældri notkun"</string>
<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>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 0199f54..54049d7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Connessione a rete a consumo effettuata"</string>
<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 a50a22d..6cc4347 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"יש חיבור לרשת המבוססת על חיוב לפי שימוש בנתונים"</string>
<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 a1d1b70..ec674ad 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"従量制ネットワークに接続しました"</string>
<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 77fd4b1..30ab3b4 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"დაკავშირებულია ფასიან ქსელთან"</string>
<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 eb5cd54..ef78527 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Трафик саналатын желіге қосылды."</string>
<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 38abb80..ed59c74 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"បានភ្ជាប់ទៅបណ្ដាញដែលផ្អែកតាមទិន្នន័យដែលប្រើ"</string>
<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 560fba15..995dc86 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"ಮಾಪನ ಮಾಡಲಾದ ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
<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 f43ce16..7863d48 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"데이터 전송량 제한이 있는 네트워크에 연결됨"</string>
<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 35b1ecac..45d05c6 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Чектелген трафикке туташтырылды"</string>
<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 f60fe7f..ca6e06c 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍທີ່ມີການວັດແທກແລ້ວ"</string>
<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 e66e3c5..173b57e 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Prisijungta prie matuojamo tinklo"</string>
<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>
@@ -208,7 +207,7 @@
<string name="enable_adb" msgid="8072776357237289039">"USB perkrova"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"Derinimo režimas, kai prijungtas USB"</string>
<string name="clear_adb_keys" msgid="3010148733140369917">"Panaikinti USB derinimo prieigos teises"</string>
- <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derinimas"</string>
+ <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derin."</string>
<string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Derinimo režimas, kai prisijungta prie „Wi‑Fi“"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"Klaida"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Belaidžio ryšio derinimas"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 90bcc04..0ef3f0e 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Поврзано на мрежа со ограничен интернет"</string>
<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 ba1987b..e6289ae 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"മീറ്റർ ചെയ്ത നെറ്റ്വർക്കിലേക്ക് കണക്റ്റ് ചെയ്തു"</string>
<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 45a831d..ead712c 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Хязгаартай сүлжээнд холбогдсон"</string>
<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>
@@ -398,8 +397,8 @@
<item msgid="1282170165150762976">"Дижитал агуулгад зориулан тааруулсан өнгө"</item>
</string-array>
<string name="inactive_apps_title" msgid="5372523625297212320">"Зогсолтын горимын апп"</string>
- <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Унтраах/асаахын тулд дарна уу."</string>
- <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Унтраах/асаахын тулд дарна уу."</string>
+ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Асаах/унтраахын тулд дарна уу."</string>
+ <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Асаах/унтраахын тулд дарна уу."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"Апп зогсолтын горимын төлөв:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
<string name="transcode_settings_title" msgid="2581975870429850549">"Медиа хөрвүүлгийн тохиргоо"</string>
<string name="transcode_user_control" msgid="6176368544817731314">"Хөрвүүлгийн өгөгдмөлийг дарах"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 1e69b28..edcc71b 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"मर्यादित नेटवर्कशी कनेक्ट केले आहे"</string>
<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 71c9eea..8a14437 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Disambungkan kepada rangkaian bermeter"</string>
<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 3b3983e..377c8b2 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"အခမဲ့မဟုတ်သော ကွန်ရက်သို့ ချိတ်ဆက်ထားသည်"</string>
<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 a8c01b3..f0e773b 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Koble til et nettverk med datamåling"</string>
<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 c285637..0bde70f 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"प्रयोगसम्बन्धी सीमा तोकिएको नेटवर्कमा कनेक्ट गरियो"</string>
<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 9f4ea68..2bc1e8c 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Verbonden met netwerk met datalimiet"</string>
<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 31bd7af..787c871 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"ମିଟରଯୁକ୍ତ ନେଟୱାର୍କ ସହ ସଂଯୋଗ କରାଯାଇଛି"</string>
<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/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index f21c4ce..9483e06 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"ਮੀਟਰਬੱਧ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
<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 c9c4a6c..c7f1595 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Połączono z siecią z pomiarem użycia danych"</string>
<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-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 4b71085..63d8b8f 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"S-a conectat la o rețea contorizată"</string>
<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 4aeb985..ac43e49 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Подключено к сети с ограниченным трафиком"</string>
<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 525d423..08fd9a0 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"මනුගත ජාලයට සම්බන්ධ කර ඇත"</string>
<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 ee9ae6a..8f05684 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Pripojené k meranej sieti"</string>
<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 ff60a32..3dccf3b 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Povezano z omrežjem z omejenim prenosom podatkov"</string>
<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 78e6ed6..c09ad4c 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Lidhur me një rrjet me matje"</string>
<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 8bb9277..b5a91bf 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Повезани сте на мрежу са ограничењем"</string>
<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 03fb223..16a1be6 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Ansluten till nätverk med datapriser"</string>
<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/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 631a413..58896c8 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Imeunganishwa kwenye mtandao unaopima data"</string>
<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 5796603..71cf7d4 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"கட்டண நெட்வொர்க்குடன் இணைக்கப்பட்டுள்ளது"</string>
<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 9027ca1..c3a65e7 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"డేటా నియంత్రణ నెట్వర్క్కు కనెక్ట్ చేయబడింది"</string>
<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 8358dbb..903accb 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"เชื่อมต่อกับเครือข่ายแบบจำกัดปริมาณแล้ว"</string>
<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 d5250a4..b259201 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Nakakonekta sa nakametrong network"</string>
<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 8e0b889..a0bc362 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Sayaçlı ağa bağlı"</string>
<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 c2f71c3..509c8a6 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Установлено з\'єднання з мережею з тарифікацією трафіку"</string>
<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 a0c5f94..e097ab2 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"میٹرڈ نیٹ ورک سے منسلک ہے"</string>
<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 1fb610b..8c2a95d 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Trafik hisoblanadigan tarmoqqa ulandi"</string>
<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>
@@ -156,7 +155,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"Foydalanuvchi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"Ba’zi birlamchi sozlamalar o‘rnatilgan"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"Birlamchi sozlamalar belgilanmagan"</string>
- <string name="tts_settings" msgid="8130616705989351312">"Nutq sintezi sozlamalari"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"Matnni nutqqa aylantirish sozlamalari"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"Nutq sintezi"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Nutq tezligi"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"Matnni o‘qish tezligi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 1f3ea48..7a4d89b 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Đã kết nối với mạng có đo lượng dữ liệu"</string>
<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 828d25f..7cd61fc 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"已连接到按流量计费的网络"</string>
<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 54e1f41..458071c 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"已連結至按用量收費的網絡"</string>
<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 b8f1f58..867ed8b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"已連線到計量付費網路"</string>
<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 d680b66..e64dbd3 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -36,8 +36,7 @@
<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_to_metered_access_point" msgid="9179693207918156341">"Kuxhunywe kunethiwekhi eyenziwe imitha"</string>
<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/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 5563003..7556ace 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1484,5 +1484,5 @@
<!-- Content description of the Ethernet connection when disconnected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_ethernet_disconnected">Ethernet disconnected.</string>
<!-- Content description of the Ethernet connection when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_ethernet_connected">Ethernet connected.</string>
+ <string name="accessibility_ethernet_connected">Ethernet.</string>
</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 66165b6..ad6a531 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -147,5 +147,10 @@
VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.ONE_HANDED_KEYGUARD_SIDE,
+ new InclusiveIntegerRangeValidator(
+ /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT,
+ /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 44864a6..a6e2af9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -42,6 +42,7 @@
import android.provider.settings.validators.SecureSettingsValidators;
import android.provider.settings.validators.SystemSettingsValidators;
import android.provider.settings.validators.Validator;
+import android.telephony.SubscriptionManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.BackupUtils;
@@ -95,10 +96,11 @@
private static final String KEY_NETWORK_POLICIES = "network_policies";
private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+ private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
- private static final int STATE_VERSION = 8;
+ private static final int STATE_VERSION = 9;
// Versioning of the Network Policies backup payload.
private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
@@ -106,19 +108,20 @@
// Slots in the checksum array. Never insert new items in the middle
// of this array; new slots must be appended.
- private static final int STATE_SYSTEM = 0;
- private static final int STATE_SECURE = 1;
- private static final int STATE_LOCALE = 2;
- private static final int STATE_WIFI_SUPPLICANT = 3;
- private static final int STATE_WIFI_CONFIG = 4;
- private static final int STATE_GLOBAL = 5;
- private static final int STATE_LOCK_SETTINGS = 6;
- private static final int STATE_SOFTAP_CONFIG = 7;
- private static final int STATE_NETWORK_POLICIES = 8;
- private static final int STATE_WIFI_NEW_CONFIG = 9;
- private static final int STATE_DEVICE_CONFIG = 10;
+ private static final int STATE_SYSTEM = 0;
+ private static final int STATE_SECURE = 1;
+ private static final int STATE_LOCALE = 2;
+ private static final int STATE_WIFI_SUPPLICANT = 3;
+ private static final int STATE_WIFI_CONFIG = 4;
+ private static final int STATE_GLOBAL = 5;
+ private static final int STATE_LOCK_SETTINGS = 6;
+ private static final int STATE_SOFTAP_CONFIG = 7;
+ private static final int STATE_NETWORK_POLICIES = 8;
+ private static final int STATE_WIFI_NEW_CONFIG = 9;
+ private static final int STATE_DEVICE_CONFIG = 10;
+ private static final int STATE_SIM_SPECIFIC_SETTINGS = 11;
- private static final int STATE_SIZE = 11; // The current number of state items
+ private static final int STATE_SIZE = 12; // The current number of state items
// Number of entries in the checksum array at various version numbers
private static final int STATE_SIZES[] = {
@@ -130,7 +133,8 @@
8, // version 5 added STATE_SOFTAP_CONFIG
9, // version 6 added STATE_NETWORK_POLICIES
10, // version 7 added STATE_WIFI_NEW_CONFIG
- STATE_SIZE // version 8 added STATE_DEVICE_CONFIG
+ 11, // version 8 added STATE_DEVICE_CONFIG
+ STATE_SIZE // version 9 added STATE_SIM_SPECIFIC_SETTINGS
};
private static final int FULL_BACKUP_ADDED_GLOBAL = 2; // added the "global" entry
@@ -218,6 +222,7 @@
byte[] netPoliciesData = getNetworkPolicies();
byte[] wifiFullConfigData = getNewWifiConfigData();
byte[] deviceSpecificInformation = getDeviceSpecificConfiguration();
+ byte[] simSpecificSettingsData = getSimSpecificSettingsData();
long[] stateChecksums = readOldChecksums(oldState);
@@ -246,6 +251,9 @@
stateChecksums[STATE_DEVICE_CONFIG] =
writeIfChanged(stateChecksums[STATE_DEVICE_CONFIG], KEY_DEVICE_SPECIFIC_CONFIG,
deviceSpecificInformation, data);
+ stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
+ writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
+ KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data);
writeNewChecksums(stateChecksums, newState);
}
@@ -386,6 +394,12 @@
preservedSettings);
break;
+ case KEY_SIM_SPECIFIC_SETTINGS:
+ byte[] restoredSimSpecificSettings = new byte[size];
+ data.readEntityData(restoredSimSpecificSettings, 0, size);
+ restoreSimSpecificSettings(restoredSimSpecificSettings);
+ break;
+
default :
data.skipEntityData();
@@ -1189,6 +1203,20 @@
return true;
}
+ private byte[] getSimSpecificSettingsData() {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ byte[] simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
+ Log.i(TAG, "sim specific data of length + " + simSpecificData.length
+ + " successfully retrieved");
+
+ return simSpecificData;
+ }
+
+ private void restoreSimSpecificSettings(byte[] data) {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ subManager.restoreAllSimSpecificSettingsFromBackup(data);
+ }
+
private void updateWindowManagerIfNeeded(Integer previousDensity) {
int newDensity;
try {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 438cec8..c11877a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -283,6 +283,7 @@
Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1af7781..eab0990 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -122,6 +122,8 @@
<uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+ <uses-permission android:name="android.permission.QUERY_USERS" />
+ <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
<uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
<uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/>
<uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"/>
diff --git a/packages/SystemUI/docs/sos_gesture.md b/packages/SystemUI/docs/sos_gesture.md
new file mode 100644
index 0000000..1a9144b
--- /dev/null
+++ b/packages/SystemUI/docs/sos_gesture.md
@@ -0,0 +1,26 @@
+# How 5-tapping power launches Emergency Sos
+
+_as of Jan 2021_
+
+Note that the flow is a simplified version of the camera launch flow.
+
+
+### Sequence of events
+
+
+1. [PhoneWindowManager.java](/services/core/java/com/android/server/policy/PhoneWindowManager.java) is responsible for all power button presses (see `interceptPowerKeyDown`).
+2. Even though PWMgr has a lot of logic to detect all manner of power button multipresses and gestures, it also checks with GestureLauncherService, which is also [offered the chance](/services/core/java/com/android/server/policy/PhoneWindowManager.java#943) to [intercept](/services/core/java/com/android/server/GestureLauncherService.java#358) the power key.
+3. GLS is responsible for the emergoncy sos timeout, and if it detects one, it [forwards it to the StatusBarManagerService](/services/core/java/com/android/server/GestureLauncherService.java#475) (which hands it off to SystemUI).
+4. Inside SystemUI, [onEmergencyActionLaunchGestureDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#4039) and determines
+ 1. If the gesture is enabled (else do nothing)
+ 2. If there is an app to handle the gesture (else do nothing)
+ 2. whether the screen is on; if not, we need to delay until that happens
+5. Assuming there is an app, and the setting is one launch Emergengy Flow immediately. [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#4077)
+ 1. Note that we cannot have an intent resolver, so we launch the default.
+
+**Which intent launches?**
+
+Due to the nature of the gesture, we need the flow to work behind the lockscreen, and without disambiguation.
+Thus, we always launch the same intent, and verify that there is only one matching intent-filter in the system image.
+
+[The emergengy sos intent action](packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java#36).
diff --git a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
index 99c70a5..b96c07e 100644
--- a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
+++ b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml
@@ -17,9 +17,8 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/wallpaperTextColorSecondary">
<item android:id="@android:id/background">
- <shape
- android:color="@android:color/transparent">
- <stroke android:width="1dp" android:color="?android:attr/textColorSecondary"/>
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorAccent"/>
<corners android:radius="24dp"/>
</shape>
</item>
@@ -29,4 +28,4 @@
<corners android:radius="24dp"/>
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
index 7986809..b5f55af 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -24,7 +24,7 @@
<include
style="@style/BouncerSecurityContainer"
layout="@layout/keyguard_host_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
index 370576b..0ee1b69 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
@@ -43,7 +43,7 @@
<com.android.keyguard.EmergencyButton
android:id="@+id/emergency_call_button"
android:layout_width="wrap_content"
- android:layout_height="32dp"
+ android:layout_height="48dp"
android:layout_marginBottom="12dp"
android:text="@*android:string/lockscreen_emergency_call"
style="@style/Keyguard.TextView.EmergencyButton" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index 04e645b..c75ee51 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -41,13 +41,14 @@
android:layout_gravity="center">
<com.android.keyguard.KeyguardSecurityViewFlipper
android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="@dimen/keyguard_security_view_top_margin"
android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
+ android:layout_gravity="center"
android:gravity="center">
</com.android.keyguard.KeyguardSecurityViewFlipper>
</com.android.keyguard.KeyguardSecurityContainer>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index dc2d11d..1a38585 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -67,6 +67,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal" />
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index aa14645a..6ae759c 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -188,6 +188,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardPINView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index 64ccefd..f709424 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -198,6 +198,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardSimPinView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 70f495c..2f9fed6 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -199,5 +199,6 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:gravity="center_horizontal"/>
</com.android.keyguard.KeyguardSimPukView>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
new file mode 100644
index 0000000..e09bf7e
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <bool name="can_use_one_handed_bouncer">true</bool>
+</resources>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 8d9d6ee..6176f7c 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,4 +22,5 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
+ <bool name="can_use_one_handed_bouncer">false</bool>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index aa87107..a928b75 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -40,6 +40,8 @@
<dimen name="keyguard_security_view_top_margin">8dp</dimen>
<dimen name="keyguard_security_view_lateral_margin">36dp</dimen>
+ <dimen name="keyguard_eca_top_margin">24dp</dimen>
+
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
<dimen name="eca_overlap">-10dip</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 2e99dea..cd82b80 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -23,12 +23,13 @@
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
</style>
<style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textSize">14dp</item>
<item name="android:background">@drawable/kg_emergency_button_background</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:paddingLeft">12dp</item>
<item name="android:paddingRight">12dp</item>
+ <item name="android:stateListAnimator">@null</item>
</style>
<style name="NumPadKey" parent="Theme.SystemUI">
<item name="android:colorControlNormal">?android:attr/colorBackground</item>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
index 108591b..d097472 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
@@ -15,22 +15,24 @@
~ limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingMode="stack">
+ android:paddingMode="stack" >
<item android:id="@android:id/background"
android:gravity="center_vertical|fill_horizontal">
- <layer-list >
+ <layer-list>
<item>
<shape
android:tint="?android:attr/colorControlActivated"
android:alpha="?android:attr/disabledAlpha">
- <size android:height="48dp" />
+ <size android:height="@dimen/rounded_slider_height" />
<solid android:color="@color/white_disabled" />
- <corners android:radius="24dp" />
+ <corners android:radius="@dimen/rounded_slider_corner_radius" />
</shape>
</item>
<item
- android:gravity="center_vertical|start"
- android:start="32dp">
+ android:gravity="center_vertical|left"
+ android:height="@dimen/rounded_slider_icon_size"
+ android:width="@dimen/rounded_slider_icon_size"
+ android:left="@dimen/rounded_slider_icon_inset">
<com.android.systemui.util.AlphaTintDrawableWrapper
android:drawable="@drawable/ic_brightness"
android:tint="?android:attr/colorControlActivated" />
@@ -39,10 +41,8 @@
</item>
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
- <clip
- android:drawable="@drawable/brightness_progress_full_drawable"
- android:clipOrientation="horizontal"
- android:gravity="left"
- />
+ <com.android.systemui.util.RoundedCornerProgressDrawable
+ android:drawable="@drawable/brightness_progress_full_drawable"
+ />
</item>
</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
index b5def5e..41140a7 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
@@ -15,18 +15,21 @@
~ limitations under the License.
-->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true">
<item android:id="@+id/slider_foreground">
<shape>
- <size android:height="48dp" />
+ <size android:height="@dimen/rounded_slider_height" />
<solid android:color="?android:attr/colorControlActivated" />
- <corners android:radius="24dp"/>
+ <corners android:radius="@dimen/rounded_slider_corner_radius"/>
</shape>
</item>
<item
android:id="@+id/slider_icon"
- android:gravity="center_vertical|start"
- android:start="32dp">
+ android:gravity="center_vertical|right"
+ android:height="@dimen/rounded_slider_icon_size"
+ android:width="@dimen/rounded_slider_icon_size"
+ android:right="@dimen/rounded_slider_icon_inset">
<com.android.systemui.util.AlphaTintDrawableWrapper
android:drawable="@drawable/ic_brightness"
android:tint="?android:attr/colorBackground"
diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
new file mode 100644
index 0000000..cb4686dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners android:radius="@dimen/notification_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
index 59dad0e..b8ea622 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
@@ -17,6 +17,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid
- android:color="?android:attr/textColorPrimary" />
+ android:color="?android:attr/textColorSecondary" />
<corners android:radius="2dp" />
</shape>
diff --git a/packages/SystemUI/res/drawable/udfps_progress_bar.xml b/packages/SystemUI/res/drawable/udfps_progress_bar.xml
new file mode 100644
index 0000000..e5389f3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/udfps_progress_bar.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape
+ android:innerRadiusRatio="2.2"
+ android:shape="ring"
+ android:thickness="@dimen/udfps_enroll_progress_thickness"
+ android:useLevel="false"
+ android:tint="?android:colorControlNormal">
+ <solid android:color="@*android:color/white_disabled_material" />
+ </shape>
+ </item>
+ <item android:id="@android:id/progress">
+ <rotate
+ android:fromDegrees="270"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:toDegrees="270">
+ <shape
+ android:innerRadiusRatio="2.2"
+ android:shape="ring"
+ android:thickness="@dimen/udfps_enroll_progress_thickness"
+ android:tint="?android:attr/colorControlActivated">
+ <solid android:color="@android:color/white" />
+ </shape>
+ </rotate>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_in_dialog.xml b/packages/SystemUI/res/layout/controls_in_dialog.xml
new file mode 100644
index 0000000..983999f
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_in_dialog.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/control_detail_root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginVertical="@dimen/controls_activity_view_top_offset"
+ android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset"
+ android:padding="8dp"
+ android:orientation="vertical"
+ android:background="@drawable/controls_dialog_bg">
+
+ <com.android.systemui.globalactions.MinHeightScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:scrollbars="none">
+
+ <LinearLayout
+ android:id="@+id/global_actions_controls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ android:clipToPadding="false" />
+
+ </com.android.systemui.globalactions.MinHeightScrollView>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 04de978..862076b 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -81,6 +81,17 @@
android:contentDescription="@string/accessibility_phone_button"
android:tint="?attr/wallpaperTextColor" />
+ <ImageView
+ android:id="@+id/alt_left_button"
+ android:layout_height="@dimen/keyguard_affordance_height"
+ android:layout_width="@dimen/keyguard_affordance_width"
+ android:layout_gravity="bottom|start"
+ android:scaleType="center"
+ android:tint="?attr/wallpaperTextColor"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="48dp"
+ android:visibility="gone" />
+
<FrameLayout
android:id="@+id/overlay_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 2a055fc..e2f3e2a 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -22,82 +22,102 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ImageView
- android:id="@+id/preview"
+ <Button
+ android:id="@+id/save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginVertical="8dp"
- android:layout_marginHorizontal="48dp"
- android:adjustViewBounds="true"
- app:layout_constrainedHeight="true"
- app:layout_constrainedWidth="true"
- app:layout_constraintBottom_toBottomOf="@id/guideline"
- app:layout_constraintEnd_toEndOf="parent"
+ android:text="@string/save"
+ app:layout_constraintEnd_toStartOf="@id/cancel"
+ app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- tools:background="?android:colorBackground"
- tools:minHeight="100dp"
- tools:minWidth="100dp" />
+ app:layout_constraintBottom_toTopOf="@id/guideline" />
- <com.android.systemui.screenshot.CropView
- android:id="@+id/crop_view"
- android:visibility="gone"
+ <Button
+ android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginVertical="8dp"
- app:layout_constrainedHeight="true"
- app:layout_constrainedWidth="true"
- app:layout_constraintBottom_toBottomOf="@id/guideline"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
+ android:text="@string/cancel"
+ app:layout_constraintEnd_toStartOf="@id/edit"
+ app:layout_constraintStart_toEndOf="@id/save"
app:layout_constraintTop_toTopOf="parent"
- app:handleThickness="3dp"
- app:handleColor="@*android:color/accent_device_default"
- app:scrimColor="#9444"
- tools:background="?android:colorBackground"
- tools:minHeight="100dp"
- tools:minWidth="100dp" />
+ app:layout_constraintBottom_toTopOf="@id/guideline" />
+
+ <Button
+ android:id="@+id/edit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/screenshot_edit_label"
+ app:layout_constraintEnd_toStartOf="@id/share"
+ app:layout_constraintStart_toEndOf="@id/cancel"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/guideline" />
+
+ <Button
+ android:id="@+id/share"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@*android:string/share"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/edit"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/guideline" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
- app:layout_constraintGuide_percent="0.9" />
+ app:layout_constraintGuide_percent="0.1" />
- <Button
- android:id="@+id/close"
+ <ImageView
+ android:id="@+id/preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_margin="8dp"
- android:text="Close"
- app:layout_constraintEnd_toStartOf="@+id/edit"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/guideline" />
-
- <Button
- android:id="@+id/edit"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="8dp"
- android:text="Edit"
- app:layout_constraintEnd_toStartOf="@+id/share"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toEndOf="@+id/close"
- app:layout_constraintTop_toTopOf="@+id/guideline" />
-
- <Button
- android:id="@+id/share"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="8dp"
- android:text="Share"
+ android:layout_marginBottom="42dp"
+ android:layout_marginHorizontal="48dp"
+ android:adjustViewBounds="true"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintTop_toBottomOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.5"
- app:layout_constraintStart_toEndOf="@+id/edit"
- app:layout_constraintTop_toTopOf="@+id/guideline" />
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ tools:background="?android:colorBackground"
+ tools:minHeight="100dp"
+ tools:minWidth="100dp" />
+
+ <com.android.systemui.screenshot.CropView
+ android:id="@+id/crop_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="42dp"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintTop_toBottomOf="@id/guideline"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:handleThickness="@dimen/screenshot_crop_handle_thickness"
+ app:handleColor="@*android:color/accent_device_default"
+ app:scrimColor="@color/screenshot_crop_scrim"
+ tools:background="?android:colorBackground"
+ tools:minHeight="100dp"
+ tools:minWidth="100dp" />
+
+ <com.android.systemui.screenshot.MagnifierView
+ android:id="@+id/magnifier"
+ android:visibility="invisible"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ app:layout_constraintTop_toBottomOf="@id/guideline"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:handleThickness="@dimen/screenshot_crop_handle_thickness"
+ app:handleColor="@*android:color/accent_device_default"
+ app:scrimColor="@color/screenshot_crop_scrim"
+ app:borderThickness="4dp"
+ app:borderColor="#fff"
+ />
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 75f76b4..d6385ff 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -50,18 +50,22 @@
android:layout="@layout/qs_panel"
android:layout_width="@dimen/qs_panel_width"
android:layout_height="match_parent"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
android:clipToPadding="false"
android:clipChildren="false"
- systemui:viewType="com.android.systemui.plugins.qs.QS" />
+ systemui:viewType="com.android.systemui.plugins.qs.QS"
+ systemui:layout_constraintStart_toStartOf="parent"
+ systemui:layout_constraintEnd_toEndOf="parent"
+ />
<com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
android:layout_marginTop="@dimen/notification_panel_margin_top"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="match_parent"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:layout_marginBottom="@dimen/close_handle_underlap" />
+ android:layout_marginBottom="@dimen/close_handle_underlap"
+ systemui:layout_constraintStart_toStartOf="parent"
+ systemui:layout_constraintEnd_toEndOf="parent"
+ />
<include layout="@layout/ambient_indication"
android:id="@+id/ambient_indication_container" />
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index c078805..6ae306e 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -20,4 +20,17 @@
android:id="@+id/udfps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- systemui:sensorTouchAreaCoefficient="0.5"/>
+ systemui:sensorTouchAreaCoefficient="0.5">
+
+ <!-- Enrollment progress bar-->
+ <com.android.systemui.biometrics.UdfpsProgressBar
+ android:id="@+id/progress_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:max="100"
+ android:padding="@dimen/udfps_enroll_progress_thickness"
+ android:progress="0"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+
+</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 6dff2e3..a6321fe 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -19,4 +19,7 @@
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">2</integer>
+
+ <!-- Whether to use the split 2-column notification shade -->
+ <bool name="config_use_split_notification_shade">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index dec83d9..722f148 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -39,7 +39,6 @@
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
<item>@string/config_systemUIVendorServiceComponent</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
- <item>com.android.systemui.SizeCompatModeActivityController</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.toast.ToastUI</item>
<item>com.android.systemui.wmshell.WMShell</item>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index e2fe223..8166e35 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -177,5 +177,17 @@
<attr name="handleColor" format="color" />
<attr name="scrimColor" format="color" />
</declare-styleable>
+
+ <declare-styleable name="MagnifierView">
+ <attr name="handleThickness" format="dimension" />
+ <attr name="handleColor" format="color" />
+ <attr name="scrimColor" format="color" />
+ <attr name="borderThickness" format="dimension" />
+ <attr name="borderColor" format="color" />
+ </declare-styleable>
+
+ <declare-styleable name="RoundedCornerProgressDrawable">
+ <attr name="android:drawable" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a7cf3e9..5fb6de7 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -197,6 +197,9 @@
<color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color>
<color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
+ <!-- Long screenshot UI -->
+ <color name="screenshot_crop_scrim">#9444</color>
+
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
<color name="GM2_grey_100">#F1F3F4</color>
@@ -261,6 +264,7 @@
<color name="control_enabled_cool_foreground">@color/GM2_blue_300</color>
<color name="control_thumbnail_tint">#33000000</color>
<color name="control_thumbnail_shadow_color">@*android:color/black</color>
+ <color name="controls_lockscreen_scrim">#AA000000</color>
<!-- Docked misalignment message -->
<color name="misalignment_text_color">#F28B82</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 09710d7..bb04c3b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -301,7 +301,6 @@
<item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.biometrics.AuthController</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
- <item>com.android.systemui.SizeCompatModeActivityController</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.theme.ThemeOverlayController</item>
<item>com.android.systemui.accessibility.WindowMagnification</item>
@@ -566,4 +565,7 @@
<!-- Whether wallet view is shown in landscape / seascape orientations -->
<bool name="global_actions_show_landscape_wallet_view">false</bool>
+
+ <!-- Whether to use the split 2-column notification shade -->
+ <bool name="config_use_split_notification_shade">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1fac96b..afa98b5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -345,6 +345,7 @@
<dimen name="screenshot_action_chip_padding_end">16dp</dimen>
<dimen name="screenshot_action_chip_text_size">14sp</dimen>
<dimen name="screenshot_dismissal_height_delta">80dp</dimen>
+ <dimen name="screenshot_crop_handle_thickness">3dp</dimen>
<!-- The width of the view containing navigation buttons -->
@@ -1116,6 +1117,9 @@
<!-- Y translation for credential contents when animating in -->
<dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
+ <!-- UDFPS enrollment progress bar thickness -->
+ <dimen name="udfps_enroll_progress_thickness">12dp</dimen>
+
<!-- Wireless Charging Animation values -->
<dimen name="wireless_charging_dots_radius_start">0dp</dimen>
<dimen name="wireless_charging_dots_radius_end">4dp</dimen>
@@ -1259,6 +1263,7 @@
<!-- Home Controls activity view detail panel-->
<dimen name="controls_activity_view_top_offset">100dp</dimen>
+ <dimen name="controls_activity_view_side_offset">12dp</dimen>
<dimen name="controls_activity_view_text_size">17sp</dimen>
<dimen name="controls_activity_view_corner_radius">@*android:dimen/config_bottomDialogCornerRadius</dimen>
@@ -1333,4 +1338,12 @@
<dimen name="people_space_widget_radius">24dp</dimen>
<dimen name="people_space_widget_round_radius">100dp</dimen>
<dimen name="people_space_widget_background_padding">6dp</dimen>
+
+ <dimen name="rounded_slider_height">48dp</dimen>
+ <!-- rounded_slider_height / 2 -->
+ <dimen name="rounded_slider_corner_radius">24dp</dimen>
+ <!-- rounded_slider_height / 2 -->
+ <dimen name="rounded_slider_icon_size">24dp</dimen>
+ <!-- rounded_slider_icon_size / 2 -->
+ <dimen name="rounded_slider_icon_inset">12dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 81b7a7b..d932395 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2816,4 +2816,6 @@
<!-- No translation [CHAR LIMIT=0] -->
<string name="qs_remove_labels" translatable="false"></string>
+
+ <string name="qs_tile_label_fontFamily" translatable="false">@*android:string/config_headlineFontFamily</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ad4e78e..7c72548 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -196,7 +196,7 @@
<style name="TextAppearance.QS.TileLabel">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:fontFamily">@string/qs_tile_label_fontFamily</item>
</style>
<style name="TextAppearance.QS.TileLabel.Secondary">
@@ -354,15 +354,17 @@
</style>
<style name="LockPatternStyle">
- <item name="*android:regularColor">?android:attr/textColorPrimary</item>
+ <item name="*android:regularColor">?android:attr/colorAccent</item>
<item name="*android:successColor">?android:attr/textColorPrimary</item>
<item name="*android:errorColor">?android:attr/colorError</item>
+ <item name="*android:dotColor">?android:attr/textColorSecondary</item>
</style>
<style name="LockPatternStyleBiometricPrompt">
<item name="*android:regularColor">?android:attr/colorForeground</item>
<item name="*android:successColor">?android:attr/colorForeground</item>
<item name="*android:errorColor">?android:attr/colorError</item>
+ <item name="*android:dotColor">?android:attr/textColorSecondary</item>
</style>
<style name="qs_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
@@ -660,6 +662,19 @@
<item name="android:windowExitAnimation">@anim/bottomsheet_out</item>
</style>
+ <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
+ <item name="android:windowAnimationStyle">@style/Animation.Fade</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:windowBackground">@color/controls_lockscreen_scrim</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ </style>
+
+ <style name="Animation.Fade">
+ <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
+ <item name="android:windowExitAnimation">@android:anim/fade_out</item>
+ </style>
+
<style name="Control" />
<style name="Control.MenuItem">
@@ -750,4 +765,12 @@
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
</style>
+
+ <style name="UdfpsProgressBarStyle"
+ parent="android:style/Widget.Material.ProgressBar.Horizontal">
+ <item name="android:indeterminate">false</item>
+ <item name="android:max">10000</item>
+ <item name="android:mirrorForRtl">false</item>
+ <item name="android:progressDrawable">@drawable/udfps_progress_bar</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index 10e28c4..d0c63a8 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -15,8 +15,10 @@
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="180dp"
+ android:minWidth="140dp"
android:minHeight="40dp"
+ android:minResizeWidth="110dp"
+ android:minResizeHeight="40dp"
android:updatePeriodMillis="60000"
android:previewImage="@drawable/ic_android"
android:resizeMode="horizontal|vertical"
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
similarity index 67%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
index 19b20f2..e5ced3e 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
@@ -14,7 +14,12 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package com.android.systemui.shared.recents;
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
+ */
+oneway interface ISplitScreenListener {
+ void onStagePositionChanged(int stage, int position);
+ void onTaskStageChanged(int taskId, int stage);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 388eeb6..e38cf23 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -16,6 +16,7 @@
package com.android.systemui.shared.recents;
+import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
@@ -23,9 +24,11 @@
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.UserHandle;
import android.view.MotionEvent;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.ISplitScreenListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteTransitionCompat;
@@ -202,4 +205,49 @@
/** Unegisters a RemoteTransitionCompat that will handle transitions. */
void unregisterRemoteTransition(in RemoteTransitionCompat remoteTransition) = 33;
+
+// SplitScreen APIs...copied from SplitScreen.java
+ /**
+ * Stage position isn't specified normally meaning to use what ever it is currently set to.
+ */
+ //int STAGE_POSITION_UNDEFINED = -1;
+ /**
+ * Specifies that a stage is positioned at the top half of the screen if
+ * in portrait mode or at the left half of the screen if in landscape mode.
+ */
+ //int STAGE_POSITION_TOP_OR_LEFT = 0;
+ /**
+ * Specifies that a stage is positioned at the bottom half of the screen if
+ * in portrait mode or at the right half of the screen if in landscape mode.
+ */
+ //int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
+
+ /**
+ * Stage type isn't specified normally meaning to use what ever the default is.
+ * E.g. exit split-screen and launch the app in fullscreen.
+ */
+ //int STAGE_TYPE_UNDEFINED = -1;
+ /**
+ * The main stage type.
+ * @see MainStage
+ */
+ //int STAGE_TYPE_MAIN = 0;
+ /**
+ * The side stage type.
+ * @see SideStage
+ */
+ //int STAGE_TYPE_SIDE = 1;
+
+ void registerSplitScreenListener(in ISplitScreenListener listener) = 34;
+ void unregisterSplitScreenListener(in ISplitScreenListener listener) = 35;
+
+ /** Hides the side-stage if it is currently visible. */
+ void setSideStageVisibility(in boolean visible) = 36;
+ /** Removes the split-screen stages. */
+ void exitSplitScreen() = 37;
+ void startTask(in int taskId, in int stage, in int position, in Bundle options) = 38;
+ void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
+ in Bundle options, in UserHandle user) = 39;
+ void startIntent(
+ in PendingIntent intent, in int stage, in int position, in Bundle options) = 40;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 3584c82..e2ca349 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -72,9 +72,13 @@
return ActivityOptions.makeRemoteTransition(remoteTransition.getTransition());
}
+ /**
+ * Returns ActivityOptions for overriding task transition animation.
+ */
public static ActivityOptions makeCustomAnimation(Context context, int enterResId,
int exitResId, final Runnable callback, final Handler callbackHandler) {
- return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler,
+ return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId,
+ callbackHandler,
new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index a56c6a1..e6477f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -18,9 +18,11 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionOldType;
import android.os.RemoteException;
import android.util.Log;
@@ -65,13 +67,17 @@
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(wallpapers);
+ final RemoteAnimationTargetCompat[] nonAppsCompat =
+ RemoteAnimationTargetCompat.wrap(nonApps);
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -83,8 +89,8 @@
}
}
};
- remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
- animationFinishedCallback);
+ remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat,
+ nonAppsCompat, animationFinishedCallback);
}
@Override
@@ -104,6 +110,9 @@
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
+ // TODO(bc-unlock): Build wrapped object for non-apps target.
+ final RemoteAnimationTargetCompat[] nonAppsCompat =
+ new RemoteAnimationTargetCompat[0];
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -147,7 +156,10 @@
}
}
t.apply();
- remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
+ // TODO(bc-unlcok): Pass correct transit type.
+ remoteAnimationAdapter.onAnimationStart(
+ TRANSIT_OLD_NONE,
+ appsCompat, wallpapersCompat, nonAppsCompat,
animationFinishedCallback);
}
};
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 33372f6..0076292 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -16,8 +16,11 @@
package com.android.systemui.shared.system;
+import android.view.WindowManager;
+
public interface RemoteAnimationRunnerCompat {
- void onAnimationStart(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback);
+ void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+ RemoteAnimationTargetCompat[] nonApps, Runnable finishedCallback);
void onAnimationCancelled();
}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 8d010c7..6c77af7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -19,7 +19,6 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ITaskStackListener;
import android.content.ComponentName;
-import android.os.IBinder;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -80,7 +79,6 @@
public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) { }
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }
- public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { }
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index a907e66..8f08f5a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -18,15 +18,14 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
-import android.window.TaskSnapshot;
import android.app.TaskStackListener;
import android.content.ComponentName;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Trace;
import android.util.Log;
+import android.window.TaskSnapshot;
import com.android.internal.os.SomeArgs;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -88,13 +87,12 @@
private static final int ON_TASK_MOVED_TO_FRONT = 14;
private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 15;
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16;
- private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
- private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
- private static final int ON_TASK_DISPLAY_CHANGED = 19;
- private static final int ON_TASK_LIST_UPDATED = 20;
- private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 21;
- private static final int ON_TASK_DESCRIPTION_CHANGED = 22;
- private static final int ON_ACTIVITY_ROTATION = 23;
+ private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 17;
+ private static final int ON_TASK_DISPLAY_CHANGED = 18;
+ private static final int ON_TASK_LIST_UPDATED = 19;
+ private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 20;
+ private static final int ON_TASK_DESCRIPTION_CHANGED = 21;
+ private static final int ON_ACTIVITY_ROTATION = 22;
/**
* List of {@link TaskStackChangeListener} registered from {@link #addListener}.
@@ -248,13 +246,6 @@
}
@Override
- public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
- mHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId,
- 0 /* unused */,
- activityToken).sendToTarget();
- }
-
- @Override
public void onTaskDisplayChanged(int taskId, int newDisplayId) {
mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
}
@@ -391,13 +382,6 @@
}
break;
}
- case ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onSizeCompatModeActivityChanged(
- msg.arg1, (IBinder) msg.obj);
- }
- break;
- }
case ON_BACK_PRESSED_ON_TASK_ROOT: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onBackPressedOnTaskRoot(
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index b7d7498..707ee29 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -154,7 +154,7 @@
**/
public void reloadColors() {
int color = Utils.getColorAttrDefaultColor(getContext(),
- android.R.attr.textColorSecondary);
+ android.R.attr.textColorPrimaryInverse);
setTextColor(color);
setBackground(getContext()
.getDrawable(com.android.systemui.R.drawable.kg_emergency_button_background));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 85e9ca0..276036c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -74,16 +74,7 @@
@Override
public void onDisplayChanged(int displayId) {
- if (displayId == DEFAULT_DISPLAY) return;
- final Presentation presentation = mPresentations.get(displayId);
- if (presentation != null && mShowing) {
- hidePresentation(displayId);
- // update DisplayInfo.
- final Display display = mDisplayService.getDisplay(displayId);
- if (display != null) {
- showPresentation(display);
- }
- }
+
}
@Override
@@ -305,15 +296,16 @@
}
@Override
+ public void onDisplayChanged() {
+ updateBounds();
+ getWindow().getDecorView().requestLayout();
+ }
+
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
- .getBounds();
- mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
- mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
- mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
- mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+ updateBounds();
setContentView(LayoutInflater.from(mContext)
.inflate(R.layout.keyguard_presentation, null));
@@ -338,5 +330,14 @@
mKeyguardClockSwitchController.init();
}
+
+ private void updateBounds() {
+ final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
+ .getBounds();
+ mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
+ mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
+ mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
+ mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 30db341..c182fd1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -29,12 +29,17 @@
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.OrientationEventListener;
import android.view.VelocityTracker;
+import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimationControlListener;
@@ -55,6 +60,7 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.List;
@@ -99,6 +105,12 @@
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
+ private boolean mIsSecurityViewLeftAligned = true;
+ private boolean mOneHandedMode = false;
+ private SecurityMode mSecurityMode = SecurityMode.Invalid;
+ private ViewPropertyAnimator mRunningOneHandedAnimator;
+ private final OrientationEventListener mOrientationEventListener;
+
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -157,16 +169,20 @@
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+
void userActivity();
+
void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
- * @param strongAuth wheher the user has authenticated with strong authentication like
- * pattern, password or PIN but not by trust agents or fingerprint
+ * @param strongAuth wheher the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
void finish(boolean strongAuth, int targetUserId);
+
void reset();
+
void onCancelClicked();
}
@@ -224,11 +240,136 @@
super(context, attrs, defStyle);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
mViewConfiguration = ViewConfiguration.get(context);
+
+ mOrientationEventListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ updateLayoutForSecurityMode(mSecurityMode);
+ }
+ };
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
+ mSecurityMode = securityMode;
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
+
+ updateLayoutForSecurityMode(securityMode);
+ mOrientationEventListener.enable();
+ }
+
+ void updateLayoutForSecurityMode(SecurityMode securityMode) {
+ mSecurityMode = securityMode;
+ mOneHandedMode = canUseOneHandedBouncer();
+
+ if (mOneHandedMode) {
+ mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
+ }
+
+ updateSecurityViewGravity();
+ updateSecurityViewLocation(false);
+ }
+
+ /** Return whether the one-handed keyguard should be enabled. */
+ private boolean canUseOneHandedBouncer() {
+ // Is it enabled?
+ if (!getResources().getBoolean(
+ com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
+ return false;
+ }
+
+ if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
+ return false;
+ }
+
+ return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ }
+
+ /** Read whether the one-handed keyguard should be on the left/right from settings. */
+ private boolean isOneHandedKeyguardLeftAligned(Context context) {
+ try {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
+ == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+ } catch (Settings.SettingNotFoundException ex) {
+ return true;
+ }
+ }
+
+ private void updateSecurityViewGravity() {
+ View securityView = findKeyguardSecurityView();
+
+ if (securityView == null) {
+ return;
+ }
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
+
+ if (mOneHandedMode) {
+ lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ } else {
+ lp.gravity = Gravity.CENTER;
+ }
+
+ securityView.setLayoutParams(lp);
+ }
+
+ /**
+ * Moves the inner security view to the correct location (in one handed mode) with animation.
+ * This is triggered when the user taps on the side of the screen that is not currently occupied
+ * by the security view .
+ */
+ private void updateSecurityViewLocation(boolean animate) {
+ View securityView = findKeyguardSecurityView();
+
+ if (securityView == null) {
+ return;
+ }
+
+ if (!mOneHandedMode) {
+ securityView.setTranslationX(0);
+ return;
+ }
+
+ if (mRunningOneHandedAnimator != null) {
+ mRunningOneHandedAnimator.cancel();
+ mRunningOneHandedAnimator = null;
+ }
+
+ int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
+
+ if (animate) {
+ mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
+ mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningOneHandedAnimator = null;
+ }
+ });
+
+ mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mRunningOneHandedAnimator.start();
+ } else {
+ securityView.setTranslationX(targetTranslation);
+ }
+ }
+
+ @Nullable
+ private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+
+ if (isKeyguardSecurityView(child)) {
+ return (KeyguardSecurityViewFlipper) child;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isKeyguardSecurityView(View view) {
+ return view instanceof KeyguardSecurityViewFlipper;
}
public void onPause() {
@@ -237,6 +378,7 @@
mAlertDialog = null;
}
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
+ mOrientationEventListener.disable();
}
@Override
@@ -318,19 +460,44 @@
if (mSwipeListener != null) {
mSwipeListener.onSwipeUp();
}
+ } else {
+ if (!mIsDragging) {
+ handleTap(event);
+ }
}
}
return true;
}
+ private void handleTap(MotionEvent event) {
+ // If we're using a fullscreen security mode, skip
+ if (!mOneHandedMode) {
+ return;
+ }
+
+ // Did the tap hit the "other" side of the bouncer?
+ if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f))
+ || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) {
+ mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
+
+ Settings.Global.putInt(
+ mContext.getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
+ mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
+ : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
+
+ updateSecurityViewLocation(true);
+ }
+ }
+
void setSwipeListener(SwipeListener swipeListener) {
mSwipeListener = swipeListener;
}
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
- .setStartVelocity(startVelocity)
- .animateToFinalPosition(0);
+ .setStartVelocity(startVelocity)
+ .animateToFinalPosition(0);
}
public void startDisappearAnimation(SecurityMode securitySelection) {
@@ -440,18 +607,17 @@
return insets.inset(0, 0, 0, inset);
}
-
private void showDialog(String title, String message) {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
}
mAlertDialog = new AlertDialog.Builder(mContext)
- .setTitle(title)
- .setMessage(message)
- .setCancelable(false)
- .setNeutralButton(R.string.ok, null)
- .create();
+ .setTitle(title)
+ .setMessage(message)
+ .setCancelable(false)
+ .setNeutralButton(R.string.ok, null)
+ .create();
if (!(mContext instanceof Activity)) {
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
@@ -489,6 +655,44 @@
}
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int maxHeight = 0;
+ int maxWidth = 0;
+ int childState = 0;
+
+ int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(widthMeasureSpec) / 2,
+ MeasureSpec.getMode(widthMeasureSpec));
+
+ for (int i = 0; i < getChildCount(); i++) {
+ final View view = getChildAt(i);
+ if (view.getVisibility() != GONE) {
+ if (mOneHandedMode && isKeyguardSecurityView(view)) {
+ measureChildWithMargins(view, halfWidthMeasureSpec, 0,
+ heightMeasureSpec, 0);
+ } else {
+ measureChildWithMargins(view, widthMeasureSpec, 0,
+ heightMeasureSpec, 0);
+ }
+ final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ maxWidth = Math.max(maxWidth,
+ view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ maxHeight = Math.max(maxHeight,
+ view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ childState = combineMeasuredStates(childState, view.getMeasuredState());
+ }
+ }
+
+ // Check against our minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 1a8d420..fdab8db 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -404,6 +404,7 @@
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
+ mView.updateLayoutForSecurityMode(securityMode);
}
mSecurityCallback.onSecurityModeChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index c77c867..631c248 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -92,4 +92,13 @@
throw new IllegalStateException("Unknown security quality:" + security);
}
}
+
+ /**
+ * Returns whether the given security view should be used in a "one handed" way. This can be
+ * used to change how the security view is drawn (e.g. take up less of the screen, and align to
+ * one side).
+ */
+ public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
+ return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 7773fe9..75ef4b32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -128,6 +128,8 @@
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
+ if (child.getVisibility() != View.VISIBLE) continue;
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) {
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
deleted file mode 100644
index 7a52d27..0000000
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import android.content.Context;
-
-import com.android.systemui.dagger.SysUISingleton;
-
-import javax.inject.Inject;
-
-/**
- * Shows a restart-activity button when the foreground activity is in size compatibility mode.
- *
- * // TODO remove this class after cleanup all dependencies.
- * @deprecated Use {@link com.android.wm.shell.sizecompatui.SizeCompatUIController}
- */
-@Deprecated
-@SysUISingleton
-public class SizeCompatModeActivityController extends SystemUI {
-
- @Inject
- SizeCompatModeActivityController(Context context) {
- super(context);
- }
-
- @Override
- public void start() { }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 055270d..7d06dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
-import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -137,7 +136,8 @@
mActivityTaskManager.getTasks(1);
if (!runningTasks.isEmpty()) {
final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(clientPackage)) {
+ if (!topPackage.contentEquals(clientPackage)
+ && !Utils.isSystem(mContext, clientPackage)) {
Log.w(TAG, "Evicting client due to: " + topPackage);
mCurrentDialog.dismissWithoutCallback(true /* animate */);
mCurrentDialog = null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index 68f1414..5290986 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -28,7 +28,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -39,6 +38,7 @@
private static final String TAG = "UdfpsAnimationEnroll";
private static final float SHADOW_RADIUS = 5.f;
+ private static final float PROGRESS_BAR_RADIUS = 140.f;
@Nullable private RectF mSensorRect;
@NonNull private final Paint mSensorPaint;
@@ -70,9 +70,6 @@
@Override
public void draw(@NonNull Canvas canvas) {
- canvas.save();
- canvas.translate(getPaddingX(), getPaddingY());
-
final boolean isNightMode = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
if (!isNightMode) {
@@ -81,30 +78,22 @@
}
}
mFingerprintDrawable.draw(canvas);
-
- canvas.restore();
}
@Override
public int getPaddingX() {
- return (int) Math.ceil(SHADOW_RADIUS);
+ return (int) Math.ceil(PROGRESS_BAR_RADIUS);
}
@Override
public int getPaddingY() {
- return (int) Math.ceil(SHADOW_RADIUS);
+ return (int) Math.ceil(PROGRESS_BAR_RADIUS);
}
@Override
public void setAlpha(int alpha) {
super.setAlpha(alpha);
-
- // Gradually fade into the notification shade color. This needs to be done because the
- // UDFPS view is drawn on a layer on top of the notification shade
- final float percent = alpha / 255.f;
- mSensorPaint.setColor(ColorUtils.blendARGB(mNotificationShadeColor, Color.WHITE, percent));
- mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0,
- ColorUtils.blendARGB(mNotificationShadeColor, Color.BLACK, percent));
+ mSensorPaint.setAlpha(alpha);
}
@Override
@@ -116,12 +105,4 @@
public int getOpacity() {
return 0;
}
-
- public void onEnrollmentProgress(int remaining) {
- Log.d(TAG, "Remaining: " + remaining);
- }
-
- public void onEnrollmentHelp() {
- Log.d(TAG, "onEnrollmentHelp");
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index fded737..41ea4d6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -25,22 +25,21 @@
import android.view.View;
import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
/**
* Class that coordinates non-HBM animations (such as enroll, keyguard, BiometricPrompt,
* FingerprintManager).
*/
public class UdfpsAnimationView extends View implements DozeReceiver,
- ScrimController.ScrimChangedListener {
+ StatusBar.ExpansionChangedListener {
private static final String TAG = "UdfpsAnimationView";
@NonNull private UdfpsView mParent;
@Nullable private UdfpsAnimation mUdfpsAnimation;
@NonNull private RectF mSensorRect;
- private int mNotificationPanelAlpha;
-
+ private int mAlpha;
public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -52,12 +51,24 @@
super.onDraw(canvas);
if (mUdfpsAnimation != null) {
- final int alpha = mParent.shouldPauseAuth() ? 255 - mNotificationPanelAlpha : 255;
+ final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255;
mUdfpsAnimation.setAlpha(alpha);
mUdfpsAnimation.draw(canvas);
}
}
+ private int expansionToAlpha(float expansion) {
+ // Fade to 0 opacity when reaching this expansion amount
+ final float maxExpansion = 0.4f;
+
+ if (expansion >= maxExpansion) {
+ return 0; // transparent
+ }
+
+ final float percent = expansion / maxExpansion;
+ return (int) ((1 - percent) * 255);
+ }
+
void setParent(@NonNull UdfpsView parent) {
mParent = parent;
}
@@ -87,20 +98,22 @@
}
@Override
- public void onAlphaChanged(float alpha) {
- mNotificationPanelAlpha = (int) (alpha * 255);
+ public void onExpansionChanged(float expansion, boolean expanded) {
+ mAlpha = expansionToAlpha(expansion);
postInvalidate();
}
- void onEnrollmentProgress(int remaining) {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining);
+ public int getPaddingX() {
+ if (mUdfpsAnimation == null) {
+ return 0;
}
+ return mUdfpsAnimation.getPaddingX();
}
- void onEnrollmentHelp() {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
+ public int getPaddingY() {
+ if (mUdfpsAnimation == null) {
+ return 0;
}
+ return mUdfpsAnimation.getPaddingY();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ac4b93a..e7b08e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -45,6 +45,7 @@
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.concurrency.DelayableExecutor;
import javax.inject.Inject;
@@ -83,6 +84,7 @@
private boolean mIsOverlayRequested;
// Reason the overlay has been requested. See IUdfpsOverlayController for definitions.
private int mRequestReason;
+ @Nullable UdfpsEnrollHelper mEnrollHelper;
// The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
// to turn off high brightness mode. To get around this limitation, the state of the AOD
@@ -94,6 +96,12 @@
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@Override
public void showUdfpsOverlay(int sensorId, int reason) {
+ if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
+ || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
+ mEnrollHelper = new UdfpsEnrollHelper(reason);
+ } else {
+ mEnrollHelper = null;
+ }
UdfpsController.this.showOverlay(reason);
}
@@ -155,7 +163,7 @@
WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
- @NonNull ScrimController scrimController) {
+ @Nullable StatusBar statusBar) {
mContext = context;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
// fingerprint manager should never be null.
@@ -186,7 +194,7 @@
mView.setSensorProperties(mSensorProps);
mView.setHbmCallback(this);
- scrimController.addScrimChangedListener(mView);
+ statusBar.addExpansionChangedListener(mView);
statusBarStateController.addCallback(mView);
mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
@@ -259,15 +267,17 @@
// Transform dimensions if the device is in landscape mode.
switch (mContext.getDisplay().getRotation()) {
case Surface.ROTATION_90:
- mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
- mCoreLayoutParams.y =
- p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+ mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ - paddingX;
+ mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ - paddingY;
break;
case Surface.ROTATION_270:
- mCoreLayoutParams.x =
- p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
- mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+ mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ - paddingX;
+ mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ - paddingY;
break;
default:
@@ -293,7 +303,7 @@
try {
Log.v(TAG, "showUdfpsOverlay | adding window");
final UdfpsAnimation animation = getUdfpsAnimationForReason(reason);
- mView.setUdfpsAnimation(animation);
+ mView.setExtras(animation, mEnrollHelper);
mWindowManager.addView(mView, computeLayoutParams(animation));
mView.setOnTouchListener(mOnTouchListener);
mIsOverlayShowing = true;
@@ -310,7 +320,8 @@
private UdfpsAnimation getUdfpsAnimationForReason(int reason) {
Log.d(TAG, "getUdfpsAnimationForReason: " + reason);
switch (reason) {
- case IUdfpsOverlayController.REASON_ENROLL:
+ case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
+ case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
return new UdfpsAnimationEnroll(mContext);
case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
return new UdfpsAnimationKeyguard(mView, mContext, mStatusBarStateController);
@@ -326,7 +337,7 @@
mFgExecutor.execute(() -> {
if (mIsOverlayShowing) {
Log.v(TAG, "hideUdfpsOverlay | removing window");
- mView.setUdfpsAnimation(null);
+ mView.setExtras(null, null);
mView.setOnTouchListener(null);
// Reset the controller back to its starting state.
onFingerUp();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
new file mode 100644
index 0000000..2442633
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -0,0 +1,60 @@
+/*
+ * 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.biometrics;
+
+import android.hardware.fingerprint.IUdfpsOverlayController;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Helps keep track of enrollment state and animates the progress bar accordingly.
+ */
+public class UdfpsEnrollHelper {
+ private static final String TAG = "UdfpsEnrollHelper";
+
+ // IUdfpsOverlayController reason
+ private final int mEnrollReason;
+
+ private int mTotalSteps = -1;
+ private int mCurrentProgress = 0;
+
+ public UdfpsEnrollHelper(int reason) {
+ mEnrollReason = reason;
+ }
+
+ boolean shouldShowProgressBar() {
+ return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
+ }
+
+ void onEnrollmentProgress(int remaining, @NonNull UdfpsProgressBar progressBar) {
+ if (mTotalSteps == -1) {
+ mTotalSteps = remaining;
+ }
+
+ mCurrentProgress = progressBar.getMax() * Math.max(0, mTotalSteps + 1 - remaining)
+ / (mTotalSteps + 1);
+ progressBar.setProgress(mCurrentProgress, true /* animate */);
+ }
+
+ void updateProgress(@NonNull UdfpsProgressBar progressBar) {
+ progressBar.setProgress(mCurrentProgress);
+ }
+
+ void onEnrollmentHelp() {
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java
new file mode 100644
index 0000000..84e2fab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java
@@ -0,0 +1,59 @@
+/*
+ * 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.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ProgressBar;
+
+import com.android.systemui.R;
+
+/**
+ * A (determinate) progress bar in the form of a ring. The progress bar goes clockwise starting
+ * from the 12 o'clock position. This view maintain equal width and height using a strategy similar
+ * to "centerInside" for ImageView.
+ */
+public class UdfpsProgressBar extends ProgressBar {
+
+ public UdfpsProgressBar(Context context) {
+ this(context, null);
+ }
+
+ public UdfpsProgressBar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, R.style.UdfpsProgressBarStyle);
+ }
+
+ public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ final int measuredHeight = getMeasuredHeight();
+ final int measuredWidth = getMeasuredWidth();
+
+ final int length = Math.min(measuredHeight, measuredWidth);
+ setMeasuredDimension(length, length);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index d448ed8..00cb28b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -39,14 +39,14 @@
import com.android.systemui.R;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
/**
* A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other
* animations.
*/
public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator,
- StatusBarStateController.StateListener, ScrimController.ScrimChangedListener {
+ StatusBarStateController.StateListener, StatusBar.ExpansionChangedListener {
private static final String TAG = "UdfpsView";
private static final int DEBUG_TEXT_SIZE_PX = 32;
@@ -56,6 +56,8 @@
@NonNull private final RectF mSensorRect;
@NonNull private final Paint mDebugTextPaint;
+ @Nullable private UdfpsProgressBar mProgressBar;
+
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -64,6 +66,7 @@
private boolean mIlluminationRequested;
private int mStatusBarState;
private boolean mNotificationShadeExpanded;
+ @Nullable private UdfpsEnrollHelper mEnrollHelper;
public UdfpsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -108,8 +111,17 @@
mSensorProps = properties;
}
- void setUdfpsAnimation(@Nullable UdfpsAnimation animation) {
+ void setExtras(@Nullable UdfpsAnimation animation, @Nullable UdfpsEnrollHelper enrollHelper) {
mAnimationView.setAnimation(animation);
+ mEnrollHelper = enrollHelper;
+
+ if (enrollHelper != null) {
+ mEnrollHelper.updateProgress(mProgressBar);
+ mProgressBar.setVisibility(enrollHelper.shouldShowProgressBar()
+ ? View.VISIBLE : View.GONE);
+ } else {
+ mProgressBar.setVisibility(View.GONE);
+ }
}
@Override
@@ -133,14 +145,23 @@
}
@Override
- public void onAlphaChanged(float alpha) {
- mAnimationView.onAlphaChanged(alpha);
+ public void onExpansionChanged(float expansion, boolean expanded) {
+ mAnimationView.onExpansionChanged(expansion, expanded);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mProgressBar = findViewById(R.id.progress_bar);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- mSensorRect.set(0, 0, 2 * mSensorProps.sensorRadius, 2 * mSensorProps.sensorRadius);
+ mSensorRect.set(0 + mAnimationView.getPaddingX(),
+ 0 + mAnimationView.getPaddingY(),
+ 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingX(),
+ 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingY());
+
mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect));
mAnimationView.onSensorRectUpdated(new RectF(mSensorRect));
}
@@ -229,10 +250,10 @@
}
void onEnrollmentProgress(int remaining) {
- mAnimationView.onEnrollmentProgress(remaining);
+ mEnrollHelper.onEnrollmentProgress(remaining, mProgressBar);
}
void onEnrollmentHelp() {
- mAnimationView.onEnrollmentHelp();
+
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index fd5e85a..076c7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -16,13 +16,16 @@
package com.android.systemui.biometrics;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.UserManager;
@@ -116,4 +119,10 @@
return false;
}
+
+ static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) {
+ final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+ == PackageManager.PERMISSION_GRANTED;
+ return hasPermission && "android".equals(clientPackage);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
new file mode 100644
index 0000000..8e878cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.controls.ui
+
+import android.app.Dialog
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+import com.android.systemui.broadcast.BroadcastDispatcher
+
+/**
+ * Show the controls space inside a dialog, as from the lock screen.
+ */
+class ControlsDialog(
+ thisContext: Context,
+ val broadcastDispatcher: BroadcastDispatcher
+) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
+
+ private val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.getAction()
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ dismiss()
+ }
+ }
+ }
+
+ init {
+ window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
+
+ setContentView(R.layout.controls_in_dialog)
+
+ requireViewById<ViewGroup>(R.id.control_detail_root).apply {
+ setOnClickListener { dismiss() }
+ (getParent() as View).setOnClickListener { dismiss() }
+ }
+ }
+
+ fun show(
+ controller: ControlsUiController
+ ): ControlsDialog {
+ super.show()
+
+ val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
+ vg.alpha = 0f
+ controller.show(vg, { /* do nothing */ })
+
+ vg.animate()
+ .alpha(1f)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .setDuration(300)
+
+ val filter = IntentFilter()
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+ broadcastDispatcher.registerReceiver(receiver, filter)
+
+ return this
+ }
+
+ override fun dismiss() {
+ broadcastDispatcher.unregisterReceiver(receiver)
+
+ if (!isShowing()) return
+
+ super.dismiss()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 55359ea..e5c9d10 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -18,7 +18,6 @@
import com.android.systemui.LatencyTester;
import com.android.systemui.ScreenDecorations;
-import com.android.systemui.SizeCompatModeActivityController;
import com.android.systemui.SliceBroadcastRelayHandler;
import com.android.systemui.SystemUI;
import com.android.systemui.accessibility.SystemActions;
@@ -113,13 +112,6 @@
@ClassKey(ShortcutKeyDispatcher.class)
public abstract SystemUI bindsShortcutKeyDispatcher(ShortcutKeyDispatcher sysui);
- /** Inject into SizeCompatModeActivityController. */
- @Binds
- @IntoMap
- @ClassKey(SizeCompatModeActivityController.class)
- public abstract SystemUI bindsSizeCompatModeActivityController(
- SizeCompatModeActivityController sysui);
-
/** Inject into SliceBroadcastRelayHandler. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 1b033e9..17f7ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,7 +17,13 @@
package com.android.systemui.keyguard;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+import android.app.ActivityTaskManager;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
@@ -26,8 +32,17 @@
import android.os.IBinder;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -43,6 +58,21 @@
static final String TAG = "KeyguardService";
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
+ /**
+ * Run Keyguard animation as remote animation in System UI instead of local animation in
+ * the server process.
+ *
+ * Note: Must be consistent with WindowManagerService.
+ */
+ private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+ "persist.wm.enable_remote_keyguard_animation";
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ private static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
@@ -52,6 +82,21 @@
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+
+ if (sEnableRemoteKeyguardAnimation) {
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ final RemoteAnimationAdapter exitAnimationAdapter =
+ new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ exitAnimationAdapter);
+ final RemoteAnimationAdapter occludeAnimationAdapter =
+ new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter);
+ ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
+ DEFAULT_DISPLAY, definition);
+ }
}
@Override
@@ -76,6 +121,48 @@
}
}
+ private final IRemoteAnimationRunner.Stub mExitAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override // Binder interface
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
+ checkPermission();
+ mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers,
+ null /* nonApps */, finishedCallback);
+ Trace.endSection();
+ }
+
+ @Override // Binder interface
+ public void onAnimationCancelled() {
+ }
+ };
+
+ private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override // Binder interface
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
+ // run animation.
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+
+ @Override // Binder interface
+ public void onAnimationCancelled() {
+ }
+ };
+
private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
@Override // Binder interface
@@ -225,6 +312,11 @@
mKeyguardViewMediator.onBootCompleted();
}
+ /**
+ * @deprecated When remote animation is enabled, this won't be called anymore. Use
+ * {@code IRemoteAnimationRunner#onAnimationStart} instead.
+ */
+ @Deprecated
@Override
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e732669..5a918d4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -27,6 +27,9 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
@@ -65,8 +68,13 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationTarget;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -85,6 +93,7 @@
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -1318,6 +1327,7 @@
if (mHiding && isOccluded) {
// We're in the process of going away but WindowManager wants to show a
// SHOW_WHEN_LOCKED activity instead.
+ // TODO(bc-unlock): Migrate to remote animation.
startKeyguardExitAnimation(0, 0);
}
@@ -1703,7 +1713,9 @@
Trace.beginSection(
"KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
- handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
+ handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration,
+ params.mApps, params.mWallpapers, params.mNonApps,
+ params.mFinishedCallback);
mFalsingCollector.onSuccessfulUnlock();
Trace.endSection();
break;
@@ -1990,15 +2002,19 @@
if (mShowing && !mOccluded) {
mKeyguardGoingAwayRunnable.run();
} else {
+ // TODO(bc-unlock): Fill parameters
handleStartKeyguardExitAnimation(
SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
- mHideAnimation.getDuration());
+ mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */,
+ null /* nonApps */, null /* finishedCallback */);
}
}
Trace.endSection();
}
- private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+ " fadeoutDuration=" + fadeoutDuration);
@@ -2031,6 +2047,49 @@
mWakeAndUnlocking = false;
mDismissCallbackRegistry.notifyDismissSucceeded();
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
+
+ // TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
+ // apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
+ // supported, so it's always null.
+ mContext.getMainExecutor().execute(() -> {
+ if (finishedCallback == null) {
+ return;
+ }
+
+ // TODO(bc-unlock): Sample animation, just to apply alpha animation on the app.
+ final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(
+ mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+ final RemoteAnimationTarget primary = apps[0];
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.setDuration(400 /* duration */);
+ anim.setInterpolator(Interpolators.LINEAR);
+ anim.addUpdateListener((ValueAnimator animation) -> {
+ SurfaceParams params = new SurfaceParams.Builder(primary.leash)
+ .withAlpha(animation.getAnimatedFraction())
+ .build();
+ applier.scheduleApply(params);
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+ });
+ anim.start();
+ });
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
adjustStatusBarLocked();
@@ -2223,10 +2282,55 @@
return mKeyguardViewControllerLazy.get();
}
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and WindowManager has started running keyguard exit
+ * animation.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+ * @deprecated Will be migrate to remote animation soon.
+ */
+ @Deprecated
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ startKeyguardExitAnimation(0, startTime, fadeoutDuration, null, null, null, null);
+ }
+
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and System UI should start running keyguard exit animation.
+ *
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ public void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ startKeyguardExitAnimation(transit, 0, 0, apps, wallpapers, nonApps, finishedCallback);
+ }
+
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and start running keyguard exit animation.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ private void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+ long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation");
Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
- new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
+ new StartKeyguardExitAnimParams(transit, startTime, fadeoutDuration, apps,
+ wallpapers, nonApps, finishedCallback));
mHandler.sendMessage(msg);
Trace.endSection();
}
@@ -2300,12 +2404,26 @@
private static class StartKeyguardExitAnimParams {
+ @WindowManager.TransitionOldType int mTransit;
long startTime;
long fadeoutDuration;
+ RemoteAnimationTarget[] mApps;
+ RemoteAnimationTarget[] mWallpapers;
+ RemoteAnimationTarget[] mNonApps;
+ IRemoteAnimationFinishedCallback mFinishedCallback;
- private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+ private StartKeyguardExitAnimParams(@WindowManager.TransitionOldType int transit,
+ long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ this.mTransit = transit;
this.startTime = startTime;
this.fadeoutDuration = fadeoutDuration;
+ this.mApps = apps;
+ this.mWallpapers = wallpapers;
+ this.mNonApps = nonApps;
+ this.mFinishedCallback = finishedCallback;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 4384610..dab4d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -666,7 +666,7 @@
}
private void initSecondaryHomeHandleForRotation() {
- if (!canShowSecondaryHandle()) {
+ if (mNavBarMode != NAV_BAR_MODE_GESTURAL) {
return;
}
@@ -1524,7 +1524,7 @@
}
private boolean canShowSecondaryHandle() {
- return mNavBarMode == NAV_BAR_MODE_GESTURAL;
+ return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null;
}
private final Consumer<Integer> mRotationWatcher = rotation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
index 453e85a..517f8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
@@ -51,7 +51,7 @@
* Reload the drawable from resource id, should reapply the previous dark intensity.
*/
public void updateIcon(int lightIconColor, int darkIconColor) {
- if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) {
+ if (mIconResId == 0) {
return;
}
final KeyButtonDrawable currentDrawable = getImageDrawable();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a4e9189..580cbcf 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -24,10 +24,8 @@
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -36,12 +34,9 @@
import android.util.Log;
import android.view.ViewGroup;
-import androidx.preference.PreferenceManager;
-
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import java.util.List;
@@ -128,26 +123,17 @@
/** Stores the user selected configuration for {@code mAppWidgetId}. */
private void storeWidgetConfiguration(PeopleSpaceTile tile) {
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- SharedPreferences.Editor editor = sp.edit();
if (PeopleSpaceUtils.DEBUG) {
Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
+ tile.getId() + " for widget ID: "
+ mAppWidgetId);
}
- // Ensure updates to app widget can be retrieved from both appWidget Id and tile ID.
- editor.putString(String.valueOf(mAppWidgetId), tile.getId());
- editor.putInt(tile.getId(), mAppWidgetId);
- editor.apply();
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
- Bundle options = new Bundle();
- options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, tile);
- appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options);
- int[] widgetIds = appWidgetManager.getAppWidgetIds(
- new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
+
+ PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
+ int[] widgetIds = new int[mAppWidgetId];
// TODO: Populate new widget with existing conversation notification, if there is any.
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
- mNotificationManager);
+ mPeopleManager);
finishActivity();
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 91e7968..994dc6d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -18,6 +18,7 @@
import static android.app.Notification.EXTRA_MESSAGES;
+import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -70,11 +71,13 @@
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -89,6 +92,13 @@
private static final int MIN_HOUR = 1;
private static final int ONE_DAY = 1;
public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
+ public static final String PACKAGE_NAME = "package_name";
+ public static final String USER_ID = "user_id";
+ public static final String SHORTCUT_ID = "shortcut_id";
+
+ public static final String EMPTY_STRING = "";
+ public static final int INVALID_WIDGET_ID = -1;
+ public static final int INVALID_USER_ID = -1;
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
@@ -171,70 +181,174 @@
* notification being posted or removed.
*/
public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds,
- AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
- IPeopleManager peopleManager = IPeopleManager.Stub.asInterface(
- ServiceManager.getService(Context.PEOPLE_SERVICE));
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
- try {
- List<PeopleSpaceTile> tiles =
- PeopleSpaceUtils.getTiles(context, notificationManager,
- peopleManager, launcherApps);
- for (int appWidgetId : appWidgetIds) {
- String shortcutId = sp.getString(String.valueOf(appWidgetId), null);
- if (DEBUG) {
- Log.d(TAG, "Widget ID: " + appWidgetId + " Shortcut ID: " + shortcutId);
- }
-
- Optional<PeopleSpaceTile> entry = tiles.stream().filter(
- e -> e.getId().equals(shortcutId)).findFirst();
-
- if (!entry.isPresent() || shortcutId == null) {
- if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
- //TODO: Delete app widget id when crash is fixed (b/175486868)
- continue;
- }
- // Augment current tile based on stored fields.
- PeopleSpaceTile tile = augmentTileFromStorage(entry.get(), appWidgetManager,
- appWidgetId);
-
- RemoteViews views = createRemoteViews(context, tile, appWidgetId);
-
- // Tell the AppWidgetManager to perform an update on the current app widget.
- appWidgetManager.updateAppWidget(appWidgetId, views);
-
- widgetIdToTile.put(appWidgetId, tile);
+ for (int appWidgetId : appWidgetIds) {
+ PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+ appWidgetId);
+ if (tile == null) {
+ if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
+ //TODO: Delete app widget id when crash is fixed (b/172932636)
+ continue;
}
- } catch (Exception e) {
- Log.e(TAG, "Failed to retrieve conversations to set tiles: " + e);
+
+ if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
+ RemoteViews views = createRemoteViews(context, tile, appWidgetId);
+
+ // Tell the AppWidgetManager to perform an update on the current app widget.
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+
+ widgetIdToTile.put(appWidgetId, tile);
}
getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
}
- /** Augment {@link PeopleSpaceTile} with fields from stored tile. */
- @VisibleForTesting
- static PeopleSpaceTile augmentTileFromStorage(PeopleSpaceTile tile,
- AppWidgetManager appWidgetManager, int appWidgetId) {
- Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
- PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
- if (storedTile == null) {
- return tile;
+ @Nullable
+ private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
+ AppWidgetManager appWidgetManager,
+ Context context, int appWidgetId) {
+ try {
+ // Migrate storage for existing users.
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+ int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
+ if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ shortcutId = sp.getString(String.valueOf(appWidgetId), null);
+ if (shortcutId == null) {
+ Log.e(TAG, "Cannot restore widget");
+ return null;
+ }
+ migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
+ pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+ userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ }
+
+ // Check if tile is cached.
+ Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+ PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ if (tile != null) {
+ return tile;
+ }
+
+ // If tile is null, we need to retrieve from persisted storage.
+ if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots");
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
+ if (channel == null) {
+ Log.d(TAG, "Could not retrieve conversation from storage");
+ return null;
+ }
+ return new PeopleSpaceTile.Builder(channel, launcherApps).build();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+ return null;
}
- return tile.toBuilder()
- .setBirthdayText(storedTile.getBirthdayText())
- .setNotificationKey(storedTile.getNotificationKey())
- .setNotificationContent(storedTile.getNotificationContent())
- .setNotificationDataUri(storedTile.getNotificationDataUri())
- .build();
}
- /** If incoming notification changed tile, store the changes in the tile options. */
- public static void storeNotificationChange(StatusBarNotification sbn,
+ /** Best-effort attempts to migrate existing users to the new storage format. */
+ // TODO: Remove after sufficient time. Temporary migration storage for existing users.
+ private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
+ int appWidgetId) {
+ try {
+ List<PeopleSpaceTile> tiles =
+ PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
+ IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE)),
+ context.getSystemService(LauncherApps.class));
+ Optional<PeopleSpaceTile> entry = tiles.stream().filter(
+ e -> e.getId().equals(shortcutId)).findFirst();
+ if (entry.isPresent()) {
+ if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
+ setStorageForTile(context, entry.get(), appWidgetId);
+ } else {
+ Log.e(TAG, "Could not migrate user");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Could not query conversations");
+ }
+ }
+
+ /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
+ public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
+ // Write relevant persisted storage.
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
+ widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
+ int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+ widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
+ widgetEditor.apply();
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(String.valueOf(appWidgetId), tile.getId());
+ String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
+ // Don't overwrite existing widgets with the same key.
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.add(String.valueOf(appWidgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.apply();
+
+ // Write cached storage.
+ updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
+ tile);
+ }
+
+ /** Removes stored data when tile is deleted. */
+ public static void removeStorageForTile(Context context, int widgetId) {
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ String packageName = widgetSp.getString(PACKAGE_NAME, null);
+ String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+ int userId = widgetSp.getInt(USER_ID, -1);
+
+ // Delete widgetId mapping to key.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sp.edit();
+ String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.remove(String.valueOf(widgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.remove(String.valueOf(widgetId));
+ editor.apply();
+
+ // Delete all data specifically mapped to widgetId.
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.remove(PACKAGE_NAME);
+ widgetEditor.remove(USER_ID);
+ widgetEditor.remove(SHORTCUT_ID);
+ widgetEditor.apply();
+ }
+
+ /**
+ * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
+ * requested update data.
+ */
+ public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
+ String packageName, int userId) {
+ SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
+ int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
+ String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
+ return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
+ && storedUserId == userId;
+ }
+
+ /**
+ * If incoming notification changed tile, store the changes in the tile options.
+ */
+ public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
+ Context context,
+ StatusBarNotification sbn,
NotificationAction notificationAction, AppWidgetManager appWidgetManager,
int appWidgetId) {
- Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
- PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+ appWidgetId);
if (storedTile == null) {
if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
return;
@@ -263,7 +377,7 @@
.setNotificationDataUri(null)
.build();
}
- updateAppWidgetOptions(appWidgetManager, appWidgetId, storedTile);
+ updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
}
private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
@@ -677,4 +791,23 @@
}
return lookupKeysWithBirthdaysToday;
}
+
+ /**
+ * Returns the uniquely identifying key for the conversation.
+ *
+ * <p>{@code userId} will always be a number, so we put user ID as the
+ * delimiter between the app-provided strings of shortcut ID and package name.
+ *
+ * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
+ * a {@code packageName} to always start with a letter. This restriction means we are
+ * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
+ * case is impossible given the package name restrictions:
+ * <ul>
+ * <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
+ * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
+ * </ul>
+ */
+ public static String getKey(String shortcutId, String packageName, int userId) {
+ return shortcutId + "/" + userId + "/" + packageName;
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index ad6046b..bee54e4 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,8 +16,8 @@
package com.android.systemui.people.widget;
-import android.app.INotificationManager;
import android.app.NotificationChannel;
+import android.app.people.IPeopleManager;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +29,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.appwidget.IAppWidgetService;
@@ -37,7 +38,8 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
-import java.util.Objects;
+import java.util.HashSet;
+import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -51,7 +53,7 @@
private final Context mContext;
private IAppWidgetService mAppWidgetService;
private AppWidgetManager mAppWidgetManager;
- private INotificationManager mNotificationManager;
+ private IPeopleManager mPeopleManager;
@Inject
public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
@@ -59,11 +61,13 @@
mContext = context;
mAppWidgetService = appWidgetService;
mAppWidgetManager = AppWidgetManager.getInstance(context);
- mNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mPeopleManager = IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE));
}
- /** Constructor used for testing. */
+ /**
+ * Constructor used for testing.
+ */
@VisibleForTesting
protected PeopleSpaceWidgetManager(Context context) {
if (DEBUG) Log.d(TAG, "constructor");
@@ -72,16 +76,20 @@
ServiceManager.getService(Context.APPWIDGET_SERVICE));
}
- /** AppWidgetManager setter used for testing. */
+ /**
+ * AppWidgetManager setter used for testing.
+ */
@VisibleForTesting
protected void setAppWidgetManager(IAppWidgetService appWidgetService,
- AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
+ AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
mAppWidgetService = appWidgetService;
mAppWidgetManager = appWidgetManager;
- mNotificationManager = notificationManager;
+ mPeopleManager = peopleManager;
}
- /** Updates People Space widgets. */
+ /**
+ * Updates People Space widgets.
+ */
public void updateWidgets() {
try {
if (DEBUG) Log.d(TAG, "updateWidgets called");
@@ -99,7 +107,7 @@
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
- mAppWidgetManager, mNotificationManager);
+ mAppWidgetManager, mPeopleManager);
} else {
mAppWidgetService
.notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
@@ -114,30 +122,38 @@
* Check if any existing People tiles match the incoming notification change, and store the
* change in the tile if so.
*/
- public void storeNotificationChange(StatusBarNotification sbn,
+ public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction) {
- if (DEBUG) Log.d(TAG, "storeNotificationChange called");
+ RemoteViews views = new RemoteViews(
+ mContext.getPackageName(), R.layout.people_space_small_avatar_tile);
+ if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (!showSingleConversation) {
return;
}
try {
+ String sbnShortcutId = sbn.getShortcutId();
+ if (sbnShortcutId == null) {
+ return;
+ }
int[] widgetIds = mAppWidgetService.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
if (widgetIds.length == 0) {
return;
}
-
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- for (int widgetId : widgetIds) {
- String shortcutId = sp.getString(String.valueOf(widgetId), null);
- if (!Objects.equals(sbn.getShortcutId(), shortcutId)) {
- continue;
- }
+ int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
+ String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ if (storedWidgetIds.isEmpty()) {
+ return;
+ }
+ for (String widgetIdString : storedWidgetIds) {
+ int widgetId = Integer.parseInt(widgetIdString);
if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
- PeopleSpaceUtils.storeNotificationChange(
+ PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
sbn, notificationAction, mAppWidgetManager, widgetId);
}
} catch (Exception e) {
@@ -159,8 +175,7 @@
public void onNotificationPosted(
StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted");
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
}
@Override
@@ -169,8 +184,7 @@
NotificationListenerService.RankingMap rankingMap
) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved");
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -179,8 +193,7 @@
NotificationListenerService.RankingMap rankingMap,
int reason) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -207,4 +220,4 @@
}
};
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 7f204cc..f5577d3 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,8 +16,8 @@
package com.android.systemui.people.widget;
-import android.app.INotificationManager;
import android.app.PendingIntent;
+import android.app.people.IPeopleManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
@@ -53,8 +53,8 @@
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
- appWidgetManager, INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE)));
+ appWidgetManager, IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE)));
return;
}
// Perform this loop procedure for each App Widget that belongs to this provider
@@ -91,6 +91,7 @@
for (int widgetId : appWidgetIds) {
if (DEBUG) Log.d(TAG, "Widget removed");
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+ PeopleSpaceUtils.removeStorageForTile(context, widgetId);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index e9207f1..d248ab5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -29,6 +29,8 @@
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.qs.tileimpl.QSTileBaseView;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -87,11 +89,13 @@
private final Executor mExecutor;
private final TunerService mTunerService;
private boolean mShowCollapsedOnKeyguard;
+ private final FeatureFlags mFeatureFlags;
@Inject
public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
- QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService) {
+ QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService,
+ FeatureFlags featureFlags) {
mQs = qs;
mQuickQsPanel = quickPanel;
mQsPanelController = qsPanelController;
@@ -100,6 +104,7 @@
mHost = qsTileHost;
mExecutor = executor;
mTunerService = tunerService;
+ mFeatureFlags = featureFlags;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
qs.getView().addOnLayoutChangeListener(this);
@@ -228,6 +233,7 @@
// Quick tiles.
QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
if (quickTileView == null) continue;
+ View qqsBgCircle = ((QSTileBaseView) quickTileView).getBgCircle();
getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
getRelativePosition(loc2, tileIcon, view);
@@ -249,6 +255,11 @@
translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
+ if (mFeatureFlags.isQSLabelsEnabled()) {
+ firstPageBuilder.addFloat(qqsBgCircle, "alpha", 1, 1, 0);
+ mAllViews.add(qqsBgCircle);
+ }
+
} else { // These tiles disappear when expanding
firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index d0601f0..91ae571 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -777,10 +777,6 @@
updatePadding();
}
- boolean useSideLabels() {
- return mSideLabels;
- }
-
private class H extends Handler {
private static final int SHOW_DETAIL = 1;
private static final int SET_TILE_VISIBILITY = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 30774be..f56a890 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -19,7 +19,6 @@
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_SIDE_LABELS;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import android.annotation.NonNull;
@@ -93,8 +92,7 @@
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSlider.Factory brightnessSliderFactory,
- @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
- @Named(QS_SIDE_LABELS) boolean useSideLabels) {
+ @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager);
mQsSecurityFooter = qsSecurityFooter;
@@ -110,7 +108,7 @@
mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider);
mQsLabelsFlag = qsLabelsFlag;
- mSideLabels = useSideLabels;
+ mSideLabels = qsLabelsFlag;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index e2d7d20..a0db200 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
+import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import com.android.internal.logging.MetricsLogger;
@@ -40,6 +41,8 @@
@QSScope
public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
+ private boolean mUseSideLabels;
+
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
newConfig -> {
int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
@@ -54,9 +57,10 @@
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager) {
+ DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
uiEventLogger, qsLogger, dumpManager);
+ mUseSideLabels = qsLabelsFlag;
}
@Override
@@ -97,7 +101,7 @@
break;
}
}
- if (mView.useSideLabels()) {
+ if (mUseSideLabels) {
List<QSTile> newTiles = new ArrayList<>();
for (int i = 0; i < tiles.size(); i += 2) {
newTiles.add(tiles.get(i));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index ad4c8bb..9ab2d73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -18,7 +18,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Named;
@@ -28,7 +27,6 @@
@Module
public interface QSFlagsModule {
String QS_LABELS_FLAG = "qs_labels_flag";
- String QS_SIDE_LABELS = "qs_side_labels";
@Provides
@SysUISingleton
@@ -36,12 +34,4 @@
static boolean provideQSFlag(FeatureFlags featureFlags) {
return featureFlags.isQSLabelsEnabled();
}
-
- @Provides
- @SysUISingleton
- @Named(QS_SIDE_LABELS)
- static boolean provideSideLabels(SecureSettings secureSettings,
- @Named(QS_LABELS_FLAG) boolean qsLabels) {
- return qsLabels && secureSettings.getInt("sysui_side_labels", 0) != 0;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 6e28cd8..56d06eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -315,7 +315,8 @@
}
try {
if (DEBUG) Log.d(TAG, "Adding token");
- mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY);
+ mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY,
+ null /* options */);
mIsTokenGranted = true;
} catch (RemoteException e) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9e582dd..11e6330 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -14,7 +14,7 @@
package com.android.systemui.qs.tileimpl;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_SIDE_LABELS;
+import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
import android.content.Context;
import android.os.Build;
@@ -98,7 +98,7 @@
@Inject
public QSFactoryImpl(
Lazy<QSHost> qsHostLazy,
- @Named(QS_SIDE_LABELS) boolean useSideLabels,
+ @Named(QS_LABELS_FLAG) boolean useSideLabels,
Provider<CustomTile.Builder> customTileBuilderProvider,
Provider<WifiTile> wifiTileProvider,
Provider<InternetTile> internetTileProvider,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 2dbd2cf..a699e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -36,7 +36,7 @@
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
- private static final int MAX_LABEL_LINES = 2;
+ protected int mMaxLabelLines = 2;
private View mDivider;
protected TextView mLabel;
protected TextView mSecondLine;
@@ -109,10 +109,17 @@
// Remeasure view if the primary label requires more then 2 lines or the secondary label
// text will be cut off.
- if (mLabel.getLineCount() > MAX_LABEL_LINES || !TextUtils.isEmpty(mSecondLine.getText())
+ if (mLabel.getLineCount() > mMaxLabelLines || !TextUtils.isEmpty(mSecondLine.getText())
&& mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
- mLabel.setSingleLine();
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (!mLabel.isSingleLine()) {
+ mLabel.setSingleLine();
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ } else {
+ if (mLabel.isSingleLine()) {
+ mLabel.setSingleLine(false);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 2ef78c2..dc81b702 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -24,7 +24,6 @@
import android.graphics.drawable.RippleDrawable
import android.service.quicksettings.Tile.STATE_ACTIVE
import android.view.Gravity
-import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import com.android.systemui.R
@@ -42,38 +41,23 @@
init {
orientation = HORIZONTAL
- mDualTargetAllowed = true
+ mDualTargetAllowed = false
mBg.setImageDrawable(null)
- createDivider()
mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
+ mMaxLabelLines = 3
}
override fun createLabel() {
super.createLabel()
findViewById<LinearLayout>(R.id.label_group)?.gravity = Gravity.START
mLabel.gravity = Gravity.START
+ mLabel.textDirection = TEXT_DIRECTION_LOCALE
mSecondLine.gravity = Gravity.START
+ mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
- mLabelContainer.setPadding(padding, padding, padding, padding)
- (mLabelContainer.layoutParams as LayoutParams).gravity = Gravity.CENTER_VERTICAL
- }
-
- fun createDivider() {
- divider = LayoutInflater.from(context).inflate(R.layout.qs_tile_label_divider, this, false)
- val position = indexOfChild(mLabelContainer)
- addView(divider, position)
- }
-
- override fun init(
- click: OnClickListener?,
- secondaryClick: OnClickListener?,
- longClick: OnLongClickListener?
- ) {
- super.init(click, secondaryClick, longClick)
- mLabelContainer.setOnClickListener {
- longClick?.onLongClick(it)
- }
- mLabelContainer.isClickable = false
+ mLabelContainer.setPaddingRelative(0, padding, padding, padding)
+ (mLabelContainer.layoutParams as LayoutParams).gravity =
+ Gravity.CENTER_VERTICAL or Gravity.START
}
override fun updateRippleSize() {
@@ -83,7 +67,7 @@
val d = super.newTileBackground()
if (paintDrawable == null) {
paintDrawable = PaintDrawable(Color.WHITE).apply {
- setCornerRadius(30f)
+ setCornerRadius(50f)
}
}
if (d is RippleDrawable) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 1b2ad4c..191b85b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -54,6 +54,9 @@
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.WifiIcons;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
import javax.inject.Inject;
/** Quick settings tile: Internet **/
@@ -65,7 +68,7 @@
protected final NetworkController mController;
private final DataUsageController mDataController;
private final QSTile.SignalState mStateBeforeClick = newTileState();
- // The last updated tile state, 0: mobile, 1: wifi
+ // The last updated tile state, 0: mobile, 1: wifi, 2: ethernet.
private int mLastTileState = -1;
protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
@@ -140,6 +143,21 @@
return string;
}
+ private static final class EthernetCallbackInfo {
+ boolean mConnected;
+ int mEthernetSignalIconId;
+ String mEthernetContentDescription;
+
+ @Override
+ public String toString() {
+ return new StringBuilder("EthernetCallbackInfo[")
+ .append("mConnected=").append(mConnected)
+ .append(",mEthernetSignalIconId=").append(mEthernetSignalIconId)
+ .append(",mEthernetContentDescription=").append(mEthernetContentDescription)
+ .append(']').toString();
+ }
+ }
+
private static final class WifiCallbackInfo {
boolean mAirplaneModeEnabled;
boolean mEnabled;
@@ -212,6 +230,8 @@
protected final class InternetSignalCallback implements SignalCallback {
final WifiCallbackInfo mWifiInfo = new WifiCallbackInfo();
final CellularCallbackInfo mCellularInfo = new CellularCallbackInfo();
+ final EthernetCallbackInfo mEthernetInfo = new EthernetCallbackInfo();
+
@Override
public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
@@ -230,14 +250,12 @@
}
// When airplane mode is enabled, we need to refresh the Internet Tile even if the WiFi
// is not the default network.
- if (qsIcon == null && !mWifiInfo.mAirplaneModeEnabled) {
+ if (qsIcon == null) {
return;
}
- if (qsIcon != null) {
- mWifiInfo.mConnected = qsIcon.visible;
- mWifiInfo.mWifiSignalIconId = qsIcon.icon;
- mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
- }
+ mWifiInfo.mConnected = qsIcon.visible;
+ mWifiInfo.mWifiSignalIconId = qsIcon.icon;
+ mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
mWifiInfo.mEnabled = enabled;
mWifiInfo.mSsid = description;
mWifiInfo.mActivityIn = activityIn;
@@ -287,6 +305,20 @@
}
@Override
+ public void setEthernetIndicators(IconState icon) {
+ if (DEBUG) {
+ Log.d(TAG, "setEthernetIndicators: "
+ + "icon = " + (icon == null ? "" : icon.toString()));
+ }
+ mEthernetInfo.mConnected = icon.visible;
+ mEthernetInfo.mEthernetSignalIconId = icon.icon;
+ mEthernetInfo.mEthernetContentDescription = icon.contentDescription;
+ if (icon.visible) {
+ refreshState(mEthernetInfo);
+ }
+ }
+
+ @Override
public void setNoSims(boolean show, boolean simDetected) {
if (DEBUG) {
Log.d(TAG, "setNoSims: "
@@ -299,7 +331,6 @@
mCellularInfo.mMobileSignalIconId = 0;
mCellularInfo.mQsTypeIcon = 0;
}
- refreshState(mCellularInfo);
}
@Override
@@ -310,7 +341,9 @@
}
mCellularInfo.mAirplaneModeEnabled = icon.visible;
mWifiInfo.mAirplaneModeEnabled = icon.visible;
- refreshState(mCellularInfo);
+ if (!mSignalCallback.mEthernetInfo.mConnected) {
+ refreshState(mCellularInfo);
+ }
}
@Override
@@ -330,6 +363,15 @@
mWifiInfo.mNoNetworksAvailable = noNetworksAvailable;
refreshState(mWifiInfo);
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder("InternetSignalCallback[")
+ .append("mWifiInfo=").append(mWifiInfo)
+ .append(",mCellularInfo=").append(mCellularInfo)
+ .append(",mEthernetInfo=").append(mEthernetInfo)
+ .append(']').toString();
+ }
}
@Override
@@ -340,6 +382,9 @@
} else if (arg instanceof WifiCallbackInfo) {
mLastTileState = 1;
handleUpdateWifiState(state, arg);
+ } else if (arg instanceof EthernetCallbackInfo) {
+ mLastTileState = 2;
+ handleUpdateEthernetState(state, arg);
} else {
// handleUpdateState will be triggered when user expands the QuickSetting panel with
// arg = null, in this case the last updated CellularCallbackInfo or WifiCallbackInfo
@@ -348,6 +393,8 @@
handleUpdateCellularState(state, mSignalCallback.mCellularInfo);
} else if (mLastTileState == 1) {
handleUpdateWifiState(state, mSignalCallback.mWifiInfo);
+ } else if (mLastTileState == 2) {
+ handleUpdateEthernetState(state, mSignalCallback.mEthernetInfo);
}
}
}
@@ -440,7 +487,6 @@
Log.d(TAG, "handleUpdateCellularState: " + "CellularCallbackInfo = " + cb.toString());
}
final Resources r = mContext.getResources();
- // TODO(b/174753536): Use the new "Internet" string as state.label once available.
state.label = r.getString(R.string.quick_settings_internet_label);
state.state = Tile.STATE_ACTIVE;
boolean mobileDataEnabled = mDataController.isMobileDataSupported()
@@ -478,6 +524,18 @@
}
}
+ private void handleUpdateEthernetState(SignalState state, Object arg) {
+ EthernetCallbackInfo cb = (EthernetCallbackInfo) arg;
+ if (DEBUG) {
+ Log.d(TAG, "handleUpdateEthernetState: " + "EthernetCallbackInfo = " + cb.toString());
+ }
+ final Resources r = mContext.getResources();
+ state.label = r.getString(R.string.quick_settings_internet_label);
+ state.state = Tile.STATE_ACTIVE;
+ state.icon = ResourceIcon.get(cb.mEthernetSignalIconId);
+ state.secondaryLabel = cb.mEthernetContentDescription;
+ }
+
private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
if (TextUtils.isEmpty(dataType)) {
return Html.fromHtml((current == null ? "" : current.toString()), 0);
@@ -519,4 +577,15 @@
return d;
}
}
+
+ /**
+ * Dumps the state of this tile along with its name.
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(this.getClass().getSimpleName() + ":");
+ pw.print(" "); pw.println(getState().toString());
+ pw.print(" "); pw.println("mLastTileState=" + mLastTileState);
+ pw.print(" "); pw.println("mSignalCallback=" + mSignalCallback.toString());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 760ebad..a9f76f6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -35,6 +35,7 @@
import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -83,6 +84,7 @@
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.ISplitScreenListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -98,6 +100,7 @@
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.transition.Transitions;
import java.io.FileDescriptor;
@@ -133,7 +136,8 @@
private final Context mContext;
private final Optional<Pip> mPipOptional;
private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
- private final Optional<LegacySplitScreen> mSplitScreenOptional;
+ private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+ private final Optional<SplitScreen> mSplitScreenOptional;
private SysUiState mSysUiState;
private final Handler mHandler;
private final Lazy<NavigationBarController> mNavBarControllerLazy;
@@ -263,7 +267,7 @@
}
final long token = Binder.clearCallingIdentity();
try {
- return mSplitScreenOptional.map(splitScreen ->
+ return mLegacySplitScreenOptional.map(splitScreen ->
splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds())
.orElse(null);
} finally {
@@ -401,7 +405,7 @@
@Override
public void setSplitScreenMinimized(boolean minimized) {
- mSplitScreenOptional.ifPresent(
+ mLegacySplitScreenOptional.ifPresent(
splitScreen -> splitScreen.setMinimized(minimized));
}
@@ -559,6 +563,105 @@
}
}
+ @Override
+ public void registerSplitScreenListener(ISplitScreenListener listener) {
+ if (!verifyCaller("registerSplitScreenListener")) {
+ return;
+ }
+ mISplitScreenListener = listener;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(
+ s -> s.registerSplitScreenListener(mSplitScreenListener));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterSplitScreenListener(ISplitScreenListener listener) {
+ if (!verifyCaller("unregisterSplitScreenListener")) {
+ return;
+ }
+ mISplitScreenListener = null;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(
+ s -> s.unregisterSplitScreenListener(mSplitScreenListener));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setSideStageVisibility(boolean visible) {
+ if (!verifyCaller("setSideStageVisibility")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(s -> s.setSideStageVisibility(visible));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void exitSplitScreen() {
+ if (!verifyCaller("exitSplitScreen")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(s -> s.exitSplitScreen());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void startTask(int taskId, int stage, int position, Bundle options) {
+ if (!verifyCaller("startTask")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(
+ s -> s.startTask(taskId, stage, position, options));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ Bundle options, UserHandle user) {
+ if (!verifyCaller("startShortcut")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(s ->
+ s.startShortcut(packageName, shortcutId, stage, position, options, user));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void startIntent(PendingIntent intent, int stage, int position, Bundle options) {
+ if (!verifyCaller("startIntent")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(s ->
+ s.startIntent(intent, stage, position, options));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -658,6 +761,32 @@
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
= this::cleanupAfterDeath;
+ private ISplitScreenListener mISplitScreenListener;
+ private final SplitScreen.SplitScreenListener mSplitScreenListener =
+ new SplitScreen.SplitScreenListener() {
+ @Override
+ public void onStagePositionChanged(int stage, int position) {
+ try {
+ if (mISplitScreenListener != null) {
+ mISplitScreenListener.onStagePositionChanged(stage, position);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "onStagePositionChanged", e);
+ }
+ }
+
+ @Override
+ public void onTaskStageChanged(int taskId, int stage) {
+ try {
+ if (mISplitScreenListener != null) {
+ mISplitScreenListener.onTaskStageChanged(taskId, stage);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "onTaskStageChanged", e);
+ }
+ }
+ };
+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
public OverviewProxyService(Context context, CommandQueue commandQueue,
@@ -665,7 +794,8 @@
NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
Optional<Pip> pipOptional,
- Optional<LegacySplitScreen> splitScreenOptional,
+ Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<SplitScreen> splitScreenOptional,
Optional<Lazy<StatusBar>> statusBarOptionalLazy,
Optional<OneHanded> oneHandedOptional,
BroadcastDispatcher broadcastDispatcher,
@@ -718,9 +848,10 @@
});
mCommandQueue = commandQueue;
- splitScreenOptional.ifPresent(splitScreen ->
- splitScreen.registerBoundsChangeListener(mSplitScreenBoundsChangeListener));
mSplitScreenOptional = splitScreenOptional;
+ legacySplitScreenOptional.ifPresent(splitScreen ->
+ splitScreen.registerBoundsChangeListener(mSplitScreenBoundsChangeListener));
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
// Listen for user setup
startTracking();
@@ -835,7 +966,7 @@
startConnectionToCurrentUser();
// Clean up the minimized state if launcher dies
- mSplitScreenOptional.ifPresent(
+ mLegacySplitScreenOptional.ifPresent(
splitScreen -> splitScreen.setMinimized(false));
// Clean up any registered remote transitions
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 9037192..5438743 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -187,7 +187,7 @@
* @param refreshRate Desired refresh rate
* @return array with supported width, height, and refresh rate
*/
- private int[] getSupportedSize(int screenWidth, int screenHeight, int refreshRate) {
+ private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate) {
double maxScale = 0;
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
@@ -207,25 +207,33 @@
int width = vc.getSupportedWidths().getUpper();
int height = vc.getSupportedHeights().getUpper();
- if (width >= screenWidth && height >= screenHeight
- && vc.isSizeSupported(screenWidth, screenHeight)) {
+ int screenWidthAligned = screenWidth;
+ if (screenWidthAligned % vc.getWidthAlignment() != 0) {
+ screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment());
+ }
+ int screenHeightAligned = screenHeight;
+ if (screenHeightAligned % vc.getHeightAlignment() != 0) {
+ screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment());
+ }
+ if (width >= screenWidthAligned && height >= screenHeightAligned
+ && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) {
// Desired size is supported, now get the rate
- int maxRate = vc.getSupportedFrameRatesFor(screenWidth, screenHeight)
- .getUpper().intValue();
+ int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned,
+ screenHeightAligned).getUpper().intValue();
if (maxRate < refreshRate) {
refreshRate = maxRate;
}
Log.d(TAG, "Screen size supported at rate " + refreshRate);
- return new int[]{screenWidth, screenHeight, refreshRate};
+ return new int[]{screenWidthAligned, screenHeightAligned, refreshRate};
}
// Otherwise, continue searching
double scale = Math.min(((double) width / screenWidth),
((double) height / screenHeight));
if (scale > maxScale) {
- maxScale = scale;
+ maxScale = Math.min(1, scale);
maxInfo = vc;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 8e182b4..c8afd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -35,7 +35,7 @@
* cropped out.
*/
public class CropView extends View {
- private enum CropBoundary {
+ public enum CropBoundary {
NONE, TOP, BOTTOM
}
@@ -48,8 +48,14 @@
private float mTopCrop = 0f;
private float mBottomCrop = 1f;
+ // When the user is dragging a handle, these variables store the distance between the top/bottom
+ // crop values and
+ private float mTopDelta = 0f;
+ private float mBottomDelta = 0f;
+
private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE;
- private float mLastY;
+ private float mStartingY; // y coordinate of ACTION_DOWN
+ private CropInteractionListener mCropInteractionListener;
public CropView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
@@ -73,54 +79,84 @@
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- drawShade(canvas, 0, mTopCrop);
- drawShade(canvas, mBottomCrop, 1f);
- drawHandle(canvas, mTopCrop);
- drawHandle(canvas, mBottomCrop);
+ float top = mTopCrop + mTopDelta;
+ float bottom = mBottomCrop + mBottomDelta;
+ drawShade(canvas, 0, top);
+ drawShade(canvas, bottom, 1f);
+ drawHandle(canvas, top);
+ drawHandle(canvas, bottom);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int topPx = fractionToPixels(mTopCrop);
int bottomPx = fractionToPixels(mBottomCrop);
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
- if (mCurrentDraggingBoundary != CropBoundary.NONE) {
- mLastY = event.getY();
- }
- return true;
- }
- if (event.getAction() == MotionEvent.ACTION_MOVE
- && mCurrentDraggingBoundary != CropBoundary.NONE) {
- float delta = event.getY() - mLastY;
- if (mCurrentDraggingBoundary == CropBoundary.TOP) {
- mTopCrop = pixelsToFraction((int) MathUtils.constrain(topPx + delta, 0,
- bottomPx - 2 * mCropTouchMargin));
- } else { // Bottom
- mBottomCrop = pixelsToFraction((int) MathUtils.constrain(bottomPx + delta,
- topPx + 2 * mCropTouchMargin, getMeasuredHeight()));
- }
- mLastY = event.getY();
- invalidate();
- return true;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ mStartingY = event.getY();
+ updateListener(event);
+ }
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ float delta = event.getY() - mStartingY;
+ if (mCurrentDraggingBoundary == CropBoundary.TOP) {
+ mTopDelta = pixelsToFraction((int) MathUtils.constrain(delta, -topPx,
+ bottomPx - 2 * mCropTouchMargin - topPx));
+ } else { // Bottom
+ mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta,
+ topPx + 2 * mCropTouchMargin - bottomPx,
+ getMeasuredHeight() - bottomPx));
+ }
+ updateListener(event);
+ invalidate();
+ return true;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ // Commit the delta to the stored crop values.
+ mTopCrop += mTopDelta;
+ mBottomCrop += mBottomDelta;
+ mTopDelta = 0;
+ mBottomDelta = 0;
+ updateListener(event);
+ }
}
return super.onTouchEvent(event);
}
/**
- * @return value [0,1] representing the position of the top crop boundary.
+ * @return value [0,1] representing the position of the top crop boundary. Does not reflect
+ * changes from any in-progress touch input.
*/
public float getTopBoundary() {
return mTopCrop;
}
/**
- * @return value [0,1] representing the position of the bottom crop boundary.
+ * @return value [0,1] representing the position of the bottom crop boundary. Does not reflect
+ * changes from any in-progress touch input.
*/
public float getBottomBoundary() {
return mBottomCrop;
}
+ public void setCropInteractionListener(CropInteractionListener listener) {
+ mCropInteractionListener = listener;
+ }
+
+ private void updateListener(MotionEvent event) {
+ if (mCropInteractionListener != null) {
+ float boundaryPosition = (mCurrentDraggingBoundary == CropBoundary.TOP)
+ ? mTopCrop + mTopDelta : mBottomCrop + mBottomDelta;
+ mCropInteractionListener.onCropMotionEvent(event, mCurrentDraggingBoundary,
+ boundaryPosition, fractionToPixels(boundaryPosition));
+ }
+ }
+
private void drawShade(Canvas canvas, float fracStart, float fracEnd) {
canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(),
fractionToPixels(fracEnd), mShadePaint);
@@ -148,4 +184,17 @@
}
return CropBoundary.NONE;
}
+
+ /**
+ * Listen for crop motion events and state.
+ */
+ public interface CropInteractionListener {
+ /**
+ * Called whenever CropView has a MotionEvent that can impact the position of the crop
+ * boundaries.
+ */
+ void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition,
+ int boundaryPositionPx);
+
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 8ff66f5..20f8451 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -108,6 +108,15 @@
}
Bitmap toBitmap() {
+ return toBitmap(new Rect(0, 0, getWidth(), getHeight()));
+ }
+
+ /**
+ * @param bounds Selected portion of the tile set's bounds (equivalent to tile bounds coord
+ * space). For example, to get the whole doc, use Rect(0, 0, getWidth(),
+ * getHeight()).
+ */
+ Bitmap toBitmap(Rect bounds) {
if (mTiles.isEmpty()) {
return null;
}
@@ -115,6 +124,9 @@
output.setPosition(0, 0, getWidth(), getHeight());
RecordingCanvas canvas = output.beginRecording();
canvas.translate(-getLeft(), -getTop());
+ // Additional translation to account for the requested bounds
+ canvas.translate(-bounds.left, -bounds.top);
+ canvas.clipRect(bounds);
for (ImageTile tile : mTiles) {
canvas.save();
canvas.translate(tile.getLeft(), tile.getTop());
@@ -122,7 +134,7 @@
canvas.restore();
}
output.endRecording();
- return HardwareRenderer.createHardwareBitmap(output, getWidth(), getHeight());
+ return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
}
int getLeft() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
new file mode 100644
index 0000000..f887151
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
@@ -0,0 +1,184 @@
+/*
+ * 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.screenshot;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * MagnifierView shows a full-res cropped circular display of a given ImageTileSet, contents and
+ * positioning dereived from events from a CropView to which it listens.
+ *
+ * Not meant to be a general-purpose magnifier!
+ */
+public class MagnifierView extends View implements CropView.CropInteractionListener {
+ private Drawable mDrawable;
+
+ private final Paint mShadePaint;
+ private final Paint mHandlePaint;
+
+ private Path mOuterCircle;
+ private Path mInnerCircle;
+
+ private Path mCheckerboard;
+ private Paint mCheckerboardPaint;
+ private final float mBorderPx;
+ private final int mBorderColor;
+ private float mCheckerboardBoxSize = 40;
+
+ private float mLastCropPosition;
+ private CropView.CropBoundary mCropBoundary;
+
+ public MagnifierView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public MagnifierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ TypedArray t = context.getTheme().obtainStyledAttributes(
+ attrs, R.styleable.MagnifierView, 0, 0);
+ mShadePaint = new Paint();
+ mShadePaint.setColor(t.getColor(R.styleable.MagnifierView_scrimColor, Color.TRANSPARENT));
+ mHandlePaint = new Paint();
+ mHandlePaint.setColor(t.getColor(R.styleable.MagnifierView_handleColor, Color.BLACK));
+ mHandlePaint.setStrokeWidth(
+ t.getDimensionPixelSize(R.styleable.MagnifierView_handleThickness, 20));
+ mBorderPx = t.getDimensionPixelSize(R.styleable.MagnifierView_borderThickness, 0);
+ mBorderColor = t.getColor(R.styleable.MagnifierView_borderColor, Color.WHITE);
+ t.recycle();
+ mCheckerboardPaint = new Paint();
+ mCheckerboardPaint.setColor(Color.GRAY);
+ }
+
+ public void setImageTileset(ImageTileSet tiles) {
+ if (tiles != null) {
+ mDrawable = tiles.getDrawable();
+ mDrawable.setBounds(0, 0, tiles.getWidth(), tiles.getHeight());
+ } else {
+ mDrawable = null;
+ }
+ invalidate();
+ }
+
+ @Override
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ int radius = getWidth() / 2;
+ mOuterCircle = new Path();
+ mOuterCircle.addCircle(radius, radius, radius, Path.Direction.CW);
+ mInnerCircle = new Path();
+ mInnerCircle.addCircle(radius, radius, radius - mBorderPx, Path.Direction.CW);
+ mCheckerboard = generateCheckerboard();
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // TODO: just draw a circle at the end instead of clipping like this?
+ canvas.clipPath(mOuterCircle);
+ canvas.drawColor(mBorderColor);
+ canvas.clipPath(mInnerCircle);
+
+ // Draw a checkerboard pattern for out of bounds.
+ canvas.drawPath(mCheckerboard, mCheckerboardPaint);
+
+ if (mDrawable != null) {
+ canvas.save();
+ // Translate such that the center of this view represents the center of the crop
+ // boundary.
+ canvas.translate(-mDrawable.getBounds().width() / 2 + getWidth() / 2,
+ -mDrawable.getBounds().height() * mLastCropPosition + getHeight() / 2);
+ mDrawable.draw(canvas);
+ canvas.restore();
+ }
+
+ Rect scrimRect = new Rect(0, 0, getWidth(), getHeight() / 2);
+ if (mCropBoundary == CropView.CropBoundary.BOTTOM) {
+ scrimRect.offset(0, getHeight() / 2);
+ }
+ canvas.drawRect(scrimRect, mShadePaint);
+
+ canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mHandlePaint);
+ }
+
+ @Override
+ public void onCropMotionEvent(MotionEvent event, CropView.CropBoundary boundary,
+ float cropPosition, int cropPositionPx) {
+ mCropBoundary = boundary;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLastCropPosition = cropPosition;
+ setTranslationY(cropPositionPx - getHeight() / 2);
+ setPivotX(getWidth() / 2);
+ setPivotY(getHeight() / 2);
+ setScaleX(0.2f);
+ setScaleY(0.2f);
+ setAlpha(0f);
+ setTranslationX((getParentWidth() - getWidth()) / 2);
+ setVisibility(View.VISIBLE);
+ animate().alpha(1f).translationX(0).scaleX(1f).scaleY(1f).start();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mLastCropPosition = cropPosition;
+ setTranslationY(cropPositionPx - getHeight() / 2);
+ invalidate();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ animate().alpha(0).translationX((getParentWidth() - getWidth()) / 2).scaleX(0.2f)
+ .scaleY(0.2f).withEndAction(() -> setVisibility(View.INVISIBLE)).start();
+ break;
+ }
+ }
+
+ private Path generateCheckerboard() {
+ Path path = new Path();
+ int checkerWidth = (int) Math.ceil(getWidth() / mCheckerboardBoxSize);
+ int checkerHeight = (int) Math.ceil(getHeight() / mCheckerboardBoxSize);
+
+ for (int row = 0; row < checkerHeight; row++) {
+ // Alternate starting on the first and second column;
+ int colStart = (row % 2 == 0) ? 0 : 1;
+ for (int col = colStart; col < checkerWidth; col += 2) {
+ path.addRect(col * mCheckerboardBoxSize,
+ row * mCheckerboardBoxSize,
+ (col + 1) * mCheckerboardBoxSize,
+ (row + 1) * mCheckerboardBoxSize,
+ Path.Direction.CW);
+ }
+ }
+ return path;
+ }
+
+ private int getParentWidth() {
+ return ((View) getParent()).getWidth();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 0a7eea4..953b40b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -388,12 +388,6 @@
}
});
- // ignore system bar insets for the purpose of window layout
- mScreenshotView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
- new WindowInsets.Builder(insets)
- .setInsets(WindowInsets.Type.all(), Insets.NONE)
- .build()));
-
// TODO(159460485): Remove this when focus is handled properly in the system
mScreenshotView.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
@@ -533,9 +527,6 @@
attachWindow();
- if (DEBUG_WINDOW) {
- Log.d(TAG, "setContentView: " + mScreenshotView);
- }
mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -549,7 +540,13 @@
}
});
mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setContentView: " + mScreenshotView);
+ }
setContentView(mScreenshotView);
+ // ignore system bar insets for the purpose of window layout
+ mWindow.getDecorView().setOnApplyWindowInsetsListener(
+ (v, insets) -> WindowInsets.CONSUMED);
cancelTimeout(); // restarted after animation
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 3bc5ebf..5c650ad 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -325,8 +325,6 @@
Log.d(TAG, "createAnim: bounds=" + bounds + " showFlash=" + showFlash);
}
- Rect previewBounds = new Rect();
- mScreenshotPreview.getBoundsOnScreen(previewBounds);
Rect targetPosition = new Rect();
mScreenshotPreview.getHitRect(targetPosition);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index fd7db4b..25438a6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -51,6 +52,13 @@
public class ScrollCaptureController implements OnComputeInternalInsetsListener {
private static final String TAG = "ScrollCaptureController";
+ // TODO: Support saving without additional action.
+ private enum PendingAction {
+ SHARE,
+ EDIT,
+ SAVE
+ }
+
public static final int MAX_PAGES = 5;
public static final int MAX_HEIGHT = 12000;
@@ -68,12 +76,12 @@
private RequestCallback mCallback;
private Window mWindow;
private ImageView mPreview;
- private View mClose;
+ private View mSave;
+ private View mCancel;
private View mEdit;
private View mShare;
-
- private ListenableFuture<ImageExporter.Result> mExportFuture;
- private Runnable mPendingAction;
+ private CropView mCropView;
+ private MagnifierView mMagnifierView;
public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
@@ -108,15 +116,19 @@
.addOnComputeInternalInsetsListener(this);
mPreview = findViewById(R.id.preview);
- mClose = findViewById(R.id.close);
+ mSave = findViewById(R.id.save);
+ mCancel = findViewById(R.id.cancel);
mEdit = findViewById(R.id.edit);
mShare = findViewById(R.id.share);
+ mCropView = findViewById(R.id.crop_view);
+ mMagnifierView = findViewById(R.id.magnifier);
+ mCropView.setCropInteractionListener(mMagnifierView);
- mClose.setOnClickListener(this::onClicked);
+ mSave.setOnClickListener(this::onClicked);
+ mCancel.setOnClickListener(this::onClicked);
mEdit.setOnClickListener(this::onClicked);
mShare.setOnClickListener(this::onClicked);
- //mPreview.setImageDrawable(mImageTileSet.getDrawable());
mConnection.start(this::startCapture);
}
@@ -127,7 +139,8 @@
}
void disableButtons() {
- mClose.setEnabled(false);
+ mSave.setEnabled(false);
+ mCancel.setEnabled(false);
mEdit.setEnabled(false);
mShare.setEnabled(false);
}
@@ -136,64 +149,79 @@
Log.d(TAG, "button clicked!");
int id = v.getId();
- if (id == R.id.close) {
- v.setPressed(true);
- disableButtons();
- finish();
+ v.setPressed(true);
+ disableButtons();
+ if (id == R.id.save) {
+ startExport(PendingAction.SAVE);
+ } else if (id == R.id.cancel) {
+ doFinish();
} else if (id == R.id.edit) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT);
- v.setPressed(true);
- disableButtons();
- edit();
+ startExport(PendingAction.EDIT);
} else if (id == R.id.share) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE);
- v.setPressed(true);
- disableButtons();
- share();
- }
- }
-
- private void finish() {
- if (mExportFuture == null) {
- doFinish();
- } else {
- mExportFuture.addListener(this::doFinish, mUiExecutor);
+ startExport(PendingAction.SHARE);
}
}
private void doFinish() {
mPreview.setImageDrawable(null);
+ mMagnifierView.setImageTileset(null);
mImageTileSet.clear();
mCallback.onFinish();
mWindow.getDecorView().getViewTreeObserver()
.removeOnComputeInternalInsetsListener(this);
}
- private void edit() {
- String editorPackage = mContext.getString(R.string.config_screenshotEditor);
- sendIntentWhenReady(Intent.ACTION_EDIT, editorPackage);
- }
-
- private void share() {
- sendIntentWhenReady(Intent.ACTION_SEND, null);
- }
-
- void sendIntentWhenReady(String action, String component) {
- if (mExportFuture != null) {
- mExportFuture.addListener(() -> {
- try {
- ImageExporter.Result result = mExportFuture.get();
- sendIntent(action, component, result.uri);
- mCallback.onFinish();
- } catch (InterruptedException | ExecutionException e) {
- Log.e(TAG, "failed to export", e);
- mCallback.onFinish();
+ private void startExport(PendingAction action) {
+ Rect croppedPortion = new Rect(
+ 0,
+ (int) (mImageTileSet.getHeight() * mCropView.getTopBoundary()),
+ mImageTileSet.getWidth(),
+ (int) (mImageTileSet.getHeight() * mCropView.getBottomBoundary()));
+ ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
+ mBgExecutor, mRequestId, mImageTileSet.toBitmap(croppedPortion), mCaptureTime);
+ exportFuture.addListener(() -> {
+ try {
+ ImageExporter.Result result = exportFuture.get();
+ if (action == PendingAction.EDIT) {
+ doEdit(result.uri);
+ } else if (action == PendingAction.SHARE) {
+ doShare(result.uri);
}
+ doFinish();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "failed to export", e);
+ mCallback.onFinish();
+ }
+ }, mUiExecutor);
+ }
- }, mUiExecutor);
- } else {
- mPendingAction = this::edit;
+ private void doEdit(Uri uri) {
+ String editorPackage = mContext.getString(R.string.config_screenshotEditor);
+ Intent intent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ intent.setComponent(ComponentName.unflattenFromString(editorPackage));
}
+ intent.setType("image/png");
+ intent.setData(uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+
+ private void doShare(Uri uri) {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("image/png");
+ intent.setData(uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ Intent sharingChooserIntent = Intent.createChooser(intent, null)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
}
private void setContentView(@IdRes int id) {
@@ -248,25 +276,7 @@
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
- if (mPendingAction != null) {
- mExportFuture.addListener(mPendingAction, mUiExecutor);
- }
+ mMagnifierView.setImageTileset(mImageTileSet);
}
}
-
- void sendIntent(String action, String component, Uri uri) {
- Intent intent = new Intent(action);
- if (!TextUtils.isEmpty(component)) {
- intent.setComponent(ComponentName.unflattenFromString(component));
- }
- intent.setType("image/png");
- intent.setData(uri);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
index 8dcc8b4..3c7d78c 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
@@ -18,7 +18,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
@@ -30,25 +29,21 @@
private static final String THICK_BRIGHTNESS_SLIDER = "sysui_thick_brightness";
private final FeatureFlags mFeatureFlags;
- private final boolean mUseThickSlider;
- private final boolean mUseMirrorOnThickSlider;
@Inject
- public BrightnessControllerSettings(SecureSettings settings, FeatureFlags featureFlags) {
+ public BrightnessControllerSettings(FeatureFlags featureFlags) {
mFeatureFlags = featureFlags;
- mUseThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 0;
- mUseMirrorOnThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 2;
}
// Changing this setting between zero and non-zero may crash systemui down the line. Better to
// restart systemui after changing it.
/** */
boolean useThickSlider() {
- return mUseThickSlider && mFeatureFlags.useNewBrightnessSlider();
+ return mFeatureFlags.useNewBrightnessSlider();
}
/** */
boolean useMirrorOnThickSlider() {
- return !useThickSlider() || (useThickSlider() && mUseMirrorOnThickSlider);
+ return !useThickSlider();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index 53ff1df..a6aec3b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -17,7 +17,6 @@
package com.android.systemui.settings.brightness;
import android.content.Context;
-import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.view.LayoutInflater;
@@ -33,6 +32,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.util.RoundedCornerProgressDrawable;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -292,8 +292,8 @@
if (b.getProgressDrawable() instanceof LayerDrawable) {
Drawable progress = ((LayerDrawable) b.getProgressDrawable())
.findDrawableByLayerId(com.android.internal.R.id.progress);
- if (progress instanceof ClipDrawable) {
- Drawable inner = ((ClipDrawable) progress).getDrawable();
+ if (progress instanceof RoundedCornerProgressDrawable) {
+ Drawable inner = ((RoundedCornerProgressDrawable) progress).getDrawable();
if (inner instanceof LayerDrawable) {
return (LayerDrawable) inner;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 88b9c6c..d08f973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -33,6 +33,7 @@
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
+import android.view.WindowManager;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -159,8 +160,10 @@
}
@Override
- public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] remoteAnimationTargets,
RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
+ RemoteAnimationTarget[] remoteAnimationNonAppTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
mMainExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 93156d8..0957f78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -69,6 +69,8 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
+import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
@@ -89,6 +91,10 @@
*/
@Module(includes = { NotificationSectionHeadersModule.class })
public interface NotificationsModule {
+ @Binds
+ StackScrollAlgorithm.SectionProvider bindSectionProvider(
+ NotificationSectionsManager impl);
+
/** Provides an instance of {@link NotificationEntryManager} */
@SysUISingleton
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index a12179c..756fe6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -22,6 +22,7 @@
import android.util.MathUtils;
import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -30,9 +31,12 @@
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider;
+import javax.inject.Inject;
+
/**
* A global state to track all input states for the algorithm.
*/
+@SysUISingleton
public class AmbientState {
private static final float MAX_PULSE_HEIGHT = 100000f;
@@ -83,6 +87,7 @@
/** Tracks the state from AlertingNotificationManager#hasNotifications() */
private boolean mHasAlertEntries;
+ @Inject
public AmbientState(
Context context,
@NonNull SectionProvider sectionProvider) {
@@ -98,7 +103,7 @@
mBaseZHeight = getBaseHeight(mZDistanceBetweenElements);
}
- void setIsShadeOpening(boolean isOpening) {
+ public void setIsShadeOpening(boolean isOpening) {
mIsShadeOpening = isOpening;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 3f3be44..2c7c5cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -491,7 +491,8 @@
NotificationSectionsManager notificationSectionsManager,
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
- SysuiStatusBarStateController statusbarStateController
+ SysuiStatusBarStateController statusbarStateController,
+ AmbientState ambientState
) {
super(context, attrs, 0, 0);
Resources res = getResources();
@@ -500,7 +501,7 @@
mSectionsManager.initialize(this, LayoutInflater.from(context));
mSections = mSectionsManager.createSectionsForBuckets();
- mAmbientState = new AmbientState(context, mSectionsManager);
+ mAmbientState = ambientState;
mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
.getDefaultColor();
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
@@ -549,10 +550,6 @@
}
}
- void setIsShadeOpening(boolean isOpening) {
- mAmbientState.setIsShadeOpening(isOpening);
- }
-
void setSectionPadding(float margin) {
mAmbientState.setSectionPadding(margin);
requestChildrenUpdate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 879dad8..a516742 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -45,7 +45,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
-import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
@@ -271,10 +270,6 @@
}
};
- public void setIsShadeOpening(boolean isOpening) {
- mView.setIsShadeOpening(isOpening);
- }
-
public void setSectionPadding(float padding) {
mView.setSectionPadding(padding);
}
@@ -851,11 +846,14 @@
return mView.getChildAtRawPosition(x, y);
}
- public FrameLayout.LayoutParams getLayoutParams() {
- return (FrameLayout.LayoutParams) mView.getLayoutParams();
+ public ViewGroup.LayoutParams getLayoutParams() {
+ return mView.getLayoutParams();
}
- public void setLayoutParams(FrameLayout.LayoutParams lp) {
+ /**
+ * Updates layout parameters on the root view
+ */
+ public void setLayoutParams(ViewGroup.LayoutParams lp) {
mView.setLayoutParams(lp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e6efba7..5d2203b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -286,8 +286,7 @@
float currentYPosition = -algorithmState.scrollY;
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
- currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition,
- false /* reverse */);
+ currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
}
}
@@ -301,10 +300,6 @@
* @param currentYPosition The Y position of the current pass of the algorithm. For a forward
* pass, this should be the top of the child; for a reverse pass, the
* bottom of the child.
- * @param reverse Whether we're laying out children in the reverse direction (Y
- * positions
- * decreasing) instead of the forward direction (Y positions
- * increasing).
* @return The Y position after laying out the child. This will be the {@code currentYPosition}
* for the next call to this method, after adjusting for any gaps between children.
*/
@@ -312,8 +307,7 @@
int i,
StackScrollAlgorithmState algorithmState,
AmbientState ambientState,
- float currentYPosition,
- boolean reverse) {
+ float currentYPosition) {
ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
final boolean applyGapHeight =
@@ -323,20 +317,12 @@
ExpandableViewState childViewState = child.getViewState();
childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
- if (applyGapHeight && !reverse) {
+ if (applyGapHeight) {
currentYPosition += mGapHeight;
}
-
int childHeight = getMaxAllowedChildHeight(child);
- if (reverse) {
- childViewState.yTranslation = currentYPosition
- - (childHeight + mPaddingBetweenElements);
- if (currentYPosition <= 0) {
- childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
- }
- } else {
- childViewState.yTranslation = currentYPosition;
- }
+ childViewState.yTranslation = currentYPosition;
+
boolean isFooterView = child instanceof FooterView;
boolean isEmptyShadeView = child instanceof EmptyShadeView;
@@ -362,16 +348,9 @@
clampPositionToShelf(child, childViewState, ambientState);
}
- if (reverse) {
- currentYPosition = childViewState.yTranslation;
- if (applyGapHeight) {
- currentYPosition -= mGapHeight;
- }
- } else {
- currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
- if (currentYPosition <= 0) {
- childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
- }
+ currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
+ if (currentYPosition <= 0) {
+ childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
}
if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 0a366c9..dd1419f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -48,6 +48,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
+import android.provider.Settings;
import android.service.media.CameraPrewarmService;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -60,6 +61,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,6 +73,10 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.controls.dagger.ControlsComponent;
+import com.android.systemui.controls.ui.ControlsDialog;
+import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.IntentButtonProvider;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
@@ -117,11 +123,13 @@
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
+ // TODO(b/179494051): May no longer be needed
private final boolean mShowLeftAffordance;
private final boolean mShowCameraAffordance;
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
+ private ImageView mAltLeftButton;
private ViewGroup mIndicationArea;
private TextView mEnterpriseDisclosure;
private TextView mIndicationText;
@@ -171,6 +179,11 @@
private int mBurnInYOffset;
private ActivityIntentHelper mActivityIntentHelper;
+ private ControlsDialog mControlsDialog;
+ private ControlsComponent mControlsComponent;
+ private int mLockScreenMode;
+ private BroadcastDispatcher mBroadcastDispatcher;
+
public KeyguardBottomAreaView(Context context) {
this(context, null);
}
@@ -236,6 +249,7 @@
mOverlayContainer = findViewById(R.id.overlay_container);
mRightAffordanceView = findViewById(R.id.camera_button);
mLeftAffordanceView = findViewById(R.id.left_button);
+ mAltLeftButton = findViewById(R.id.alt_left_button);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
mEnterpriseDisclosure = findViewById(
R.id.keyguard_indication_enterprise_disclosure);
@@ -334,6 +348,11 @@
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
mLeftAffordanceView.setLayoutParams(lp);
updateLeftAffordanceIcon();
+
+ lp = mAltLeftButton.getLayoutParams();
+ lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
+ lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
+ mAltLeftButton.setLayoutParams(lp);
}
private void updateRightAffordanceIcon() {
@@ -392,10 +411,17 @@
}
private void updateLeftAffordanceIcon() {
+ if (mDozing) {
+ mAltLeftButton.setVisibility(GONE);
+ } else if (mAltLeftButton.getDrawable() != null) {
+ mAltLeftButton.setVisibility(VISIBLE);
+ }
+
if (!mShowLeftAffordance || mDozing) {
mLeftAffordanceView.setVisibility(GONE);
return;
}
+
IconState state = mLeftButton.getIcon();
mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
if (state.drawable != mLeftAffordanceView.getDrawable()
@@ -669,6 +695,9 @@
public void startFinishDozeAnimation() {
long delay = 0;
+ if (mAltLeftButton.getVisibility() == View.VISIBLE) {
+ startFinishDozeAnimationElement(mAltLeftButton, delay);
+ }
if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mLeftAffordanceView, delay);
delay += DOZE_ANIMATION_STAGGER_DELAY;
@@ -744,6 +773,10 @@
if (dozing) {
mOverlayContainer.setVisibility(INVISIBLE);
+ if (mControlsDialog != null) {
+ mControlsDialog.dismiss();
+ mControlsDialog = null;
+ }
} else {
mOverlayContainer.setVisibility(VISIBLE);
if (animate) {
@@ -773,6 +806,7 @@
mLeftAffordanceView.setAlpha(alpha);
mRightAffordanceView.setAlpha(alpha);
mIndicationArea.setAlpha(alpha);
+ mAltLeftButton.setAlpha(alpha);
}
private class DefaultLeftButton implements IntentButton {
@@ -844,4 +878,54 @@
}
return insets;
}
+
+ /**
+ * Show or hide controls, depending on the lock screen mode and controls
+ * availability.
+ */
+ public void setupControls(ControlsComponent component, BroadcastDispatcher dispatcher) {
+ mControlsComponent = component;
+ mBroadcastDispatcher = dispatcher;
+ setupControls();
+ }
+
+ private void setupControls() {
+ if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ mAltLeftButton.setVisibility(View.GONE);
+ mAltLeftButton.setOnClickListener(null);
+ return;
+ }
+
+ if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) {
+ return;
+ }
+
+ if (mControlsComponent.getControlsListingController().isPresent()) {
+ mControlsComponent.getControlsListingController().get()
+ .addCallback(list -> {
+ if (!list.isEmpty()) {
+ mAltLeftButton.setImageDrawable(list.get(0).loadIcon());
+ mAltLeftButton.setVisibility(View.VISIBLE);
+ mAltLeftButton.setOnClickListener((v) -> {
+ ControlsUiController ui = mControlsComponent
+ .getControlsUiController().get();
+ mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher)
+ .show(ui);
+ });
+
+ } else {
+ mAltLeftButton.setVisibility(View.GONE);
+ mAltLeftButton.setOnClickListener(null);
+ }
+ });
+ }
+ }
+
+ /**
+ * Optionally add controls when in the new lockscreen mode
+ */
+ public void onLockScreenModeChanged(int mode) {
+ mLockScreenMode = mode;
+ setupControls();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a5284f1..e0ef3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -80,8 +80,10 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
@@ -95,6 +97,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -120,6 +123,7 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -227,6 +231,7 @@
public void onLockScreenModeChanged(int mode) {
mLockScreenMode = mode;
mClockPositionAlgorithm.onLockScreenModeChanged(mode);
+ mKeyguardBottomArea.onLockScreenModeChanged(mode);
}
@Override
@@ -291,7 +296,10 @@
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final QSDetailDisplayer mQSDetailDisplayer;
+ private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
+ private final ControlsComponent mControlsComponent;
+
// Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
// If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
private final int mMaxKeyguardNotifications;
@@ -500,6 +508,7 @@
private NotificationShelfController mNotificationShelfController;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
+ private BroadcastDispatcher mBroadcastDispatcher;
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
@@ -552,10 +561,15 @@
AuthController authController,
QSDetailDisplayer qsDetailDisplayer,
ScrimController scrimController,
- MediaDataManager mediaDataManager) {
+ MediaDataManager mediaDataManager,
+ AmbientState ambientState,
+ FeatureFlags featureFlags,
+ ControlsComponent controlsComponent,
+ BroadcastDispatcher broadcastDispatcher) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
- latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager);
+ latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
+ ambientState);
mView = view;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
@@ -568,6 +582,7 @@
mNotificationIconAreaController = notificationIconAreaController;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mQSDetailDisplayer = qsDetailDisplayer;
+ mFeatureFlags = featureFlags;
mView.setWillNotDraw(!DEBUG);
mLayoutInflater = layoutInflater;
mFalsingManager = falsingManager;
@@ -584,6 +599,7 @@
mBiometricUnlockController = biometricUnlockController;
mScrimController = scrimController;
mMediaDataManager = mediaDataManager;
+ mControlsComponent = controlsComponent;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) {
mQs.animateHeaderSlidingOut();
@@ -622,6 +638,7 @@
mEntryManager = notificationEntryManager;
mConversationNotificationManager = conversationNotificationManager;
mAuthController = authController;
+ mBroadcastDispatcher = broadcastDispatcher;
mView.setBackgroundColor(Color.TRANSPARENT);
OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
@@ -753,21 +770,38 @@
public void updateResources() {
int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
- int panelGravity = mResources.getInteger(R.integer.notification_panel_layout_gravity);
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
- if (lp.width != qsWidth || lp.gravity != panelGravity) {
+ ViewGroup.LayoutParams lp = mQsFrame.getLayoutParams();
+ if (lp.width != qsWidth) {
lp.width = qsWidth;
- lp.gravity = panelGravity;
mQsFrame.setLayoutParams(lp);
}
int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
lp = mNotificationStackScrollLayoutController.getLayoutParams();
- if (lp.width != panelWidth || lp.gravity != panelGravity) {
+ if (lp.width != panelWidth) {
lp.width = panelWidth;
- lp.gravity = panelGravity;
mNotificationStackScrollLayoutController.setLayoutParams(lp);
}
+
+ if (shouldUseSplitNotificationShade()) {
+ // In order to change the constraints at runtime, all children of the Constraint Layout
+ // must have ids.
+ ensureAllViewsHaveIds(mNotificationContainerParent);
+ }
+ }
+
+ private boolean shouldUseSplitNotificationShade() {
+ return mFeatureFlags.isTwoColumnNotificationShadeEnabled()
+ && mResources.getBoolean(R.bool.config_use_split_notification_shade);
+ }
+
+ private static void ensureAllViewsHaveIds(ViewGroup parentView) {
+ for (int i = 0; i < parentView.getChildCount(); i++) {
+ View childView = parentView.getChildAt(i);
+ if (childView.getId() == View.NO_ID) {
+ childView.setId(View.generateViewId());
+ }
+ }
}
private void reInflateViews() {
@@ -813,6 +847,7 @@
mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
mKeyguardBottomArea.setStatusBar(mStatusBar);
mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
+ mKeyguardBottomArea.setupControls(mControlsComponent, mBroadcastDispatcher);
}
private void updateMaxDisplayedNotifications(boolean recompute) {
@@ -2411,11 +2446,6 @@
}
@Override
- protected void setIsShadeOpening(boolean isOpening) {
- mNotificationStackScrollLayoutController.setIsShadeOpening(isOpening);
- }
-
- @Override
public void setSectionPadding(float padding) {
if (padding == mSectionPadding) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 27c94d2..b367406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -28,6 +28,7 @@
import android.widget.FrameLayout;
import androidx.annotation.DimenRes;
+import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.systemui.R;
import com.android.systemui.fragments.FragmentHostManager;
@@ -42,7 +43,7 @@
/**
* The container with notification stack scroller and quick settings inside.
*/
-public class NotificationsQuickSettingsContainer extends FrameLayout
+public class NotificationsQuickSettingsContainer extends ConstraintLayout
implements OnInflateListener, FragmentListener,
AboveShelfObserver.HasViewAboveShelfChangedListener {
@@ -68,11 +69,11 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mQsFrame = (FrameLayout) findViewById(R.id.qs_frame);
+ mQsFrame = findViewById(R.id.qs_frame);
mStackScroller = findViewById(R.id.notification_stack_scroller);
mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
- ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher);
+ ViewStub userSwitcher = findViewById(R.id.keyguard_user_switcher);
userSwitcher.setOnInflateListener(this);
mUserSwitcher = userSwitcher;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index bee0cb1..28cfe21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -53,7 +53,7 @@
if (DEBUG) LOG("go state: %d -> %d", mState, state);
mState = state;
if (mPanel != null) {
- mPanel.setIsShadeOpening(state == STATE_OPENING);
+ mPanel.getAmbientState().setIsShadeOpening(state == STATE_OPENING);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 3031b8a..55744f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -27,6 +27,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.SystemClock;
@@ -54,6 +55,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -145,6 +147,9 @@
private float mInitialTouchX;
private boolean mTouchDisabled;
+ // AmbientState will never be null since it provides an @Inject constructor for Dagger to call.
+ private AmbientState mAmbientState;
+
/**
* Whether or not the PanelView can be expanded or collapsed with a drag.
*/
@@ -223,13 +228,19 @@
mJustPeeked = true;
}
+ protected AmbientState getAmbientState() {
+ return mAmbientState;
+ }
+
public PanelViewController(PanelView view,
FalsingManager falsingManager, DozeLog dozeLog,
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
LatencyTracker latencyTracker,
FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
- StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
+ StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+ AmbientState ambientState) {
+ mAmbientState = ambientState;
mView = view;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
@@ -776,8 +787,6 @@
*/
protected abstract boolean isTrackingBlocked();
- protected abstract void setIsShadeOpening(boolean isShadeOpening);
-
protected abstract void setSectionPadding(float padding);
protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index c0a5ffa..31a432e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.leak.RotationUtils;
+import java.util.List;
import java.util.Objects;
public class PhoneStatusBarView extends PanelBar {
@@ -75,6 +76,8 @@
@Nullable
private DisplayCutout mDisplayCutout;
private int mStatusBarHeight;
+ @Nullable
+ private List<StatusBar.ExpansionChangedListener> mExpansionChangedListeners;
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -93,6 +96,11 @@
mBar = bar;
}
+ public void setExpansionChangedListeners(
+ @Nullable List<StatusBar.ExpansionChangedListener> listeners) {
+ mExpansionChangedListeners = listeners;
+ }
+
public void setScrimController(ScrimController scrimController) {
mScrimController = scrimController;
}
@@ -277,6 +285,12 @@
if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
mBar.getNavigationBarView().onStatusBarPanelStateChanged();
}
+
+ if (mExpansionChangedListeners != null) {
+ for (StatusBar.ExpansionChangedListener listener : mExpansionChangedListeners) {
+ listener.onExpansionChanged(frac, expanded);
+ }
+ }
}
private void updateScrimFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e39065b..7b2330b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -34,7 +34,6 @@
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -64,8 +63,6 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -201,16 +198,6 @@
private boolean mWakeLockHeld;
private boolean mKeyguardOccluded;
- /**
- * Notifies listeners of animation-related changes (currently just opacity changes).
- */
- public interface ScrimChangedListener {
- void onAlphaChanged(float alpha);
- }
-
- @NonNull
- private final List<ScrimChangedListener> mScrimChangedListeners;
-
@Inject
public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters,
AlarmManager alarmManager, KeyguardStateController keyguardStateController,
@@ -223,7 +210,6 @@
ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(featureFlags.isShadeOpaque()
? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA);
mBlurUtils = blurUtils;
- mScrimChangedListeners = new ArrayList<>();
mKeyguardStateController = keyguardStateController;
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -301,10 +287,6 @@
mScrimVisibleListener = listener;
}
- public void addScrimChangedListener(@NonNull ScrimChangedListener listener) {
- mScrimChangedListeners.add(listener);
- }
-
public void transitionTo(ScrimState state) {
transitionTo(state, null);
}
@@ -580,10 +562,6 @@
throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
+ ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha);
}
-
- for (ScrimChangedListener listener : mScrimChangedListeners) {
- listener.onAlphaChanged(mBehindAlpha);
- }
}
private void applyAndDispatchExpansion() {
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 4a3d8d6..7095afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -338,6 +338,10 @@
ONLY_CORE_APPS = onlyCoreApps;
}
+ public interface ExpansionChangedListener {
+ void onExpansionChanged(float expansion, boolean expanded);
+ }
+
/**
* The {@link StatusBarState} of the status bar.
*/
@@ -439,6 +443,8 @@
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSlider.Factory mBrightnessSliderFactory;
+ private final List<ExpansionChangedListener> mExpansionChangedListeners;
+
// for disabling the status bar
private int mDisabled1 = 0;
private int mDisabled2 = 0;
@@ -838,6 +844,8 @@
mNotificationIconAreaController = notificationIconAreaController;
mBrightnessSliderFactory = brightnessSliderFactory;
+ mExpansionChangedListeners = new ArrayList<>();
+
mBubbleExpandListener =
(isExpanding, key) -> {
mContext.getMainExecutor().execute(() -> {
@@ -1077,6 +1085,7 @@
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanelViewController);
mStatusBarView.setScrimController(mScrimController);
+ mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
// CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
@@ -4550,4 +4559,8 @@
public void suppressAmbientDisplay(boolean suppressed) {
mDozeServiceHost.setDozeSuppressed(suppressed);
}
+
+ public void addExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
+ mExpansionChangedListeners.add(listener);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
index 2eff04e..80b75a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
@@ -53,13 +53,21 @@
public void notifyListeners(SignalCallback callback) {
boolean ethernetVisible = mCurrentState.connected;
String contentDescription = getTextIfExists(getContentDescription()).toString();
-
// TODO: wire up data transfer using WifiSignalPoller.
callback.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(),
contentDescription));
}
@Override
+ public int getContentDescription() {
+ if (mCurrentState.connected) {
+ return getIcons().contentDesc[1];
+ } else {
+ return getIcons().discContentDesc;
+ }
+ }
+
+ @Override
public State cleanState() {
return new State();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f9450ae..e13e30b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -524,6 +524,10 @@
return mWifiSignalController.isCarrierMergedWifi(subId);
}
+ boolean isEthernetDefault() {
+ return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+ }
+
String getNonDefaultMobileDataNetworkName(int subId) {
MobileSignalController controller = getControllerWithSubId(subId);
return controller != null ? controller.getNonDefaultCarrierName() : "";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 9669522..b2120d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -106,7 +106,8 @@
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
if (mProviderModel) {
IconState qsIcon = null;
- if (mCurrentState.isDefault) {
+ if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
+ && !mNetworkController.isEthernetDefault())) {
qsIcon = new IconState(mCurrentState.connected,
mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
: getQsCurrentIconId(), contentDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
new file mode 100644
index 0000000..1af2c9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.util
+
+import android.content.res.Resources
+import android.content.res.TypedArray
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import android.util.AttributeSet
+import com.android.systemui.R
+import org.xmlpull.v1.XmlPullParser
+
+/**
+ * [DrawableWrapper] to use in the progress of a slider.
+ *
+ * This drawable is used to change the bounds of the enclosed drawable depending on the level to
+ * simulate a sliding progress, instead of using clipping or scaling. That way, the shape of the
+ * edges is maintained.
+ *
+ * Meant to be used with a rounded ends background, it will also prevent deformation when the slider
+ * is meant to be smaller than the rounded corner. The background should have rounded corners that
+ * are half of the height.
+ */
+class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawable) {
+
+ constructor() : this(null)
+
+ companion object {
+ private const val MAX_LEVEL = 10000 // Taken from Drawable
+ }
+
+ private var clipPath: Path = Path()
+
+ init {
+ setClipPath(Rect())
+ }
+
+ override fun inflate(
+ r: Resources,
+ parser: XmlPullParser,
+ attrs: AttributeSet,
+ theme: Resources.Theme?
+ ) {
+ val a = obtainAttributes(r, theme, attrs, R.styleable.RoundedCornerProgressDrawable)
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
+ super.inflate(r, parser, attrs, theme)
+
+ updateStateFromTypedArray(a)
+ if (drawable == null) {
+ throw IllegalStateException("${this::class.java.simpleName} needs a drawable")
+ }
+ a.recycle()
+ }
+
+ override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean {
+ onLevelChange(level)
+ return super.onLayoutDirectionChanged(layoutDirection)
+ }
+
+ private fun updateStateFromTypedArray(a: TypedArray) {
+ if (a.hasValue(R.styleable.RoundedCornerProgressDrawable_android_drawable)) {
+ setDrawable(a.getDrawable(R.styleable.RoundedCornerProgressDrawable_android_drawable))
+ }
+ }
+
+ override fun onBoundsChange(bounds: Rect) {
+ setClipPath(bounds)
+ super.onBoundsChange(bounds)
+ onLevelChange(level)
+ }
+
+ private fun setClipPath(bounds: Rect) {
+ clipPath.reset()
+ clipPath.addRoundRect(
+ bounds.left.toFloat(),
+ bounds.top.toFloat(),
+ bounds.right.toFloat(),
+ bounds.bottom.toFloat(),
+ bounds.height().toFloat() / 2,
+ bounds.height().toFloat() / 2,
+ Path.Direction.CW
+ )
+ }
+
+ override fun onLevelChange(level: Int): Boolean {
+ val db = drawable?.bounds!!
+ val width = bounds.width() * level / MAX_LEVEL
+ // Extra space on the left to keep the rounded shape on the right end
+ val leftBound = bounds.left - bounds.height()
+ drawable?.setBounds(leftBound, db.top, bounds.left + width, db.bottom)
+ return super.onLevelChange(level)
+ }
+
+ override fun draw(canvas: Canvas) {
+ canvas.save()
+ canvas.clipPath(clipPath)
+ super.draw(canvas)
+ canvas.restore()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 416de04..0795d89 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -29,15 +29,19 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
+import com.android.wm.shell.pip.tv.TvPipTransition;
+import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -58,6 +62,7 @@
PipTaskOrganizer pipTaskOrganizer,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
+ PipTransitionController pipTransitionController,
TvPipNotificationController tvPipNotificationController,
TaskStackListenerImpl taskStackListener,
WindowManagerShellWrapper windowManagerShellWrapper,
@@ -68,6 +73,7 @@
pipBoundsState,
pipBoundsAlgorithm,
pipTaskOrganizer,
+ pipTransitionController,
tvPipMenuController,
pipMediaController,
tvPipNotificationController,
@@ -92,6 +98,16 @@
// Handler needed for loadDrawableAsync() in PipControlsViewController
@WMSingleton
@Provides
+ static PipTransitionController provideTvPipTransition(
+ Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+ PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipBoundsState pipBoundsState, TvPipMenuController pipMenuController) {
+ return new TvPipTransition(pipBoundsState, pipMenuController,
+ pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+ }
+
+ @WMSingleton
+ @Provides
static TvPipMenuController providesTvPipMenuController(
Context context,
PipBoundsState pipBoundsState,
@@ -113,16 +129,26 @@
@WMSingleton
@Provides
+ static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
+ pipSurfaceTransactionHelper) {
+ return new PipAnimationController(pipSurfaceTransactionHelper);
+ }
+
+ @WMSingleton
+ @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context,
TvPipMenuController tvPipMenuController,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipAnimationController pipAnimationController,
+ PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
- tvPipMenuController, pipSurfaceTransactionHelper, splitScreenOptional,
- displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+ tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
+ pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
+ shellTaskOrganizer, mainExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 12a3b5d..2aaa095 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -17,13 +17,11 @@
package com.android.systemui.wmshell;
import android.animation.AnimationHandler;
-import android.app.ActivityTaskManager;
import android.content.Context;
import android.os.Handler;
import android.view.IWindowManager;
import com.android.systemui.dagger.WMSingleton;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
@@ -41,18 +39,19 @@
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransition;
+import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -104,12 +103,13 @@
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
- PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
+ PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
+ WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(context, displayController,
pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController,
- phonePipMenuController, pipTaskOrganizer, pipTouchHandler,
+ phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, mainExecutor));
}
@@ -143,12 +143,13 @@
PhonePipMenuController menuPhoneController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer,
+ PipTransitionController pipTransitionController,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTouchHandler(context, menuPhoneController, pipBoundsAlgorithm,
- pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger,
- mainExecutor);
+ pipBoundsState, pipTaskOrganizer, pipTransitionController,
+ floatingContentCoordinator, pipUiEventLogger, mainExecutor);
}
@WMSingleton
@@ -157,12 +158,32 @@
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipMenuController menuPhoneController,
+ PipAnimationController pipAnimationController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ PipTransitionController pipTransitionController,
Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
- menuPhoneController, pipSurfaceTransactionHelper, splitScreenOptional,
- displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+ menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
+ pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
+ shellTaskOrganizer, mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
+ pipSurfaceTransactionHelper) {
+ return new PipAnimationController(pipSurfaceTransactionHelper);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipTransitionController providePipTransitionController(Context context,
+ Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+ PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipBoundsState pipBoundsState, PhonePipMenuController pipMenuController) {
+ return new PipTransition(context, pipBoundsState, pipMenuController,
+ pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 1e473cd..3e873d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -44,7 +44,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -88,7 +88,7 @@
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
- private ScrimController mScrimController;
+ private StatusBar mStatusBar;
private FakeExecutor mFgExecutor;
@@ -126,7 +126,7 @@
mWindowManager,
mStatusBarStateController,
mFgExecutor,
- mScrimController);
+ mStatusBar);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
@@ -241,6 +241,6 @@
@Test
public void registersViewForCallbacks() throws RemoteException {
verify(mStatusBarStateController).addCallback(mUdfpsView);
- verify(mScrimController).addScrimChangedListener(mUdfpsView);
+ verify(mStatusBar).addExpansionChangedListener(mUdfpsView);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index b7d1bc6..6db21f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -362,34 +362,6 @@
}
@Test
- public void testAugmentTileFromStorageWithNotification() {
- PeopleSpaceTile tile =
- new PeopleSpaceTile
- .Builder("id", "userName", ICON, new Intent())
- .build();
- PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT);
-
- assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
- assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
- assertThat(actual.getNotificationDataUri()).isEqualTo(URI);
- }
-
- @Test
- public void testAugmentTileFromStorageWithoutNotification() {
- PeopleSpaceTile tile =
- new PeopleSpaceTile
- .Builder("id", "userName", ICON, new Intent())
- .build();
- PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT);
-
- assertThat(actual.getNotificationKey()).isEqualTo(null);
- assertThat(actual.getNotificationKey()).isEqualTo(null);
- assertThat(actual.getNotificationDataUri()).isEqualTo(null);
- }
-
- @Test
public void testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday() {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
when(mMockCursor.moveToNext()).thenReturn(true, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 8c0afb8..ef314ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -21,6 +21,8 @@
import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -31,26 +33,24 @@
import static java.util.Objects.requireNonNull;
-import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.Person;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
-import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
import androidx.preference.PreferenceManager;
import androidx.test.filters.SmallTest;
@@ -58,6 +58,7 @@
import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.SbnBuilder;
@@ -74,26 +75,27 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final long MIN_LINGER_DURATION = 5;
- private static final String TEST_PACKAGE_A = "com.test.package_a";
+ private static final String TEST_PACKAGE_A = "com.android.systemui.tests";
private static final String TEST_PACKAGE_B = "com.test.package_b";
private static final String TEST_CHANNEL_ID = "channel_id";
private static final String TEST_CHANNEL_NAME = "channel_name";
private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
private static final String TEST_CONVERSATION_ID = "conversation_id";
private static final int WIDGET_ID_WITH_SHORTCUT = 1;
+ private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
private static final String SHORTCUT_ID = "101";
private static final String OTHER_SHORTCUT_ID = "102";
- private static final String NOTIFICATION_KEY = "notification_key";
- private static final String NOTIFICATION_CONTENT = "notification_content";
+ private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
+ private static final String NOTIFICATION_CONTENT = "message text";
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
private static final Person PERSON = new Person.Builder()
@@ -105,10 +107,14 @@
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
.Builder(SHORTCUT_ID, "username", ICON, new Intent())
- .setNotificationKey(NOTIFICATION_KEY)
+ .setPackageName(TEST_PACKAGE_A)
+ .setUid(0)
+ .setNotificationKey(NOTIFICATION_KEY + "1")
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
+ private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
+ SHORTCUT_ID).setLongLabel("name").build();
private PeopleSpaceWidgetManager mManager;
@@ -119,175 +125,101 @@
@Mock
private AppWidgetManager mAppWidgetManager;
@Mock
- private INotificationManager mINotificationManager;
+ private IPeopleManager mIPeopleManager;
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
+ @Captor
+ private ArgumentCaptor<Bundle> mBundleArgumentCaptor;
private final NoManSimulator mNoMan = new NoManSimulator();
private final FakeSystemClock mClock = new FakeSystemClock();
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mManager =
new PeopleSpaceWidgetManager(mContext);
- mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mINotificationManager);
+ mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
mManager.attach(mListenerService);
verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
mNoMan.addListener(serviceListener);
+ // Default to single People tile widgets.
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2);
+ Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- SharedPreferences.Editor editor = sp.edit();
- editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
- editor.apply();
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+
Bundle options = new Bundle();
- options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
-
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
.thenReturn(new Bundle());
+ when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(
+ getConversationWithShortcutId(SHORTCUT_ID));
}
@Test
- public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException {
+ public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
+ StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbn)
+ .setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotNotifySingleConversationAppWidgetIfNoWidgets() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
+ public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
+ Notification notificationWithoutShortcut = new Notification.Builder(mContext)
+ .setContentTitle("TEST_TITLE")
+ .setContentText("TEST_TEXT")
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+ )
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(new SbnBuilder()
+ .setNotification(notificationWithoutShortcut)
+ .setPkg(TEST_PACKAGE_A)
+ .build())
+ .setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class));
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testNotifyAppWidgetIfNotificationPosted() throws RemoteException {
- int[] widgetIdsArray = {1};
+ public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
+ int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
+ StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbnWithoutPackageName)
+ .setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, times(1))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifySingleConversationAppWidgetOnceIfNotificationPosted()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
-
- verify(mIAppWidgetService, never())
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
- any(RemoteViews.class));
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifySingleConversationAppWidgetTwiceIfTwoNotificationsPosted()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_B)
- .setId(2));
-
- verify(mIAppWidgetService, never())
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
- any(RemoteViews.class));
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifyAppWidgetTwiceIfTwoNotificationsPosted() throws RemoteException {
- int[] widgetIdsArray = {1, 2};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_B)
- .setId(2));
-
- verify(mIAppWidgetService, times(2))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ any());
}
@Test
- public void testNotifyAppWidgetTwiceIfNotificationPostedAndRemoved() throws RemoteException {
- int[] widgetIdsArray = {1, 2};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
-
- verify(mIAppWidgetService, times(2))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
- }
-
- @Test
- public void testDoNotNotifyAppWidgetIfNonConversationChannelModified() throws RemoteException {
+ public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -298,13 +230,12 @@
UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ any());
}
@Test
- public void testNotifyAppWidgetIfConversationChannelModified() throws RemoteException {
+ public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -316,45 +247,57 @@
UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, times(1))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotUpdateNotificationPostedIfNoExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, never())
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotUpdateNotificationRemovedIfNoExistingTile() throws RemoteException {
+ public void testDoNotUpdateNotificationPostedIfDifferentPackageName() throws Exception {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setPkg(TEST_PACKAGE_B)
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbnWithDifferentPackageName)
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
+ }
+
+ @Test
+ public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(4);
NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
@@ -362,99 +305,215 @@
verify(mAppWidgetManager, never())
.updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testUpdateNotificationPostedIfExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
+ StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setPkg(TEST_PACKAGE_B)
+ .build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
+ .setSbn(sbnWithDifferentPackageName)
+ .setId(1));
+ mClock.advanceTime(4);
+ NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationPostedIfExistingTile() throws Exception {
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, times(1))
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(options);
+ // Set the same Person associated on another People Tile widget ID.
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+ SECOND_WIDGET_ID_WITH_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
+ throws Exception {
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(options);
+ // Set the same Person associated on another People Tile widget ID.
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+ SECOND_WIDGET_ID_WITH_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, never()).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
}
@Test
public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- Notification notification = new Notification.Builder(mContext)
+ Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(SHORTCUT_ID)
.build();
StatusBarNotification sbn = new SbnBuilder()
- .setNotification(notification)
+ .setNotification(notificationWithoutMessagingStyle)
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, never())
.updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
}
@Test
- public void testUpdateNotificationRemovedIfExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, times(2))
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(null);
+ assertThat(tile.getNotificationContent()).isEqualTo(null);
+ assertThat(tile.getNotificationDataUri()).isEqualTo(null);
+ verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(),
+ any());
}
- /** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */
- private List<ConversationChannelWrapper> getConversationWithShortcutId() {
- List<ConversationChannelWrapper> convos = new ArrayList<>();
- ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
- convo1.setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID).setLongLabel(
- "name").build());
- convos.add(convo1);
- return convos;
+ /**
+ * Returns a single conversation associated with {@code shortcutId}.
+ */
+ private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
+ ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+ "name").build();
+ ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+ 0L, false);
+ return convo;
}
- private StatusBarNotification createConversationNotification(String shortcutId) {
- Notification notification = new Notification.Builder(mContext)
+ private Notification createMessagingStyleNotification(String shortcutId) {
+ return new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(shortcutId)
.setStyle(new Notification.MessagingStyle(PERSON)
- .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+ .addMessage(
+ new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+ PERSON))
)
.build();
+ }
+
+ private StatusBarNotification createConversationNotification(String shortcutId) {
+ Notification notification = createMessagingStyleNotification(shortcutId);
return new SbnBuilder()
.setNotification(notification)
+ .setPkg(TEST_PACKAGE_A)
.build();
}
+
+ private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
+ SharedPreferences widgetSp = mContext.getSharedPreferences(
+ String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, packageName);
+ widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, shortcutId);
+ widgetEditor.putInt(PeopleSpaceUtils.USER_ID, 0);
+ widgetEditor.apply();
+
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(String.valueOf(widgetId), shortcutId);
+ String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.add(String.valueOf(widgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.apply();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index c6f97fa..4381158 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -118,7 +118,7 @@
mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- /* labelsFlag */ false, /* sideLabels */ false);
+ /* labelsFlag */ false);
mController.init();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index c490c4c..107160f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -83,7 +83,8 @@
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager
+ dumpManager,
+ false
)
controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
index b63274b..451c78f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -71,7 +72,8 @@
@Mock private NavigationModeController mMockNavModeController;
@Mock private NotificationShadeWindowController mMockStatusBarWinController;
@Mock private Optional<Pip> mMockPipOptional;
- @Mock private Optional<LegacySplitScreen> mMockSplitScreenOptional;
+ @Mock private Optional<LegacySplitScreen> mMockLegacySplitScreenOptional;
+ @Mock private Optional<SplitScreen> mMockSplitScreenOptional;
@Mock private Optional<Lazy<StatusBar>> mMockStatusBarOptionalLazy;
@Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
@Mock private PackageManager mPackageManager;
@@ -89,8 +91,8 @@
mSpiedOverviewProxyService = spy(new OverviewProxyService(mSpiedContext, mMockCommandQueue,
mMockNavBarControllerLazy, mMockNavModeController, mMockStatusBarWinController,
- mMockSysUiState, mMockPipOptional, mMockSplitScreenOptional,
- mMockStatusBarOptionalLazy, mMockOneHandedOptional,
+ mMockSysUiState, mMockPipOptional, mMockLegacySplitScreenOptional,
+ mMockSplitScreenOptional, mMockStatusBarOptionalLazy, mMockOneHandedOptional,
mMockBroadcastDispatcher, mMockTransitions));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index ceb4d84..461f64e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -83,6 +83,7 @@
private NotificationStackScrollLayout mStackScroller; // Normally test this
private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below
+ private AmbientState mAmbientState;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private StatusBar mBar;
@@ -123,6 +124,9 @@
});
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+ // Interact with real instance of AmbientState.
+ mAmbientState = new AmbientState(mContext, mNotificationSectionsManager);
+
// The actual class under test. You may need to work with this class directly when
// testing anonymous class members of mStackScroller, like mMenuEventListener,
// which refer to members of NotificationStackScrollLayout. The spy
@@ -134,7 +138,8 @@
mNotificationSectionsManager,
mGroupMembershipManger,
mGroupExpansionManager,
- mStatusBarStateController
+ mStatusBarStateController,
+ mAmbientState
);
mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
mNotificationSwipeHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index aa7143e..c07ba72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -61,13 +61,16 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -80,6 +83,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -202,7 +206,16 @@
private ScrimController mScrimController;
@Mock
private MediaDataManager mMediaDataManager;
-
+ @Mock
+ private FeatureFlags mFeatureFlags;
+ @Mock
+ private ControlsComponent mControlsComponent;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ @Mock
+ private AmbientState mAmbientState;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -217,6 +230,8 @@
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
mDisplayMetrics.density = 100;
when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
+ when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400);
+ when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
@@ -235,6 +250,8 @@
when(mView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+ when(mView.findViewById(R.id.notification_container_parent))
+ .thenReturn(mNotificationContainerParent);
FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
mDisplayMetrics);
@@ -262,6 +279,11 @@
.thenReturn(mKeyguardClockSwitchController);
when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
.thenReturn(mKeyguardStatusViewController);
+ when(mQsFrame.getLayoutParams()).thenReturn(
+ new ViewGroup.LayoutParams(600, 400));
+ when(mNotificationStackScrollLayoutController.getLayoutParams()).thenReturn(
+ new ViewGroup.LayoutParams(600, 400));
+
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
mLayoutInflater,
@@ -282,7 +304,11 @@
mAuthController,
new QSDetailDisplayer(),
mScrimController,
- mMediaDataManager);
+ mMediaDataManager,
+ mAmbientState,
+ mFeatureFlags,
+ mControlsComponent,
+ mBroadcastDispatcher);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);
@@ -397,6 +423,25 @@
verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
+ @Test
+ public void testAllChildrenOfNotificationContainer_haveIds() {
+ when(mNotificationContainerParent.getChildCount()).thenReturn(2);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+
+ View view1 = new View(mContext);
+ view1.setId(1);
+ when(mNotificationContainerParent.getChildAt(0)).thenReturn(view1);
+
+ View view2 = mock(View.class);
+ when(mNotificationContainerParent.getChildAt(1)).thenReturn(view2);
+
+ mNotificationPanelViewController.updateResources();
+
+ assertThat(mNotificationContainerParent.getChildAt(0).getId()).isEqualTo(1);
+ assertThat(mNotificationContainerParent.getChildAt(1).getId()).isNotEqualTo(View.NO_ID);
+ }
+
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 4d95ef1..6dcad25 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -20,14 +20,11 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
@@ -45,7 +42,7 @@
private static final String TAG = "VpnDisconnected";
- private IConnectivityManager mService;
+ private ConnectivityManager mService;
private int mUserId;
private String mVpnPackage;
@@ -53,10 +50,9 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
mUserId = UserHandle.myUserId();
- mVpnPackage = getAlwaysOnVpnPackage();
+ final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
+ mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId);
if (mVpnPackage == null) {
finish();
return;
@@ -102,15 +98,6 @@
}
}
- private String getAlwaysOnVpnPackage() {
- try {
- return mService.getAlwaysOnVpnPackage(mUserId);
- } catch (RemoteException e) {
- Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e);
- return null;
- }
- }
-
private CharSequence getVpnLabel() {
try {
return VpnConfig.getVpnLabel(this, mVpnPackage);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index e66f2cc..aab01d0 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,15 +18,12 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
import android.net.VpnManager;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.Html;
@@ -48,7 +45,8 @@
private String mPackage;
- private IConnectivityManager mService;
+ private ConnectivityManager mCm; // TODO: switch entirely to VpnManager once VPN code moves
+ private VpnManager mVm;
public ConfirmDialog() {
this(VpnManager.TYPE_VPN_SERVICE);
@@ -62,10 +60,10 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPackage = getCallingPackage();
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ mCm = getSystemService(ConnectivityManager.class);
+ mVm = getSystemService(VpnManager.class);
- if (prepareVpn()) {
+ if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) {
setResult(RESULT_OK);
finish();
return;
@@ -74,7 +72,7 @@
finish();
return;
}
- final String alwaysOnVpnPackage = getAlwaysOnVpnPackage();
+ final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
// Can't prepare new vpn app when another vpn is always-on
if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
finish();
@@ -97,24 +95,6 @@
button.setFilterTouchesWhenObscured(true);
}
- private String getAlwaysOnVpnPackage() {
- try {
- return mService.getAlwaysOnVpnPackage(UserHandle.myUserId());
- } catch (RemoteException e) {
- Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e);
- // Fallback to null to show the dialog
- return null;
- }
- }
-
- private boolean prepareVpn() {
- try {
- return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- }
-
private CharSequence getVpnLabel() {
try {
return VpnConfig.getVpnLabel(this, mPackage);
@@ -146,10 +126,10 @@
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
+ if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) {
// Authorize this app to initiate VPN connections in the future without user
// intervention.
- mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
+ mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 01dca7e..1fc74f7 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -16,13 +16,11 @@
package com.android.vpndialogs;
-import android.content.Context;
import android.content.DialogInterface;
-import android.net.IConnectivityManager;
+import android.net.VpnManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
@@ -41,7 +39,7 @@
private VpnConfig mConfig;
- private IConnectivityManager mService;
+ private VpnManager mVm;
private TextView mDuration;
private TextView mDataTransmitted;
@@ -55,11 +53,9 @@
super.onCreate(savedInstanceState);
try {
+ mVm = getSystemService(VpnManager.class);
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
- mConfig = mService.getVpnConfig(UserHandle.myUserId());
+ mConfig = mVm.getVpnConfig(UserHandle.myUserId());
// mConfig can be null if we are a restricted user, in that case don't show this dialog
if (mConfig == null) {
@@ -118,9 +114,9 @@
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
final int myUserId = UserHandle.myUserId();
if (mConfig.legacy) {
- mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
+ mVm.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
} else {
- mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
+ mVm.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
}
}
} catch (Exception e) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 8f093c7..065e2bb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1113,7 +1113,7 @@
try {
final IBinder overlayWindowToken = new Binder();
mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY,
- displayId);
+ displayId, null /* options */);
synchronized (mLock) {
mOverlayWindowTokens.put(displayId, overlayWindowToken);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
index 7824fd9..9c54100 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
@@ -44,7 +44,7 @@
@Override
protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mCompletedTapCount + 1 == mTargetFingerCount) {
+ if (mCompletedTapCount + 1 == mTargetTapCount) {
// Calling super.onUp would complete the multi-tap version of this.
cancelGesture(event, rawEvent, policyFlags);
} else {
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f9f064c..e4a86c3 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -55,7 +55,6 @@
import android.content.pm.LauncherApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
@@ -66,9 +65,8 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
import android.graphics.Point;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -119,7 +117,6 @@
import com.android.internal.widget.IRemoteViewsFactory;
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
-import com.android.server.policy.IconUtilities;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -253,8 +250,6 @@
private boolean mSafeMode;
private int mMaxWidgetBitmapMemory;
- private IconUtilities mIconUtilities;
-
AppWidgetServiceImpl(Context context) {
mContext = context;
}
@@ -271,7 +266,6 @@
mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
mBackupRestoreController = new BackupRestoreController();
mSecurityPolicy = new SecurityPolicy();
- mIconUtilities = new IconUtilities(mContext);
computeMaximumWidgetBitmapMemory();
registerBroadcastReceiver();
@@ -578,44 +572,6 @@
}
}
- private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- // Load the unbadged application icon and pass it to the widget to appear on
- // the masked view.
- Context userContext = mContext.createPackageContextAsUser(providerPackage, 0,
- UserHandle.of(providerUserId));
- PackageManager pm = userContext.getPackageManager();
- Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm).mutate();
- // Create a bitmap of the icon which is what the widget's remoteview requires.
- icon.setColorFilter(mIconUtilities.getDisabledColorFilter());
- return mIconUtilities.createIconBitmap(icon);
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "Fail to get application icon", e);
- // Provider package removed, no need to mask its views as its state will be
- // purged very soon.
- return null;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge,
- PendingIntent onClickIntent) {
- RemoteViews views = new RemoteViews(mContext.getPackageName(),
- R.layout.work_widget_mask_view);
- if (icon != null) {
- views.setImageViewBitmap(R.id.work_widget_app_icon, icon);
- }
- if (!showBadge) {
- views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
- }
- if (onClickIntent != null) {
- views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent);
- }
- return views;
- }
-
/**
* Mask the target widget belonging to the specified provider, or all active widgets
* of the provider if target widget == null.
@@ -625,59 +581,63 @@
if (widgetCount == 0) {
return;
}
- final String providerPackage = provider.id.componentName.getPackageName();
- final int providerUserId = provider.getUserId();
- Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId);
- if (iconBitmap == null) {
- return;
- }
- final boolean showBadge;
- final Intent onClickIntent;
+ RemoteViews views = new RemoteViews(mContext.getPackageName(),
+ R.layout.work_widget_mask_view);
+ ApplicationInfo appInfo = provider.info.providerInfo.applicationInfo;
+ final int appUserId = provider.getUserId();
+ boolean showBadge;
+
final long identity = Binder.clearCallingIdentity();
try {
+ final Intent onClickIntent;
+
if (provider.maskedBySuspendedPackage) {
- showBadge = mUserManager.hasBadge(providerUserId);
+ showBadge = mUserManager.hasBadge(appUserId);
final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
- providerPackage, providerUserId);
+ appInfo.packageName, appUserId);
if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
- providerUserId, true);
+ appUserId, true);
} else {
final SuspendDialogInfo dialogInfo =
- mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
- suspendingPackage, providerUserId);
+ mPackageManagerInternal.getSuspendedDialogInfo(
+ appInfo.packageName, suspendingPackage, appUserId);
// onUnsuspend is null because we don't want to start any activity on
// unsuspending from a suspended widget.
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
- providerPackage, suspendingPackage, dialogInfo, null, null,
- providerUserId);
+ appInfo.packageName, suspendingPackage, dialogInfo, null, null,
+ appUserId);
}
} else if (provider.maskedByQuietProfile) {
showBadge = true;
- onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
- providerUserId);
+ onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId);
} else /* provider.maskedByLockedProfile */ {
showBadge = true;
- onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
- providerUserId);
+ onClickIntent = mKeyguardManager
+ .createConfirmDeviceCredentialIntent(null, null, appUserId);
if (onClickIntent != null) {
- onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
- | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ onClickIntent.setFlags(
+ FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
}
+
+ if (onClickIntent != null) {
+ views.setOnClickPendingIntent(R.id.work_widget_mask_frame,
+ PendingIntent.getActivity(mContext, 0, onClickIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
+ }
+
+ Icon icon = appInfo.icon != 0
+ ? Icon.createWithResource(appInfo.packageName, appInfo.icon)
+ : Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+ views.setImageViewIcon(R.id.work_widget_app_icon, icon);
+ if (!showBadge) {
+ views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
+ }
+
for (int j = 0; j < widgetCount; j++) {
Widget widget = provider.widgets.get(j);
if (targetWidget != null && targetWidget != widget) continue;
- PendingIntent intent = null;
- if (onClickIntent != null) {
- // Rare informational activity click is okay being
- // immutable; the tradeoff is more security in exchange for
- // losing bounds-based window animations
- intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
- onClickIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- }
- RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
if (widget.replaceWithMaskedViewsLocked(views)) {
scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
}
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 3c5268c..ba2a63a 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -3,6 +3,7 @@
aabhinav@google.com
bryanmawhinney@google.com
jstemmer@google.com
+millmore@google.com
nathch@google.com
niagra@google.com
niamhfw@google.com
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 04e08ae..55e3ef2 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -926,7 +926,7 @@
mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
deviceProfile, FgThread.getExecutor(), desc -> {
try {
- result.complete(requireNonNull(desc));
+ result.complete(desc);
} catch (Exception e) {
result.completeExceptionally(e);
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6de227e..37d2cdc 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -100,10 +100,10 @@
libs: [
"services.net",
"android.hardware.light-V2.0-java",
- "android.hardware.gnss-java",
- "android.hardware.power-java",
+ "android.hardware.gnss-V1-java",
+ "android.hardware.power-V1-java",
"android.hardware.power-V1.0-java",
- "android.hardware.vibrator-java",
+ "android.hardware.vibrator-V1-java",
"android.net.ipsec.ike.stubs.module_lib",
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
@@ -128,22 +128,22 @@
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
"android.hardware.health-V2.1-java",
- "android.hardware.light-java",
+ "android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.0-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.1-java",
- "android.hardware.biometrics.face-java",
+ "android.hardware.biometrics.face-V1-java",
"android.hardware.biometrics.fingerprint-V2.3-java",
- "android.hardware.biometrics.fingerprint-java",
+ "android.hardware.biometrics.fingerprint-V1-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
- "android.hardware.rebootescrow-java",
+ "android.hardware.rebootescrow-V1-java",
"android.hardware.soundtrigger-V2.3-java",
- "android.hardware.power.stats-java",
+ "android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
- "dnsresolver_aidl_interface-java",
+ "dnsresolver_aidl_interface-V7-java",
"icu4j_calendar_astronomer",
"netd-client",
"overlayable_policy_aidl-java",
@@ -158,9 +158,9 @@
srcs: [":services.core.unboosted"],
tools: ["lockedregioncodeinjection"],
cmd: "$(location lockedregioncodeinjection) " +
- " --targets \"Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowManagerGlobalLock;\" " +
- " --pre \"com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection\" " +
- " --post \"com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection\" " +
+ " --targets \"Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/am/ActivityManagerGlobalLock;,Lcom/android/server/wm/WindowManagerGlobalLock;\" " +
+ " --pre \"com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/am/ActivityManagerService.boostPriorityForProcLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection\" " +
+ " --post \"com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/am/ActivityManagerService.resetPriorityAfterProcLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection\" " +
" -o $(out) " +
" -i $(in)",
out: ["services.core.priorityboosted.jar"],
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
new file mode 100644
index 0000000..5eed0b5
--- /dev/null
+++ b/services/core/java/android/content/pm/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/pm/OWNERS
\ No newline at end of file
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index dad8bd8..6886cde 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -997,28 +997,6 @@
public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
/**
- * Register to listen for loading progress of an installed package.
- * @param packageName The name of the installed package
- * @param callback To loading reporting progress
- * @param userId The user under which to check.
- * @return Whether the registration was successful. It can fail if the package has not been
- * installed yet.
- */
- public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName,
- @NonNull InstalledLoadingProgressCallback callback, int userId);
-
- /**
- * Unregister to stop listening to loading progress of an installed package
- * @param packageName The name of the installed package
- * @param callback To unregister
- * @return True if the callback is removed from registered callback list. False is the callback
- * does not exist on the registered callback list, which can happen if the callback has
- * already been unregistered.
- */
- public abstract boolean unregisterInstalledLoadingProgressCallback(@NonNull String packageName,
- @NonNull InstalledLoadingProgressCallback callback);
-
- /**
* Returns the string representation of a known package. For example,
* {@link #PACKAGE_SETUP_WIZARD} is represented by the string Setup Wizard.
*
@@ -1137,7 +1115,8 @@
*/
public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
- @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler);
/**
diff --git a/services/core/java/android/os/OWNERS b/services/core/java/android/os/OWNERS
new file mode 100644
index 0000000..d0a2daf
--- /dev/null
+++ b/services/core/java/android/os/OWNERS
@@ -0,0 +1 @@
+per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/android/power/PowerStatsInternal.java b/services/core/java/android/power/PowerStatsInternal.java
index f7417da..40107a5 100644
--- a/services/core/java/android/power/PowerStatsInternal.java
+++ b/services/core/java/android/power/PowerStatsInternal.java
@@ -18,8 +18,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
import java.util.concurrent.CompletableFuture;
@@ -51,4 +55,45 @@
@NonNull
public abstract CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
int[] energyConsumerIds);
+
+ /**
+ * Returns the power entity info for all available {@link PowerEntity}
+ *
+ * @return List of available {@link PowerEntity}
+ */
+ public abstract PowerEntity[] getPowerEntityInfo();
+
+ /**
+ * Returns a CompletableFuture that will get a {@link StateResidencyResult} array for the
+ * available requested power entities.
+ *
+ * @param powerEntityIds Array of {@link PowerEntity.id} for which state residency is being
+ * requested.
+ *
+ * @return A Future containing a list of {@link StateResidencyResult} objects containing state
+ * residency results for all listed {@link PowerEntity.id}.
+ */
+ @NonNull
+ public abstract CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+ int[] powerEntityIds);
+
+ /**
+ * Returns the channel info for all available {@link Channel}
+ *
+ * @return List of available {@link Channel}
+ */
+ public abstract Channel[] getEnergyMeterInfo();
+
+ /**
+ * Returns a CompletableFuture that will get a {@link EnergyMeasurement} array for the
+ * available requested channels.
+ *
+ * @param channelIds Array of {@link Channel.id} for accumulated energy is being requested.
+ *
+ * @return A Future containing a list of {@link EnergyMeasurement} objects containing
+ * accumulated energy measurements for all listed {@link Channel.id}.
+ */
+ @NonNull
+ public abstract CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+ int[] channelIds);
}
diff --git a/services/core/java/com/android/server/BundleUtils.java b/services/core/java/com/android/server/BundleUtils.java
new file mode 100644
index 0000000..20ebe29
--- /dev/null
+++ b/services/core/java/com/android/server/BundleUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
+/**
+ * Utility methods for handling {@link Bundle}.
+ *
+ */
+public final class BundleUtils {
+ private BundleUtils() {
+ }
+
+ /**
+ * Returns true if {@code in} is null or empty.
+ */
+ public static boolean isEmpty(@Nullable Bundle in) {
+ return (in == null) || (in.size() == 0);
+ }
+
+ /**
+ * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
+ * bundle.
+ *
+ * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
+ * {@link Bundle#EMPTY})
+ */
+ public static @NonNull Bundle clone(@Nullable Bundle in) {
+ return (in != null) ? new Bundle(in) : new Bundle();
+ }
+
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 96c3e57..3933e37 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -120,6 +120,7 @@
import android.net.NetworkTestResultParcelable;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
+import android.net.OemNetworkPreferences;
import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
import android.net.QosCallbackException;
@@ -222,6 +223,7 @@
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -280,15 +282,18 @@
// connect anyway?" dialog after the user selects a network that doesn't validate.
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
- // Default to 30s linger time-out. Modifiable only for testing.
+ // Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing.
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+ private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
// The maximum number of network request allowed per uid before an exception is thrown.
private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
@VisibleForTesting
protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
+ @VisibleForTesting
+ protected int mNascentDelayMs;
// How long to delay to removal of a pending intent based request.
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
@@ -739,11 +744,11 @@
}
private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
- boolean isFallbackNetwork) {
+ boolean isDefaultNetwork) {
if (DBG) {
log("Sending " + state
+ " broadcast for type " + type + " " + nai.toShortString()
- + " isFallbackNetwork=" + isFallbackNetwork);
+ + " isDefaultNetwork=" + isDefaultNetwork);
}
}
@@ -762,10 +767,10 @@
list.add(nai);
}
- // Send a broadcast if this is the first network of its type or if it's the fallback.
- final boolean isFallbackNetwork = mService.isFallbackNetwork(nai);
- if ((list.size() == 1) || isFallbackNetwork) {
- maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isFallbackNetwork);
+ // Send a broadcast if this is the first network of its type or if it's the default.
+ final boolean isDefaultNetwork = mService.isDefaultNetwork(nai);
+ if ((list.size() == 1) || isDefaultNetwork) {
+ maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
}
}
@@ -794,7 +799,7 @@
", sending connected broadcast");
final NetworkAgentInfo replacement = list.get(0);
maybeLogBroadcast(replacement, DetailedState.CONNECTED, type,
- mService.isFallbackNetwork(replacement));
+ mService.isDefaultNetwork(replacement));
mService.sendLegacyNetworkBroadcast(replacement, DetailedState.CONNECTED, type);
}
}
@@ -810,14 +815,14 @@
// send out another legacy broadcast - currently only used for suspend/unsuspend
// toggle
public void update(NetworkAgentInfo nai) {
- final boolean isFallback = mService.isFallbackNetwork(nai);
+ final boolean isDefault = mService.isDefaultNetwork(nai);
final DetailedState state = nai.networkInfo.getDetailedState();
for (int type = 0; type < mTypeLists.length; type++) {
final ArrayList<NetworkAgentInfo> list = mTypeLists[type];
final boolean contains = (list != null && list.contains(nai));
final boolean isFirst = contains && (nai == list.get(0));
- if (isFirst || contains && isFallback) {
- maybeLogBroadcast(nai, state, type, isFallback);
+ if (isFirst || contains && isDefault) {
+ maybeLogBroadcast(nai, state, type, isDefault);
mService.sendLegacyNetworkBroadcast(nai, state, type);
}
}
@@ -990,6 +995,15 @@
}
/**
+ * Gets the UID that owns a socket connection. Needed because opening SOCK_DIAG sockets
+ * requires CAP_NET_ADMIN, which the unit tests do not have.
+ */
+ public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote) {
+ return InetDiagMessage.getConnectionOwnerUid(protocol, local, remote);
+ }
+
+ /**
* @see MultinetworkPolicyTracker
*/
public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(
@@ -1022,12 +1036,12 @@
mMetricsLog = logger;
mNetworkRanker = new NetworkRanker();
- final NetworkRequest fallbackRequest = createDefaultInternetRequestForTransport(
+ final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport(
-1, NetworkRequest.Type.REQUEST);
- mFallbackRequest = new NetworkRequestInfo(null, fallbackRequest, new Binder());
- mNetworkRequests.put(fallbackRequest, mFallbackRequest);
- mDefaultNetworkRequests.add(mFallbackRequest);
- mNetworkRequestInfoLogs.log("REGISTER " + mFallbackRequest);
+ mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder());
+ mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
+ mDefaultNetworkRequests.add(mDefaultRequest);
+ mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
@@ -1053,6 +1067,8 @@
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+ // TODO: Consider making the timer customizable.
+ mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
@@ -1217,6 +1233,14 @@
mDnsManager = new DnsManager(mContext, mDnsResolver);
registerPrivateDnsSettingsCallbacks();
+
+ mNoServiceNetwork = new NetworkAgentInfo(null,
+ new Network(NO_SERVICE_NET_ID),
+ new NetworkInfo(TYPE_NONE, 0, "", ""),
+ new LinkProperties(), new NetworkCapabilities(), 0, mContext,
+ null, new NetworkAgentConfig(), this, null,
+ null, null, 0, INVALID_UID,
+ mQosCallbackTracker);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1366,7 +1390,7 @@
}
private NetworkState getUnfilteredActiveNetworkState(int uid) {
- NetworkAgentInfo nai = getFallbackNetwork();
+ NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
final Network[] networks = getVpnUnderlyingNetworks(uid);
if (networks != null) {
@@ -1499,7 +1523,7 @@
}
}
- NetworkAgentInfo nai = getFallbackNetwork();
+ NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
ignoreBlocked)) {
return null;
@@ -1638,21 +1662,28 @@
HashMap<Network, NetworkCapabilities> result = new HashMap<>();
- final NetworkAgentInfo nai = getFallbackNetwork();
- NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
- if (nc != null) {
- result.put(
- nai.network,
- createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, mDeps.getCallingUid(), callingPackageName));
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ if (!nri.isBeingSatisfied()) {
+ continue;
+ }
+ final NetworkAgentInfo nai = nri.getSatisfier();
+ final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
+ if (null != nc
+ && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ && !result.containsKey(nai.network)) {
+ result.put(
+ nai.network,
+ createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+ nc, mDeps.getCallingUid(), callingPackageName));
+ }
}
// No need to check mLockdownEnabled. If it's true, getVpnUnderlyingNetworks returns null.
final Network[] networks = getVpnUnderlyingNetworks(Binder.getCallingUid());
- if (networks != null) {
- for (Network network : networks) {
- nc = getNetworkCapabilitiesInternal(network);
- if (nc != null) {
+ if (null != networks) {
+ for (final Network network : networks) {
+ final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
+ if (null != nc) {
result.put(
network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
@@ -1674,9 +1705,7 @@
/**
* Return LinkProperties for the active (i.e., connected) default
- * network interface. It is assumed that at most one default network
- * is active at a time. If more than one is active, it is indeterminate
- * which will be returned.
+ * network interface for the calling uid.
* @return the ip properties for the active network, or {@code null} if
* none is active
*/
@@ -2007,7 +2036,7 @@
mHandler.sendMessage(mHandler.obtainMessage(
EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
new PrivateDnsValidationUpdate(netId,
- InetAddress.parseNumericAddress(ipAddress),
+ InetAddresses.parseNumericAddress(ipAddress),
hostname, validated)));
} catch (IllegalArgumentException e) {
loge("Error parsing ip address in validation event");
@@ -2025,7 +2054,7 @@
// TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one
// callback from each caller type. Need to re-factor NetdEventListenerService to allow
// multiple NetworkMonitor registrants.
- if (nai != null && nai.satisfies(mFallbackRequest.mRequests.get(0))) {
+ if (nai != null && nai.satisfies(mDefaultRequest.mRequests.get(0))) {
nai.networkMonitor().notifyDnsResponse(returnCode);
}
}
@@ -2119,8 +2148,8 @@
private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
boolean isBackgroundRestricted) {
- return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
- isNetworkMetered, isBackgroundRestricted);
+ return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted);
}
/**
@@ -2582,12 +2611,12 @@
pw.println();
pw.println();
- final NetworkAgentInfo fallbackNai = getFallbackNetwork();
+ final NetworkAgentInfo defaultNai = getDefaultNetwork();
pw.print("Active default network: ");
- if (fallbackNai == null) {
+ if (defaultNai == null) {
pw.println("none");
} else {
- pw.println(fallbackNai.network.getNetId());
+ pw.println(defaultNai.network.getNetId());
}
pw.println();
@@ -2703,9 +2732,9 @@
pw.println(nai.requestAt(i).toString());
}
pw.decreaseIndent();
- pw.println("Lingered:");
+ pw.println("Inactivity Timers:");
pw.increaseIndent();
- nai.dumpLingerTimers(pw);
+ nai.dumpInactivityTimers(pw);
pw.decreaseIndent();
pw.decreaseIndent();
}
@@ -2970,7 +2999,7 @@
final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
final boolean wasValidated = nai.lastValidated;
- final boolean wasFallback = isFallbackNetwork(nai);
+ final boolean wasDefault = isDefaultNetwork(nai);
if (DBG) {
final String logMsg = !TextUtils.isEmpty(redirectUrl)
@@ -2979,7 +3008,7 @@
log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
}
if (valid != nai.lastValidated) {
- if (wasFallback) {
+ if (wasDefault) {
mMetricsLog.logDefaultNetworkValidity(valid);
}
final int oldScore = nai.getCurrentScore();
@@ -3300,27 +3329,27 @@
}
/**
- * Updates the linger state from the network requests inside the NAI.
+ * Updates the inactivity state from the network requests inside the NAI.
* @param nai the agent info to update
* @param now the timestamp of the event causing this update
- * @return whether the network was lingered as a result of this update
+ * @return whether the network was inactive as a result of this update
*/
- private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
- // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
- // 2. If the network was lingering and there are now requests, unlinger it.
+ private boolean updateInactivityState(@NonNull final NetworkAgentInfo nai, final long now) {
+ // 1. Update the inactivity timer. If it's changed, reschedule or cancel the alarm.
+ // 2. If the network was inactive and there are now requests, unset inactive.
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
- // one lingered request, start lingering.
- nai.updateLingerTimer();
- if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
- if (DBG) log("Unlingering " + nai.toShortString());
- nai.unlinger();
+ // one lingered request, set inactive.
+ nai.updateInactivityTimer();
+ if (nai.isInactive() && nai.numForegroundNetworkRequests() > 0) {
+ if (DBG) log("Unsetting inactive " + nai.toShortString());
+ nai.unsetInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
- } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
+ } else if (unneeded(nai, UnneededFor.LINGER) && nai.getInactivityExpiry() > 0) {
if (DBG) {
- final int lingerTime = (int) (nai.getLingerExpiry() - now);
- log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
+ final int lingerTime = (int) (nai.getInactivityExpiry() - now);
+ log("Setting inactive " + nai.toShortString() + " for " + lingerTime + "ms");
}
- nai.linger();
+ nai.setInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
return true;
}
@@ -3334,7 +3363,6 @@
if (VDBG) log("NetworkFactory connected");
// Finish setting up the full connection
NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
- npi.completeConnection();
sendAllRequestsToProvider(npi);
} else {
loge("Error connecting NetworkFactory");
@@ -3355,13 +3383,13 @@
loge("Error connecting NetworkAgent");
mNetworkAgentInfos.remove(nai);
if (nai != null) {
- final boolean wasFallback = isFallbackNetwork(nai);
+ final boolean wasDefault = isDefaultNetwork(nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.remove(nai.network.getNetId());
}
mNetIdManager.releaseNetId(nai.network.getNetId());
// Just in case.
- mLegacyTypeTracker.remove(nai, wasFallback);
+ mLegacyTypeTracker.remove(nai, wasDefault);
}
}
}
@@ -3400,8 +3428,8 @@
nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
null, null);
}
- final boolean wasFallback = isFallbackNetwork(nai);
- if (wasFallback) {
+ final boolean wasDefault = isDefaultNetwork(nai);
+ if (wasDefault) {
mDefaultInetConditionPublished = 0;
// Log default network disconnection before required book-keeping.
// Let rematchAllNetworksAndRequests() below record a new default network event
@@ -3436,17 +3464,20 @@
propagateUnderlyingNetworkCapabilities(nai.network);
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
- NetworkRequest request = nai.requestAt(i);
+ final NetworkRequest request = nai.requestAt(i);
final NetworkRequestInfo nri = mNetworkRequests.get(request);
final NetworkAgentInfo currentNetwork = nri.getSatisfier();
if (currentNetwork != null
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
+ // uid rules for this network will be removed in destroyNativeNetwork(nai).
nri.setSatisfier(null, null);
- sendUpdatedScoreToFactories(request, null);
+ if (request.isRequest()) {
+ sendUpdatedScoreToFactories(request, null);
+ }
- if (mFallbackRequest == nri) {
+ if (mDefaultRequest == nri) {
// TODO : make battery stats aware that since 2013 multiple interfaces may be
- // active at the same time. For now keep calling this with the fallback
+ // active at the same time. For now keep calling this with the default
// network, because while incorrect this is the closest to the old (also
// incorrect) behavior.
mNetworkActivityTracker.updateDataActivityTracking(
@@ -3456,11 +3487,11 @@
}
}
}
- nai.clearLingerState();
+ nai.clearInactivityState();
// TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
- // Currently, deleting it breaks tests that check for the fallback network disconnecting.
+ // Currently, deleting it breaks tests that check for the default network disconnecting.
// Find out why, fix the rematch code, and delete this.
- mLegacyTypeTracker.remove(nai, wasFallback);
+ mLegacyTypeTracker.remove(nai, wasDefault);
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
if (nai.created) {
@@ -3468,10 +3499,9 @@
// (routing rules, DNS, etc).
// This may be slow as it requires a lot of netd shelling out to ip and
// ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
- // after we've rematched networks with requests which should make a potential
- // fallback network the default or requested a new network from the
- // NetworkProviders, so network traffic isn't interrupted for an unnecessarily
- // long time.
+ // after we've rematched networks with requests (which might change the default
+ // network or service a new request from an app), so network traffic isn't interrupted
+ // for an unnecessarily long time.
destroyNativeNetwork(nai);
mDnsManager.removeNetwork(nai.network);
}
@@ -3556,18 +3586,15 @@
}
}
rematchAllNetworksAndRequests();
- // If an active request exists, return as its score has already been sent if needed.
- if (null != nri.getActiveRequest()) {
+ // If the nri is satisfied, return as its score has already been sent if needed.
+ if (nri.isBeingSatisfied()) {
return;
}
// As this request was not satisfied on rematch and thus never had any scores sent to the
// factories, send null now for each request of type REQUEST.
for (final NetworkRequest req : nri.mRequests) {
- if (!req.isRequest()) {
- continue;
- }
- sendUpdatedScoreToFactories(req, null);
+ if (req.isRequest()) sendUpdatedScoreToFactories(req, null);
}
}
@@ -3607,7 +3634,7 @@
return true;
}
- if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) {
+ if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) {
return false;
}
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
@@ -3700,7 +3727,7 @@
if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
return;
}
- if (nri.getSatisfier() != null) {
+ if (nri.isBeingSatisfied()) {
return;
}
if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
@@ -3744,7 +3771,7 @@
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (null != nri.getActiveRequest()) {
- if (nri.getActiveRequest().isRequest()) {
+ if (!nri.getActiveRequest().isListen()) {
removeSatisfiedNetworkRequestFromNetwork(nri);
} else {
nri.setSatisfier(null, null);
@@ -3799,7 +3826,7 @@
// If there are still lingered requests on this network, don't tear it down,
// but resume lingering instead.
final long now = SystemClock.elapsedRealtime();
- if (updateLingerState(nai, now)) {
+ if (updateInactivityState(nai, now)) {
notifyNetworkLosing(nai, now);
}
if (unneeded(nai, UnneededFor.TEARDOWN)) {
@@ -4260,7 +4287,7 @@
@Override
public NetworkRequest getDefaultRequest() {
- return mFallbackRequest.mRequests.get(0);
+ return mDefaultRequest.mRequests.get(0);
}
private class InternalHandler extends Handler {
@@ -4506,7 +4533,7 @@
// revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event.
final NetworkAgentInfo nai;
if (network == null) {
- nai = getFallbackNetwork();
+ nai = getDefaultNetwork();
} else {
nai = getNetworkAgentInfoForNetwork(network);
}
@@ -4525,7 +4552,7 @@
Network network, int uid, boolean hasConnectivity) {
final NetworkAgentInfo nai;
if (network == null) {
- nai = getFallbackNetwork();
+ nai = getDefaultNetwork();
} else {
nai = getNetworkAgentInfoForNetwork(network);
}
@@ -4838,7 +4865,7 @@
}
synchronized (mVpns) {
throwIfLockdownEnabled();
- mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
+ mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
}
}
@@ -4891,7 +4918,8 @@
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
if (underlyingNetworks == null) {
- final NetworkAgentInfo defaultNai = getFallbackNetwork();
+ final NetworkAgentInfo defaultNai = getDefaultNetworkForUid(
+ nai.networkCapabilities.getOwnerUid());
if (defaultNai != null) {
underlyingNetworks = new Network[] { defaultNai.network };
}
@@ -4942,8 +4970,10 @@
}
}
- private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
- final Network defaultNetwork = getNetwork(getFallbackNetwork());
+ // TODO This needs to be the default network that applies to the NAI.
+ private Network[] underlyingNetworksOrDefault(final int ownerUid,
+ Network[] underlyingNetworks) {
+ final Network defaultNetwork = getNetwork(getDefaultNetworkForUid(ownerUid));
if (underlyingNetworks == null && defaultNetwork != null) {
// null underlying networks means to track the default.
underlyingNetworks = new Network[] { defaultNetwork };
@@ -4956,7 +4986,8 @@
// TODO: support more than one level of underlying networks, either via a fixed-depth search
// (e.g., 2 levels of underlying networks), or via loop detection, or....
if (!nai.supportsUnderlyingNetworks()) return false;
- final Network[] underlying = underlyingNetworksOrDefault(nai.declaredUnderlyingNetworks);
+ final Network[] underlying = underlyingNetworksOrDefault(
+ nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks);
return ArrayUtils.contains(underlying, network);
}
@@ -5414,27 +5445,21 @@
private static class NetworkProviderInfo {
public final String name;
public final Messenger messenger;
- private final AsyncChannel mAsyncChannel;
private final IBinder.DeathRecipient mDeathRecipient;
public final int providerId;
NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
- int providerId, IBinder.DeathRecipient deathRecipient) {
+ int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
this.name = name;
this.messenger = messenger;
this.providerId = providerId;
- mAsyncChannel = asyncChannel;
mDeathRecipient = deathRecipient;
- if ((mAsyncChannel == null) == (mDeathRecipient == null)) {
- throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient");
+ if (mDeathRecipient == null) {
+ throw new AssertionError("Must pass a deathRecipient");
}
}
- boolean isLegacyNetworkFactory() {
- return mAsyncChannel != null;
- }
-
void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
try {
messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
@@ -5445,38 +5470,19 @@
}
void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
- servingProviderId, request);
- } else {
- sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
+ sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
servingProviderId, request);
- }
}
void cancelRequest(NetworkRequest request) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
- } else {
- sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
- }
+ sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
}
void connect(Context context, Handler handler) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.connect(context, handler, messenger);
- } else {
- try {
- messenger.getBinder().linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e) {
- mDeathRecipient.binderDied();
- }
- }
- }
-
- void completeConnection() {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ try {
+ messenger.getBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ mDeathRecipient.binderDied();
}
}
}
@@ -5512,9 +5518,8 @@
mActiveRequest = activeRequest;
}
- // The network currently satisfying this request, or null if none. Must only be touched
- // on the handler thread. This only makes sense for network requests and not for listens,
- // as defined by NetworkRequest#isRequest(). For listens, this is always null.
+ // The network currently satisfying this NRI. Only one request in an NRI can have a
+ // satisfier. For non-multilayer requests, only non-listen requests can have a satisfier.
@Nullable
private NetworkAgentInfo mSatisfier;
NetworkAgentInfo getSatisfier() {
@@ -5537,6 +5542,18 @@
final int mUid;
final Messenger messenger;
+ /**
+ * Get the list of UIDs this nri applies to.
+ */
+ @NonNull
+ private Set<UidRange> getUids() {
+ // networkCapabilities.getUids() returns a defensive copy.
+ // multilayer requests will all have the same uids so return the first one.
+ final Set<UidRange> uids = null == mRequests.get(0).networkCapabilities.getUids()
+ ? new ArraySet<>() : mRequests.get(0).networkCapabilities.getUids();
+ return uids;
+ }
+
NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
@@ -5570,6 +5587,13 @@
this(r, null);
}
+ // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
+ // set to the mNoServiceNetwork in which case mActiveRequest will be null thus returning
+ // false.
+ boolean isBeingSatisfied() {
+ return (null != mSatisfier && null != mActiveRequest);
+ }
+
boolean isMultilayerRequest() {
return mRequests.size() > 1;
}
@@ -5595,7 +5619,9 @@
@Override
public String toString() {
- return "uid/pid:" + mUid + "/" + mPid + " " + mRequests
+ return "uid/pid:" + mUid + "/" + mPid + " active request Id: "
+ + (mActiveRequest == null ? null : mActiveRequest.requestId)
+ + " " + mRequests
+ (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -5951,15 +5977,6 @@
EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));
}
- @Override
- public int registerNetworkFactory(Messenger messenger, String name) {
- enforceNetworkFactoryPermission();
- NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
- nextNetworkProviderId(), null /* deathRecipient */);
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
- return npi.providerId;
- }
-
private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
if (mNetworkProviderInfos.containsKey(npi.messenger)) {
// Avoid creating duplicates. even if an app makes a direct AIDL call.
@@ -5973,10 +5990,7 @@
if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
mNetworkProviderInfos.put(npi.messenger, npi);
npi.connect(mContext, mTrackerHandler);
- if (!npi.isLegacyNetworkFactory()) {
- // Legacy NetworkFactories get their requests when their AsyncChannel connects.
- sendAllRequestsToProvider(npi);
- }
+ sendAllRequestsToProvider(npi);
}
@Override
@@ -5995,11 +6009,6 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
}
- @Override
- public void unregisterNetworkFactory(Messenger messenger) {
- unregisterNetworkProvider(messenger);
- }
-
private void handleUnregisterNetworkProvider(Messenger messenger) {
NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
if (npi == null) {
@@ -6050,11 +6059,31 @@
// The always-on request for an Internet-capable network that apps without a specific default
// fall back to.
@NonNull
- private final NetworkRequestInfo mFallbackRequest;
+ private final NetworkRequestInfo mDefaultRequest;
// Collection of NetworkRequestInfo's used for default networks.
@NonNull
private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
+ private boolean isPerAppDefaultRequest(@NonNull final NetworkRequestInfo nri) {
+ return (mDefaultNetworkRequests.contains(nri) && mDefaultRequest != nri);
+ }
+
+ /**
+ * Determine if an nri is a managed default request that disallows default networking.
+ * @param nri the request to evaluate
+ * @return true if device-default networking is disallowed
+ */
+ private boolean isDefaultBlocked(@NonNull final NetworkRequestInfo nri) {
+ // Check if this nri is a managed default that supports the default network at its
+ // lowest priority request.
+ final NetworkRequest defaultNetworkRequest = mDefaultRequest.mRequests.get(0);
+ final NetworkCapabilities lowestPriorityNetCap =
+ nri.mRequests.get(nri.mRequests.size() - 1).networkCapabilities;
+ return isPerAppDefaultRequest(nri)
+ && !(defaultNetworkRequest.networkCapabilities.equalRequestableCapabilities(
+ lowestPriorityNetCap));
+ }
+
// Request used to optionally keep mobile data active even when higher
// priority networks like Wi-Fi are active.
private final NetworkRequest mDefaultMobileDataRequest;
@@ -6066,10 +6095,37 @@
// Request used to optionally keep vehicle internal network always active
private final NetworkRequest mDefaultVehicleRequest;
- // TODO: b/178729499 update this in favor of a method taking in a UID.
- // The NetworkAgentInfo currently satisfying the fallback request, if any.
- private NetworkAgentInfo getFallbackNetwork() {
- return mFallbackRequest.mSatisfier;
+ // TODO replace with INetd.DUMMY_NET_ID when available.
+ private static final int NO_SERVICE_NET_ID = 51;
+ // Sentinel NAI used to direct apps with default networks that should have no connectivity to a
+ // network with no service. This NAI should never be matched against, nor should any public API
+ // ever return the associated network. For this reason, this NAI is not in the list of available
+ // NAIs. It is used in computeNetworkReassignment() to be set as the satisfier for non-device
+ // default requests that don't support using the device default network which will ultimately
+ // allow ConnectivityService to use this no-service network when calling makeDefaultForApps().
+ @VisibleForTesting
+ final NetworkAgentInfo mNoServiceNetwork;
+
+ // The NetworkAgentInfo currently satisfying the default request, if any.
+ private NetworkAgentInfo getDefaultNetwork() {
+ return mDefaultRequest.mSatisfier;
+ }
+
+ private NetworkAgentInfo getDefaultNetworkForUid(final int uid) {
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ // Currently, all network requests will have the same uids therefore checking the first
+ // one is sufficient. If/when uids are tracked at the nri level, this can change.
+ final Set<UidRange> uids = nri.mRequests.get(0).networkCapabilities.getUids();
+ if (null == uids) {
+ continue;
+ }
+ for (final UidRange range : uids) {
+ if (range.contains(uid)) {
+ return nri.getSatisfier();
+ }
+ }
+ }
+ return getDefaultNetwork();
}
@Nullable
@@ -6086,8 +6142,8 @@
}
@VisibleForTesting
- protected boolean isFallbackNetwork(NetworkAgentInfo nai) {
- return nai == getFallbackNetwork();
+ protected boolean isDefaultNetwork(NetworkAgentInfo nai) {
+ return nai == getDefaultNetwork();
}
// TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
@@ -6156,8 +6212,6 @@
LinkProperties lp = new LinkProperties(linkProperties);
- // TODO: Instead of passing mFallbackRequest, provide an API to determine whether a Network
- // satisfies mFallbackRequest.
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
@@ -6234,7 +6288,7 @@
// for (LinkProperties lp : newLp.getStackedLinks()) {
// updateMtu(lp, null);
// }
- if (isFallbackNetwork(networkAgent)) {
+ if (isDefaultNetwork(networkAgent)) {
updateTcpBufferSizes(newLp.getTcpBufferSizes());
}
@@ -6246,7 +6300,7 @@
// updateDnses will fetch the private DNS configuration from DnsManager.
mDnsManager.updatePrivateDnsStatus(netId, newLp);
- if (isFallbackNetwork(networkAgent)) {
+ if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
updateProxy(newLp, oldLp);
@@ -6580,7 +6634,8 @@
@VisibleForTesting
void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
@NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
- underlyingNetworks = underlyingNetworksOrDefault(underlyingNetworks);
+ underlyingNetworks = underlyingNetworksOrDefault(
+ agentCaps.getOwnerUid(), underlyingNetworks);
int[] transportTypes = agentCaps.getTransportTypes();
int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -6952,8 +7007,8 @@
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
- // Don't send listening requests to factories. b/17393458
- if (nr.isListen()) continue;
+ // Don't send listening or track default request to factories. b/17393458
+ if (!nr.isRequest()) continue;
sendUpdatedScoreToFactories(nr, nai);
}
}
@@ -7015,10 +7070,10 @@
ensureRunningOnConnectivityServiceThread();
for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
for (final NetworkRequest req : nri.mRequests) {
- if (req.isListen() && nri.getActiveRequest() == req) {
+ if (!req.isRequest() && nri.getActiveRequest() == req) {
break;
}
- if (req.isListen()) {
+ if (!req.isRequest()) {
continue;
}
// Only set the nai for the request it is satisfying.
@@ -7168,8 +7223,8 @@
if (nai.numRequestNetworkRequests() != 0) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
- // Ignore listening requests.
- if (nr.isListen()) continue;
+ // Ignore listening and track default requests.
+ if (!nr.isRequest()) continue;
loge("Dead network still had at least " + nr);
break;
}
@@ -7186,13 +7241,13 @@
// If we get here it means that the last linger timeout for this network expired. So there
// must be no other active linger timers, and we must stop lingering.
- oldNetwork.clearLingerState();
+ oldNetwork.clearInactivityState();
if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
// Tear the network down.
teardownUnneededNetwork(oldNetwork);
} else {
- // Put the network in the background.
+ // Put the network in the background if it doesn't satisfy any foreground request.
updateCapabilitiesForNetwork(oldNetwork);
}
}
@@ -7224,21 +7279,20 @@
log("Switching to new default network for: " + nri + " using " + newDefaultNetwork);
}
- try {
- // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes.
- if (mFallbackRequest != nri) {
- return;
- }
-
- if (null != newDefaultNetwork) {
- mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
- } else {
- mNetd.networkClearDefault();
- }
- } catch (RemoteException | ServiceSpecificException e) {
- loge("Exception setting default network :" + e);
+ // Fix up the NetworkCapabilities of any networks that have this network as underlying.
+ if (newDefaultNetwork != null) {
+ propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
}
+ // Set an app level managed default and return since further processing only applies to the
+ // default network.
+ if (mDefaultRequest != nri) {
+ makeDefaultForApps(nri, oldDefaultNetwork, newDefaultNetwork);
+ return;
+ }
+
+ makeDefaultNetwork(newDefaultNetwork);
+
if (oldDefaultNetwork != null) {
mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
}
@@ -7249,10 +7303,6 @@
updateTcpBufferSizes(null != newDefaultNetwork
? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
notifyIfacesChangedForNetworkStats();
- // Fix up the NetworkCapabilities of any networks that have this network as underlying.
- if (newDefaultNetwork != null) {
- propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
- }
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
@@ -7276,6 +7326,49 @@
prevNetwork, prevScore, prevLp, prevNc);
}
+ private void makeDefaultForApps(@NonNull final NetworkRequestInfo nri,
+ @Nullable final NetworkAgentInfo oldDefaultNetwork,
+ @Nullable final NetworkAgentInfo newDefaultNetwork) {
+ try {
+ if (VDBG) {
+ log("Setting default network for " + nri
+ + " using UIDs " + nri.getUids()
+ + " with old network " + (oldDefaultNetwork != null
+ ? oldDefaultNetwork.network().getNetId() : "null")
+ + " and new network " + (newDefaultNetwork != null
+ ? newDefaultNetwork.network().getNetId() : "null"));
+ }
+ if (nri.getUids().isEmpty()) {
+ throw new IllegalStateException("makeDefaultForApps called without specifying"
+ + " any applications to set as the default." + nri);
+ }
+ if (null != newDefaultNetwork) {
+ mNetd.networkAddUidRanges(
+ newDefaultNetwork.network.getNetId(),
+ toUidRangeStableParcels(nri.getUids()));
+ }
+ if (null != oldDefaultNetwork) {
+ mNetd.networkRemoveUidRanges(
+ oldDefaultNetwork.network.getNetId(),
+ toUidRangeStableParcels(nri.getUids()));
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ loge("Exception setting OEM network preference default network :" + e);
+ }
+ }
+
+ private void makeDefaultNetwork(@Nullable final NetworkAgentInfo newDefaultNetwork) {
+ try {
+ if (null != newDefaultNetwork) {
+ mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
+ } else {
+ mNetd.networkClearDefault();
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ loge("Exception setting default network :" + e);
+ }
+ }
+
private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
// For consistency with previous behaviour, send onLost callbacks before onAvailable.
processNewlyLostListenRequests(nai);
@@ -7397,9 +7490,9 @@
@Nullable final NetworkAgentInfo previousSatisfier,
@Nullable final NetworkAgentInfo newSatisfier,
final long now) {
- if (newSatisfier != null) {
+ if (null != newSatisfier && mNoServiceNetwork != newSatisfier) {
if (VDBG) log("rematch for " + newSatisfier.toShortString());
- if (previousSatisfier != null) {
+ if (null != previousSatisfier && mNoServiceNetwork != previousSatisfier) {
if (VDBG || DDBG) {
log(" accepting network in place of " + previousSatisfier.toShortString());
}
@@ -7408,12 +7501,20 @@
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
+
+ // To prevent constantly CPU wake up for nascent timer, if a network comes up
+ // and immediately satisfies a request then remove the timer. This will happen for
+ // all networks except in the case of an underlying network for a VCN.
+ if (newSatisfier.isNascent()) {
+ newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
+ }
+
newSatisfier.unlingerRequest(newRequest.requestId);
if (!newSatisfier.addRequest(newRequest)) {
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ newRequest);
}
- } else {
+ } else if (null != previousSatisfier) {
if (DBG) {
log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
+ " request " + previousRequest.requestId);
@@ -7464,7 +7565,11 @@
break;
}
}
- if (bestNetwork != nri.mSatisfier) {
+ if (null == bestNetwork && isDefaultBlocked(nri)) {
+ // Remove default networking if disallowed for managed default requests.
+ bestNetwork = mNoServiceNetwork;
+ }
+ if (nri.getSatisfier() != bestNetwork) {
// bestNetwork may be null if no network can satisfy this request.
changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
@@ -7546,19 +7651,19 @@
}
}
- // Update the linger state before processing listen callbacks, because the background
- // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+ // Update the inactivity state before processing listen callbacks, because the background
+ // computation depends on whether the network is inactive. Don't send the LOSING callbacks
// just yet though, because they have to be sent after the listens are processed to keep
// backward compatibility.
- final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
+ final ArrayList<NetworkAgentInfo> inactiveNetworks = new ArrayList<>();
for (final NetworkAgentInfo nai : nais) {
- // Rematching may have altered the linger state of some networks, so update all linger
- // timers. updateLingerState reads the state from the network agent and does nothing
- // if the state has not changed : the source of truth is controlled with
- // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
- // called while rematching the individual networks above.
- if (updateLingerState(nai, now)) {
- lingeredNetworks.add(nai);
+ // Rematching may have altered the inactivity state of some networks, so update all
+ // inactivity timers. updateInactivityState reads the state from the network agent
+ // and does nothing if the state has not changed : the source of truth is controlled
+ // with NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which
+ // have been called while rematching the individual networks above.
+ if (updateInactivityState(nai, now)) {
+ inactiveNetworks.add(nai);
}
}
@@ -7575,7 +7680,11 @@
processNewlySatisfiedListenRequests(nai);
}
- for (final NetworkAgentInfo nai : lingeredNetworks) {
+ for (final NetworkAgentInfo nai : inactiveNetworks) {
+ // For nascent networks, if connecting with no foreground request, skip broadcasting
+ // LOSING for backward compatibility. This is typical when mobile data connected while
+ // wifi connected with mobile data always-on enabled.
+ if (nai.isNascent()) continue;
notifyNetworkLosing(nai, now);
}
@@ -7584,7 +7693,7 @@
// Tear down all unneeded networks.
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (nai.getLingerExpiry() > 0) {
+ if (nai.getInactivityExpiry() > 0) {
// This network has active linger timers and no requests, but is not
// lingering. Linger it.
//
@@ -7592,7 +7701,7 @@
// and became unneeded due to another network improving its score to the
// point where this network will no longer be able to satisfy any requests
// even if it validates.
- if (updateLingerState(nai, now)) {
+ if (updateInactivityState(nai, now)) {
notifyNetworkLosing(nai, now);
}
} else {
@@ -7624,34 +7733,34 @@
private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
@NonNull final NetworkReassignment changes,
@NonNull final Collection<NetworkAgentInfo> nais) {
- final NetworkReassignment.RequestReassignment fallbackReassignment =
- changes.getReassignment(mFallbackRequest);
- final NetworkAgentInfo oldFallbackNetwork =
- null != fallbackReassignment ? fallbackReassignment.mOldNetwork : null;
- final NetworkAgentInfo newFallbackNetwork =
- null != fallbackReassignment ? fallbackReassignment.mNewNetwork : null;
+ final NetworkReassignment.RequestReassignment reassignmentOfDefault =
+ changes.getReassignment(mDefaultRequest);
+ final NetworkAgentInfo oldDefaultNetwork =
+ null != reassignmentOfDefault ? reassignmentOfDefault.mOldNetwork : null;
+ final NetworkAgentInfo newDefaultNetwork =
+ null != reassignmentOfDefault ? reassignmentOfDefault.mNewNetwork : null;
- if (oldFallbackNetwork != newFallbackNetwork) {
+ if (oldDefaultNetwork != newDefaultNetwork) {
// Maintain the illusion : since the legacy API only understands one network at a time,
// if the default network changed, apps should see a disconnected broadcast for the
// old default network before they see a connected broadcast for the new one.
- if (oldFallbackNetwork != null) {
- mLegacyTypeTracker.remove(oldFallbackNetwork.networkInfo.getType(),
- oldFallbackNetwork, true);
+ if (oldDefaultNetwork != null) {
+ mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
+ oldDefaultNetwork, true);
}
- if (newFallbackNetwork != null) {
+ if (newDefaultNetwork != null) {
// The new default network can be newly null if and only if the old default
// network doesn't satisfy the default request any more because it lost a
// capability.
- mDefaultInetConditionPublished = newFallbackNetwork.lastValidated ? 100 : 0;
+ mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
mLegacyTypeTracker.add(
- newFallbackNetwork.networkInfo.getType(), newFallbackNetwork);
+ newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
// If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
// to reflect the NetworkInfo of this new network. This broadcast has to be sent
// after the disconnect broadcasts above, but before the broadcasts sent by the
// legacy type tracker below.
// TODO : refactor this, it's too complex
- notifyLockdownVpn(newFallbackNetwork);
+ notifyLockdownVpn(newDefaultNetwork);
}
}
@@ -7686,7 +7795,7 @@
}
// A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
- // because usually there are no NetworkRequests it satisfies (e.g., mFallbackRequest
+ // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
// wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
// newNetwork to the tracker explicitly (it's a no-op if it has already been added).
if (nai.isVPN()) {
@@ -7697,9 +7806,9 @@
private void updateInetCondition(NetworkAgentInfo nai) {
// Don't bother updating until we've graduated to validated at least once.
if (!nai.everValidated) return;
- // For now only update icons for the fallback connection.
+ // For now only update icons for the default connection.
// TODO: Update WiFi and cellular icons separately. b/17237507
- if (!isFallbackNetwork(nai)) return;
+ if (!isDefaultNetwork(nai)) return;
int newInetCondition = nai.lastValidated ? 100 : 0;
// Don't repeat publish.
@@ -7816,6 +7925,15 @@
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
+ // Before first rematching networks, put an inactivity timer without any request, this
+ // allows {@code updateInactivityState} to update the state accordingly and prevent
+ // tearing down for any {@code unneeded} evaluation in this period.
+ // Note that the timer will not be rescheduled since the expiry time is
+ // fixed after connection regardless of the network satisfying other requests or not.
+ // But it will be removed as soon as the network satisfies a request for the first time.
+ networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE,
+ SystemClock.elapsedRealtime(), mNascentDelayMs);
+
// Consider network even though it is not yet validated.
rematchAllNetworksAndRequests();
@@ -7869,7 +7987,7 @@
// Notify the requests on this NAI that the network is now lingered.
private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
- final int lingerTime = (int) (nai.getLingerExpiry() - now);
+ final int lingerTime = (int) (nai.getInactivityExpiry() - now);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
}
@@ -7967,8 +8085,8 @@
intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
}
NetworkAgentInfo newDefaultAgent = null;
- if (nai.isSatisfyingRequest(mFallbackRequest.mRequests.get(0).requestId)) {
- newDefaultAgent = getFallbackNetwork();
+ if (nai.isSatisfyingRequest(mDefaultRequest.mRequests.get(0).requestId)) {
+ newDefaultAgent = mDefaultRequest.getSatisfier();
if (newDefaultAgent != null) {
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
newDefaultAgent.networkInfo);
@@ -8016,9 +8134,14 @@
private Network[] getDefaultNetworks() {
ensureRunningOnConnectivityServiceThread();
final ArrayList<Network> defaultNetworks = new ArrayList<>();
- final NetworkAgentInfo fallbackNetwork = getFallbackNetwork();
+ final Set<Integer> activeNetIds = new ArraySet<>();
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ if (nri.isBeingSatisfied()) {
+ activeNetIds.add(nri.getSatisfier().network().netId);
+ }
+ }
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- if (nai.everConnected && (nai == fallbackNetwork || nai.isVPN())) {
+ if (nai.everConnected && (activeNetIds.contains(nai.network().netId) || nai.isVPN())) {
defaultNetworks.add(nai.network);
}
}
@@ -8068,7 +8191,6 @@
int user = UserHandle.getUserId(mDeps.getCallingUid());
final boolean success;
synchronized (mVpns) {
- throwIfLockdownEnabled();
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
return success;
@@ -8351,7 +8473,7 @@
throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
}
- final int uid = InetDiagMessage.getConnectionOwnerUid(connectionInfo.protocol,
+ final int uid = mDeps.getConnectionOwnerUid(connectionInfo.protocol,
connectionInfo.local, connectionInfo.remote);
/* Filter out Uids not associated with the VPN. */
@@ -9002,6 +9124,7 @@
}
}
}
+
/**
* Registers {@link QosSocketFilter} with {@link IQosCallback}.
*
@@ -9051,4 +9174,10 @@
public void unregisterQosCallback(@NonNull final IQosCallback callback) {
mQosCallbackTracker.unregisterCallback(callback);
}
+
+ @Override
+ public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+ // TODO http://b/176495594 track multiple default networks with networkPreferences
+ if (DBG) log("setOemNetworkPreference() called with: " + preference.toString());
+ }
}
diff --git a/services/core/java/com/android/server/LockGuard.java b/services/core/java/com/android/server/LockGuard.java
index 5ce16c4..b894f34 100644
--- a/services/core/java/com/android/server/LockGuard.java
+++ b/services/core/java/com/android/server/LockGuard.java
@@ -22,8 +22,6 @@
import android.util.ArraySet;
import android.util.Slog;
-import com.android.internal.os.BackgroundThread;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -74,8 +72,9 @@
public static final int INDEX_PACKAGES = 3;
public static final int INDEX_STORAGE = 4;
public static final int INDEX_WINDOW = 5;
- public static final int INDEX_ACTIVITY = 6;
- public static final int INDEX_DPMS = 7;
+ public static final int INDEX_PROC = 6;
+ public static final int INDEX_ACTIVITY = 7;
+ public static final int INDEX_DPMS = 8;
private static Object[] sKnownFixed = new Object[INDEX_DPMS + 1];
@@ -229,6 +228,7 @@
case INDEX_PACKAGES: return "PACKAGES";
case INDEX_STORAGE: return "STORAGE";
case INDEX_WINDOW: return "WINDOW";
+ case INDEX_PROC: return "PROCESS";
case INDEX_ACTIVITY: return "ACTIVITY";
case INDEX_DPMS: return "DPMS";
default: return Integer.toString(index);
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e96fd39..96f832d 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -50,6 +50,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.NetdUtils;
+import com.android.net.module.util.NetworkStackConstants;
import java.io.UncheckedIOException;
import java.net.Inet4Address;
@@ -280,10 +281,12 @@
// Add global routes (but as non-default, non-internet providing network)
if (allowIPv4) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV4_ADDR_ANY, 0), null, iface));
}
if (allowIPv6) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface));
}
final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp,
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 6a72010..4dce59f 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -66,6 +66,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
@@ -290,8 +291,10 @@
public Vcn newVcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
- @NonNull VcnConfig config) {
- return new Vcn(vcnContext, subscriptionGroup, config);
+ @NonNull VcnConfig config,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback safemodeCallback) {
+ return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
}
/** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -382,6 +385,7 @@
// delay)
for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
final VcnConfig config = mConfigs.get(entry.getKey());
+
if (config == null
|| !snapshot.packageHasPermissionsForSubscriptionGroup(
entry.getKey(), config.getProvisioningPackageName())) {
@@ -395,10 +399,13 @@
// correct instance is torn down. This could happen as a result of a
// Carrier App manually removing/adding a VcnConfig.
if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
- mVcns.remove(uuidToTeardown).teardownAsynchronously();
+ stopVcnLocked(uuidToTeardown);
}
}
}, instanceToTeardown, CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ } else {
+ // If this VCN's status has not changed, update it with the new snapshot
+ entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
}
}
}
@@ -406,14 +413,44 @@
}
@GuardedBy("mLock")
+ private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
+ final Vcn vcnToTeardown = mVcns.remove(uuidToTeardown);
+ if (vcnToTeardown == null) {
+ return;
+ }
+
+ vcnToTeardown.teardownAsynchronously();
+
+ // Now that the VCN is removed, notify all registered listeners to refresh their
+ // UnderlyingNetworkPolicy.
+ notifyAllPolicyListenersLocked();
+ }
+
+ @GuardedBy("mLock")
+ private void notifyAllPolicyListenersLocked() {
+ for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) {
+ Binder.withCleanCallingIdentity(() -> policyListener.mListener.onPolicyChanged());
+ }
+ }
+
+ @GuardedBy("mLock")
private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
Slog.v(TAG, "Starting VCN config for subGrp: " + subscriptionGroup);
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN.
- final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config);
+ final VcnSafemodeCallbackImpl safemodeCallback =
+ new VcnSafemodeCallbackImpl(subscriptionGroup);
+
+ final Vcn newInstance =
+ mDeps.newVcn(
+ mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
mVcns.put(subscriptionGroup, newInstance);
+
+ // Now that a new VCN has started, notify all registered listeners to refresh their
+ // UnderlyingNetworkPolicy.
+ notifyAllPolicyListenersLocked();
}
@GuardedBy("mLock")
@@ -476,9 +513,7 @@
synchronized (mLock) {
mConfigs.remove(subscriptionGroup);
- if (mVcns.containsKey(subscriptionGroup)) {
- mVcns.remove(subscriptionGroup).teardownAsynchronously();
- }
+ stopVcnLocked(subscriptionGroup);
writeConfigsToDiskLocked();
}
@@ -508,7 +543,7 @@
}
}
- /** Get current configuration list for testing purposes */
+ /** Get current VCNs for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Map<ParcelUuid, Vcn> getAllVcns() {
synchronized (mLock) {
@@ -610,8 +645,8 @@
synchronized (mLock) {
ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
- // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
- if (mVcns.containsKey(subGroup)) {
+ Vcn vcn = mVcns.get(subGroup);
+ if (vcn != null && vcn.isActive()) {
isVcnManagedNetwork = true;
}
}
@@ -623,4 +658,31 @@
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
}
+
+ /** Callback for signalling when a Vcn has entered Safemode. */
+ public interface VcnSafemodeCallback {
+ /** Called by a Vcn to signal that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
+ private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+ @NonNull private final ParcelUuid mSubGroup;
+
+ private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+ mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
+ }
+
+ @Override
+ public void onEnteredSafemode() {
+ synchronized (mLock) {
+ // Ignore if this subscription group doesn't exist anymore
+ if (!mVcns.containsKey(mSubGroup)) {
+ return;
+ }
+
+ notifyAllPolicyListenersLocked();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6216fc0..a4ff230 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -19,6 +19,9 @@
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
+import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
+import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
import static android.os.Process.NFC_UID;
@@ -48,7 +51,6 @@
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
-import android.app.ApplicationExitInfo;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
@@ -621,14 +623,14 @@
final boolean callerFg;
if (caller != null) {
- final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
+ final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + callingPid
+ ") when starting service " + service);
}
- callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
+ callerFg = callerApp.mState.getSetSchedGroup() != ProcessList.SCHED_GROUP_BACKGROUND;
} else {
callerFg = true;
}
@@ -656,7 +658,7 @@
// If we're starting indirectly (e.g. from PendingIntent), figure out whether
// we're launching into an app in a background state. This keys off of the same
// idleness state tracking as e.g. O+ background service start policy.
- final boolean bgLaunch = !mAm.isUidActiveLocked(r.appInfo.uid);
+ final boolean bgLaunch = !mAm.isUidActiveLOSP(r.appInfo.uid);
// If the app has strict background restrictions, we treat any bg service
// start analogously to the legacy-app forced-restrictions case, regardless
@@ -671,34 +673,29 @@
}
if (fgRequired) {
- if (isFgsBgStart(r.mAllowStartForeground)) {
- if (!r.mLoggedInfoAllowStartForeground) {
- Slog.wtf(TAG, "Background started FGS " + r.mInfoAllowStartForeground);
- r.mLoggedInfoAllowStartForeground = true;
+ logFgsBackgroundStart(r);
+ if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
+ String msg = "startForegroundService() not allowed due to "
+ + "mAllowStartForeground false: service "
+ + r.shortInstanceName;
+ Slog.w(TAG, msg);
+ showFgsBgRestrictedNotificationLocked(r);
+ ApplicationInfo aInfo = null;
+ try {
+ aInfo = AppGlobals.getPackageManager().getApplicationInfo(
+ callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
+ userId);
+ } catch (android.os.RemoteException e) {
+ // pm is in same process, this will never happen.
}
- if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
- String msg = "startForegroundService() not allowed due to "
- + "mAllowStartForeground false: service "
- + r.shortInstanceName;
- Slog.w(TAG, msg);
- showFgsBgRestrictedNotificationLocked(r);
- ApplicationInfo aInfo = null;
- try {
- aInfo = AppGlobals.getPackageManager().getApplicationInfo(
- callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
- userId);
- } catch (android.os.RemoteException e) {
- // pm is in same process, this will never happen.
- }
- if (aInfo == null) {
- throw new SecurityException("startServiceLocked failed, "
- + "could not resolve client package " + callingPackage);
- }
- if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
- throw new IllegalStateException(msg);
- }
- return null;
+ if (aInfo == null) {
+ throw new SecurityException("startServiceLocked failed, "
+ + "could not resolve client package " + callingPackage);
}
+ if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
+ throw new IllegalStateException(msg);
+ }
+ return null;
}
}
@@ -732,7 +729,7 @@
if (forcedStandby || (!r.startRequested && !fgRequired)) {
// Before going further -- if this app is not allowed to start services in the
// background, then at this point we aren't going to let it period.
- final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
+ final int allowed = mAm.getAppStartModeLOSP(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
@@ -757,7 +754,7 @@
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
- UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(r.appInfo.uid);
+ UidRecord uidRec = mAm.mProcessList.getUidRecordLOSP(r.appInfo.uid);
return new ComponentName("?", "app is in background uid " + uidRec);
}
}
@@ -830,7 +827,7 @@
if (!callerFg && !fgRequired && r.app == null
&& mAm.mUserController.hasStartedUserState(r.userId)) {
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
- if (proc == null || proc.getCurProcState() > ActivityManager.PROCESS_STATE_RECEIVER) {
+ if (proc == null || proc.mState.getCurProcState() > PROCESS_STATE_RECEIVER) {
// If this is not coming from a foreground caller, then we may want
// to delay the start if there are already other background services
// that are starting. This is to avoid process start spam when lots
@@ -858,7 +855,7 @@
}
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
addToStarting = true;
- } else if (proc.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
+ } else if (proc.mState.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
// We slightly loosen when we will enqueue this new service as a background
// starting service we are waiting for, to also include processes that are
// currently running other services or receivers.
@@ -867,9 +864,9 @@
"Not delaying, but counting as bg: " + r);
} else if (DEBUG_DELAYED_STARTS) {
StringBuilder sb = new StringBuilder(128);
- sb.append("Not potential delay (state=").append(proc.getCurProcState())
- .append(' ').append(proc.adjType);
- String reason = proc.makeAdjReason();
+ sb.append("Not potential delay (state=").append(proc.mState.getCurProcState())
+ .append(' ').append(proc.mState.getAdjType());
+ String reason = proc.mState.makeAdjReason();
if (reason != null) {
sb.append(' ');
sb.append(reason);
@@ -1164,7 +1161,7 @@
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "stopService: " + service
+ " type=" + resolvedType);
- final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
+ final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);
if (caller != null && callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
@@ -1200,7 +1197,7 @@
for (int i = services.mServicesByInstanceName.size() - 1; i >= 0; i--) {
ServiceRecord service = services.mServicesByInstanceName.valueAt(i);
if (service.appInfo.uid == uid && service.startRequested) {
- if (mAm.getAppStartModeLocked(service.appInfo.uid, service.packageName,
+ if (mAm.getAppStartModeLOSP(service.appInfo.uid, service.packageName,
service.appInfo.targetSdkVersion, -1, false, false, false)
!= ActivityManager.APP_START_MODE_NORMAL) {
if (stopping == null) {
@@ -1630,13 +1627,13 @@
}
void foregroundServiceProcStateChangedLocked(UidRecord uidRec) {
- ServiceMap smap = mServiceMap.get(UserHandle.getUserId(uidRec.uid));
+ ServiceMap smap = mServiceMap.get(UserHandle.getUserId(uidRec.getUid()));
if (smap != null) {
boolean changed = false;
for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) {
ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j);
- if (active.mUid == uidRec.uid) {
- if (uidRec.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
+ if (active.mUid == uidRec.getUid()) {
+ if (uidRec.getCurProcState() <= PROCESS_STATE_TOP) {
if (!active.mAppOnTop) {
active.mAppOnTop = true;
changed = true;
@@ -1655,7 +1652,7 @@
}
private boolean appIsTopLocked(int uid) {
- return mAm.getUidState(uid) <= ActivityManager.PROCESS_STATE_TOP;
+ return mAm.getUidStateLocked(uid) <= PROCESS_STATE_TOP;
}
/**
@@ -1687,13 +1684,13 @@
default:
mAm.enforcePermission(
android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
- r.app.pid, r.appInfo.uid, "startForeground");
+ r.app.getPid(), r.appInfo.uid, "startForeground");
}
} else {
if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
mAm.enforcePermission(
android.Manifest.permission.FOREGROUND_SERVICE,
- r.app.pid, r.appInfo.uid, "startForeground");
+ r.app.getPid(), r.appInfo.uid, "startForeground");
}
int manifestType = r.serviceInfo.getForegroundServiceType();
@@ -1734,6 +1731,7 @@
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
}
+ final ProcessServiceRecord psr = r.app.mServices;
try {
boolean ignoreForeground = false;
final int mode = mAm.getAppOpsManager().checkOpNoThrow(
@@ -1763,30 +1761,24 @@
+ r.shortInstanceName);
// Back off of any foreground expectations around this service, since we've
// just turned down its fg request.
- updateServiceForegroundLocked(r.app, false);
+ updateServiceForegroundLocked(psr, false);
ignoreForeground = true;
}
if (!ignoreForeground) {
- if (isFgsBgStart(r.mAllowStartForeground)) {
- if (!r.mLoggedInfoAllowStartForeground) {
- Slog.wtf(TAG, "Background started FGS "
- + r.mInfoAllowStartForeground);
- r.mLoggedInfoAllowStartForeground = true;
- }
- if (r.mAllowStartForeground == FGS_FEATURE_DENIED
- && isBgFgsRestrictionEnabled(r)) {
- final String msg = "Service.startForeground() not allowed due to "
- + "mAllowStartForeground false: service "
- + r.shortInstanceName;
- Slog.w(TAG, msg);
- showFgsBgRestrictedNotificationLocked(r);
- updateServiceForegroundLocked(r.app, true);
- ignoreForeground = true;
- if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
- r.appInfo.uid)) {
- throw new IllegalStateException(msg);
- }
+ logFgsBackgroundStart(r);
+ if (r.mAllowStartForeground == FGS_FEATURE_DENIED
+ && isBgFgsRestrictionEnabled(r)) {
+ final String msg = "Service.startForeground() not allowed due to "
+ + "mAllowStartForeground false: service "
+ + r.shortInstanceName;
+ Slog.w(TAG, msg);
+ showFgsBgRestrictedNotificationLocked(r);
+ updateServiceForegroundLocked(psr, true);
+ ignoreForeground = true;
+ if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
+ r.appInfo.uid)) {
+ throw new IllegalStateException(msg);
}
}
}
@@ -1813,10 +1805,12 @@
active.mPackageName = r.packageName;
active.mUid = r.appInfo.uid;
active.mShownWhileScreenOn = mScreenOn;
- if (r.app != null && r.app.uidRecord != null) {
- active.mAppOnTop = active.mShownWhileTop =
- r.app.uidRecord.getCurProcState()
- <= ActivityManager.PROCESS_STATE_TOP;
+ if (r.app != null) {
+ final UidRecord uidRec = r.app.getUidRecord();
+ if (uidRec != null) {
+ active.mAppOnTop = active.mShownWhileTop =
+ uidRec.getCurProcState() <= PROCESS_STATE_TOP;
+ }
}
active.mStartTime = active.mStartVisibleTime
= SystemClock.elapsedRealtime();
@@ -1848,7 +1842,7 @@
}
postFgsNotificationLocked(r);
if (r.app != null) {
- updateServiceForegroundLocked(r.app, true);
+ updateServiceForegroundLocked(psr, true);
}
getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
mAm.notifyPackageUse(r.serviceInfo.packageName,
@@ -1900,7 +1894,7 @@
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
mAm.updateLruProcessLocked(r.app, false, null);
- updateServiceForegroundLocked(r.app, true);
+ updateServiceForegroundLocked(r.app.mServices, true);
}
}
// Leave the time-to-display as already set: re-entering foreground mode will
@@ -2138,7 +2132,7 @@
}
private boolean isNotTop() {
- return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP;
+ return mProcessRecord.mState.getCurProcState() != PROCESS_STATE_TOP;
}
private void incrementOpCount(int op, boolean allowed) {
@@ -2236,36 +2230,45 @@
}
}
- private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
+ private void updateServiceForegroundLocked(ProcessServiceRecord psr, boolean oomAdj) {
boolean anyForeground = false;
int fgServiceTypes = 0;
- for (int i = proc.numberOfRunningServices() - 1; i >= 0; i--) {
- ServiceRecord sr = proc.getRunningServiceAt(i);
+ for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+ ServiceRecord sr = psr.getRunningServiceAt(i);
if (sr.isForeground || sr.fgRequired) {
anyForeground = true;
fgServiceTypes |= sr.foregroundServiceType;
}
}
- mAm.updateProcessForegroundLocked(proc, anyForeground, fgServiceTypes, oomAdj);
+ mAm.updateProcessForegroundLocked(psr.mApp, anyForeground, fgServiceTypes, oomAdj);
}
- private void updateAllowlistManagerLocked(ProcessRecord proc) {
- proc.mAllowlistManager = false;
- for (int i = proc.numberOfRunningServices() - 1; i >= 0; i--) {
- ServiceRecord sr = proc.getRunningServiceAt(i);
+ private void updateAllowlistManagerLocked(ProcessServiceRecord psr) {
+ psr.mAllowlistManager = false;
+ for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+ ServiceRecord sr = psr.getRunningServiceAt(i);
if (sr.whitelistManager) {
- proc.mAllowlistManager = true;
+ psr.mAllowlistManager = true;
break;
}
}
}
- public void updateServiceConnectionActivitiesLocked(ProcessRecord clientProc) {
+ private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) {
+ final ProcessServiceRecord psr = service.app.mServices;
+ psr.stopService(service);
+ psr.updateBoundClientUids();
+ if (service.whitelistManager) {
+ updateAllowlistManagerLocked(psr);
+ }
+ }
+
+ void updateServiceConnectionActivitiesLocked(ProcessServiceRecord clientPsr) {
ArraySet<ProcessRecord> updatedProcesses = null;
- for (int i = 0; i < clientProc.connections.size(); i++) {
- final ConnectionRecord conn = clientProc.connections.valueAt(i);
+ for (int i = 0; i < clientPsr.numberOfConnections(); i++) {
+ final ConnectionRecord conn = clientPsr.getConnectionAt(i);
final ProcessRecord proc = conn.binding.service.app;
- if (proc == null || proc == clientProc) {
+ if (proc == null || proc == clientPsr.mApp) {
continue;
} else if (updatedProcesses == null) {
updatedProcesses = new ArraySet<>();
@@ -2273,11 +2276,11 @@
continue;
}
updatedProcesses.add(proc);
- updateServiceClientActivitiesLocked(proc, null, false);
+ updateServiceClientActivitiesLocked(proc.mServices, null, false);
}
}
- private boolean updateServiceClientActivitiesLocked(ProcessRecord proc,
+ private boolean updateServiceClientActivitiesLocked(ProcessServiceRecord psr,
ConnectionRecord modCr, boolean updateLru) {
if (modCr != null && modCr.binding.client != null) {
if (!modCr.binding.client.hasActivities()) {
@@ -2288,14 +2291,14 @@
}
boolean anyClientActivities = false;
- for (int i = proc.numberOfRunningServices() - 1; i >= 0 && !anyClientActivities; i--) {
- ServiceRecord sr = proc.getRunningServiceAt(i);
+ for (int i = psr.numberOfRunningServices() - 1; i >= 0 && !anyClientActivities; i--) {
+ ServiceRecord sr = psr.getRunningServiceAt(i);
ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = sr.getConnections();
for (int conni = connections.size() - 1; conni >= 0 && !anyClientActivities; conni--) {
ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
for (int cri=clist.size()-1; cri>=0; cri--) {
ConnectionRecord cr = clist.get(cri);
- if (cr.binding.client == null || cr.binding.client == proc) {
+ if (cr.binding.client == null || cr.binding.client == psr.mApp) {
// Binding to ourself is not interesting.
continue;
}
@@ -2306,10 +2309,10 @@
}
}
}
- if (anyClientActivities != proc.hasClientActivities()) {
- proc.setHasClientActivities(anyClientActivities);
+ if (anyClientActivities != psr.hasClientActivities()) {
+ psr.setHasClientActivities(anyClientActivities);
if (updateLru) {
- mAm.updateLruProcessLocked(proc, anyClientActivities, null);
+ mAm.updateLruProcessLocked(psr.mApp, anyClientActivities, null);
}
return true;
}
@@ -2325,7 +2328,7 @@
+ " flags=0x" + Integer.toHexString(flags));
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
- final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
+ final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
@@ -2397,7 +2400,8 @@
"BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND");
}
- final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
+ final boolean callerFg = callerApp.mState.getSetSchedGroup()
+ != ProcessList.SCHED_GROUP_BACKGROUND;
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
@@ -2451,7 +2455,7 @@
}
mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
- callerApp.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
+ callerApp.mState.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
s.instanceName, s.processName);
// Once the apps have become associated, if one of them is caller is ephemeral
// the target app should now be able to see the calling app
@@ -2469,10 +2473,11 @@
if (activity != null) {
activity.addConnection(c);
}
- b.client.connections.add(c);
+ final ProcessServiceRecord clientPsr = b.client.mServices;
+ clientPsr.addConnection(c);
c.startAssociationIfNeeded();
if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
- b.client.hasAboveClient = true;
+ clientPsr.setHasAboveClient(true);
}
if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
s.whitelistManager = true;
@@ -2486,7 +2491,7 @@
}
if (s.app != null) {
- updateServiceClientActivitiesLocked(s.app, c, true);
+ updateServiceClientActivitiesLocked(s.app.mServices, c, true);
}
ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
if (clist == null) {
@@ -2508,17 +2513,18 @@
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false);
if (s.app != null) {
+ ProcessServiceRecord servicePsr = s.app.mServices;
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
- s.app.treatLikeActivity = true;
+ servicePsr.setTreatLikeActivity(true);
}
if (s.whitelistManager) {
- s.app.mAllowlistManager = true;
+ servicePsr.mAllowlistManager = true;
}
// This could have made the service more important.
- mAm.updateLruProcessLocked(s.app,
- (callerApp.hasActivitiesOrRecentTasks() && s.app.hasClientActivities())
- || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
- && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
+ mAm.updateLruProcessLocked(s.app, (callerApp.hasActivitiesOrRecentTasks()
+ && servicePsr.hasClientActivities())
+ || (callerApp.mState.getCurProcState() <= PROCESS_STATE_TOP
+ && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
b.client);
needOomAdj = true;
mAm.enqueueOomAdjTargetLocked(s.app);
@@ -2638,14 +2644,15 @@
final ServiceRecord srec = crec.binding.service;
if (srec != null && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
if (srec.app != null) {
+ final ProcessServiceRecord psr = srec.app.mServices;
if (group > 0) {
- srec.app.connectionService = srec;
- srec.app.connectionGroup = group;
- srec.app.connectionImportance = importance;
+ psr.setConnectionService(srec);
+ psr.setConnectionGroup(group);
+ psr.setConnectionImportance(importance);
} else {
- srec.app.connectionService = null;
- srec.app.connectionGroup = 0;
- srec.app.connectionImportance = 0;
+ psr.setConnectionService(null);
+ psr.setConnectionGroup(0);
+ psr.setConnectionImportance(0);
}
} else {
if (group > 0) {
@@ -2683,15 +2690,14 @@
final ProcessRecord app = r.binding.service.app;
if (app != null) {
- if (app.mAllowlistManager) {
- updateAllowlistManagerLocked(app);
+ final ProcessServiceRecord psr = app.mServices;
+ if (psr.mAllowlistManager) {
+ updateAllowlistManagerLocked(psr);
}
// This could have made the service less important.
if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
- app.treatLikeActivity = true;
- mAm.updateLruProcessLocked(app,
- app.hasClientActivities()
- || app.treatLikeActivity, null);
+ psr.setTreatLikeActivity(true);
+ mAm.updateLruProcessLocked(app, true, null);
}
mAm.enqueueOomAdjTargetLocked(app);
}
@@ -2725,7 +2731,7 @@
boolean inFg = false;
for (int i=b.apps.size()-1; i>=0; i--) {
ProcessRecord client = b.apps.valueAt(i).client;
- if (client != null && client.setSchedGroup
+ if (client != null && client.mState.getSetSchedGroup()
!= ProcessList.SCHED_GROUP_BACKGROUND) {
inFg = true;
break;
@@ -3039,7 +3045,7 @@
// happen.)
boolean timeoutNeeded = true;
if ((mAm.mBootPhase < SystemService.PHASE_THIRD_PARTY_APPS_CAN_START)
- && (r.app != null) && (r.app.pid == android.os.Process.myPid())) {
+ && (r.app != null) && (r.app.getPid() == ActivityManagerService.MY_PID)) {
Slog.w(TAG, "Too early to start/bind service in system_server: Phase=" + mAm.mBootPhase
+ " " + r.getComponentName());
@@ -3047,6 +3053,7 @@
}
long now = SystemClock.uptimeMillis();
+ ProcessServiceRecord psr;
if (r.executeNesting == 0) {
r.executeFg = fg;
ServiceState stracker = r.getTracker();
@@ -3054,16 +3061,20 @@
stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
}
if (r.app != null) {
- r.app.executingServices.add(r);
- r.app.execServicesFg |= fg;
- if (timeoutNeeded && r.app.executingServices.size() == 1) {
+ psr = r.app.mServices;
+ psr.startExecutingService(r);
+ psr.setExecServicesFg(psr.shouldExecServicesFg() || fg);
+ if (timeoutNeeded && psr.numberOfExecutingServices() == 1) {
scheduleServiceTimeoutLocked(r.app);
}
}
- } else if (r.app != null && fg && !r.app.execServicesFg) {
- r.app.execServicesFg = true;
- if (timeoutNeeded) {
- scheduleServiceTimeoutLocked(r.app);
+ } else if (r.app != null && fg) {
+ psr = r.app.mServices;
+ if (!psr.shouldExecServicesFg()) {
+ psr.setExecServicesFg(true);
+ if (timeoutNeeded) {
+ scheduleServiceTimeoutLocked(r.app);
+ }
}
}
r.executeFg |= fg;
@@ -3073,7 +3084,7 @@
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
- if (r.app == null || r.app.thread == null) {
+ if (r.app == null || r.app.getThread() == null) {
// If service is not currently running, can't yet bind.
return false;
}
@@ -3082,9 +3093,9 @@
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, execInFg, "bind");
- r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
- r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
- r.app.getReportedProcState());
+ r.app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
+ r.app.getThread().scheduleBindService(r, i.intent.getIntent(), rebind,
+ r.app.mState.getReportedProcState());
if (!rebind) {
i.requested = true;
}
@@ -3307,7 +3318,7 @@
boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
boolean enqueueOomAdj)
throws TransactionTooLargeException {
- if (r.app != null && r.app.thread != null) {
+ if (r.app != null && r.app.getThread() != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
@@ -3365,19 +3376,26 @@
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
- if (app != null && app.thread != null) {
- try {
- app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
- realStartServiceLocked(r, app, execInFg, enqueueOomAdj);
- return null;
- } catch (TransactionTooLargeException e) {
- throw e;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
- }
+ if (app != null) {
+ final IApplicationThread thread = app.getThread();
+ final int pid = app.getPid();
+ final UidRecord uidRecord = app.getUidRecord();
+ if (thread != null) {
+ try {
+ app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode,
+ mAm.mProcessStats);
+ realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
+ enqueueOomAdj);
+ return null;
+ } catch (TransactionTooLargeException e) {
+ throw e;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
+ }
- // If a dead object exception was thrown -- fall through to
- // restart the application.
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
+ }
}
} else {
// If this service runs in an isolated process, then each time
@@ -3402,8 +3420,8 @@
if (app == null && !permissionsReviewRequired && !packageFrozen) {
// TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
// was initiated from a notification tap or not.
- if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
- hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
+ if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
+ hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
@@ -3459,21 +3477,23 @@
* The "start" here means bring up the instance in the client, and this method is called
* from bindService() as well.
*/
- private final void realStartServiceLocked(ServiceRecord r,
- ProcessRecord app, boolean execInFg, boolean enqueueOomAdj) throws RemoteException {
- if (app.thread == null) {
+ private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
+ IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
+ boolean enqueueOomAdj) throws RemoteException {
+ if (thread == null) {
throw new RemoteException();
}
if (DEBUG_MU)
Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ ", ProcessRecord.uid = " + app.uid);
- r.setProcess(app);
+ r.setProcess(app, thread, pid, uidRecord);
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
- final boolean newService = app.startService(r);
+ final ProcessServiceRecord psr = app.mServices;
+ final boolean newService = psr.startService(r);
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false, null);
- updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
+ updateServiceForegroundLocked(psr, /* oomAdj= */ false);
if (enqueueOomAdj) {
mAm.enqueueOomAdjTargetLocked(app);
} else {
@@ -3488,7 +3508,7 @@
nameTerm = lastPeriod >= 0 ? r.shortInstanceName.substring(lastPeriod)
: r.shortInstanceName;
EventLogTags.writeAmCreateService(
- r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
+ r.userId, System.identityHashCode(r), nameTerm, r.app.uid, pid);
}
final int uid = r.appInfo.uid;
@@ -3499,10 +3519,10 @@
mAm.mBatteryStatsService.noteServiceStartLaunch(uid, packageName, serviceName);
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
- app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
- app.thread.scheduleCreateService(r, r.serviceInfo,
+ app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
+ thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
- app.getReportedProcState());
+ app.mState.getReportedProcState());
r.postNotification();
created = true;
} catch (DeadObjectException e) {
@@ -3517,8 +3537,8 @@
// Cleanup.
if (newService) {
- app.stopService(r);
- r.setProcess(null);
+ psr.stopService(r);
+ r.setProcess(null, null, 0, null);
}
// Retry.
@@ -3529,15 +3549,15 @@
}
if (r.whitelistManager) {
- app.mAllowlistManager = true;
+ psr.mAllowlistManager = true;
}
requestServiceBindingsLocked(r, execInFg);
- updateServiceClientActivitiesLocked(app, null, true);
+ updateServiceClientActivitiesLocked(psr, null, true);
if (newService && created) {
- app.addBoundClientUidsOfNewService(r);
+ psr.addBoundClientUidsOfNewService(r);
}
// If the service is in the started state, and there are no
@@ -3631,7 +3651,7 @@
slice.setInlineCountLimit(4);
Exception caughtException = null;
try {
- r.app.thread.scheduleServiceArgs(r, slice);
+ r.app.getThread().scheduleServiceArgs(r, slice);
} catch (TransactionTooLargeException e) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
+ " args, first: " + args.get(0).args);
@@ -3723,7 +3743,7 @@
boolean needOomAdj = false;
// Tell the service that it has been unbound.
- if (r.app != null && r.app.thread != null) {
+ if (r.app != null && r.app.getThread() != null) {
for (int i = r.bindings.size() - 1; i >= 0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing down binding " + ibr
@@ -3734,7 +3754,7 @@
needOomAdj = true;
ibr.hasBound = false;
ibr.requested = false;
- r.app.thread.scheduleUnbindService(r,
+ r.app.getThread().scheduleUnbindService(r,
ibr.intent.getIntent());
} catch (Exception e) {
Slog.w(TAG, "Exception when unbinding service "
@@ -3781,7 +3801,7 @@
r.destroyTime = SystemClock.uptimeMillis();
if (LOG_SERVICE_START_STOP) {
EventLogTags.writeAmDestroyService(
- r.userId, System.identityHashCode(r), (r.app != null) ? r.app.pid : -1);
+ r.userId, System.identityHashCode(r), (r.app != null) ? r.app.getPid() : -1);
}
final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -3843,20 +3863,15 @@
if (r.app != null) {
mAm.mBatteryStatsService.noteServiceStopLaunch(r.appInfo.uid, r.name.getPackageName(),
r.name.getClassName());
- r.app.stopService(r);
- r.app.updateBoundClientUids();
- if (r.whitelistManager) {
- updateAllowlistManagerLocked(r.app);
- }
- if (r.app.thread != null) {
- updateServiceForegroundLocked(r.app, false);
+ stopServiceAndUpdateAllowlistManagerLocked(r);
+ if (r.app.getThread() != null) {
+ updateServiceForegroundLocked(r.app.mServices, false);
try {
bumpServiceExecutingLocked(r, false, "destroy");
mDestroyingServices.add(r);
r.destroying = true;
- mAm.updateOomAdjLocked(r.app, true,
- OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
- r.app.thread.scheduleStopService(r);
+ needOomAdj = true;
+ r.app.getThread().scheduleStopService(r);
} catch (Exception e) {
Slog.w(TAG, "Exception when destroying service "
+ r.shortInstanceName, e);
@@ -3918,16 +3933,17 @@
c.activity.removeConnection(c);
}
if (b.client != skipApp) {
- b.client.connections.remove(c);
+ final ProcessServiceRecord psr = b.client.mServices;
+ psr.removeConnection(c);
if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
- b.client.updateHasAboveClientLocked();
+ psr.updateHasAboveClientLocked();
}
// If this connection requested whitelist management, see if we should
// now clear that state.
if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
s.updateWhitelistManager();
if (!s.whitelistManager && s.app != null) {
- updateAllowlistManagerLocked(s.app);
+ updateAllowlistManagerLocked(s.app.mServices);
}
}
// And do the same for bg activity starts ability.
@@ -3938,7 +3954,7 @@
s.updateIsAllowedBgFgsStartsByBinding();
}
if (s.app != null) {
- updateServiceClientActivitiesLocked(s.app, c, true);
+ updateServiceClientActivitiesLocked(s.app.mServices, c, true);
}
}
clist = mServiceConnections.get(binder);
@@ -3959,12 +3975,12 @@
if (!c.serviceDead) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Disconnecting binding " + b.intent
+ ": shouldUnbind=" + b.intent.hasBound);
- if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
+ if (s.app != null && s.app.getThread() != null && b.intent.apps.size() == 0
&& b.intent.hasBound) {
try {
bumpServiceExecutingLocked(s, false, "unbind");
if (b.client != s.app && (c.flags&Context.BIND_WAIVE_PRIORITY) == 0
- && s.app.setProcState <= ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+ && s.app.mState.getSetProcState() <= PROCESS_STATE_HEAVY_WEIGHT) {
// If this service's process is not already in the cached list,
// then update it in the LRU list here because this may be causing
// it to go down there and we want it to start out near the top.
@@ -3980,7 +3996,7 @@
// Assume the client doesn't want to know about a rebind;
// we will deal with that later if it asks for one.
b.intent.doRebind = false;
- s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
+ s.app.getThread().scheduleUnbindService(s, b.intent.intent.getIntent());
} catch (Exception e) {
Slog.w(TAG, "Exception when unbinding service " + s.shortInstanceName, e);
serviceProcessGoneLocked(s, enqueueOomAdj);
@@ -4111,19 +4127,20 @@
r.executeNesting--;
if (r.executeNesting <= 0) {
if (r.app != null) {
+ final ProcessServiceRecord psr = r.app.mServices;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"Nesting at 0 of " + r.shortInstanceName);
- r.app.execServicesFg = false;
- r.app.executingServices.remove(r);
- if (r.app.executingServices.size() == 0) {
+ psr.setExecServicesFg(false);
+ psr.stopExecutingService(r);
+ if (psr.numberOfExecutingServices() == 0) {
if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
"No more executingServices of " + r.shortInstanceName);
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
} else if (r.executeFg) {
// Need to re-evaluate whether the app still needs to be in the foreground.
- for (int i=r.app.executingServices.size()-1; i>=0; i--) {
- if (r.app.executingServices.valueAt(i).executeFg) {
- r.app.execServicesFg = true;
+ for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
+ if (psr.getExecutingServiceAt(i).executeFg) {
+ psr.setExecServicesFg(true);
break;
}
}
@@ -4153,13 +4170,9 @@
}
if (finishing) {
if (r.app != null && !r.app.isPersistent()) {
- r.app.stopService(r);
- r.app.updateBoundClientUids();
- if (r.whitelistManager) {
- updateAllowlistManagerLocked(r.app);
- }
+ stopServiceAndUpdateAllowlistManagerLocked(r);
}
- r.setProcess(null);
+ r.setProcess(null, null, 0, null);
}
}
}
@@ -4178,11 +4191,15 @@
continue;
}
+ final IApplicationThread thread = proc.getThread();
+ final int pid = proc.getPid();
+ final UidRecord uidRecord = proc.getUidRecord();
mPendingServices.remove(i);
i--;
proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode,
mAm.mProcessStats);
- realStartServiceLocked(sr, proc, sr.createdFromFg, true);
+ realStartServiceLocked(sr, proc, thread, pid, uidRecord, sr.createdFromFg,
+ true);
didSomething = true;
if (!isServiceNeededLocked(sr, false, false)) {
// We were waiting for this service to start, but it is actually no
@@ -4257,13 +4274,9 @@
didSomething = true;
Slog.i(TAG, " Force stopping service " + service);
if (service.app != null && !service.app.isPersistent()) {
- service.app.stopService(service);
- service.app.updateBoundClientUids();
- if (service.whitelistManager) {
- updateAllowlistManagerLocked(service.app);
- }
+ stopServiceAndUpdateAllowlistManagerLocked(service);
}
- service.setProcess(null);
+ service.setProcess(null, null, 0, null);
service.isolatedProc = null;
if (mTmpCollectionResults == null) {
mTmpCollectionResults = new ArrayList<>();
@@ -4363,7 +4376,7 @@
} else {
sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
sr.getLastStartId(), baseIntent, null, 0));
- if (sr.app != null && sr.app.thread != null) {
+ if (sr.app != null && sr.app.getThread() != null) {
// We always run in the foreground, since this is called as
// part of the "remove task" UI operation.
try {
@@ -4381,13 +4394,14 @@
}
final void killServicesLocked(ProcessRecord app, boolean allowRestart) {
+ final ProcessServiceRecord psr = app.mServices;
// Report disconnected services.
if (false) {
// XXX we are letting the client link to the service for
// death notifications.
- int numberOfRunningServices = app.numberOfRunningServices();
+ int numberOfRunningServices = psr.numberOfRunningServices();
for (int sIndex = 0; sIndex < numberOfRunningServices; sIndex++) {
- ServiceRecord r = app.getRunningServiceAt(sIndex);
+ ServiceRecord r = psr.getRunningServiceAt(sIndex);
ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> cl = connections.valueAt(conni);
@@ -4409,25 +4423,25 @@
}
// Clean up any connections this application has to other services.
- for (int i = app.connections.size() - 1; i >= 0; i--) {
- ConnectionRecord r = app.connections.valueAt(i);
+ for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
+ ConnectionRecord r = psr.getConnectionAt(i);
removeConnectionLocked(r, app, null, true);
}
- updateServiceConnectionActivitiesLocked(app);
- app.connections.clear();
+ updateServiceConnectionActivitiesLocked(psr);
+ psr.removeAllConnections();
- app.mAllowlistManager = false;
+ psr.mAllowlistManager = false;
// Clear app state from services.
- for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
- ServiceRecord sr = app.getRunningServiceAt(i);
+ for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+ ServiceRecord sr = psr.getRunningServiceAt(i);
mAm.mBatteryStatsService.noteServiceStopLaunch(sr.appInfo.uid, sr.name.getPackageName(),
sr.name.getClassName());
if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
- sr.app.stopService(sr);
- sr.app.updateBoundClientUids();
+ sr.app.mServices.stopService(sr);
+ sr.app.mServices.updateBoundClientUids();
}
- sr.setProcess(null);
+ sr.setProcess(null, null, 0, null);
sr.isolatedProc = null;
sr.executeNesting = 0;
sr.forceClearTracker();
@@ -4449,7 +4463,7 @@
for (int appi=b.apps.size()-1; appi>=0; appi--) {
final ProcessRecord proc = b.apps.keyAt(appi);
// If the process is already gone, skip it.
- if (proc.killedByAm || proc.thread == null) {
+ if (proc.isKilledByAm() || proc.getThread() == null) {
continue;
}
// Only do this for processes that have an auto-create binding;
@@ -4457,7 +4471,7 @@
// service to restart.
final AppBindRecord abind = b.apps.valueAt(appi);
boolean hasCreate = false;
- for (int conni=abind.connections.size()-1; conni>=0; conni--) {
+ for (int conni = abind.connections.size() - 1; conni >= 0; conni--) {
ConnectionRecord conn = abind.connections.valueAt(conni);
if ((conn.flags&(Context.BIND_AUTO_CREATE|Context.BIND_ALLOW_OOM_MANAGEMENT
|Context.BIND_WAIVE_PRIORITY)) == Context.BIND_AUTO_CREATE) {
@@ -4469,13 +4483,15 @@
continue;
}
// XXX turned off for now until we have more time to get a better policy.
- if (false && proc != null && !proc.isPersistent() && proc.thread != null
- && proc.pid != 0 && proc.pid != ActivityManagerService.MY_PID
- && proc.setProcState >= ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- proc.kill("bound to service " + sr.shortInstanceName
+ /*
+ if (false && proc != null && !proc.isPersistent() && proc.getThread() != null
+ && proc.getPid() != 0 && proc.getPid() != ActivityManagerService.MY_PID
+ && proc.mState.getSetProcState() >= PROCESS_STATE_LAST_ACTIVITY) {
+ proc.killLocked("bound to service " + sr.shortInstanceName
+ " in dying proc " + (app != null ? app.processName : "??"),
ApplicationExitInfo.REASON_OTHER, true);
}
+ */
}
}
}
@@ -4483,14 +4499,14 @@
ServiceMap smap = getServiceMapLocked(app.userId);
// Now do remaining service cleanup.
- for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
- ServiceRecord sr = app.getRunningServiceAt(i);
+ for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+ ServiceRecord sr = psr.getRunningServiceAt(i);
// Unless the process is persistent, this process record is going away,
// so make sure the service is cleaned out of it.
if (!app.isPersistent()) {
- app.stopService(sr);
- app.updateBoundClientUids();
+ psr.stopService(sr);
+ psr.updateBoundClientUids();
}
// Sanity check: if the service listed for the app is not one
@@ -4512,7 +4528,7 @@
Slog.w(TAG, "Service crashed " + sr.crashCount
+ " times, stopping: " + sr);
EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
- sr.userId, sr.crashCount, sr.shortInstanceName, app.pid);
+ sr.userId, sr.crashCount, sr.shortInstanceName, sr.app.getPid());
bringDownServiceLocked(sr, true);
} else if (!allowRestart
|| !mAm.mUserController.isUserRunning(sr.userId, 0)) {
@@ -4541,8 +4557,8 @@
mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
if (!allowRestart) {
- app.stopAllServices();
- app.clearBoundClientUids();
+ psr.stopAllServices();
+ psr.clearBoundClientUids();
// Make sure there are no more restarting services for this process.
for (int i=mRestartingServices.size()-1; i>=0; i--) {
@@ -4581,7 +4597,7 @@
}
}
- app.executingServices.clear();
+ psr.stopAllExecutingServices();
}
ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) {
@@ -4589,7 +4605,7 @@
new ActivityManager.RunningServiceInfo();
info.service = r.name;
if (r.app != null) {
- info.pid = r.app.pid;
+ info.pid = r.app.getPid();
}
info.uid = r.appInfo.uid;
info.process = r.processName;
@@ -4605,7 +4621,7 @@
if (r.startRequested) {
info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED;
}
- if (r.app != null && r.app.pid == ActivityManagerService.MY_PID) {
+ if (r.app != null && r.app.getPid() == ActivityManagerService.MY_PID) {
info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
}
if (r.app != null && r.app.isPersistent()) {
@@ -4704,16 +4720,17 @@
// The app's being debugged, ignore timeout.
return;
}
- if (proc.executingServices.size() == 0 || proc.thread == null) {
+ final ProcessServiceRecord psr = proc.mServices;
+ if (psr.numberOfExecutingServices() == 0 || proc.getThread() == null) {
return;
}
final long now = SystemClock.uptimeMillis();
final long maxTime = now -
- (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
+ (psr.shouldExecServicesFg() ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
- for (int i=proc.executingServices.size()-1; i>=0; i--) {
- ServiceRecord sr = proc.executingServices.valueAt(i);
+ for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
+ ServiceRecord sr = psr.getExecutingServiceAt(i);
if (sr.executingStart < maxTime) {
timeout = sr;
break;
@@ -4722,7 +4739,7 @@
nextTime = sr.executingStart;
}
}
- if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
+ if (timeout != null && mAm.mProcessList.isInLruListLOSP(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
@@ -4737,7 +4754,7 @@
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
- mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
+ mAm.mHandler.sendMessageAtTime(msg, psr.shouldExecServicesFg()
? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
}
}
@@ -4791,24 +4808,24 @@
}
void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
- mAm.crashApplication(app.uid, app.pid, app.info.packageName, app.userId,
+ mAm.crashApplication(app.uid, app.getPid(), app.info.packageName, app.userId,
"Context.startForegroundService() did not then call Service.startForeground(): "
+ serviceRecord, false /*force*/);
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
- if (proc.executingServices.size() == 0 || proc.thread == null) {
+ if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
- mAm.mHandler.sendMessageDelayed(msg,
- proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
+ mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
+ ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
- if (r.app.executingServices.size() == 0 || r.app.thread == null) {
+ if (r.app.mServices.numberOfExecutingServices() == 0 || r.app.getThread() == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
@@ -5005,7 +5022,7 @@
if (proc == null) {
return;
}
- final IApplicationThread thread = proc.thread;
+ final IApplicationThread thread = proc.getThread();
if (thread == null) {
return;
}
@@ -5331,20 +5348,21 @@
pw.print(Integer.toHexString(System.identityHashCode(r)));
pw.print(" pid=");
if (r.app != null) {
- pw.print(r.app.pid);
+ pw.print(r.app.getPid());
pw.print(" user="); pw.println(r.userId);
} else pw.println("(not running)");
if (dumpAll) {
r.dump(pw, innerPrefix);
}
}
- if (r.app != null && r.app.thread != null) {
+ IApplicationThread thread;
+ if (r.app != null && (thread = r.app.getThread()) != null) {
pw.print(prefix); pw.println(" Client:");
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
- r.app.thread.dumpService(tp.getWriteFd(), r, args);
+ thread.dumpService(tp.getWriteFd(), r, args);
tp.setBufferPrefix(prefix + " ");
tp.go(fd);
} finally {
@@ -5406,10 +5424,10 @@
boolean allowBackgroundActivityStarts) {
int ret = FGS_FEATURE_DENIED;
- final int uidState = mAm.getUidState(callingUid);
+ final int uidState = mAm.getUidStateLocked(callingUid);
if (ret == FGS_FEATURE_DENIED) {
// Is the calling UID at PROCESS_STATE_TOP or above?
- if (uidState <= ActivityManager.PROCESS_STATE_TOP) {
+ if (uidState <= PROCESS_STATE_TOP) {
ret = FGS_FEATURE_ALLOWED_BY_UID_STATE;
}
}
@@ -5450,14 +5468,16 @@
}
if (ret == FGS_FEATURE_DENIED) {
- for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
- final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
+ final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, pr -> {
if (pr.uid == callingUid) {
if (pr.getWindowProcessController().areBackgroundFgsStartsAllowed()) {
- ret = FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER;
- break;
+ return FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER;
}
}
+ return null;
+ });
+ if (allowedType != null) {
+ ret = allowedType;
}
}
@@ -5512,46 +5532,37 @@
int ret = allowWhileInUse;
final StringBuilder sb = new StringBuilder(64);
- final int uidState = mAm.getUidState(callingUid);
+ final int uidState = mAm.getUidStateLocked(callingUid);
if (ret == FGS_FEATURE_DENIED) {
// Is the calling UID at PROCESS_STATE_TOP or above?
- if (uidState <= ActivityManager.PROCESS_STATE_TOP) {
+ if (uidState <= PROCESS_STATE_TOP) {
sb.append("uidState=").append(uidState);
ret = FGS_FEATURE_ALLOWED_BY_UID_STATE;
}
}
if (ret == FGS_FEATURE_DENIED) {
- for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
- final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
- if (pr.uid == callingUid) {
- if (pr.mAllowStartFgs) {
- ret = FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD;
- break;
- } else if (pr.isAllowedStartFgsState()) {
- ret = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
- break;
- }
- }
- }
- }
-
- if (ret == FGS_FEATURE_DENIED) {
- for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
- final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
- if (pr.uid == callingUid) {
- if (pr.areBackgroundFgsStartsAllowedByToken()) {
- ret = FGS_FEATURE_ALLOWED_BY_FGS_BINDING;
- break;
+ final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, app -> {
+ if (app.uid == callingUid) {
+ final ProcessStateRecord state = app.mState;
+ if (state.getAllowedStartFgs() != FGS_FEATURE_DENIED) {
+ return state.getAllowedStartFgs();
+ } else if (state.isAllowedStartFgsState()) {
+ return FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+ } else if (state.areBackgroundFgsStartsAllowedByToken()) {
+ return FGS_FEATURE_ALLOWED_BY_FGS_BINDING;
} else {
- final ActiveInstrumentation instr = pr.getActiveInstrumentation();
+ final ActiveInstrumentation instr = app.getActiveInstrumentation();
if (instr != null
&& instr.mHasBackgroundForegroundServiceStartsPermission) {
- ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION;
- break;
+ return FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION;
}
}
}
+ return null;
+ });
+ if (allowedType != null) {
+ ret = allowedType;
}
}
@@ -5570,7 +5581,7 @@
}
if (ret == FGS_FEATURE_DENIED) {
- if (mAm.isAllowlistedForFgsStartLocked(callingUid)) {
+ if (mAm.isAllowlistedForFgsStartLOSP(callingUid)) {
// uid is on DeviceIdleController's user/system allowlist
// or AMS's FgsStartTempAllowList.
ret = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
@@ -5646,7 +5657,7 @@
return CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, uid);
}
- private static String fgsCodeToString(@FgsFeatureRetCode int code) {
+ static String fgsCodeToString(@FgsFeatureRetCode int code) {
switch (code) {
case FGS_FEATURE_DENIED:
return "DENIED";
@@ -5732,4 +5743,23 @@
return mAm.mConstants.mFlagFgsStartRestrictionEnabled
&& CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
}
+
+ private void logFgsBackgroundStart(ServiceRecord r) {
+ // Only log if FGS is started from background.
+ if (!isFgsBgStart(r.mAllowStartForeground)) {
+ return;
+ }
+ if (!r.mLoggedInfoAllowStartForeground) {
+ final String msg = "Background started FGS: "
+ + ((r.mAllowStartForeground != FGS_FEATURE_DENIED) ? "Allowed " : "Disallowed ")
+ + r.mInfoAllowStartForeground;
+ Slog.wtfQuiet(TAG, msg);
+ if (r.mAllowStartForeground != FGS_FEATURE_DENIED) {
+ Slog.i(TAG, msg);
+ } else {
+ Slog.w(TAG, msg);
+ }
+ r.mLoggedInfoAllowStartForeground = true;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActiveUids.java b/services/core/java/com/android/server/am/ActiveUids.java
index 27be53a..31db95b7 100644
--- a/services/core/java/com/android/server/am/ActiveUids.java
+++ b/services/core/java/com/android/server/am/ActiveUids.java
@@ -21,21 +21,29 @@
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.CompositeRWLock;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
/** Class for tracking active uids for running processes. */
final class ActiveUids {
- private ActivityManagerService mService;
+ private final ActivityManagerService mService;
+ private final ActivityManagerGlobalLock mProcLock;
- private boolean mPostChangesToAtm;
+ private final boolean mPostChangesToAtm;
+
+ @CompositeRWLock({"mService", "mProcLock"})
private final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
ActiveUids(ActivityManagerService service, boolean postChangesToAtm) {
mService = service;
+ mProcLock = service != null ? service.mProcLock : null;
mPostChangesToAtm = postChangesToAtm;
}
+ @GuardedBy({"mService", "mProcLock"})
void put(int uid, UidRecord value) {
mActiveUids.put(uid, value);
if (mPostChangesToAtm) {
@@ -43,6 +51,7 @@
}
}
+ @GuardedBy({"mService", "mProcLock"})
void remove(int uid) {
mActiveUids.remove(uid);
if (mPostChangesToAtm) {
@@ -50,38 +59,45 @@
}
}
+ @GuardedBy({"mService", "mProcLock"})
void clear() {
mActiveUids.clear();
// It is only called for a temporal container with mPostChangesToAtm == false or test case.
// So there is no need to notify activity task manager.
}
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
UidRecord get(int uid) {
return mActiveUids.get(uid);
}
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
int size() {
return mActiveUids.size();
}
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
UidRecord valueAt(int index) {
return mActiveUids.valueAt(index);
}
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
int keyAt(int index) {
return mActiveUids.keyAt(index);
}
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
int indexOfKey(int uid) {
return mActiveUids.indexOfKey(uid);
}
- boolean dump(PrintWriter pw, String dumpPackage, int dumpAppId,
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean dump(final PrintWriter pw, String dumpPackage, int dumpAppId,
String header, boolean needSep) {
boolean printed = false;
for (int i = 0; i < mActiveUids.size(); i++) {
final UidRecord uidRec = mActiveUids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.getUid()) != dumpAppId) {
continue;
}
if (!printed) {
@@ -91,16 +107,16 @@
}
pw.print(" "); pw.println(header);
}
- pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
+ pw.print(" UID "); UserHandle.formatUid(pw, uidRec.getUid());
pw.print(": "); pw.println(uidRec);
- pw.print(" curProcState="); pw.print(uidRec.mCurProcState);
+ pw.print(" curProcState="); pw.print(uidRec.getCurProcState());
pw.print(" curCapability=");
- ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability);
+ ActivityManager.printCapabilitiesFull(pw, uidRec.getCurCapability());
pw.println();
- for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) {
+ uidRec.forEachProcess(app -> {
pw.print(" proc=");
- pw.println(uidRec.procRecords.valueAt(j));
- }
+ pw.println(app);
+ });
}
return printed;
}
@@ -108,7 +124,7 @@
void dumpProto(ProtoOutputStream proto, String dumpPackage, int dumpAppId, long fieldId) {
for (int i = 0; i < mActiveUids.size(); i++) {
UidRecord uidRec = mActiveUids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.getUid()) != dumpAppId) {
continue;
}
uidRec.dumpDebug(proto, fieldId);
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/am/ActivityManagerGlobalLock.java
similarity index 72%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/am/ActivityManagerGlobalLock.java
index 19b20f2..7823038 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/am/ActivityManagerGlobalLock.java
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package com.android.server.am;
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Interface to mark whichever class with the implementation is considered as a global lock
+ * to be used in the package of ActivityManagerService.
+ */
+interface ActivityManagerGlobalLock {
+}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/am/ActivityManagerProcLock.java
similarity index 63%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/am/ActivityManagerProcLock.java
index 19b20f2..3ef19ad 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/am/ActivityManagerProcLock.java
@@ -14,7 +14,15 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package com.android.server.am;
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Class that is used to generate an instance of the {@link ActivityManagerService#mProcLock},
+ * so the CPU booster can identify the critical section.
+ *
+ * <p>
+ * Use "-Lpr" as the suffix of functions locked with this lock.
+ * </p>
+ */
+final class ActivityManagerProcLock implements ActivityManagerGlobalLock {
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c8f5f8e..9382e1a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -27,6 +27,7 @@
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
@@ -110,7 +111,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -145,6 +145,7 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.ActivityThread;
+import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
@@ -236,7 +237,6 @@
import android.os.FactoryTest;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IDeviceIdentifiersPolicyService;
import android.os.IPermissionController;
@@ -249,6 +249,7 @@
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
+import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -294,6 +295,7 @@
import android.view.WindowManager;
import android.view.autofill.AutofillManagerInternal;
+import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsActiveCallback;
@@ -396,7 +398,7 @@
import java.util.concurrent.atomic.AtomicInteger;
public class ActivityManagerService extends IActivityManager.Stub
- implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
+ implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
/**
* Priority we boost main thread and RT of top app to.
@@ -416,7 +418,6 @@
static final String TAG_NETWORK = TAG + POSTFIX_NETWORK;
static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
private static final String TAG_POWER = TAG + POSTFIX_POWER;
- static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
@@ -527,12 +528,55 @@
final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
+ @CompositeRWLock({"this", "mProcLock"})
final ArrayList<ActiveInstrumentation> mActiveInstrumentation = new ArrayList<>();
public final IntentFirewall mIntentFirewall;
public OomAdjProfiler mOomAdjProfiler = new OomAdjProfiler();
+ /**
+ * The global lock for AMS, it's de-facto the ActivityManagerService object as of now.
+ */
+ final ActivityManagerGlobalLock mGlobalLock = ActivityManagerService.this;
+
+ /**
+ * Whether or not to enable the {@link #mProcLock}. If {@code false}, the {@link #mProcLock}
+ * will be equivalent to the {@link #mGlobalLock}.
+ */
+ private static final boolean ENABLE_PROC_LOCK = true;
+
+ /**
+ * The lock for process management.
+ *
+ * <p>
+ * This lock is widely used in conjunction with the {@link #mGlobalLock} at present,
+ * where it'll require any of the locks to read from a data class, and both of the locks
+ * to write into that data class.
+ *
+ * For the naming convention of function suffixes:
+ * <ul>
+ * <li>-LOSP: Locked with any Of global am Service or Process lock</li>
+ * <li>-LSP: Locked with both of global am Service and Process lock</li>
+ * <li>-Locked: Locked with global am service lock alone</li>
+ * <li>-LPr: Locked with Process lock alone</li>
+ * </ul>
+ * For the simplicity, the getters/setters of the fields in data classes usually don't end with
+ * the above suffixes even if they're guarded by the locks here.
+ * </p>
+ *
+ * <p>
+ * In terms of locking order, it should be right below to the {@link #mGlobalLock},
+ * and above everything else which used to be underneath the {@link #mGlobalLock}.
+ * As of today, the core components(services/providers/broadcasts) are still guarded by
+ * the {@link #mGlobalLock} alone, so be cautious, avoid from acquiring the {@link #mGlobalLock}
+ * while holding this lock.
+ * </p>
+ *
+ */
+ final ActivityManagerGlobalLock mProcLock = ENABLE_PROC_LOCK
+ ? new ActivityManagerProcLock() : mGlobalLock;
+
// Whether we should use SCHED_FIFO for UI and RenderThreads.
final boolean mUseFifoUiScheduling;
@@ -548,7 +592,10 @@
// so that dispatch of foreground broadcasts gets precedence.
final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[3];
+ @GuardedBy("this")
BroadcastStats mLastBroadcastStats;
+
+ @GuardedBy("this")
BroadcastStats mCurBroadcastStats;
BroadcastQueue broadcastQueueForIntent(Intent intent) {
@@ -569,10 +616,11 @@
/**
* The package name of the DeviceOwner. This package is not permitted to have its data cleared.
+ * <p>Not actually used</p>
*/
- String mDeviceOwnerName;
+ private volatile String mDeviceOwnerName;
- private int mDeviceOwnerUid = Process.INVALID_UID;
+ private volatile int mDeviceOwnerUid = Process.INVALID_UID;
/**
* Map userId to its companion app uids.
@@ -643,6 +691,25 @@
sThreadPriorityBooster.reset();
}
+ private static ThreadPriorityBooster sProcThreadPriorityBooster = new ThreadPriorityBooster(
+ THREAD_PRIORITY_FOREGROUND, LockGuard.INDEX_PROC);
+
+ static void boostPriorityForProcLockedSection() {
+ if (ENABLE_PROC_LOCK) {
+ sProcThreadPriorityBooster.boost();
+ } else {
+ sThreadPriorityBooster.boost();
+ }
+ }
+
+ static void resetPriorityAfterProcLockedSection() {
+ if (ENABLE_PROC_LOCK) {
+ sProcThreadPriorityBooster.reset();
+ } else {
+ sThreadPriorityBooster.reset();
+ }
+ }
+
/**
* Process management.
*/
@@ -663,20 +730,23 @@
/**
* Non-persistent appId allowlist for background restrictions
*/
- int[] mBackgroundAppIdAllowlist = new int[] {
+ @CompositeRWLock({"this", "mProcLock"})
+ private int[] mBackgroundAppIdAllowlist = new int[] {
BLUETOOTH_UID
};
/**
* Broadcast actions that will always be deliverable to unlaunched/background apps
*/
- ArraySet<String> mBackgroundLaunchBroadcasts;
+ @GuardedBy("this")
+ private ArraySet<String> mBackgroundLaunchBroadcasts;
/**
* When an app has restrictions on the other apps that can have associations with it,
* it appears here with a set of the allowed apps and also track debuggability of the app.
*/
- ArrayMap<String, PackageAssociationInfo> mAllowedAssociations;
+ @GuardedBy("this")
+ private ArrayMap<String, PackageAssociationInfo> mAllowedAssociations;
/**
* Tracks association information for a particular package along with debuggability.
@@ -754,24 +824,24 @@
return mPidMap.indexOfKey(key);
}
- void doAddInternal(ProcessRecord app) {
- mPidMap.put(app.pid, app);
+ void doAddInternal(int pid, ProcessRecord app) {
+ mPidMap.put(pid, app);
}
- boolean doRemoveInternal(ProcessRecord app) {
- final ProcessRecord existingApp = mPidMap.get(app.pid);
- if (existingApp != null && existingApp.startSeq == app.startSeq) {
- mPidMap.remove(app.pid);
+ boolean doRemoveInternal(int pid, ProcessRecord app) {
+ final ProcessRecord existingApp = mPidMap.get(pid);
+ if (existingApp != null && existingApp.getStartSeq() == app.getStartSeq()) {
+ mPidMap.remove(pid);
return true;
}
return false;
}
- boolean doRemoveIfNoThreadInternal(ProcessRecord app) {
- if (app == null || app.thread != null) {
+ boolean doRemoveIfNoThreadInternal(int pid, ProcessRecord app) {
+ if (app == null || app.getThread() != null) {
return false;
}
- return doRemoveInternal(app);
+ return doRemoveInternal(pid, app);
}
}
@@ -782,18 +852,20 @@
* <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
* method.
*/
+ @GuardedBy("this")
void addPidLocked(ProcessRecord app) {
+ final int pid = app.getPid();
synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.doAddInternal(app);
+ mPidsSelfLocked.doAddInternal(pid, app);
}
synchronized (sActiveProcessInfoSelfLocked) {
if (app.processInfo != null) {
- sActiveProcessInfoSelfLocked.put(app.pid, app.processInfo);
+ sActiveProcessInfoSelfLocked.put(pid, app.processInfo);
} else {
- sActiveProcessInfoSelfLocked.remove(app.pid);
+ sActiveProcessInfoSelfLocked.remove(pid);
}
}
- mAtmInternal.onProcessMapped(app.pid, app.getWindowProcessController());
+ mAtmInternal.onProcessMapped(pid, app.getWindowProcessController());
}
/**
@@ -801,16 +873,17 @@
* <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
* method.
*/
- void removePidLocked(ProcessRecord app) {
+ @GuardedBy("this")
+ void removePidLocked(int pid, ProcessRecord app) {
final boolean removed;
synchronized (mPidsSelfLocked) {
- removed = mPidsSelfLocked.doRemoveInternal(app);
+ removed = mPidsSelfLocked.doRemoveInternal(pid, app);
}
if (removed) {
synchronized (sActiveProcessInfoSelfLocked) {
- sActiveProcessInfoSelfLocked.remove(app.pid);
+ sActiveProcessInfoSelfLocked.remove(pid);
}
- mAtmInternal.onProcessUnMapped(app.pid);
+ mAtmInternal.onProcessUnMapped(pid);
}
}
@@ -819,16 +892,18 @@
* <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
* method.
*/
- boolean removePidIfNoThread(ProcessRecord app) {
+ @GuardedBy("this")
+ private boolean removePidIfNoThreadLocked(ProcessRecord app) {
final boolean removed;
+ final int pid = app.getPid();
synchronized (mPidsSelfLocked) {
- removed = mPidsSelfLocked.doRemoveIfNoThreadInternal(app);
+ removed = mPidsSelfLocked.doRemoveIfNoThreadInternal(pid, app);
}
if (removed) {
synchronized (sActiveProcessInfoSelfLocked) {
- sActiveProcessInfoSelfLocked.remove(app.pid);
+ sActiveProcessInfoSelfLocked.remove(pid);
}
- mAtmInternal.onProcessUnMapped(app.pid);
+ mAtmInternal.onProcessUnMapped(pid);
}
return removed;
}
@@ -865,6 +940,7 @@
proto.end(pToken);
}
}
+ @GuardedBy("this")
final SparseArray<ImportanceToken> mImportantProcesses = new SparseArray<ImportanceToken>();
/**
@@ -872,12 +948,14 @@
* system was ready. We don't start them at that point, but ensure they
* are started by the time booting is complete.
*/
+ @GuardedBy("this")
final ArrayList<ProcessRecord> mProcessesOnHold = new ArrayList<ProcessRecord>();
/**
* List of persistent applications that are in the process
* of being started.
*/
+ @GuardedBy("this")
final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
private final ActivityMetricsLaunchObserver mActivityLaunchObserver =
@@ -925,6 +1003,7 @@
* Keeps track of all IIntentReceivers that have been registered for broadcasts.
* Hash keys are the receiver IBinder, hash value is a ReceiverList.
*/
+ @GuardedBy("this")
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
/**
@@ -977,6 +1056,7 @@
* by the user ID the sticky is for, and can include UserHandle.USER_ALL
* for stickies that are sent to all users.
*/
+ @GuardedBy("this")
final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
new SparseArray<ArrayMap<String, ArrayList<Intent>>>();
@@ -1016,6 +1096,7 @@
* have seen. Mapping is target uid -> target component -> source uid -> source process name
* -> association data.
*/
+ @GuardedBy("this")
final SparseArray<ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>>>
mAssociations = new SparseArray<>();
boolean mTrackingAssociations;
@@ -1067,16 +1148,19 @@
/**
* Power-save whitelisted app-ids (not including except-idle-whitelisted ones).
*/
+ @CompositeRWLock({"this", "mProcLock"})
int[] mDeviceIdleAllowlist = new int[0];
/**
* Power-save whitelisted app-ids (including except-idle-whitelisted ones).
*/
+ @CompositeRWLock({"this", "mProcLock"})
int[] mDeviceIdleExceptIdleAllowlist = new int[0];
/**
* Set of app ids that are temporarily allowed to escape bg check due to high-pri message
*/
+ @CompositeRWLock({"this", "mProcLock"})
int[] mDeviceIdleTempAllowlist = new int[0];
static final class PendingTempAllowlist {
@@ -1102,11 +1186,13 @@
}
}
+ @CompositeRWLock({"this", "mProcLock"})
final PendingTempAllowlists mPendingTempAllowlist = new PendingTempAllowlists(this);
/**
* The temp-allowlist that is allowed to start FGS from background.
*/
+ @CompositeRWLock({"this", "mProcLock"})
final FgsStartTempAllowList mFgsStartTempAllowList = new FgsStartTempAllowList();
/**
@@ -1122,11 +1208,6 @@
ArrayMap<String, IBinder> mAppBindArgs;
ArrayMap<String, IBinder> mIsolatedAppBindArgs;
- /**
- * Temporary to avoid allocations. Protected by main lock.
- */
- final StringBuilder mStringBuilder = new StringBuilder(256);
-
volatile boolean mProcessesReady = false;
volatile boolean mSystemReady = false;
volatile boolean mOnBattery = false;
@@ -1147,6 +1228,7 @@
/**
* Last time (in uptime) at which we checked for power usage.
*/
+ @GuardedBy("mProcLock")
long mLastPowerCheckUptime;
/**
@@ -1162,10 +1244,14 @@
/**
* The uptime of the last time we performed idle maintenance.
*/
+ @GuardedBy("mProcLock")
long mLastIdleTime = SystemClock.uptimeMillis();
/**
* For reporting to battery stats the current top application.
+ *
+ * <p>It has its own lock to avoid from the need of double locking if using the global
+ * ActivityManagerService lock and proc lock to guard it.</p>
*/
@GuardedBy("mCurResumedAppLock")
private String mCurResumedPackage = null;
@@ -1183,22 +1269,38 @@
* service. The ProcessMap is package/uid tuples; each of these contain
* an array of the currently foreground processes.
*/
+ @GuardedBy("this")
final ProcessMap<ArrayList<ProcessRecord>> mForegroundPackages
= new ProcessMap<ArrayList<ProcessRecord>>();
/**
* Set if the systemServer made a call to enterSafeMode.
*/
+ @GuardedBy("this")
boolean mSafeMode;
- String mDebugApp = null;
- boolean mWaitForDebugger = false;
- boolean mDebugTransient = false;
- String mOrigDebugApp = null;
- boolean mOrigWaitForDebugger = false;
+ @GuardedBy("this")
+ private String mDebugApp = null;
+
+ @GuardedBy("this")
+ private boolean mWaitForDebugger = false;
+
+ @GuardedBy("this")
+ private boolean mDebugTransient = false;
+
+ @GuardedBy("this")
+ private String mOrigDebugApp = null;
+
+ @GuardedBy("this")
+ private boolean mOrigWaitForDebugger = false;
+
+ @GuardedBy("this")
boolean mAlwaysFinishActivities = false;
- String mTrackAllocationApp = null;
+ @GuardedBy("mProcLock")
+ private String mTrackAllocationApp = null;
+
+ @GuardedBy("this")
String mNativeDebuggingApp = null;
final Injector mInjector;
@@ -1343,10 +1445,12 @@
/**
* Whether to force background check on all apps (for battery saver) or not.
*/
- boolean mForceBackgroundCheck;
+ @CompositeRWLock({"this", "mProcLock"})
+ private boolean mForceBackgroundCheck;
private static String sTheRealBuildSerial = Build.UNKNOWN;
+ @GuardedBy("mProcLock")
private ParcelFileDescriptor[] mLifeMonitorFds;
static final HostingRecord sNullHostingRecord = new HostingRecord(null);
@@ -1368,6 +1472,7 @@
/**
* The last time when the binder heavy hitter auto sampler started.
*/
+ @GuardedBy("mProcLock")
private long mLastBinderHeavyHitterAutoSamplerStart = 0L;
final AppProfiler mAppProfiler;
@@ -1405,19 +1510,19 @@
} break;
case SHOW_STRICT_MODE_VIOLATION_UI_MSG: {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
- synchronized (ActivityManagerService.this) {
+ synchronized (mProcLock) {
ProcessRecord proc = (ProcessRecord) data.get("app");
if (proc == null) {
Slog.e(TAG, "App not found when showing strict mode dialog.");
break;
}
- if (proc.getDialogController().hasViolationDialogs()) {
+ if (proc.mErrorState.getDialogController().hasViolationDialogs()) {
Slog.e(TAG, "App already has strict mode dialog: " + proc);
return;
}
AppErrorResult res = (AppErrorResult) data.get("result");
if (mAtmInternal.showStrictModeViolationDialog()) {
- proc.getDialogController().showViolationDialogs(res);
+ proc.mErrorState.getDialogController().showViolationDialogs(res);
} else {
// The device is asleep, so just pretend that the user
// saw a crash dialog and hit "force quit".
@@ -1427,15 +1532,15 @@
ensureBootCompleted();
} break;
case WAIT_FOR_DEBUGGER_UI_MSG: {
- synchronized (ActivityManagerService.this) {
+ synchronized (mProcLock) {
ProcessRecord app = (ProcessRecord) msg.obj;
if (msg.arg1 != 0) {
- if (!app.waitedForDebugger) {
- app.getDialogController().showDebugWaitingDialogs();
- app.waitedForDebugger = true;
+ if (!app.hasWaitedForDebugger()) {
+ app.mErrorState.getDialogController().showDebugWaitingDialogs();
+ app.setWaitedForDebugger(true);
}
} else {
- app.getDialogController().clearWaitingDialog();
+ app.mErrorState.getDialogController().clearWaitingDialog();
}
}
} break;
@@ -1486,23 +1591,23 @@
msg.getData().getCharSequence(SERVICE_RECORD_KEY));
} break;
case UPDATE_TIME_ZONE: {
- synchronized (ActivityManagerService.this) {
- for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord r = mProcessList.mLruProcesses.get(i);
- if (r.thread != null) {
+ synchronized (mProcLock) {
+ mProcessList.forEachLruProcessesLOSP(false, app -> {
+ final IApplicationThread thread = app.getThread();
+ if (thread != null) {
try {
- r.thread.updateTimeZone();
+ thread.updateTimeZone();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update time zone for: "
- + r.info.processName);
+ + app.info.processName);
}
- }
+ }
+ });
}
- }
} break;
case CLEAR_DNS_CACHE_MSG: {
- synchronized (ActivityManagerService.this) {
- mProcessList.clearAllDnsCacheLocked();
+ synchronized (mProcLock) {
+ mProcessList.clearAllDnsCacheLOSP();
}
} break;
case UPDATE_HTTP_PROXY_MSG: {
@@ -1557,8 +1662,8 @@
case UPDATE_TIME_PREFERENCE_MSG: {
// The user's time format preference might have changed.
// For convenience we re-use the Intent extra values.
- synchronized (ActivityManagerService.this) {
- mProcessList.updateAllTimePrefsLocked(msg.arg1);
+ synchronized (mProcLock) {
+ mProcessList.updateAllTimePrefsLOSP(msg.arg1);
}
break;
}
@@ -1566,19 +1671,21 @@
final int uid = msg.arg1;
final byte[] firstPacket = (byte[]) msg.obj;
- synchronized (mPidsSelfLocked) {
- for (int i = 0; i < mPidsSelfLocked.size(); i++) {
- final ProcessRecord p = mPidsSelfLocked.valueAt(i);
- if (p.uid == uid && p.thread != null) {
- try {
- p.thread.notifyCleartextNetwork(firstPacket);
- } catch (RemoteException ignored) {
+ synchronized (mProcLock) {
+ synchronized (mPidsSelfLocked) {
+ for (int i = 0; i < mPidsSelfLocked.size(); i++) {
+ final ProcessRecord p = mPidsSelfLocked.valueAt(i);
+ final IApplicationThread thread = p.getThread();
+ if (p.uid == uid && thread != null) {
+ try {
+ thread.notifyCleartextNetwork(firstPacket);
+ } catch (RemoteException ignored) {
+ }
}
}
}
}
- break;
- }
+ } break;
case POST_DUMP_HEAP_NOTIFICATION_MSG: {
mAppProfiler.handlePostDumpHeapNotification();
} break;
@@ -1600,8 +1707,8 @@
idleUids();
} break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
- synchronized (ActivityManagerService.this) {
- mProcessList.handleAllTrustStorageUpdateLocked();
+ synchronized (mProcLock) {
+ mProcessList.handleAllTrustStorageUpdateLOSP();
}
} break;
case BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG: {
@@ -1641,12 +1748,12 @@
0,
new HostingRecord("system"));
app.setPersistent(true);
- app.pid = MY_PID;
+ app.mPid = MY_PID;
app.getWindowProcessController().setPid(MY_PID);
- app.maxAdj = ProcessList.SYSTEM_ADJ;
+ app.mState.setMaxAdj(ProcessList.SYSTEM_ADJ);
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
addPidLocked(app);
- mProcessList.updateLruProcessLocked(app, false, null);
+ updateLruProcessLocked(app, false, null);
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
} catch (PackageManager.NameNotFoundException e) {
@@ -2099,19 +2206,11 @@
mEnableOffloadQueue = SystemProperties.getBoolean(
"persist.device_config.activity_manager_native_boot.offload_queue_enabled", false);
- // Decouple broadcast-related timing operations from other OS activity by
- // using a dedicated thread. Sharing this thread between queues is safe
- // because we know the nature of the activity on it and can't stall
- // unexpectedly.
- HandlerThread broadcastThread = new HandlerThread("broadcast");
- broadcastThread.start();
- Handler broadcastHandler = broadcastThread.getThreadHandler();
-
- mFgBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
+ mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", foreConstants, false);
- mBgBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
+ mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", backConstants, true);
- mOffloadBroadcastQueue = new BroadcastQueue(this, broadcastHandler,
+ mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
"offload", offloadConstants, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
@@ -2296,16 +2395,18 @@
if (code == SYSPROPS_TRANSACTION) {
// We need to tell all apps about the system property change.
ArrayList<IBinder> procs = new ArrayList<IBinder>();
- synchronized (this) {
- final int NP = mProcessList.mProcessNames.getMap().size();
- for (int ip = 0; ip < NP; ip++) {
- SparseArray<ProcessRecord> apps =
- mProcessList.mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia = 0; ia < NA; ia++) {
+ synchronized (mProcLock) {
+ final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
+ mProcessList.getProcessNamesLOSP().getMap();
+ final int numOfNames = pmap.size();
+ for (int ip = 0; ip < numOfNames; ip++) {
+ SparseArray<ProcessRecord> apps = pmap.valueAt(ip);
+ final int numOfApps = apps.size();
+ for (int ia = 0; ia < numOfApps; ia++) {
ProcessRecord app = apps.valueAt(ia);
- if (app.thread != null) {
- procs.add(app.thread.asBinder());
+ final IApplicationThread thread = app.getThread();
+ if (thread != null) {
+ procs.add(thread.asBinder());
}
}
}
@@ -2357,10 +2458,8 @@
// When plugging in, update the CPU stats first before changing
// the plug state.
updateCpuStatsNow();
- synchronized (this) {
- synchronized(mPidsSelfLocked) {
- mOnBattery = DEBUG_POWER ? true : onBattery;
- }
+ synchronized (mProcLock) {
+ mOnBattery = DEBUG_POWER ? true : onBattery;
mOomAdjProfiler.batteryPowerChanged(onBattery);
}
}
@@ -2455,21 +2554,25 @@
mActivityTaskManager.unregisterTaskStackListener(listener);
}
+ @GuardedBy("this")
final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
ProcessRecord client) {
mProcessList.updateLruProcessLocked(app, activityChange, client);
}
+ @GuardedBy("this")
final void removeLruProcessLocked(ProcessRecord app) {
mProcessList.removeLruProcessLocked(app);
}
+ @GuardedBy("this")
final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
return mProcessList.getProcessRecordLocked(processName, uid, keepIfLarge);
}
- final ProcessMap<ProcessRecord> getProcessNames() {
- return mProcessList.mProcessNames;
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ final ProcessMap<ProcessRecord> getProcessNamesLOSP() {
+ return mProcessList.getProcessNamesLOSP();
}
void notifyPackageUse(String packageName, int reason) {
@@ -2552,7 +2655,7 @@
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(), taskRoot);
}
- final ContentCaptureManagerInternal contentCaptureService = mContentCaptureService;
+ ContentCaptureManagerInternal contentCaptureService = mContentCaptureService;
if (contentCaptureService != null && (event == Event.ACTIVITY_PAUSED
|| event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED
|| event == Event.ACTIVITY_DESTROYED)) {
@@ -2626,19 +2729,18 @@
"getPackageProcessState");
}
- int procState = PROCESS_STATE_NONEXISTENT;
- synchronized (this) {
- for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
- final ProcessRecord proc = mProcessList.mLruProcesses.get(i);
- if (procState > proc.setProcState) {
- if (proc.getPkgList().containsKey(packageName)
- || (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
- procState = proc.setProcState;
+ final int[] procState = {PROCESS_STATE_NONEXISTENT};
+ synchronized (mProcLock) {
+ mProcessList.forEachLruProcessesLOSP(false, proc -> {
+ if (procState[0] > proc.mState.getSetProcState()) {
+ if (proc.getPkgList().containsKey(packageName) || (proc.getPkgDeps() != null
+ && proc.getPkgDeps().contains(packageName))) {
+ procState[0] = proc.mState.getSetProcState();
}
}
- }
+ });
}
- return procState;
+ return procState[0];
}
@Override
@@ -2648,11 +2750,12 @@
throw new SecurityException("Only shell can call it");
}
synchronized (this) {
- final ProcessRecord app = findProcessLocked(process, userId, "setProcessMemoryTrimLevel");
+ final ProcessRecord app = findProcessLOSP(process, userId, "setProcessMemoryTrimLevel");
if (app == null) {
throw new IllegalArgumentException("Unknown process: " + process);
}
- if (app.thread == null) {
+ final IApplicationThread thread = app.getThread();
+ if (thread == null) {
throw new IllegalArgumentException("Process has no app thread");
}
if (app.mProfile.getTrimMemoryLevel() >= level) {
@@ -2660,12 +2763,14 @@
"Unable to set a higher trim level than current level");
}
if (!(level < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN ||
- app.getCurProcState() > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)) {
+ app.mState.getCurProcState() > PROCESS_STATE_IMPORTANT_FOREGROUND)) {
throw new IllegalArgumentException("Unable to set a background trim level "
+ "on a foreground process");
}
- app.thread.scheduleTrimMemory(level);
- app.mProfile.setTrimMemoryLevel(level);
+ thread.scheduleTrimMemory(level);
+ synchronized (mProcLock) {
+ app.mProfile.setTrimMemoryLevel(level);
+ }
return true;
}
}
@@ -2822,10 +2927,9 @@
* to the process.
*/
@GuardedBy("this")
- final void handleAppDiedLocked(ProcessRecord app,
+ final void handleAppDiedLocked(ProcessRecord app, int pid,
boolean restarting, boolean allowRestart) {
- int pid = app.pid;
- boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
+ boolean kept = cleanUpApplicationRecordLocked(app, pid, restarting, allowRestart, -1,
false /*replacingPid*/);
if (!kept && !restarting) {
removeLruProcessLocked(app);
@@ -2845,24 +2949,26 @@
});
}
- ProcessRecord getRecordForAppLocked(IApplicationThread thread) {
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ ProcessRecord getRecordForAppLOSP(IApplicationThread thread) {
if (thread == null) {
return null;
}
- ProcessRecord record = mProcessList.getLRURecordForAppLocked(thread);
+ ProcessRecord record = mProcessList.getLRURecordForAppLOSP(thread);
if (record != null) return record;
// Validation: if it isn't in the LRU list, it shouldn't exist, but let's
// double-check that.
final IBinder threadBinder = thread.asBinder();
final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
- mProcessList.mProcessNames.getMap();
+ mProcessList.getProcessNamesLOSP().getMap();
for (int i = pmap.size()-1; i >= 0; i--) {
final SparseArray<ProcessRecord> procs = pmap.valueAt(i);
for (int j = procs.size()-1; j >= 0; j--) {
final ProcessRecord proc = procs.valueAt(j);
- if (proc.thread != null && proc.thread.asBinder() == threadBinder) {
+ final IApplicationThread procThread = proc.getThread();
+ if (procThread != null && procThread.asBinder() == threadBinder) {
Slog.wtf(TAG, "getRecordForApp: exists in name list but not in LRU list: "
+ proc);
return proc;
@@ -2875,7 +2981,7 @@
@GuardedBy("this")
final void appDiedLocked(ProcessRecord app, String reason) {
- appDiedLocked(app, app.pid, app.thread, false, reason);
+ appDiedLocked(app, app.getPid(), app.getThread(), false, reason);
}
@GuardedBy("this")
@@ -2892,26 +2998,31 @@
mBatteryStatsService.noteProcessDied(app.info.uid, pid);
- if (!app.killed) {
+ if (!app.isKilled()) {
if (!fromBinderDied) {
killProcessQuiet(pid);
mProcessList.noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_UNKNOWN, reason);
}
ProcessList.killProcessGroup(app.uid, pid);
- app.killed = true;
+ synchronized (mProcLock) {
+ app.setKilled(true);
+ }
}
// Clean up already done if the process has been re-started.
- if (app.pid == pid && app.thread != null &&
- app.thread.asBinder() == thread.asBinder()) {
+ IApplicationThread appThread;
+ final int setAdj = app.mState.getSetAdj();
+ final int setProcState = app.mState.getSetProcState();
+ if (app.getPid() == pid && (appThread = app.getThread()) != null
+ && appThread.asBinder() == thread.asBinder()) {
boolean doLowMem = app.getActiveInstrumentation() == null;
boolean doOomAdj = doLowMem;
- if (!app.killedByAm) {
+ if (!app.isKilledByAm()) {
reportUidInfoMessageLocked(TAG,
"Process " + app.processName + " (pid " + pid + ") has died: "
- + ProcessList.makeOomAdjString(app.setAdj, true) + " "
- + ProcessList.makeProcStateString(app.setProcState), app.info.uid);
+ + ProcessList.makeOomAdjString(setAdj, true) + " "
+ + ProcessList.makeProcStateString(setProcState), app.info.uid);
mAppProfiler.setAllowLowerMemLevelLocked(true);
} else {
// Note that we always want to do oom adj to update our state with the
@@ -2919,11 +3030,10 @@
mAppProfiler.setAllowLowerMemLevelLocked(false);
doLowMem = false;
}
- EventLogTags.writeAmProcDied(app.userId, app.pid, app.processName, app.setAdj,
- app.setProcState);
+ EventLogTags.writeAmProcDied(app.userId, pid, app.processName, setAdj, setProcState);
if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder());
- handleAppDiedLocked(app, false, true);
+ handleAppDiedLocked(app, pid, false, true);
if (doOomAdj) {
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
@@ -2931,14 +3041,14 @@
if (doLowMem) {
mAppProfiler.doLowMemReportIfNeededLocked(app);
}
- } else if (app.pid != pid) {
+ } else if (app.getPid() != pid) {
// A new process has already been started.
reportUidInfoMessageLocked(TAG,
"Process " + app.processName + " (pid " + pid
- + ") has died and restarted (pid " + app.pid + ").", app.info.uid);
+ + ") has died and restarted (pid " + app.getPid() + ").", app.info.uid);
- EventLogTags.writeAmProcDied(app.userId, app.pid, app.processName, app.setAdj,
- app.setProcState);
+ EventLogTags.writeAmProcDied(app.userId, app.getPid(), app.processName,
+ setAdj, setProcState);
} else if (DEBUG_PROCESSES) {
Slog.d(TAG_PROCESSES, "Received spurious death notification for thread "
+ thread.asBinder());
@@ -3386,9 +3496,11 @@
return;
}
synchronized (this) {
- mProcessList.killPackageProcessesLocked(packageName, appId, targetUserId,
- ProcessList.SERVICE_ADJ, ApplicationExitInfo.REASON_USER_REQUESTED,
- ApplicationExitInfo.SUBREASON_UNKNOWN, "kill background");
+ synchronized (mProcLock) {
+ mProcessList.killPackageProcessesLSP(packageName, appId, targetUserId,
+ ProcessList.SERVICE_ADJ, ApplicationExitInfo.REASON_USER_REQUESTED,
+ ApplicationExitInfo.SUBREASON_UNKNOWN, "kill background");
+ }
}
}
} finally {
@@ -3413,11 +3525,13 @@
// Allow memory level to go down (the flag needs to be set before updating oom adj)
// because this method is also used to simulate low memory.
mAppProfiler.setAllowLowerMemLevelLocked(true);
- mProcessList.killPackageProcessesLocked(null /* packageName */, -1 /* appId */,
- UserHandle.USER_ALL, ProcessList.CACHED_APP_MIN_ADJ,
- ApplicationExitInfo.REASON_USER_REQUESTED,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- "kill all background");
+ synchronized (mProcLock) {
+ mProcessList.killPackageProcessesLSP(null /* packageName */, -1 /* appId */,
+ UserHandle.USER_ALL, ProcessList.CACHED_APP_MIN_ADJ,
+ ApplicationExitInfo.REASON_USER_REQUESTED,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ "kill all background");
+ }
mAppProfiler.doLowMemReportIfNeededLocked(null);
}
@@ -3448,7 +3562,9 @@
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (this) {
- mProcessList.killAllBackgroundProcessesExceptLocked(minTargetSdk, maxProcState);
+ synchronized (mProcLock) {
+ mProcessList.killAllBackgroundProcessesExceptLSP(minTargetSdk, maxProcState);
+ }
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -3523,11 +3639,14 @@
proc = mPidsSelfLocked.get(Binder.getCallingPid());
}
if (proc != null) {
+ ArraySet<String> pkgDeps = proc.getPkgDeps();
synchronized (this) {
- if (proc.pkgDeps == null) {
- proc.pkgDeps = new ArraySet<String>(1);
+ synchronized (mProcLock) {
+ if (pkgDeps == null) {
+ proc.setPkgDeps(pkgDeps = new ArraySet<String>(1));
+ }
+ pkgDeps.add(packageName);
}
- proc.pkgDeps.add(packageName);
}
}
}
@@ -3587,12 +3706,14 @@
// Check if the caller is actually instrumented and from shell, if it's true, we may lift
// the throttle of PSS info sampling.
boolean isCallerInstrumentedFromShell = false;
- synchronized (mPidsSelfLocked) {
- ProcessRecord caller = mPidsSelfLocked.get(callingPid);
- if (caller != null) {
- final ActiveInstrumentation instr = caller.getActiveInstrumentation();
- isCallerInstrumentedFromShell = instr != null
- && (instr.mSourceUid == SHELL_UID || instr.mSourceUid == ROOT_UID);
+ synchronized (mProcLock) {
+ synchronized (mPidsSelfLocked) {
+ ProcessRecord caller = mPidsSelfLocked.get(callingPid);
+ if (caller != null) {
+ final ActiveInstrumentation instr = caller.getActiveInstrumentation();
+ isCallerInstrumentedFromShell = instr != null
+ && (instr.mSourceUid == SHELL_UID || instr.mSourceUid == ROOT_UID);
+ }
}
}
@@ -3687,10 +3808,10 @@
for (int i=pids.length-1; i>=0; i--) {
ProcessRecord proc;
int oomAdj;
- synchronized (this) {
+ synchronized (mProcLock) {
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pids[i]);
- oomAdj = proc != null ? proc.setAdj : 0;
+ oomAdj = proc != null ? proc.mState.getSetAdj() : 0;
}
}
if (!allUids || (!allUsers && UserHandle.getUserId(proc.uid) != userId)) {
@@ -3739,9 +3860,10 @@
if (callerUid == SYSTEM_UID) {
synchronized (this) {
ProcessRecord app = getProcessRecordLocked(processName, uid, true);
- if (app != null && app.thread != null) {
+ IApplicationThread thread;
+ if (app != null && (thread = app.getThread()) != null) {
try {
- app.thread.scheduleSuicide();
+ thread.scheduleSuicide();
} catch (RemoteException e) {
// If the other end already died, then our work here is done.
}
@@ -3903,6 +4025,7 @@
}
}
+ boolean didSomething;
if (doit) {
if (packageName != null) {
Slog.i(TAG, "Force stopping " + packageName + " appid=" + appId
@@ -3914,20 +4037,22 @@
mAppErrors.resetProcessCrashTime(packageName == null, appId, userId);
}
- // Notify first that the package is stopped, so its process won't be restarted unexpectedly
- // if there is an activity of the package without attached process becomes visible when
- // killing its other processes with visible activities.
- boolean didSomething =
- mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);
+ synchronized (mProcLock) {
+ // Notify first that the package is stopped, so its process won't be restarted
+ // unexpectedly if there is an activity of the package without attached process
+ // becomes visible when killing its other processes with visible activities.
+ didSomething = mAtmInternal.onForceStopPackage(
+ packageName, doit, evenPersistent, userId);
- didSomething |= mProcessList.killPackageProcessesLocked(packageName, appId, userId,
- ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
- evenPersistent, true /* setRemoved */,
- packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
- : ApplicationExitInfo.REASON_USER_REQUESTED,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- (packageName == null ? ("stop user " + userId) : ("stop " + packageName))
- + " due to " + reason);
+ didSomething |= mProcessList.killPackageProcessesLSP(packageName, appId, userId,
+ ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
+ evenPersistent, true /* setRemoved */,
+ packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
+ : ApplicationExitInfo.REASON_USER_REQUESTED,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ (packageName == null ? ("stop user " + userId) : ("stop " + packageName))
+ + " due to " + reason);
+ }
if (mServices.bringDownDisabledPackageServicesLocked(
packageName, null /* filterByClasses */, userId, evenPersistent, doit)) {
@@ -3986,26 +4111,29 @@
@GuardedBy("this")
private final void processStartTimedOutLocked(ProcessRecord app) {
- final int pid = app.pid;
- boolean gone = removePidIfNoThread(app);
+ final int pid = app.getPid();
+ boolean gone = removePidIfNoThreadLocked(app);
if (gone) {
Slog.w(TAG, "Process " + app + " failed to attach");
EventLogTags.writeAmProcessStartTimeout(app.userId, pid, app.uid, app.processName);
- mProcessList.removeProcessNameLocked(app.processName, app.uid);
- mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
- mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
- // Take care of any launching providers waiting for this process.
- mCpHelper.cleanupAppInLaunchingProvidersLocked(app, true);
- // Take care of any services that are waiting for the process.
- mServices.processStartTimedOutLocked(app);
- app.kill("start timeout", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
- if (app.isolated) {
- mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+ synchronized (mProcLock) {
+ mProcessList.removeProcessNameLocked(app.processName, app.uid);
+ mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
+ mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
+ // Take care of any launching providers waiting for this process.
+ mCpHelper.cleanupAppInLaunchingProvidersLocked(app, true);
+ // Take care of any services that are waiting for the process.
+ mServices.processStartTimedOutLocked(app);
+ app.killLocked("start timeout", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
+ true);
+ if (app.isolated) {
+ mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+ }
+ removeLruProcessLocked(app);
}
- removeLruProcessLocked(app);
final BackupRecord backupTarget = mBackupTargets.get(app.userId);
- if (backupTarget != null && backupTarget.app.pid == pid) {
+ if (backupTarget != null && backupTarget.app.getPid() == pid) {
Slog.w(TAG, "Unattached app died before backup, skipping");
mHandler.post(new Runnable() {
@Override
@@ -4043,7 +4171,7 @@
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
- if (app != null && (app.startUid != callingUid || app.startSeq != startSeq)) {
+ if (app != null && (app.getStartUid() != callingUid || app.getStartSeq() != startSeq)) {
String processName = null;
final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
if (pending != null) {
@@ -4053,14 +4181,14 @@
+ " startSeq:" + startSeq
+ " pid:" + pid
+ " belongs to another existing app:" + app.processName
- + " startSeq:" + app.startSeq;
+ + " startSeq:" + app.getStartSeq();
Slog.wtf(TAG, msg);
// SafetyNet logging for b/131105245.
- EventLog.writeEvent(0x534e4554, "131105245", app.startUid, msg);
+ EventLog.writeEvent(0x534e4554, "131105245", app.getStartUid(), msg);
// If there is already an app occupying that pid that hasn't been cleaned up
- cleanUpApplicationRecordLocked(app, false, false, -1,
- true /*replacingPid*/);
- removePidLocked(app);
+ cleanUpApplicationRecordLocked(app, pid, false, false, -1,
+ true /*replacingPid*/);
+ removePidLocked(pid, app);
app = null;
}
} else {
@@ -4071,10 +4199,10 @@
// update the internal state.
if (app == null && startSeq > 0) {
final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
- if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq
- && mProcessList.handleProcessStartedLocked(pending, pid, pending
- .isUsingWrapper(),
- startSeq, true)) {
+ if (pending != null && pending.getStartUid() == callingUid
+ && pending.getStartSeq() == startSeq
+ && mProcessList.handleProcessStartedLocked(pending, pid,
+ pending.isUsingWrapper(), startSeq, true)) {
app = pending;
}
}
@@ -4100,8 +4228,8 @@
// If this application record is still attached to a previous
// process, clean it up now.
- if (app.thread != null) {
- handleAppDiedLocked(app, true, true);
+ if (app.getThread() != null) {
+ handleAppDiedLocked(app, pid, true, true);
}
// Tell the process all about itself.
@@ -4114,7 +4242,7 @@
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
- app.deathRecipient = adr;
+ app.setDeathRecipient(adr);
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
mProcessList.startProcessLocked(app,
@@ -4123,23 +4251,25 @@
return false;
}
- EventLogTags.writeAmProcBound(app.userId, app.pid, app.processName);
+ EventLogTags.writeAmProcBound(app.userId, pid, app.processName);
- app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
- mOomAdjuster.setAttachingSchedGroupLocked(app);
- app.forcingToImportant = null;
- updateProcessForegroundLocked(app, false, 0, false);
- app.hasShownUi = false;
- app.setDebugging(false);
- app.setCached(false);
- app.killedByAm = false;
- app.killed = false;
-
-
- // We carefully use the same state that PackageManager uses for
- // filtering, since we use this flag to decide if we need to install
- // providers when user is unlocked later
- app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);
+ synchronized (mProcLock) {
+ app.mState.setCurAdj(ProcessList.INVALID_ADJ);
+ app.mState.setSetAdj(ProcessList.INVALID_ADJ);
+ app.mState.setVerifiedAdj(ProcessList.INVALID_ADJ);
+ mOomAdjuster.setAttachingSchedGroupLSP(app);
+ app.mState.setForcingToImportant(null);
+ updateProcessForegroundLocked(app, false, 0, false);
+ app.mState.setHasShownUi(false);
+ app.mState.setCached(false);
+ app.setDebugging(false);
+ app.setKilledByAm(false);
+ app.setKilled(false);
+ // We carefully use the same state that PackageManager uses for
+ // filtering, since we use this flag to decide if we need to install
+ // providers when user is unlocked later
+ app.setUnlocked(StorageManager.isUserKeyUnlocked(app.userId));
+ }
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
@@ -4179,9 +4309,11 @@
}
boolean enableTrackAllocation = false;
- if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
- enableTrackAllocation = true;
- mTrackAllocationApp = null;
+ synchronized (mProcLock) {
+ if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
+ enableTrackAllocation = true;
+ mTrackAllocationApp = null;
+ }
}
// If the app is being launched for restore or full backup, set it up specially
@@ -4202,7 +4334,7 @@
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s",
processName, app.getWindowProcessController().getConfiguration());
ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
- app.compat = compatibilityInfoForPackage(appInfo);
+ app.setCompat(compatibilityInfoForPackage(appInfo));
ProfilerInfo profilerInfo = mAppProfiler.setupProfilerInfoLocked(thread, app, instr);
@@ -4246,10 +4378,11 @@
mPlatformCompat.resetReporting(app.info);
}
final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
- if (app.isolatedEntryPoint != null) {
+ if (app.getIsolatedEntryPoint() != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
- thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
+ thread.runIsolatedEntryPoint(
+ app.getIsolatedEntryPoint(), app.getIsolatedEntryPointArgs());
} else if (instr2 != null) {
thread.bindApplication(processName, appInfo, providerList,
instr2.mClass,
@@ -4259,20 +4392,20 @@
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
- app.compat, getCommonServicesLocked(app.isolated),
+ app.getCompat(), getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions,
- app.mDisabledCompatChanges, serializedSystemFontMap);
+ app.getDisabledCompatChanges(), serializedSystemFontMap);
} else {
thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
- app.compat, getCommonServicesLocked(app.isolated),
+ app.getCompat(), getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions,
- app.mDisabledCompatChanges, serializedSystemFontMap);
+ app.getDisabledCompatChanges(), serializedSystemFontMap);
}
if (profilerInfo != null) {
profilerInfo.closeFd();
@@ -4281,9 +4414,11 @@
// Make app active after binding application or client may be running requests (e.g
// starting activities) before it is ready.
- app.makeActive(thread, mProcessStats);
- checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
- mProcessList.updateLruProcessLocked(app, false, null);
+ synchronized (mProcLock) {
+ app.makeActive(thread, mProcessStats);
+ checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
+ }
+ updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
final long now = SystemClock.uptimeMillis();
synchronized (mAppProfiler.mProfilerLock) {
@@ -4295,8 +4430,9 @@
Slog.wtf(TAG, "Exception thrown during bind of " + app, e);
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
- app.kill("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
- handleAppDiedLocked(app, false, true);
+ app.killLocked("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
+ true);
+ handleAppDiedLocked(app, pid, false, true);
return false;
}
@@ -4359,8 +4495,9 @@
}
if (badApp) {
- app.kill("error during init", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
- handleAppDiedLocked(app, false, true);
+ app.killLocked("error during init", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
+ true);
+ handleAppDiedLocked(app, pid, false, true);
return false;
}
@@ -4372,14 +4509,14 @@
FrameworkStatsLog.write(
FrameworkStatsLog.PROCESS_START_TIME,
app.info.uid,
- app.pid,
+ pid,
app.info.packageName,
FrameworkStatsLog.PROCESS_START_TIME__TYPE__COLD,
- app.startTime,
- (int) (bindApplicationTimeMillis - app.startTime),
- (int) (SystemClock.elapsedRealtime() - app.startTime),
- app.hostingRecord.getType(),
- (app.hostingRecord.getName() != null ? app.hostingRecord.getName() : ""));
+ app.getStartTime(),
+ (int) (bindApplicationTimeMillis - app.getStartTime()),
+ (int) (SystemClock.elapsedRealtime() - app.getStartTime()),
+ app.getHostingRecord().getType(),
+ (app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : ""));
return true;
}
@@ -4472,11 +4609,11 @@
// up.
final int NP = mProcessesOnHold.size();
if (NP > 0) {
- ArrayList<ProcessRecord> procs =
- new ArrayList<ProcessRecord>(mProcessesOnHold);
- for (int ip=0; ip<NP; ip++) {
- if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Starting process on hold: "
- + procs.get(ip));
+ ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(mProcessesOnHold);
+ for (int ip = 0; ip < NP; ip++) {
+ if (DEBUG_PROCESSES) {
+ Slog.v(TAG_PROCESSES, "Starting process on hold: " + procs.get(ip));
+ }
mProcessList.startProcessLocked(procs.get(ip),
new HostingRecord("on-hold"),
ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
@@ -4508,8 +4645,8 @@
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
- synchronized (ActivityManagerService.this) {
- mAppProfiler.requestPssAllProcsLocked(
+ synchronized (mProcLock) {
+ mAppProfiler.requestPssAllProcsLPr(
SystemClock.uptimeMillis(), true, false);
}
}
@@ -4944,7 +5081,7 @@
if (pr == null) {
return;
}
- pr.forcingToImportant = null;
+ pr.mState.setForcingToImportant(null);
updateProcessForegroundLocked(pr, false, 0, false);
}
updateOomAdjLocked(pr, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
@@ -4970,7 +5107,7 @@
oldToken.token.unlinkToDeath(oldToken, 0);
mImportantProcesses.remove(pid);
if (pr != null) {
- pr.forcingToImportant = null;
+ pr.mState.setForcingToImportant(null);
}
changed = true;
}
@@ -4984,7 +5121,7 @@
try {
token.linkToDeath(newToken, 0);
mImportantProcesses.put(pid, newToken);
- pr.forcingToImportant = newToken;
+ pr.mState.setForcingToImportant(newToken);
changed = true;
} catch (RemoteException e) {
// If the process died while doing this, we will later
@@ -5000,13 +5137,12 @@
}
private boolean isAppForeground(int uid) {
- synchronized (this) {
+ synchronized (mProcLock) {
UidRecord uidRec = mProcessList.mActiveUids.get(uid);
- if (uidRec == null || uidRec.idle) {
+ if (uidRec == null || uidRec.isIdle()) {
return false;
}
- return uidRec.getCurProcState()
- <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ return uidRec.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND;
}
}
@@ -5017,11 +5153,16 @@
// NOTE: this is an internal method used by the OnShellCommand implementation only and should
// be guarded by permission checking.
int getUidState(int uid) {
- synchronized (this) {
- return mProcessList.getUidProcStateLocked(uid);
+ synchronized (mProcLock) {
+ return mProcessList.getUidProcStateLOSP(uid);
}
}
+ @GuardedBy("this")
+ int getUidStateLocked(int uid) {
+ return mProcessList.getUidProcStateLOSP(uid);
+ }
+
// =========================================================
// PROCESS INFO
// =========================================================
@@ -5070,20 +5211,23 @@
throw new IllegalArgumentException("pids and scores arrays have different lengths!");
}
- synchronized (mPidsSelfLocked) {
- for (int i = 0; i < pids.length; i++) {
- ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
- if (pr != null) {
- final boolean isPendingTop =
+ synchronized (mProcLock) {
+ synchronized (mPidsSelfLocked) {
+ for (int i = 0; i < pids.length; i++) {
+ ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
+ if (pr != null) {
+ final boolean isPendingTop =
mPendingStartActivityUids.isPendingTopPid(pr.uid, pids[i]);
- states[i] = isPendingTop ? PROCESS_STATE_TOP : pr.getCurProcState();
- if (scores != null) {
- scores[i] = isPendingTop ? (ProcessList.FOREGROUND_APP_ADJ - 1) : pr.curAdj;
- }
- } else {
- states[i] = PROCESS_STATE_NONEXISTENT;
- if (scores != null) {
- scores[i] = ProcessList.INVALID_ADJ;
+ states[i] = isPendingTop ? PROCESS_STATE_TOP : pr.mState.getCurProcState();
+ if (scores != null) {
+ scores[i] = isPendingTop
+ ? (ProcessList.FOREGROUND_APP_ADJ - 1) : pr.mState.getCurAdj();
+ }
+ } else {
+ states[i] = PROCESS_STATE_NONEXISTENT;
+ if (scores != null) {
+ scores[i] = ProcessList.INVALID_ADJ;
+ }
}
}
}
@@ -5261,8 +5405,8 @@
}
public boolean isAppStartModeDisabled(int uid, String packageName) {
- synchronized (this) {
- return getAppStartModeLocked(uid, packageName, 0, -1, false, true, false)
+ synchronized (mProcLock) {
+ return getAppStartModeLOSP(uid, packageName, 0, -1, false, true, false)
== ActivityManager.APP_START_MODE_DISABLED;
}
}
@@ -5273,7 +5417,8 @@
}
// Unified app-op and target sdk check
- int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ int appRestrictedInBackgroundLOSP(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
if (DEBUG_BACKGROUND_CHECK) {
@@ -5302,7 +5447,7 @@
// If force-background-check is enabled, restrict all apps that aren't whitelisted.
if (mForceBackgroundCheck &&
!UserHandle.isCore(uid) &&
- !isOnDeviceIdleAllowlistLocked(uid, /*allowExceptIdleToo=*/ true)) {
+ !isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ true)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Force background check: " +
uid + "/" + packageName + " restricted");
@@ -5320,7 +5465,8 @@
// Service launch is available to apps with run-in-background exemptions but
// some other background operations are not. If we're doing a check
// of service-launch policy, allow those callers to proceed unrestricted.
- int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ int appServicesRestrictedInBackgroundLOSP(int uid, String packageName, int packageTargetSdk) {
// Persistent app?
if (mPackageManagerInt.isPackagePersistent(packageName)) {
if (DEBUG_BACKGROUND_CHECK) {
@@ -5331,7 +5477,7 @@
}
// Non-persistent but background whitelisted?
- if (uidOnBackgroundAllowlist(uid)) {
+ if (uidOnBackgroundAllowlistLOSP(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on background whitelist; not restricted in background");
@@ -5340,7 +5486,7 @@
}
// Is this app on the battery whitelist?
- if (isOnDeviceIdleAllowlistLocked(uid, /*allowExceptIdleToo=*/ false)) {
+ if (isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ false)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on idle whitelist; not restricted in background");
@@ -5349,25 +5495,26 @@
}
// None of the service-policy criteria apply, so we apply the common criteria
- return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
+ return appRestrictedInBackgroundLOSP(uid, packageName, packageTargetSdk);
}
- int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ int getAppStartModeLOSP(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
if (mInternal.isPendingTopUid(uid)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
- UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
+ UidRecord uidRec = mProcessList.getUidRecordLOSP(uid);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
+ packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
- + (uidRec != null ? uidRec.idle : false));
- if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) {
+ + (uidRec != null ? uidRec.isIdle() : false));
+ if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.isIdle()) {
boolean ephemeral;
if (uidRec == null) {
ephemeral = getPackageManagerInternal().isPackageEphemeral(
UserHandle.getUserId(uid), packageName);
} else {
- ephemeral = uidRec.ephemeral;
+ ephemeral = uidRec.isEphemeral();
}
if (ephemeral) {
@@ -5382,14 +5529,14 @@
return ActivityManager.APP_START_MODE_NORMAL;
}
final int startMode = (alwaysRestrict)
- ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
- : appServicesRestrictedInBackgroundLocked(uid, packageName,
+ ? appRestrictedInBackgroundLOSP(uid, packageName, packageTargetSdk)
+ : appServicesRestrictedInBackgroundLOSP(uid, packageName,
packageTargetSdk);
if (DEBUG_BACKGROUND_CHECK) {
Slog.d(TAG, "checkAllowBackground: uid=" + uid
+ " pkg=" + packageName + " startMode=" + startMode
- + " onallowlist=" + isOnDeviceIdleAllowlistLocked(uid, false)
- + " onallowlist(ei)=" + isOnDeviceIdleAllowlistLocked(uid, true));
+ + " onallowlist=" + isOnDeviceIdleAllowlistLOSP(uid, false)
+ + " onallowlist(ei)=" + isOnDeviceIdleAllowlistLOSP(uid, true));
}
if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
// This is an old app that has been forced into a "compatible as possible"
@@ -5400,8 +5547,8 @@
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(callingPid);
}
- if (proc != null &&
- !ActivityManager.isProcStateBackground(proc.getCurProcState())) {
+ if (proc != null && !ActivityManager.isProcStateBackground(
+ proc.mState.getCurProcState())) {
// Whoever is instigating this is in the foreground, so we will allow it
// to go through.
return ActivityManager.APP_START_MODE_NORMAL;
@@ -5417,7 +5564,8 @@
/**
* @return whether a UID is in the system, user or temp doze allowlist.
*/
- boolean isOnDeviceIdleAllowlistLocked(int uid, boolean allowExceptIdleToo) {
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ boolean isOnDeviceIdleAllowlistLOSP(int uid, boolean allowExceptIdleToo) {
final int appId = UserHandle.getAppId(uid);
final int[] allowlist = allowExceptIdleToo
@@ -5429,7 +5577,8 @@
|| mPendingTempAllowlist.indexOfKey(uid) >= 0;
}
- boolean isAllowlistedForFgsStartLocked(int uid) {
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ boolean isAllowlistedForFgsStartLOSP(int uid) {
return Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0
|| mFgsStartTempAllowList.isAllowed(uid);
}
@@ -5438,7 +5587,8 @@
* @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on
* the allowlist
*/
- String getPendingTempAllowlistTagForUidLocked(int uid) {
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ String getPendingTempAllowlistTagForUidLOSP(int uid) {
final PendingTempAllowlist ptw = mPendingTempAllowlist.get(uid);
return ptw != null ? ptw.tag : null;
}
@@ -5483,8 +5633,8 @@
final int modeFlags, int userId) {
enforceNotIsolatedCaller("grantUriPermission");
GrantUri grantUri = new GrantUri(userId, uri, modeFlags);
- synchronized(this) {
- final ProcessRecord r = getRecordForAppLocked(caller);
+ synchronized (this) {
+ final ProcessRecord r = getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller "
+ caller
@@ -5517,8 +5667,8 @@
public void revokeUriPermission(IApplicationThread caller, String targetPackage, Uri uri,
final int modeFlags, int userId) {
enforceNotIsolatedCaller("revokeUriPermission");
- synchronized(this) {
- final ProcessRecord r = getRecordForAppLocked(caller);
+ synchronized (this) {
+ final ProcessRecord r = getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller "
+ caller
@@ -5549,9 +5699,8 @@
@Override
public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
- synchronized (this) {
- ProcessRecord app =
- who != null ? getRecordForAppLocked(who) : null;
+ synchronized (mProcLock) {
+ final ProcessRecord app = who != null ? getRecordForAppLOSP(who) : null;
if (app == null) return;
Message msg = Message.obtain();
@@ -5850,7 +5999,8 @@
// GLOBAL MANAGEMENT
// =========================================================
- private boolean uidOnBackgroundAllowlist(final int uid) {
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ private boolean uidOnBackgroundAllowlistLOSP(final int uid) {
final int appId = UserHandle.getAppId(uid);
final int[] allowlist = mBackgroundAppIdAllowlist;
for (int i = 0, len = allowlist.length; i < len; i++) {
@@ -5894,11 +6044,13 @@
Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist");
}
synchronized (this) {
- final int num = mBackgroundAppIdAllowlist.length;
- int[] newList = new int[num + 1];
- System.arraycopy(mBackgroundAppIdAllowlist, 0, newList, 0, num);
- newList[num] = UserHandle.getAppId(uid);
- mBackgroundAppIdAllowlist = newList;
+ synchronized (mProcLock) {
+ final int num = mBackgroundAppIdAllowlist.length;
+ int[] newList = new int[num + 1];
+ System.arraycopy(mBackgroundAppIdAllowlist, 0, newList, 0, num);
+ newList[num] = UserHandle.getAppId(uid);
+ mBackgroundAppIdAllowlist = newList;
+ }
}
}
@@ -5909,18 +6061,10 @@
abiOverride, zygotePolicyFlags);
}
- @GuardedBy("this")
- final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
- boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) {
- return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks,
- false /* disableTestApiChecks */, abiOverride, zygotePolicyFlags);
- }
-
// TODO: Move to ProcessList?
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
- boolean disableHiddenApiChecks, boolean disableTestApiChecks,
- String abiOverride, int zygotePolicyFlags) {
+ boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -5932,8 +6076,8 @@
if (app == null) {
app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0,
new HostingRecord("added application",
- customProcess != null ? customProcess : info.processName));
- mProcessList.updateLruProcessLocked(app, false, null);
+ customProcess != null ? customProcess : info.processName));
+ updateLruProcessLocked(app, false, null);
updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
}
@@ -5949,13 +6093,13 @@
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.setPersistent(true);
- app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+ app.mState.setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ);
}
- if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
+ if (app.getThread() == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
mProcessList.startProcessLocked(app, new HostingRecord("added application",
customProcess != null ? customProcess : app.processName),
- zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, abiOverride);
+ zygotePolicyFlags, disableHiddenApiChecks, abiOverride);
}
return app;
@@ -6022,7 +6166,7 @@
}
void onWakefulnessChanged(int wakefulness) {
- synchronized(this) {
+ synchronized (this) {
boolean wasAwake = mWakefulness.getAndSet(wakefulness)
== PowerManagerInternal.WAKEFULNESS_AWAKE;
boolean isAwake = wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -6149,13 +6293,13 @@
}
void setTrackAllocationApp(ApplicationInfo app, String processName) {
- synchronized (this) {
- if (!Build.IS_DEBUGGABLE) {
- if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
- throw new SecurityException("Process not debuggable: " + app.packageName);
- }
+ if (!Build.IS_DEBUGGABLE) {
+ if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+ throw new SecurityException("Process not debuggable: " + app.packageName);
}
+ }
+ synchronized (mProcLock) {
mTrackAllocationApp = processName;
}
}
@@ -6212,7 +6356,7 @@
@Override
public void setUserIsMonkey(boolean userIsMonkey) {
- synchronized (this) {
+ synchronized (mProcLock) {
synchronized (mPidsSelfLocked) {
final int callingPid = Binder.getCallingPid();
ProcessRecord proc = mPidsSelfLocked.get(callingPid);
@@ -6231,7 +6375,7 @@
@Override
public boolean isUserAMonkey() {
- synchronized (this) {
+ synchronized (mProcLock) {
// If there is a controller also implies the user is a monkey.
return mUserIsMonkey || mActivityTaskManager.isControllerAMonkey();
}
@@ -6452,8 +6596,8 @@
"getUidProcessState");
}
- synchronized (this) {
- return mProcessList.getUidProcStateLocked(uid);
+ synchronized (mProcLock) {
+ return mProcessList.getUidProcStateLOSP(uid);
}
}
@@ -6479,17 +6623,18 @@
enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
"isUidActive");
}
- synchronized (this) {
- if (isUidActiveLocked(uid)) {
+ synchronized (mProcLock) {
+ if (isUidActiveLOSP(uid)) {
return true;
}
}
return mInternal.isPendingTopUid(uid);
}
- boolean isUidActiveLocked(int uid) {
- final UidRecord uidRecord = mProcessList.getUidRecordLocked(uid);
- return uidRecord != null && !uidRecord.setIdle;
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ boolean isUidActiveLOSP(int uid) {
+ final UidRecord uidRecord = mProcessList.getUidRecordLOSP(uid);
+ return uidRecord != null && !uidRecord.isSetIdle();
}
@Override
@@ -6547,7 +6692,7 @@
@Override
public void setRenderThread(int tid) {
- synchronized (this) {
+ synchronized (mProcLock) {
ProcessRecord proc;
int pid = Binder.getCallingPid();
if (pid == Process.myPid()) {
@@ -6556,32 +6701,31 @@
}
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid);
- if (proc != null && proc.renderThreadTid == 0 && tid > 0) {
- // ensure the tid belongs to the process
- if (!isThreadInProcess(pid, tid)) {
- throw new IllegalArgumentException(
+ }
+ if (proc != null && proc.getRenderThreadTid() == 0 && tid > 0) {
+ // ensure the tid belongs to the process
+ if (!isThreadInProcess(pid, tid)) {
+ throw new IllegalArgumentException(
"Render thread does not belong to process");
- }
- proc.renderThreadTid = tid;
- if (DEBUG_OOM_ADJ) {
- Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
- }
- // promote to FIFO now
- if (proc.getCurrentSchedulingGroup() == ProcessList.SCHED_GROUP_TOP_APP) {
- if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
- if (mUseFifoUiScheduling) {
- setThreadScheduler(proc.renderThreadTid,
+ }
+ proc.setRenderThreadTid(tid);
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
+ }
+ // promote to FIFO now
+ if (proc.mState.getCurrentSchedulingGroup() == ProcessList.SCHED_GROUP_TOP_APP) {
+ if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
+ if (mUseFifoUiScheduling) {
+ setThreadScheduler(proc.getRenderThreadTid(),
SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
- } else {
- setThreadPriority(proc.renderThreadTid, TOP_APP_PRIORITY_BOOST);
- }
+ } else {
+ setThreadPriority(proc.getRenderThreadTid(), TOP_APP_PRIORITY_BOOST);
}
- } else {
- if (DEBUG_OOM_ADJ) {
- Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? " +
- "PID: " + pid + ", TID: " + tid + " FIFO: " +
- mUseFifoUiScheduling);
- }
+ }
+ } else {
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? "
+ + "PID: " + pid + ", TID: " + tid + " FIFO: " + mUseFifoUiScheduling);
}
}
}
@@ -6638,11 +6782,11 @@
Slog.w(TAG, "setHasTopUi called on unknown pid: " + pid);
return;
}
- if (pr.hasTopUi() != hasTopUi) {
+ if (pr.mState.hasTopUi() != hasTopUi) {
if (DEBUG_OOM_ADJ) {
Slog.d(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + pid);
}
- pr.setHasTopUi(hasTopUi);
+ pr.mState.setHasTopUi(hasTopUi);
changed = true;
}
}
@@ -6822,42 +6966,46 @@
// manager calls in with its locks held.
boolean killed = false;
- synchronized (mPidsSelfLocked) {
- int worstType = 0;
- for (int i=0; i<pids.length; i++) {
- ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
- if (proc != null) {
- int type = proc.setAdj;
- if (type > worstType) {
- worstType = type;
+ synchronized (this) {
+ synchronized (mProcLock) {
+ synchronized (mPidsSelfLocked) {
+ int worstType = 0;
+ for (int i = 0; i < pids.length; i++) {
+ ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
+ if (proc != null) {
+ int type = proc.mState.getSetAdj();
+ if (type > worstType) {
+ worstType = type;
+ }
+ }
}
- }
- }
- // If the worst oom_adj is somewhere in the cached proc LRU range,
- // then constrain it so we will kill all cached procs.
- if (worstType < ProcessList.CACHED_APP_MAX_ADJ
- && worstType > ProcessList.CACHED_APP_MIN_ADJ) {
- worstType = ProcessList.CACHED_APP_MIN_ADJ;
- }
+ // If the worst oom_adj is somewhere in the cached proc LRU range,
+ // then constrain it so we will kill all cached procs.
+ if (worstType < ProcessList.CACHED_APP_MAX_ADJ
+ && worstType > ProcessList.CACHED_APP_MIN_ADJ) {
+ worstType = ProcessList.CACHED_APP_MIN_ADJ;
+ }
- // If this is not a secure call, don't let it kill processes that
- // are important.
- if (!secure && worstType < ProcessList.SERVICE_ADJ) {
- worstType = ProcessList.SERVICE_ADJ;
- }
+ // If this is not a secure call, don't let it kill processes that
+ // are important.
+ if (!secure && worstType < ProcessList.SERVICE_ADJ) {
+ worstType = ProcessList.SERVICE_ADJ;
+ }
- Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType);
- for (int i=0; i<pids.length; i++) {
- ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
- if (proc == null) {
- continue;
- }
- int adj = proc.setAdj;
- if (adj >= worstType && !proc.killedByAm) {
- proc.kill(reason, ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_KILL_PID, true);
- killed = true;
+ Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType);
+ for (int i = 0; i < pids.length; i++) {
+ ProcessRecord proc = mPidsSelfLocked.get(pids[i]);
+ if (proc == null) {
+ continue;
+ }
+ int adj = proc.mState.getSetAdj();
+ if (adj >= worstType && !proc.isKilledByAm()) {
+ proc.killLocked(reason, ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_KILL_PID, true);
+ killed = true;
+ }
+ }
}
}
}
@@ -6870,13 +7018,15 @@
synchronized (this) {
final long identity = Binder.clearCallingIdentity();
try {
- mProcessList.killPackageProcessesLocked(null /* packageName */, appId, userId,
- ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
- true /* callerWillRestart */, true /* doit */, true /* evenPersistent */,
- false /* setRemoved */,
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_KILL_UID,
- reason != null ? reason : "kill uid");
+ synchronized (mProcLock) {
+ mProcessList.killPackageProcessesLSP(null /* packageName */, appId, userId,
+ ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
+ true /* callerWillRestart */, true /* doit */,
+ true /* evenPersistent */, false /* setRemoved */,
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_KILL_UID,
+ reason != null ? reason : "kill uid");
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -6889,13 +7039,15 @@
synchronized (this) {
final long identity = Binder.clearCallingIdentity();
try {
- mProcessList.killPackageProcessesLocked(null /* packageName */, appId, userId,
- ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
- true /* callerWillRestart */, true /* doit */, true /* evenPersistent */,
- false /* setRemoved */,
- ApplicationExitInfo.REASON_PERMISSION_CHANGE,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- reason != null ? reason : "kill uid");
+ synchronized (mProcLock) {
+ mProcessList.killPackageProcessesLSP(null /* packageName */, appId, userId,
+ ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
+ true /* callerWillRestart */, true /* doit */,
+ true /* evenPersistent */, false /* setRemoved */,
+ ApplicationExitInfo.REASON_PERMISSION_CHANGE,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ reason != null ? reason : "kill uid");
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -6917,17 +7069,22 @@
}
boolean killed = false;
- synchronized (mPidsSelfLocked) {
- final int size = mPidsSelfLocked.size();
- for (int i = 0; i < size; i++) {
- final int pid = mPidsSelfLocked.keyAt(i);
- final ProcessRecord proc = mPidsSelfLocked.valueAt(i);
- if (proc == null) continue;
+ synchronized (this) {
+ synchronized (mProcLock) {
+ synchronized (mPidsSelfLocked) {
+ final int size = mPidsSelfLocked.size();
+ for (int i = 0; i < size; i++) {
+ final int pid = mPidsSelfLocked.keyAt(i);
+ final ProcessRecord proc = mPidsSelfLocked.valueAt(i);
+ if (proc == null) continue;
- final int adj = proc.setAdj;
- if (adj > belowAdj && !proc.killedByAm) {
- proc.kill(reason, ApplicationExitInfo.REASON_PERMISSION_CHANGE, true);
- killed = true;
+ final int adj = proc.mState.getSetAdj();
+ if (adj > belowAdj && !proc.isKilledByAm()) {
+ proc.killLocked(reason, ApplicationExitInfo.REASON_PERMISSION_CHANGE,
+ true);
+ killed = true;
+ }
+ }
}
}
}
@@ -7033,16 +7190,16 @@
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
- synchronized (this) {
+ synchronized (mProcLock) {
final long now = SystemClock.uptimeMillis();
final long timeSinceLastIdle = now - mLastIdleTime;
// Compact all non-zygote processes to freshen up the page cache.
mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
- final long lowRamSinceLastIdle = mAppProfiler.getLowRamTimeSinceIdleLocked(now);
+ final long lowRamSinceLastIdle = mAppProfiler.getLowRamTimeSinceIdleLPr(now);
mLastIdleTime = now;
- mAppProfiler.updateLowRamTimestampLocked(now);
+ mAppProfiler.updateLowRamTimestampLPr(now);
StringBuilder sb = new StringBuilder(128);
sb.append("Idle maintenance over ");
@@ -7059,13 +7216,13 @@
final long totalMemoryInKb = getTotalMemory() / 1000;
final long memoryGrowthThreshold =
Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD);
-
- for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord proc = mProcessList.mLruProcesses.get(i);
+ mProcessList.forEachLruProcessesLOSP(false, proc -> {
final ProcessProfileRecord pr = proc.mProfile;
- if (proc.notCachedSinceIdle) {
- if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
+ final ProcessStateRecord state = proc.mState;
+ final int setProcState = state.getSetProcState();
+ if (state.isNotCachedSinceIdle()) {
+ if (setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+ && setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
final long initialIdlePss, lastPss, lastSwapPss;
synchronized (mAppProfiler.mProfilerLock) {
initialIdlePss = pr.getInitialIdlePss();
@@ -7073,39 +7230,43 @@
lastSwapPss = pr.getLastSwapPss();
}
if (doKilling && initialIdlePss != 0
- && lastPss > ((initialIdlePss * 3) / 2)
+ && lastPss > (initialIdlePss * 3 / 2)
&& lastPss > (initialIdlePss + memoryGrowthThreshold)) {
- sb = new StringBuilder(128);
- sb.append("Kill");
- sb.append(proc.processName);
- sb.append(" in idle maint: pss=");
- sb.append(lastPss);
- sb.append(", swapPss=");
- sb.append(lastSwapPss);
- sb.append(", initialPss=");
- sb.append(initialIdlePss);
- sb.append(", period=");
- TimeUtils.formatDuration(timeSinceLastIdle, sb);
- sb.append(", lowRamPeriod=");
- TimeUtils.formatDuration(lowRamSinceLastIdle, sb);
- Slog.wtfQuiet(TAG, sb.toString());
- proc.kill("idle maint (pss " + lastPss
- + " from " + initialIdlePss + ")",
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_MEMORY_PRESSURE,
- true);
+ final StringBuilder sb2 = new StringBuilder(128);
+ sb2.append("Kill");
+ sb2.append(proc.processName);
+ sb2.append(" in idle maint: pss=");
+ sb2.append(lastPss);
+ sb2.append(", swapPss=");
+ sb2.append(lastSwapPss);
+ sb2.append(", initialPss=");
+ sb2.append(initialIdlePss);
+ sb2.append(", period=");
+ TimeUtils.formatDuration(timeSinceLastIdle, sb2);
+ sb2.append(", lowRamPeriod=");
+ TimeUtils.formatDuration(lowRamSinceLastIdle, sb2);
+ Slog.wtfQuiet(TAG, sb2.toString());
+ mHandler.post(() -> {
+ synchronized (ActivityManagerService.this) {
+ proc.killLocked("idle maint (pss " + lastPss
+ + " from " + initialIdlePss + ")",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_MEMORY_PRESSURE,
+ true);
+ }
+ });
}
}
- } else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME
- && proc.setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) {
- proc.notCachedSinceIdle = true;
+ } else if (setProcState < ActivityManager.PROCESS_STATE_HOME
+ && setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) {
+ state.setNotCachedSinceIdle(true);
synchronized (mAppProfiler.mProfilerLock) {
pr.setInitialIdlePss(0);
mAppProfiler.updateNextPssTimeLPf(
- proc.setProcState, proc.mProfile, now, true);
+ state.getSetProcState(), proc.mProfile, now, true);
}
}
- }
+ });
}
}
@@ -7216,7 +7377,7 @@
synchronized(this) {
if (procsToKill != null) {
- for (int i=procsToKill.size()-1; i>=0; i--) {
+ for (int i = procsToKill.size() - 1; i >= 0; i--) {
ProcessRecord proc = procsToKill.get(i);
Slog.i(TAG, "Removing system update proc: " + proc);
mProcessList.removeProcessLocked(proc, true, false,
@@ -7443,16 +7604,18 @@
private void updateForceBackgroundCheck(boolean enabled) {
synchronized (this) {
- if (mForceBackgroundCheck != enabled) {
- mForceBackgroundCheck = enabled;
+ synchronized (mProcLock) {
+ if (mForceBackgroundCheck != enabled) {
+ mForceBackgroundCheck = enabled;
- if (DEBUG_BACKGROUND_CHECK) {
- Slog.i(TAG, "Force background check " + (enabled ? "enabled" : "disabled"));
- }
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Force background check " + (enabled ? "enabled" : "disabled"));
+ }
- if (mForceBackgroundCheck) {
- // Stop background services for idle UIDs.
- mProcessList.doStopUidForIdleUidsLocked();
+ if (mForceBackgroundCheck) {
+ // Stop background services for idle UIDs.
+ mProcessList.doStopUidForIdleUidsLocked();
+ }
}
}
}
@@ -7517,7 +7680,7 @@
(r != null) ? r.uid : -1,
eventType,
processName,
- (r != null) ? r.pid : -1,
+ (r != null) ? r.getPid() : -1,
(r != null && r.info != null) ? r.info.packageName : "",
(r != null && r.info != null) ? (r.info.isInstantApp()
? FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE
@@ -7763,8 +7926,8 @@
return null;
}
- synchronized (this) {
- return mProcessList.findAppProcessLocked(app, reason);
+ synchronized (mProcLock) {
+ return mProcessList.findAppProcessLOSP(app, reason);
}
}
@@ -7773,7 +7936,7 @@
* to append various headers to the dropbox log text.
*/
void appendDropBoxProcessHeaders(ProcessRecord process, String processName,
- StringBuilder sb) {
+ final StringBuilder sb) {
// Watchdog thread ends up invoking this function (with
// a null ProcessRecord) to add the stack file to dropbox.
// Do not acquire a lock on this (am) in such cases, as it
@@ -7787,17 +7950,18 @@
// Note: ProcessRecord 'process' is guarded by the service
// instance. (notably process.pkgList, which could otherwise change
// concurrently during execution of this method)
- synchronized (this) {
+ synchronized (mProcLock) {
sb.append("Process: ").append(processName).append("\n");
- sb.append("PID: ").append(process.pid).append("\n");
+ sb.append("PID: ").append(process.getPid()).append("\n");
sb.append("UID: ").append(process.uid).append("\n");
int flags = process.info.flags;
- IPackageManager pm = AppGlobals.getPackageManager();
+ final IPackageManager pm = AppGlobals.getPackageManager();
sb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n");
+ final int callingUserId = UserHandle.getCallingUserId();
process.getPkgList().forEachPackage(pkg -> {
sb.append("Package: ").append(pkg);
try {
- PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId());
+ final PackageInfo pi = pm.getPackageInfo(pkg, 0, callingUserId);
if (pi != null) {
sb.append(" v").append(pi.getLongVersionCode());
if (pi.versionName != null) {
@@ -7816,7 +7980,7 @@
}
private static String processClass(ProcessRecord process) {
- if (process == null || process.pid == MY_PID) {
+ if (process == null || process.getPid() == MY_PID) {
return "system_server";
} else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return "system_app";
@@ -7878,8 +8042,8 @@
sb.append("Foreground: ")
.append(process.isInterestingToUserLocked() ? "Yes" : "No")
.append("\n");
- if (process.startTime > 0) {
- long runtimeMillis = SystemClock.elapsedRealtime() - process.startTime;
+ if (process.getStartTime() > 0) {
+ long runtimeMillis = SystemClock.elapsedRealtime() - process.getStartTime();
sb.append("Process-Runtime: ").append(runtimeMillis).append("\n");
}
}
@@ -7887,7 +8051,7 @@
sb.append("Activity: ").append(activityShortComponentName).append("\n");
}
if (parentShortComponentName != null) {
- if (parentProcess != null && parentProcess.pid != process.pid) {
+ if (parentProcess != null && parentProcess.getPid() != process.getPid()) {
sb.append("Parent-Process: ").append(parentProcess.processName).append("\n");
}
if (!parentShortComponentName.equals(activityShortComponentName)) {
@@ -7983,47 +8147,46 @@
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
enforceNotIsolatedCaller("getProcessesInErrorState");
// assume our apps are happy - lazy create the list
- List<ActivityManager.ProcessErrorStateInfo> errList = null;
+ final List<ActivityManager.ProcessErrorStateInfo>[] errList = new List[1];
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
int userId = UserHandle.getUserId(Binder.getCallingUid());
- synchronized (this) {
-
+ synchronized (mProcLock) {
// iterate across all processes
- for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
+ mProcessList.forEachLruProcessesLOSP(false, app -> {
if (!allUsers && app.userId != userId) {
- continue;
+ return;
}
- final boolean crashing = app.isCrashing();
- final boolean notResponding = app.isNotResponding();
- if ((app.thread != null) && (crashing || notResponding)) {
+ final ProcessErrorStateRecord errState = app.mErrorState;
+ final boolean crashing = errState.isCrashing();
+ final boolean notResponding = errState.isNotResponding();
+ if ((app.getThread() != null) && (crashing || notResponding)) {
// This one's in trouble, so we'll generate a report for it
// crashes are higher priority (in case there's a crash *and* an anr)
ActivityManager.ProcessErrorStateInfo report = null;
if (crashing) {
- report = app.crashingReport;
+ report = errState.getCrashingReport();
} else if (notResponding) {
- report = app.notRespondingReport;
+ report = errState.getNotRespondingReport();
}
if (report != null) {
- if (errList == null) {
- errList = new ArrayList<>(1);
+ if (errList[0] == null) {
+ errList[0] = new ArrayList<>(1);
}
- errList.add(report);
+ errList[0].add(report);
} else {
Slog.w(TAG, "Missing app error report, app = " + app.processName +
" crashing = " + crashing +
" notResponding = " + notResponding);
}
}
- }
+ });
}
- return errList;
+ return errList[0];
}
@Override
@@ -8039,9 +8202,9 @@
final boolean allUids = mAtmInternal.isGetTasksAllowed(
"getRunningAppProcesses", Binder.getCallingPid(), callingUid);
- synchronized (this) {
+ synchronized (mProcLock) {
// Iterate across all processes
- return mProcessList.getRunningAppProcessesLocked(allUsers, userId, allUids,
+ return mProcessList.getRunningAppProcessesLOSP(allUsers, userId, allUids,
callingUid, clientTargetSdk);
}
}
@@ -8152,13 +8315,13 @@
final int callingUid = Binder.getCallingUid();
final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid);
- synchronized (this) {
+ synchronized (mProcLock) {
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(Binder.getCallingPid());
}
if (proc != null) {
- mProcessList.fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
+ mProcessList.fillInProcMemInfoLOSP(proc, outState, clientTargetSdk);
}
}
}
@@ -8204,7 +8367,9 @@
synchronized(this) {
mConstants.dump(pw);
- mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
+ synchronized (mProcLock) {
+ mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
+ }
mOomAdjuster.dumpCacheOomRankerSettings(pw);
pw.println();
if (dumpAll) {
@@ -8336,7 +8501,9 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
- mProcessList.dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
+ synchronized (mProcLock) {
+ mProcessList.dumpProcessesLSP(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
+ }
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -8440,7 +8607,9 @@
}
// output proto is ProcessProto
synchronized (this) {
- mProcessList.writeProcessesToProtoLocked(proto, dumpPackage);
+ synchronized (mProcLock) {
+ mProcessList.writeProcessesToProtoLSP(proto, dumpPackage);
+ }
}
} else {
// default option, dump everything, output is ActivityManagerServiceProto
@@ -8459,7 +8628,9 @@
proto.end(serviceToken);
long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
- mProcessList.writeProcessesToProtoLocked(proto, dumpPackage);
+ synchronized (mProcLock) {
+ mProcessList.writeProcessesToProtoLSP(proto, dumpPackage);
+ }
proto.end(processToken);
}
}
@@ -8533,8 +8704,10 @@
opti++;
}
synchronized (this) {
- mProcessList.dumpProcessesLocked(
- fd, pw, args, opti, true, dumpPackage, dumpAppId);
+ synchronized (mProcLock) {
+ mProcessList.dumpProcessesLSP(
+ fd, pw, args, opti, true, dumpPackage, dumpAppId);
+ }
}
} else if ("oom".equals(cmd) || "o".equals(cmd)) {
synchronized (this) {
@@ -8622,6 +8795,8 @@
} else if ("settings".equals(cmd)) {
synchronized (this) {
mConstants.dump(pw);
+ }
+ synchronized (mProcLock) {
mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
mOomAdjuster.dumpCacheOomRankerSettings(pw);
}
@@ -8871,8 +9046,8 @@
return needSep;
}
- @GuardedBy("this")
- void dumpOtherProcessesInfoLocked(FileDescriptor fd, PrintWriter pw,
+ @GuardedBy({"this", "mProcLock"})
+ void dumpOtherProcessesInfoLSP(FileDescriptor fd, PrintWriter pw,
boolean dumpAll, String dumpPackage, int dumpAppId, int numPers, boolean needSep) {
if (dumpAll || dumpPackage != null) {
final SparseArray<ProcessRecord> pidToProcess = new SparseArray<>();
@@ -8880,7 +9055,7 @@
boolean printed = false;
for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
ProcessRecord r = mPidsSelfLocked.valueAt(i);
- pidToProcess.put(r.pid, r);
+ pidToProcess.put(r.getPid(), r);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
@@ -8891,7 +9066,7 @@
printed = true;
}
pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i));
- pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
+ pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
}
}
@@ -8931,8 +9106,7 @@
synchronized (mPidsSelfLocked) {
boolean printed = false;
for (int i = 0, size = mImportantProcesses.size(); i < size; i++) {
- ProcessRecord r = mPidsSelfLocked.get(
- mImportantProcesses.valueAt(i).pid);
+ ProcessRecord r = mPidsSelfLocked.get(mImportantProcesses.valueAt(i).pid);
if (dumpPackage != null && (r == null
|| !r.getPkgList().containsKey(dumpPackage))) {
continue;
@@ -8973,7 +9147,7 @@
"OnHold Norm", "OnHold PERS", dumpPackage);
}
- needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
+ needSep = mAppErrors.dumpLPr(fd, pw, needSep, dumpPackage);
needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep,
mAppProfiler.getTestPssMode(), mWakefulness.get());
@@ -9068,7 +9242,7 @@
TimeUtils.formatDuration(now, mLastIdleTime, pw);
pw.print(" mLowRamSinceLastIdle=");
TimeUtils.formatDuration(
- mAppProfiler.getLowRamTimeSinceIdleLocked(now), pw);
+ mAppProfiler.getLowRamTimeSinceIdleLPr(now), pw);
pw.println();
pw.println();
@@ -9085,8 +9259,8 @@
mUserController.dump(pw);
}
- @GuardedBy("this")
- void writeOtherProcessesInfoToProtoLocked(ProtoOutputStream proto, String dumpPackage,
+ @GuardedBy({"this", "mProcLock"})
+ void writeOtherProcessesInfoToProtoLSP(ProtoOutputStream proto, String dumpPackage,
int dumpAppId, int numPers) {
for (int i = 0, size = mActiveInstrumentation.size(); i < size; i++) {
ActiveInstrumentation ai = mActiveInstrumentation.get(i);
@@ -9103,7 +9277,7 @@
if (dumpPackage != null) {
synchronized (mPidsSelfLocked) {
- for (int i=0; i<mPidsSelfLocked.size(); i++) {
+ for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
ProcessRecord r = mPidsSelfLocked.valueAt(i);
if (!r.getPkgList().containsKey(dumpPackage)) {
continue;
@@ -9159,7 +9333,7 @@
ActivityManagerServiceDumpProcessesProto.GC_PROCS,
dumpPackage);
}
- mAppErrors.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS,
+ mAppErrors.dumpDebugLPr(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS,
dumpPackage);
mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness.get(),
mAppProfiler.getTestPssMode());
@@ -9232,7 +9406,7 @@
long now = SystemClock.uptimeMillis();
ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS,
- mAppProfiler.getLowRamTimeSinceIdleLocked(now));
+ mAppProfiler.getLowRamTimeSinceIdleLPr(now));
}
}
@@ -9579,14 +9753,13 @@
mUgmInternal.dump(pw, dumpAll, dumpPackage);
}
- private static final int dumpProcessList(PrintWriter pw,
+ private static int dumpProcessList(PrintWriter pw,
ActivityManagerService service, List list,
String prefix, String normalLabel, String persistentLabel,
String dumpPackage) {
int numPers = 0;
- final int N = list.size()-1;
- for (int i=N; i>=0; i--) {
- ProcessRecord r = (ProcessRecord)list.get(i);
+ for (int i = list.size() - 1; i >= 0; i--) {
+ ProcessRecord r = (ProcessRecord) list.get(i);
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
@@ -9602,8 +9775,8 @@
ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs,
String[] args) {
- synchronized (this) {
- return mProcessList.collectProcessesLocked(start, allPkgs, args);
+ synchronized (mProcLock) {
+ return mProcessList.collectProcessesLOSP(start, allPkgs, args);
}
}
@@ -9622,13 +9795,15 @@
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = procs.get(i);
- if (r.thread != null) {
- pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
+ final int pid = r.getPid();
+ final IApplicationThread thread = r.getThread();
+ if (thread != null) {
+ pw.println("\n** Graphics info for pid " + pid + " [" + r.processName + "] **");
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
- r.thread.dumpGfxInfo(tp.getWriteFd(), args);
+ thread.dumpGfxInfo(tp.getWriteFd(), args);
tp.go(fd);
} finally {
tp.kill();
@@ -9655,13 +9830,15 @@
for (int i = procs.size() - 1; i >= 0; i--) {
ProcessRecord r = procs.get(i);
- if (r.thread != null) {
- pw.println("\n\n** Cache info for pid " + r.pid + " [" + r.processName + "] **");
+ final int pid = r.getPid();
+ final IApplicationThread thread = r.getThread();
+ if (thread != null) {
+ pw.println("\n\n** Cache info for pid " + pid + " [" + r.processName + "] **");
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
- r.thread.dumpCacheInfo(tp.getWriteFd(), args);
+ thread.dumpCacheInfo(tp.getWriteFd(), args);
tp.go(fd);
} finally {
tp.kill();
@@ -9688,13 +9865,15 @@
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = procs.get(i);
- if (r.thread != null) {
- pw.println("\n** Database info for pid " + r.pid + " [" + r.processName + "] **");
+ final int pid = r.getPid();
+ final IApplicationThread thread = r.getThread();
+ if (thread != null) {
+ pw.println("\n** Database info for pid " + pid + " [" + r.processName + "] **");
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
- r.thread.dumpDbInfo(tp.getWriteFd(), args);
+ thread.dumpDbInfo(tp.getWriteFd(), args);
tp.go(fd);
} finally {
tp.kill();
@@ -10157,10 +10336,10 @@
final int pid;
final int oomAdj;
final boolean hasActivities;
- synchronized (this) {
- thread = r.thread;
- pid = r.pid;
- oomAdj = r.getSetAdjWithServices();
+ synchronized (mProcLock) {
+ thread = r.getThread();
+ pid = r.getPid();
+ oomAdj = r.mState.getSetAdjWithServices();
hasActivities = r.hasActivities();
}
if (thread != null) {
@@ -10231,8 +10410,8 @@
final long myTotalRss = mi.getTotalRss();
final long myTotalSwapPss = mi.getTotalSwappedOutPss();
- synchronized (this) {
- if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
+ synchronized (mProcLock) {
+ if (r.getThread() != null && oomAdj == r.mState.getSetAdjWithServices()) {
// Record this for posterity if the process has been stable.
r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime - startTime);
@@ -10742,10 +10921,10 @@
final int pid;
final int oomAdj;
final boolean hasActivities;
- synchronized (this) {
- thread = r.thread;
- pid = r.pid;
- oomAdj = r.getSetAdjWithServices();
+ synchronized (mProcLock) {
+ thread = r.getThread();
+ pid = r.getPid();
+ oomAdj = r.mState.getSetAdjWithServices();
hasActivities = r.hasActivities();
}
if (thread == null) {
@@ -10811,8 +10990,8 @@
final long myTotalRss = mi.getTotalRss();
final long myTotalSwapPss = mi.getTotalSwappedOutPss();
- synchronized (this) {
- if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
+ synchronized (mProcLock) {
+ if (r.getThread() != null && oomAdj == r.mState.getSetAdjWithServices()) {
// Record this for posterity if the process has been stable.
r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime - startTime);
@@ -11151,96 +11330,26 @@
* app that was passed in must remain on the process lists.
*/
@GuardedBy("this")
- final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
+ final boolean cleanUpApplicationRecordLocked(ProcessRecord app, int pid,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
- if (index >= 0) {
- removeLruProcessLocked(app);
- ProcessList.remove(app.pid);
- }
+ boolean restart;
+ synchronized (mProcLock) {
+ if (index >= 0) {
+ removeLruProcessLocked(app);
+ ProcessList.remove(pid);
+ }
+ restart = app.onCleanupApplicationRecordLSP(mProcessStats, allowRestart);
+ }
mAppProfiler.onCleanupApplicationRecordLocked(app);
-
- // Dismiss any open dialogs.
- app.getDialogController().clearAllErrorDialogs();
-
- app.setCrashing(false);
- app.setNotResponding(false);
-
- app.resetPackageList(mProcessStats);
- app.unlinkDeathRecipient();
- app.makeInactive(mProcessStats);
- app.waitingToKill = null;
- app.forcingToImportant = null;
- updateProcessForegroundLocked(app, false, 0, false);
- app.setHasForegroundActivities(false);
- app.hasShownUi = false;
- app.treatLikeActivity = false;
- app.hasAboveClient = false;
- app.setHasClientActivities(false);
-
- mServices.killServicesLocked(app, allowRestart);
- mPhantomProcessList.onAppDied(app.pid);
-
- boolean restart = false;
-
- // Remove published content providers.
- for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
- ContentProviderRecord cpr = app.pubProviders.valueAt(i);
- if (cpr.proc != app) {
- // If the hosting process record isn't really us, bail out
- continue;
- }
- final boolean alwaysRemove = app.bad || !allowRestart;
- final boolean inLaunching = mCpHelper.removeDyingProviderLocked(app, cpr, alwaysRemove);
- if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
- // We left the provider in the launching list, need to
- // restart it.
- restart = true;
- }
-
- cpr.provider = null;
- cpr.setProcess(null);
- }
- app.pubProviders.clear();
-
- // Take care of any launching providers waiting for this process.
- if (mCpHelper.cleanupAppInLaunchingProvidersLocked(app, false)) {
- mProcessList.noteProcessDiedLocked(app);
- restart = true;
- }
-
- // Unregister from connected content providers.
- if (!app.conProviders.isEmpty()) {
- for (int i = app.conProviders.size() - 1; i >= 0; i--) {
- ContentProviderConnection conn = app.conProviders.get(i);
- conn.provider.connections.remove(conn);
- stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
- conn.provider.appInfo.longVersionCode, conn.provider.name,
- conn.provider.info.processName);
- }
- app.conProviders.clear();
- }
-
- // At this point there may be remaining entries in mLaunchingProviders
- // where we were the only one waiting, so they are no longer of use.
- // Look for these and clean up if found.
- // XXX Commented out for now. Trying to figure out a way to reproduce
- // the actual situation to identify what is actually going on.
- if (false) {
- mCpHelper.cleanupLaunchingProvidersLocked();
- }
-
skipCurrentReceiverLocked(app);
-
- // Unregister any receivers.
- for (int i = app.receivers.size() - 1; i >= 0; i--) {
- removeReceiverLocked(app.receivers.valueAt(i));
- }
- app.receivers.clear();
+ updateProcessForegroundLocked(app, false, 0, false);
+ mServices.killServicesLocked(app, allowRestart);
+ mPhantomProcessList.onAppDied(pid);
// If the app is undergoing backup, tell the backup manager about it
final BackupRecord backupTarget = mBackupTargets.get(app.userId);
- if (backupTarget != null && app.pid == backupTarget.app.pid) {
+ if (backupTarget != null && pid == backupTarget.app.getPid()) {
if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
+ backupTarget.appInfo + " died during backup");
mHandler.post(new Runnable() {
@@ -11257,9 +11366,9 @@
});
}
- mProcessList.scheduleDispatchProcessDiedLocked(app.pid, app.info.uid);
+ mProcessList.scheduleDispatchProcessDiedLocked(pid, app.info.uid);
- // If this is a precede instance of another process instance
+ // If this is a preceding instance of another process instance
allowRestart = true;
synchronized (app) {
if (app.mSuccessor != null) {
@@ -11267,13 +11376,13 @@
// because we have created a new one already.
allowRestart = false;
// If it's persistent, add the successor to mPersistentStartingProcesses
- if (app.isPersistent() && !app.removed) {
+ if (app.isPersistent() && !app.isRemoved()) {
if (mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {
mPersistentStartingProcesses.add(app.mSuccessor);
}
}
// clean up the field so the successor's proc starter could proceed.
- app.mSuccessor.mPrecedence = null;
+ app.mSuccessor.mPredecessor = null;
app.mSuccessor = null;
// Notify if anyone is waiting for it.
app.notifyAll();
@@ -11293,7 +11402,7 @@
mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
}
mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
- } else if (!app.removed) {
+ } else if (!app.isRemoved()) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
// and start a new process for it.
@@ -11313,7 +11422,7 @@
// We have components that still need to be running in the
// process, so re-launch it.
if (index < 0) {
- ProcessList.remove(app.pid);
+ ProcessList.remove(pid);
}
// Remove provider publish timeout because we will start a new timeout when the
@@ -11321,14 +11430,13 @@
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, app);
mProcessList.addProcessNameLocked(app);
- app.pendingStart = false;
- mProcessList.startProcessLocked(app,
- new HostingRecord("restart", app.processName),
+ app.setPendingStart(false);
+ mProcessList.startProcessLocked(app, new HostingRecord("restart", app.processName),
ZYGOTE_POLICY_FLAG_EMPTY);
return true;
- } else if (app.pid > 0 && app.pid != MY_PID) {
+ } else if (pid > 0 && pid != MY_PID) {
// Goodbye!
- removePidLocked(app);
+ removePidLocked(pid, app);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
@@ -11667,12 +11775,12 @@
// process after the full backup is done and the ProcessRecord will vaporize anyway.
if (UserHandle.isApp(app.uid) &&
backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL) {
- proc.inFullBackup = true;
+ proc.setInFullBackup(true);
}
r.app = proc;
final BackupRecord backupTarget = mBackupTargets.get(targetUserId);
oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
- newBackupUid = proc.inFullBackup ? r.appInfo.uid : -1;
+ newBackupUid = proc.isInFullBackup() ? r.appInfo.uid : -1;
mBackupTargets.put(targetUserId, r);
// Try not to kill the process during backup
@@ -11680,10 +11788,11 @@
// If the process is already attached, schedule the creation of the backup agent now.
// If it is not yet live, this will be done when it attaches to the framework.
- if (proc.thread != null) {
+ final IApplicationThread thread = proc.getThread();
+ if (thread != null) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc);
try {
- proc.thread.scheduleCreateBackupAgent(app,
+ thread.scheduleCreateBackupAgent(app,
compatibilityInfoForPackage(app), backupMode, targetUserId,
operationType);
} catch (RemoteException e) {
@@ -11793,14 +11902,15 @@
// Not backing this app up any more; reset its OOM adjustment
final ProcessRecord proc = backupTarget.app;
updateOomAdjLocked(proc, true, OomAdjuster.OOM_ADJ_REASON_NONE);
- proc.inFullBackup = false;
+ proc.setInFullBackup(false);
oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
// If the app crashed during backup, 'thread' will be null here
- if (proc.thread != null) {
+ final IApplicationThread thread = proc.getThread();
+ if (thread != null) {
try {
- proc.thread.scheduleDestroyBackupAgent(appInfo,
+ thread.scheduleDestroyBackupAgent(appInfo,
compatibilityInfoForPackage(appInfo), userId);
} catch (Exception e) {
Slog.e(TAG, "Exception when unbinding backup agent:");
@@ -11895,7 +12005,7 @@
boolean instantApp;
synchronized(this) {
if (caller != null) {
- callerApp = getRecordForAppLocked(caller);
+ callerApp = getRecordForAppLOSP(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
@@ -11909,7 +12019,7 @@
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
- callingPid = callerApp.pid;
+ callingPid = callerApp.getPid();
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
@@ -11978,8 +12088,9 @@
}
synchronized (this) {
- if (callerApp != null && (callerApp.thread == null
- || callerApp.thread.asBinder() != caller.asBinder())) {
+ IApplicationThread thread;
+ if (callerApp != null && ((thread = callerApp.getThread()) == null
+ || thread.asBinder() != caller.asBinder())) {
// Original caller already died
return null;
}
@@ -11988,13 +12099,13 @@
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
- final int totalReceiversForApp = rl.app.receivers.size();
+ final int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();
if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
throw new IllegalStateException("Too many receivers, total of "
+ totalReceiversForApp + ", registered for pid: "
+ rl.pid + ", callerPackage: " + callerPackage);
}
- rl.app.receivers.add(rl);
+ rl.app.mReceivers.addReceiver(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
@@ -12080,7 +12191,7 @@
}
if (rl.app != null) {
- rl.app.receivers.remove(rl);
+ rl.app.mReceivers.removeReceiver(rl);
}
removeReceiverLocked(rl);
if (rl.linkedToDeath) {
@@ -12379,7 +12490,7 @@
}
}
if (brOptions.isDontSendToRestrictedApps()
- && !isUidActiveLocked(callingUid)
+ && !isUidActiveLOSP(callingUid)
&& isBackgroundRestrictedNoCheck(callingUid, callerPackage)) {
Slog.i(TAG, "Not sending broadcast " + action + " - app " + callerPackage
+ " has background restrictions");
@@ -12586,12 +12697,14 @@
if (killProcess) {
final int extraUid = intent.getIntExtra(Intent.EXTRA_UID,
-1);
- mProcessList.killPackageProcessesLocked(ssp,
- UserHandle.getAppId(extraUid),
- userId, ProcessList.INVALID_ADJ,
- ApplicationExitInfo.REASON_USER_REQUESTED,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- "change " + ssp);
+ synchronized (mProcLock) {
+ mProcessList.killPackageProcessesLSP(ssp,
+ UserHandle.getAppId(extraUid),
+ userId, ProcessList.INVALID_ADJ,
+ ApplicationExitInfo.REASON_USER_REQUESTED,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ "change " + ssp);
+ }
}
cleanupDisabledPackageComponentsLocked(ssp, userId,
intent.getStringArrayExtra(
@@ -12735,7 +12848,7 @@
Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
final int uid = getUidFromIntent(intent);
if (uid != -1) {
- final UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
+ final UidRecord uidRec = mProcessList.getUidRecordLOSP(uid);
if (uidRec != null) {
uidRec.updateHasInternetPermission();
}
@@ -13120,7 +13233,7 @@
synchronized(this) {
intent = verifyBroadcastLocked(intent);
- final ProcessRecord callerApp = getRecordForAppLocked(caller);
+ final ProcessRecord callerApp = getRecordForAppLOSP(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
@@ -13349,38 +13462,40 @@
|| (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
boolean disableTestApiChecks = disableHiddenApiChecks
|| (flags & INSTR_FLAG_DISABLE_TEST_API_CHECKS) != 0;
+
if (disableHiddenApiChecks || disableTestApiChecks) {
enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
"disable hidden API checks");
- enableTestApiAccess(ii.packageName);
+ enableTestApiAccess(ai.packageName);
}
final long origId = Binder.clearCallingIdentity();
ProcessRecord app;
- if (noRestart) {
- app = getProcessRecordLocked(ai.processName, ai.uid, true);
- } else {
- // Instrumentation can kill and relaunch even persistent processes
- forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
- "start instr");
- // Inform usage stats to make the target package active
- if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(ii.targetPackage, userId,
- UsageEvents.Event.SYSTEM_INTERACTION);
+ synchronized (mProcLock) {
+ if (noRestart) {
+ app = getProcessRecordLocked(ai.processName, ai.uid, true);
+ } else {
+ // Instrumentation can kill and relaunch even persistent processes
+ forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false,
+ userId, "start instr");
+ // Inform usage stats to make the target package active
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportEvent(ii.targetPackage, userId,
+ UsageEvents.Event.SYSTEM_INTERACTION);
+ }
+ app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride,
+ ZYGOTE_POLICY_FLAG_EMPTY);
}
- app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
- disableTestApiChecks, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY);
- }
-
- app.setActiveInstrumentation(activeInstr);
- activeInstr.mFinished = false;
- activeInstr.mSourceUid = callingUid;
- activeInstr.mRunningProcesses.add(app);
- if (!mActiveInstrumentation.contains(activeInstr)) {
- mActiveInstrumentation.add(activeInstr);
+ app.setActiveInstrumentation(activeInstr);
+ activeInstr.mFinished = false;
+ activeInstr.mSourceUid = callingUid;
+ activeInstr.mRunningProcesses.add(app);
+ if (!mActiveInstrumentation.contains(activeInstr)) {
+ mActiveInstrumentation.add(activeInstr);
+ }
}
if ((flags & INSTR_FLAG_DISABLE_ISOLATED_STORAGE) != 0) {
@@ -13407,7 +13522,7 @@
}
try {
- pr.thread.instrumentWithoutRestart(
+ pr.getThread().instrumentWithoutRestart(
activeInstr.mClass,
activeInstr.mArguments,
activeInstr.mWatcher,
@@ -13467,7 +13582,7 @@
}
synchronized(this) {
- ProcessRecord app = getRecordForAppLocked(target);
+ ProcessRecord app = getRecordForAppLOSP(target);
if (app == null) {
Slog.w(TAG, "addInstrumentationResults: no app for " + target);
return;
@@ -13489,36 +13604,38 @@
return;
}
- if (!instr.mFinished) {
- if (instr.mWatcher != null) {
- Bundle finalResults = instr.mCurResults;
- if (finalResults != null) {
- if (instr.mCurResults != null && results != null) {
- finalResults.putAll(results);
+ synchronized (mProcLock) {
+ if (!instr.mFinished) {
+ if (instr.mWatcher != null) {
+ Bundle finalResults = instr.mCurResults;
+ if (finalResults != null) {
+ if (instr.mCurResults != null && results != null) {
+ finalResults.putAll(results);
+ }
+ } else {
+ finalResults = results;
}
- } else {
- finalResults = results;
+ mInstrumentationReporter.reportFinished(instr.mWatcher,
+ instr.mClass, resultCode, finalResults);
}
- mInstrumentationReporter.reportFinished(instr.mWatcher,
- instr.mClass, resultCode, finalResults);
+
+ // Can't call out of the system process with a lock held, so post a message.
+ if (instr.mUiAutomationConnection != null) {
+ // Go back to the default mode of denying OP_NO_ISOLATED_STORAGE app op.
+ mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
+ app.info.packageName, AppOpsManager.MODE_ERRORED);
+ mAppOpsService.setAppOpsServiceDelegate(null);
+ getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
+ mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
+ instr.mUiAutomationConnection).sendToTarget();
+ }
+ instr.mFinished = true;
}
- // Can't call out of the system process with a lock held, so post a message.
- if (instr.mUiAutomationConnection != null) {
- // Go back to the default mode of denying OP_NO_ISOLATED_STORAGE app op.
- mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
- app.info.packageName, AppOpsManager.MODE_ERRORED);
- mAppOpsService.setAppOpsServiceDelegate(null);
- getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
- mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
- instr.mUiAutomationConnection).sendToTarget();
- }
- instr.mFinished = true;
+ instr.removeProcess(app);
+ app.setActiveInstrumentation(null);
}
- instr.removeProcess(app);
- app.setActiveInstrumentation(null);
-
if (!instr.mNoRestart) {
forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false,
app.userId,
@@ -13554,7 +13671,7 @@
}
synchronized(this) {
- ProcessRecord app = getRecordForAppLocked(target);
+ ProcessRecord app = getRecordForAppLOSP(target);
if (app == null) {
Slog.w(TAG, "finishInstrumentation: no app for " + target);
return;
@@ -13660,10 +13777,11 @@
// the current [or imminent] receiver on.
boolean isReceivingBroadcastLocked(ProcessRecord app,
ArraySet<BroadcastQueue> receivingQueues) {
- final int N = app.curReceivers.size();
- if (N > 0) {
- for (int i = 0; i < N; i++) {
- receivingQueues.add(app.curReceivers.valueAt(i).queue);
+ final ProcessReceiverRecord prr = app.mReceivers;
+ final int numOfReceivers = prr.numberOfCurReceivers();
+ if (numOfReceivers > 0) {
+ for (int i = 0; i < numOfReceivers; i++) {
+ receivingQueues.add(prr.getCurReceiverAt(i).queue);
}
return true;
}
@@ -13782,6 +13900,7 @@
/**
* Returns true if things are idle enough to perform GCs.
*/
+ @GuardedBy("this")
final boolean canGcNowLocked() {
for (BroadcastQueue q : mBroadcastQueues) {
if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isEmpty()) {
@@ -13794,77 +13913,91 @@
private void checkExcessivePowerUsage() {
updateCpuStatsNow();
- synchronized (this) {
- boolean doCpuKills = true;
- if (mLastPowerCheckUptime == 0) {
- doCpuKills = false;
- }
+ synchronized (mProcLock) {
+ final boolean doCpuKills = mLastPowerCheckUptime != 0;
final long curUptime = SystemClock.uptimeMillis();
final long uptimeSince = curUptime - mLastPowerCheckUptime;
mLastPowerCheckUptime = curUptime;
- int i = mProcessList.mLruProcesses.size();
- while (i > 0) {
- i--;
- final ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
+ mProcessList.forEachLruProcessesLOSP(false, app -> {
+ if (app.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) {
int cpuLimit;
- long checkDur = curUptime - app.getWhenUnimportant();
+ long checkDur = curUptime - app.mState.getWhenUnimportant();
if (checkDur <= mConstants.POWER_CHECK_INTERVAL) {
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1;
} else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 2)
- || app.setProcState <= ActivityManager.PROCESS_STATE_HOME) {
+ || app.mState.getSetProcState() <= ActivityManager.PROCESS_STATE_HOME) {
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2;
} else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 3)) {
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3;
} else {
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4;
}
- synchronized (mAppProfiler.mProfilerLock) {
- final ProcessProfileRecord profile = app.mProfile;
- final long curCpuTime = profile.mCurCpuTime.get();
- final long lastCpuTime = profile.mLastCpuTime.get();
- if (lastCpuTime > 0) {
- final long cputimeUsed = curCpuTime - lastCpuTime;
- if (checkExcessivePowerUsageLocked(uptimeSince, doCpuKills, cputimeUsed,
- app.processName, app.toShortString(), cpuLimit, app)) {
- app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
- + " dur=" + checkDur + " limit=" + cpuLimit,
- ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
- ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
- true);
- profile.reportExcessiveCpu();
- }
- }
- profile.mLastCpuTime.set(curCpuTime);
- }
+ updateAppProcessCpuTimeLPr(uptimeSince, doCpuKills, checkDur, cpuLimit, app);
// Also check the phantom processes if there is any
- final long chkDur = checkDur;
- final int cpuLmt = cpuLimit;
- final boolean doKill = doCpuKills;
- mPhantomProcessList.forEachPhantomProcessOfApp(app, r -> {
- if (r.mLastCputime > 0) {
- final long cputimeUsed = r.mCurrentCputime - r.mLastCputime;
- if (checkExcessivePowerUsageLocked(uptimeSince, doKill, cputimeUsed,
- app.processName, r.toString(), cpuLimit, app)) {
- mPhantomProcessList.killPhantomProcessGroupLocked(app, r,
- ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
- ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
- "excessive cpu " + cputimeUsed + " during "
- + uptimeSince + " dur=" + chkDur + " limit=" + cpuLmt);
- return false;
- }
- }
- r.mLastCputime = r.mCurrentCputime;
- return true;
- });
+ updatePhantomProcessCpuTimeLPr(
+ uptimeSince, doCpuKills, checkDur, cpuLimit, app);
}
- }
+ });
}
}
- private boolean checkExcessivePowerUsageLocked(final long uptimeSince, boolean doCpuKills,
+ @GuardedBy("mProcLock")
+ private void updateAppProcessCpuTimeLPr(final long uptimeSince, final boolean doCpuKills,
+ final long checkDur, final int cpuLimit, final ProcessRecord app) {
+ synchronized (mAppProfiler.mProfilerLock) {
+ final ProcessProfileRecord profile = app.mProfile;
+ final long curCpuTime = profile.mCurCpuTime.get();
+ final long lastCpuTime = profile.mLastCpuTime.get();
+ if (lastCpuTime > 0) {
+ final long cpuTimeUsed = curCpuTime - lastCpuTime;
+ if (checkExcessivePowerUsageLPr(uptimeSince, doCpuKills, cpuTimeUsed,
+ app.processName, app.toShortString(), cpuLimit, app)) {
+ mHandler.post(() -> {
+ synchronized (ActivityManagerService.this) {
+ app.killLocked("excessive cpu " + cpuTimeUsed + " during "
+ + uptimeSince + " dur=" + checkDur + " limit=" + cpuLimit,
+ ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
+ ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
+ true);
+ }
+ });
+ profile.reportExcessiveCpu();
+ }
+ }
+
+ profile.mLastCpuTime.set(curCpuTime);
+ }
+ }
+
+ @GuardedBy("mProcLock")
+ private void updatePhantomProcessCpuTimeLPr(final long uptimeSince, final boolean doCpuKills,
+ final long checkDur, final int cpuLimit, final ProcessRecord app) {
+ mPhantomProcessList.forEachPhantomProcessOfApp(app, r -> {
+ if (r.mLastCputime > 0) {
+ final long cpuTimeUsed = r.mCurrentCputime - r.mLastCputime;
+ if (checkExcessivePowerUsageLPr(uptimeSince, doCpuKills, cpuTimeUsed,
+ app.processName, r.toString(), cpuLimit, app)) {
+ mHandler.post(() -> {
+ synchronized (ActivityManagerService.this) {
+ mPhantomProcessList.killPhantomProcessGroupLocked(app, r,
+ ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
+ ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
+ "excessive cpu " + cpuTimeUsed + " during "
+ + uptimeSince + " dur=" + checkDur + " limit=" + cpuLimit);
+ }
+ });
+ return false;
+ }
+ }
+ r.mLastCputime = r.mCurrentCputime;
+ return true;
+ });
+ }
+
+ @GuardedBy("mProcLock")
+ private boolean checkExcessivePowerUsageLPr(final long uptimeSince, boolean doCpuKills,
final long cputimeUsed, final String processName, final String description,
final int cpuLimit, final ProcessRecord app) {
if (DEBUG_POWER) {
@@ -13909,19 +14042,20 @@
UserHandle.getUserId(uid), packages[0]);
}
+ @GuardedBy("this")
void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
- uid = uidRec != null ? uidRec.uid : uid;
+ uid = uidRec != null ? uidRec.getUid() : uid;
if (uid < 0) {
throw new IllegalArgumentException("No UidRecord or uid");
}
final int procState = uidRec != null
- ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
+ ? uidRec.getSetProcState() : PROCESS_STATE_NONEXISTENT;
final long procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
- final int capability = uidRec != null ? uidRec.setCapability : 0;
- final boolean ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
+ final int capability = uidRec != null ? uidRec.getSetCapability() : 0;
+ final boolean ephemeral = uidRec != null ? uidRec.isEphemeral() : isEphemeralLocked(uid);
- if (uidRec != null && !uidRec.idle && (change & UidRecord.CHANGE_GONE) != 0) {
+ if (uidRec != null && !uidRec.isIdle() && (change & UidRecord.CHANGE_GONE) != 0) {
// If this uid is going away, and we haven't yet reported it is gone,
// then do so now.
change |= UidRecord.CHANGE_IDLE;
@@ -13930,7 +14064,7 @@
uidRec == null ? null : uidRec.pendingChange,
uid, change, procState, procStateSeq, capability, ephemeral);
if (uidRec != null) {
- uidRec.lastReportedChange = enqueuedChange;
+ uidRec.setLastReportedChange(enqueuedChange);
uidRec.updateLastDispatchedProcStateSeq(enqueuedChange);
}
@@ -13953,21 +14087,21 @@
}
}
- final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
- synchronized (mProcessStats.mLock) {
- if (proc.thread != null) {
- proc.mProfile.setProcessTrackerState(
- proc.getReportedProcState(), memFactor, now);
- }
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ final void setProcessTrackerStateLOSP(ProcessRecord proc, int memFactor, long now) {
+ if (proc.getThread() != null) {
+ proc.mProfile.setProcessTrackerState(
+ proc.mState.getReportedProcState(), memFactor, now);
}
}
@GuardedBy("this")
final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
int fgServiceTypes, boolean oomAdj) {
- if (isForeground != proc.hasForegroundServices()
- || proc.getForegroundServiceTypes() != fgServiceTypes) {
- proc.setHasForegroundServices(isForeground, fgServiceTypes);
+ final ProcessServiceRecord psr = proc.mServices;
+ if (isForeground != psr.hasForegroundServices()
+ || psr.getForegroundServiceTypes() != fgServiceTypes) {
+ psr.setHasForegroundServices(isForeground, fgServiceTypes);
ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName,
proc.info.uid);
if (isForeground) {
@@ -13993,9 +14127,9 @@
}
}
- proc.setReportedForegroundServiceTypes(fgServiceTypes);
+ psr.setReportedForegroundServiceTypes(fgServiceTypes);
ProcessChangeItem item = mProcessList.enqueueProcessChangeItemLocked(
- proc.pid, proc.info.uid);
+ proc.getPid(), proc.info.uid);
item.changes |= ProcessChangeItem.CHANGE_FOREGROUND_SERVICES;
item.foregroundServiceTypes = fgServiceTypes;
}
@@ -14038,7 +14172,6 @@
} finally {
Binder.restoreCallingIdentity(identity);
}
-
}
}
return r;
@@ -14148,17 +14281,20 @@
final int appId = UserHandle.getAppId(pkgUid);
for (int i = mProcessList.mActiveUids.size() - 1; i >= 0; i--) {
final UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
- final long bgTime = uidRec.lastBackgroundTime;
- if (bgTime > 0 && !uidRec.idle) {
- if (UserHandle.getAppId(uidRec.uid) == appId) {
+ final long bgTime = uidRec.getLastBackgroundTime();
+ if (bgTime > 0 && !uidRec.isIdle()) {
+ final int uid = uidRec.getUid();
+ if (UserHandle.getAppId(uid) == appId) {
if (userId == UserHandle.USER_ALL
- || userId == UserHandle.getUserId(uidRec.uid)) {
- EventLogTags.writeAmUidIdle(uidRec.uid);
- uidRec.idle = true;
- uidRec.setIdle = true;
- Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid)
+ || userId == UserHandle.getUserId(uid)) {
+ EventLogTags.writeAmUidIdle(uid);
+ synchronized (mProcLock) {
+ uidRec.setIdle(true);
+ uidRec.setSetIdle(true);
+ }
+ Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uid)
+ " from package " + packageName + " user " + userId);
- doStopUidLocked(uidRec.uid, uidRec);
+ doStopUidLocked(uid, uidRec);
}
}
}
@@ -14183,11 +14319,11 @@
final void runInBackgroundDisabled(int uid) {
synchronized (this) {
- UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
+ UidRecord uidRec = mProcessList.getUidRecordLOSP(uid);
if (uidRec != null) {
// This uid is actually running... should it be considered background now?
- if (uidRec.idle) {
- doStopUidLocked(uidRec.uid, uidRec);
+ if (uidRec.isIdle()) {
+ doStopUidLocked(uidRec.getUid(), uidRec);
}
} else {
// This uid isn't actually running... still send a report about it being "stopped".
@@ -14241,7 +14377,7 @@
+ callerPid);
return;
}
- if (!pr.mAllowlistManager) {
+ if (!pr.mServices.mAllowlistManager) {
if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid)
!= PackageManager.PERMISSION_GRANTED
&& checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callerPid, callerUid)
@@ -14265,13 +14401,15 @@
*/
@GuardedBy("this")
void tempAllowlistUidLocked(int targetUid, long duration, String tag, int type) {
- mPendingTempAllowlist.put(targetUid,
- new PendingTempAllowlist(targetUid, duration, tag, type));
- setUidTempAllowlistStateLocked(targetUid, true);
- mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget();
+ synchronized (mProcLock) {
+ mPendingTempAllowlist.put(targetUid,
+ new PendingTempAllowlist(targetUid, duration, tag, type));
+ setUidTempAllowlistStateLSP(targetUid, true);
+ mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget();
- if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
- mFgsStartTempAllowList.add(targetUid, duration);
+ if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+ mFgsStartTempAllowList.add(targetUid, duration);
+ }
}
}
@@ -14281,7 +14419,7 @@
// First copy out the pending changes... we need to leave them in the map for now,
// in case someone needs to check what is coming up while we don't have the lock held.
- synchronized(this) {
+ synchronized (mProcLock) {
N = mPendingTempAllowlist.size();
list = new PendingTempAllowlist[N];
for (int i = 0; i < N; i++) {
@@ -14301,25 +14439,27 @@
}
// And now we can safely remove them from the map.
- synchronized(this) {
- for (int i = 0; i < N; i++) {
- PendingTempAllowlist ptw = list[i];
- int index = mPendingTempAllowlist.indexOfKey(ptw.targetUid);
- if (index >= 0 && mPendingTempAllowlist.valueAt(index) == ptw) {
- mPendingTempAllowlist.removeAt(index);
+ synchronized (this) {
+ synchronized (mProcLock) {
+ for (int i = 0; i < N; i++) {
+ PendingTempAllowlist ptw = list[i];
+ int index = mPendingTempAllowlist.indexOfKey(ptw.targetUid);
+ if (index >= 0 && mPendingTempAllowlist.valueAt(index) == ptw) {
+ mPendingTempAllowlist.removeAt(index);
+ }
}
}
}
}
- @GuardedBy("this")
- final void setAppIdTempAllowlistStateLocked(int uid, boolean onAllowlist) {
- mOomAdjuster.setAppIdTempAllowlistStateLocked(uid, onAllowlist);
+ @GuardedBy({"this", "mProcLock"})
+ final void setAppIdTempAllowlistStateLSP(int uid, boolean onAllowlist) {
+ mOomAdjuster.setAppIdTempAllowlistStateLSP(uid, onAllowlist);
}
- @GuardedBy("this")
- final void setUidTempAllowlistStateLocked(int uid, boolean onAllowlist) {
- mOomAdjuster.setUidTempAllowlistStateLocked(uid, onAllowlist);
+ @GuardedBy({"this", "mProcLock"})
+ final void setUidTempAllowlistStateLSP(int uid, boolean onAllowlist) {
+ mOomAdjuster.setUidTempAllowlistStateLSP(uid, onAllowlist);
}
private void trimApplications(boolean forceFullOomAdj, String oomAdjReason) {
@@ -14336,26 +14476,28 @@
for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord app = mProcessList.mRemovedProcesses.get(i);
if (!app.hasActivitiesOrRecentTasks()
- && app.curReceivers.isEmpty() && app.numberOfRunningServices() == 0) {
- Slog.i(
- TAG, "Exiting empty application process "
- + app.toShortString() + " ("
- + (app.thread != null ? app.thread.asBinder() : null)
- + ")\n");
- if (app.pid > 0 && app.pid != MY_PID) {
- app.kill("empty",
+ && app.mReceivers.numberOfCurReceivers() == 0
+ && app.mServices.numberOfRunningServices() == 0) {
+ final IApplicationThread thread = app.getThread();
+ Slog.i(TAG, "Exiting empty application process "
+ + app.toShortString() + " ("
+ + (thread != null ? thread.asBinder() : null)
+ + ")\n");
+ final int pid = app.getPid();
+ if (pid > 0 && pid != MY_PID) {
+ app.killLocked("empty",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TRIM_EMPTY,
false);
- } else if (app.thread != null) {
+ } else if (thread != null) {
try {
- app.thread.scheduleExit();
+ thread.scheduleExit();
} catch (Exception e) {
// Ignore exceptions.
}
}
didSomething = true;
- cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
+ cleanUpApplicationRecordLocked(app, pid, false, true, -1, false /*replacingPid*/);
mProcessList.mRemovedProcesses.remove(i);
if (app.isPersistent()) {
@@ -14376,7 +14518,7 @@
}
/** This method sends the specified signal to each of the persistent apps */
- public void signalPersistentProcesses(int sig) throws RemoteException {
+ public void signalPersistentProcesses(final int sig) throws RemoteException {
if (sig != SIGNAL_USR1) {
throw new SecurityException("Only SIGNAL_USR1 is allowed");
}
@@ -14387,13 +14529,12 @@
+ android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES);
}
- synchronized (this) {
- for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
- ProcessRecord r = mProcessList.mLruProcesses.get(i);
- if (r.thread != null && r.isPersistent()) {
- sendSignal(r.pid, sig);
+ synchronized (mProcLock) {
+ mProcessList.forEachLruProcessesLOSP(false, app -> {
+ if (app.getThread() != null && app.isPersistent()) {
+ sendSignal(app.getPid(), sig);
}
- }
+ });
}
}
@@ -14412,21 +14553,23 @@
}
ProcessRecord proc = null;
- synchronized (this) {
+ synchronized (mProcLock) {
if (process != null) {
- proc = findProcessLocked(process, userId, "profileControl");
+ proc = findProcessLOSP(process, userId, "profileControl");
}
- if (start && (proc == null || proc.thread == null)) {
+ if (start && (proc == null || proc.getThread() == null)) {
throw new IllegalArgumentException("Unknown process: " + process);
}
}
+
synchronized (mAppProfiler.mProfilerLock) {
return mAppProfiler.profileControlLPf(proc, start, profilerInfo, profileType);
}
}
- private ProcessRecord findProcessLocked(String process, int userId, String callName) {
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ private ProcessRecord findProcessLOSP(String process, int userId, String callName) {
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, callName, null);
ProcessRecord proc = null;
@@ -14439,8 +14582,8 @@
}
if (proc == null) {
- ArrayMap<String, SparseArray<ProcessRecord>> all
- = mProcessList.mProcessNames.getMap();
+ ArrayMap<String, SparseArray<ProcessRecord>> all =
+ mProcessList.getProcessNamesLOSP().getMap();
SparseArray<ProcessRecord> procs = all.get(process);
if (procs != null && procs.size() > 0) {
proc = procs.valueAt(0);
@@ -14462,7 +14605,6 @@
@Override
public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo,
boolean runGc, String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) {
-
try {
// note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
// its own permission (same as profileControl).
@@ -14476,9 +14618,10 @@
throw new IllegalArgumentException("null fd");
}
- synchronized (this) {
- ProcessRecord proc = findProcessLocked(process, userId, "dumpHeap");
- if (proc == null || proc.thread == null) {
+ synchronized (mProcLock) {
+ ProcessRecord proc = findProcessLOSP(process, userId, "dumpHeap");
+ IApplicationThread thread;
+ if (proc == null || (thread = proc.getThread()) == null) {
throw new IllegalArgumentException("Unknown process: " + process);
}
@@ -14500,7 +14643,7 @@
}
}, null);
- proc.thread.dumpHeap(managed, mallocInfo, runGc, path, fd, intermediateCallback);
+ thread.dumpHeap(managed, mallocInfo, runGc, path, fd, intermediateCallback);
fd = null;
return true;
}
@@ -14555,8 +14698,8 @@
}
void onCoreSettingsChange(Bundle settings) {
- synchronized (this) {
- mProcessList.updateCoreSettingsLocked(settings);
+ synchronized (mProcLock) {
+ mProcessList.updateCoreSettingsLOSP(settings);
}
}
@@ -14708,8 +14851,9 @@
return info;
}
- private boolean processSanityChecksLocked(ProcessRecord process) {
- if (process == null || process.thread == null) {
+ @GuardedBy("mProcLock")
+ private boolean processSanityChecksLPr(ProcessRecord process, IApplicationThread thread) {
+ if (process == null || thread == null) {
return false;
}
@@ -14731,34 +14875,36 @@
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
- synchronized (this) {
+ synchronized (mProcLock) {
mBinderTransactionTrackingEnabled = true;
- for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
- ProcessRecord process = mProcessList.mLruProcesses.get(i);
- if (!processSanityChecksLocked(process)) {
- continue;
+ mProcessList.forEachLruProcessesLOSP(true, process -> {
+ final IApplicationThread thread = process.getThread();
+ if (!processSanityChecksLPr(process, thread)) {
+ return;
}
try {
- process.thread.startBinderTracking();
+ thread.startBinderTracking();
} catch (RemoteException e) {
Log.v(TAG, "Process disappared");
}
- }
- return true;
+ });
}
+ return true;
}
- public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException {
- try {
- // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
- // permission (same as profileControl).
- if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.SET_ACTIVITY_WATCHER);
- }
+ @Override
+ public boolean stopBinderTrackingAndDump(final ParcelFileDescriptor fd) throws RemoteException {
+ // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own
+ // permission (same as profileControl).
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
- synchronized (this) {
+ boolean closeFd = true;
+ try {
+ synchronized (mProcLock) {
if (fd == null) {
throw new IllegalArgumentException("null fd");
}
@@ -14766,9 +14912,10 @@
PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
pw.println("Binder transaction traces for all processes.\n");
- for (ProcessRecord process : mProcessList.mLruProcesses) {
- if (!processSanityChecksLocked(process)) {
- continue;
+ mProcessList.forEachLruProcessesLOSP(true, process -> {
+ final IApplicationThread thread = process.getThread();
+ if (!processSanityChecksLPr(process, thread)) {
+ return;
}
pw.println("Traces for process: " + process.processName);
@@ -14776,7 +14923,7 @@
try {
TransferPipe tp = new TransferPipe();
try {
- process.thread.stopBinderTrackingAndDump(tp.getWriteFd());
+ thread.stopBinderTrackingAndDump(tp.getWriteFd());
tp.go(fd.getFileDescriptor());
} finally {
tp.kill();
@@ -14790,12 +14937,12 @@
process + ". Exception: " + e);
pw.flush();
}
- }
- fd = null;
+ });
+ closeFd = false;
return true;
}
} finally {
- if (fd != null) {
+ if (fd != null && closeFd) {
try {
fd.close();
} catch (IOException e) {
@@ -14840,12 +14987,12 @@
@Override
public void killForegroundAppsForUser(@UserIdInt int userId) {
- synchronized (ActivityManagerService.this) {
- final ArrayList<ProcessRecord> procs = new ArrayList<>();
- final int NP = mProcessList.mProcessNames.getMap().size();
- for (int ip = 0; ip < NP; ip++) {
+ final ArrayList<ProcessRecord> procs = new ArrayList<>();
+ synchronized (mProcLock) {
+ final int numOfProcs = mProcessList.getProcessNamesLOSP().getMap().size();
+ for (int ip = 0; ip < numOfProcs; ip++) {
final SparseArray<ProcessRecord> apps =
- mProcessList.mProcessNames.getMap().valueAt(ip);
+ mProcessList.getProcessNamesLOSP().getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
@@ -14853,19 +15000,23 @@
// We don't kill persistent processes.
continue;
}
- if (app.removed
- || (app.userId == userId && app.hasForegroundActivities())) {
+ if (app.isRemoved()
+ || (app.userId == userId && app.mState.hasForegroundActivities())) {
procs.add(app);
}
}
}
+ }
- final int N = procs.size();
- for (int i = 0; i < N; i++) {
- mProcessList.removeProcessLocked(procs.get(i), false, true,
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_KILL_ALL_FG,
- "kill all fg");
+ final int numOfProcs = procs.size();
+ if (numOfProcs > 0) {
+ synchronized (ActivityManagerService.this) {
+ for (int i = 0; i < numOfProcs; i++) {
+ mProcessList.removeProcessLocked(procs.get(i), false, true,
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_KILL_ALL_FG,
+ "kill all fg");
+ }
}
}
}
@@ -14911,22 +15062,26 @@
@Override
public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) {
synchronized (ActivityManagerService.this) {
- mDeviceIdleAllowlist = allAppids;
- mDeviceIdleExceptIdleAllowlist = exceptIdleAppids;
+ synchronized (mProcLock) {
+ mDeviceIdleAllowlist = allAppids;
+ mDeviceIdleExceptIdleAllowlist = exceptIdleAppids;
+ }
}
}
@Override
public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding,
- long durationMs, @BroadcastOptions.TempAllowListType int type) {
+ long durationMs, @TempAllowListType int type) {
synchronized (ActivityManagerService.this) {
- mDeviceIdleTempAllowlist = appids;
- if (adding) {
- if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
- mFgsStartTempAllowList.add(changingUid, durationMs);
+ synchronized (mProcLock) {
+ mDeviceIdleTempAllowlist = appids;
+ if (adding) {
+ if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+ mFgsStartTempAllowList.add(changingUid, durationMs);
+ }
}
+ setAppIdTempAllowlistStateLSP(changingUid, adding);
}
- setAppIdTempAllowlistStateLocked(changingUid, adding);
}
}
@@ -14964,10 +15119,10 @@
return;
}
}
- if (pr.hasOverlayUi() == hasOverlayUi) {
+ if (pr.mState.hasOverlayUi() == hasOverlayUi) {
return;
}
- pr.setHasOverlayUi(hasOverlayUi);
+ pr.mState.setHasOverlayUi(hasOverlayUi);
//Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid);
updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
@@ -14985,8 +15140,8 @@
+ uid + " seq: " + procStateSeq);
}
UidRecord record;
- synchronized (ActivityManagerService.this) {
- record = mProcessList.getUidRecordLocked(uid);
+ synchronized (mProcLock) {
+ record = mProcessList.getUidRecordLOSP(uid);
if (record == null) {
if (DEBUG_NETWORK) {
Slog.d(TAG_NETWORK, "No active uidRecord for uid: " + uid
@@ -15049,19 +15204,21 @@
@Override
public boolean isUidActive(int uid) {
- synchronized (ActivityManagerService.this) {
- return isUidActiveLocked(uid);
+ synchronized (mProcLock) {
+ return isUidActiveLOSP(uid);
}
}
@Override
public List<ProcessMemoryState> getMemoryStateForProcesses() {
List<ProcessMemoryState> processMemoryStates = new ArrayList<>();
- synchronized (mPidsSelfLocked) {
- for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
- final ProcessRecord r = mPidsSelfLocked.valueAt(i);
- processMemoryStates.add(
- new ProcessMemoryState(r.uid, r.pid, r.processName, r.curAdj));
+ synchronized (mProcLock) {
+ synchronized (mPidsSelfLocked) {
+ for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) {
+ final ProcessRecord r = mPidsSelfLocked.valueAt(i);
+ processMemoryStates.add(new ProcessMemoryState(
+ r.uid, r.getPid(), r.processName, r.mState.getCurAdj()));
+ }
}
}
return processMemoryStates;
@@ -15101,14 +15258,14 @@
final WindowProcessController wpc =
(WindowProcessController) procsToKill.get(i);
final ProcessRecord pr = (ProcessRecord) wpc.mOwner;
- if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- && pr.curReceivers.isEmpty()) {
- pr.kill("remove task", ApplicationExitInfo.REASON_USER_REQUESTED,
+ if (pr.mState.getSetSchedGroup() == ProcessList.SCHED_GROUP_BACKGROUND
+ && pr.mReceivers.numberOfCurReceivers() == 0) {
+ pr.killLocked("remove task", ApplicationExitInfo.REASON_USER_REQUESTED,
ApplicationExitInfo.SUBREASON_UNKNOWN, true);
} else {
// We delay killing processes that are not in the background or running a
// receiver.
- pr.waitingToKill = "remove task";
+ pr.setWaitingToKill("remove task");
}
}
}
@@ -15130,18 +15287,15 @@
public boolean hasRunningActivity(int uid, @Nullable String packageName) {
if (packageName == null) return false;
- synchronized (ActivityManagerService.this) {
- for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
- final ProcessRecord pr = mProcessList.mLruProcesses.get(i);
- if (pr.uid != uid) {
- continue;
+ synchronized (mProcLock) {
+ return mProcessList.searchEachLruProcessesLOSP(true, app -> {
+ if (app.uid == uid
+ && app.getWindowProcessController().hasRunningActivity(packageName)) {
+ return Boolean.TRUE;
}
- if (pr.getWindowProcessController().hasRunningActivity(packageName)) {
- return true;
- }
- }
+ return null;
+ }) != null;
}
- return false;
}
@Override
@@ -15572,7 +15726,7 @@
}
synchronized (mPidsSelfLocked) {
final ProcessRecord pr = mPidsSelfLocked.get(pid);
- return pr == null ? Zygote.MOUNT_EXTERNAL_NONE : pr.mountMode;
+ return pr == null ? Zygote.MOUNT_EXTERNAL_NONE : pr.getMountMode();
}
}
@@ -15603,19 +15757,17 @@
@Override
public boolean hasRunningForegroundService(int uid, int foregroundServicetype) {
synchronized (ActivityManagerService.this) {
- for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
- final ProcessRecord pr = mProcessList.mLruProcesses.get(i);
- if (pr.uid != uid) {
- continue;
+ return mProcessList.searchEachLruProcessesLOSP(true, app -> {
+ if (app.uid != uid) {
+ return null;
}
- if ((pr.getForegroundServiceTypes() & foregroundServicetype) != 0) {
- return true;
+ if ((app.mServices.getForegroundServiceTypes() & foregroundServicetype) != 0) {
+ return Boolean.TRUE;
}
- }
+ return null;
+ }) != null;
}
-
- return false;
}
@Override
@@ -15630,7 +15782,7 @@
@Override
public boolean isUidCurrentlyInstrumented(int uid) {
- synchronized (ActivityManagerService.this) {
+ synchronized (mProcLock) {
for (int i = mActiveInstrumentation.size() - 1; i >= 0; i--) {
ActiveInstrumentation activeInst = mActiveInstrumentation.get(i);
if (!activeInst.mFinished && activeInst.mTargetInfo != null
@@ -15710,6 +15862,16 @@
// PackageManagerService.
return mConstants.mBootTimeTempAllowlistDuration;
}
+
+ @Override
+ public void registerAnrController(AnrController controller) {
+ mActivityTaskManager.registerAnrController(controller);
+ }
+
+ @Override
+ public void unregisterAnrController(AnrController controller) {
+ mActivityTaskManager.unregisterAnrController(controller);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -15782,8 +15944,8 @@
Slog.d(TAG_NETWORK, "Called from " + callingUid + " to wait for seq: " + procStateSeq);
}
UidRecord record;
- synchronized (this) {
- record = mProcessList.getUidRecordLocked(callingUid);
+ synchronized (mProcLock) {
+ record = mProcessList.getUidRecordLOSP(callingUid);
if (record == null) {
return;
}
@@ -15899,11 +16061,13 @@
}
try {
synchronized(this) {
- mProcessList.killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid),
- userId, ProcessList.FOREGROUND_APP_ADJ,
- ApplicationExitInfo.REASON_DEPENDENCY_DIED,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- "dep: " + packageName);
+ synchronized (mProcLock) {
+ mProcessList.killPackageProcessesLSP(packageName, UserHandle.getAppId(pkgUid),
+ userId, ProcessList.FOREGROUND_APP_ADJ,
+ ApplicationExitInfo.REASON_DEPENDENCY_DIED,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ "dep: " + packageName);
+ }
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -15920,10 +16084,10 @@
enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
"scheduleApplicationInfoChanged()");
- synchronized (this) {
+ synchronized (mProcLock) {
final long origId = Binder.clearCallingIdentity();
try {
- updateApplicationInfoLocked(packageNames, userId);
+ updateApplicationInfoLOSP(packageNames, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -15942,12 +16106,13 @@
ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai);
}
- void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ private void updateApplicationInfoLOSP(@NonNull List<String> packagesToUpdate, int userId) {
final boolean updateFrameworkRes = packagesToUpdate.contains("android");
if (updateFrameworkRes) {
PackageParser.readConfigUseRoundIcon(null);
}
- mProcessList.updateApplicationInfoLocked(packagesToUpdate, userId, updateFrameworkRes);
+ mProcessList.updateApplicationInfoLOSP(packagesToUpdate, userId, updateFrameworkRes);
if (updateFrameworkRes) {
// Update system server components that need to know about changed overlays. Because the
@@ -15976,7 +16141,7 @@
final int batchSize;
final float threshold;
final BinderCallHeavyHitterListener listener;
- synchronized (ActivityManagerService.this) {
+ synchronized (mProcLock) {
if (mConstants.BINDER_HEAVY_HITTER_WATCHER_ENABLED) {
// Default watcher takes precedence, ignore the auto sampler.
mHandler.removeMessages(BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG);
@@ -16013,7 +16178,7 @@
final int batchSize;
final float threshold;
final long now;
- synchronized (ActivityManagerService.this) {
+ synchronized (mProcLock) {
if (!mConstants.BINDER_HEAVY_HITTER_AUTO_SAMPLER_ENABLED) {
// It's configured OFF
return;
@@ -16047,7 +16212,7 @@
* Stop the binder heavy hitter auto sampler after given timeout.
*/
private void handleBinderHeavyHitterAutoSamplerTimeOut() {
- synchronized (ActivityManagerService.this) {
+ synchronized (mProcLock) {
if (mConstants.BINDER_HEAVY_HITTER_WATCHER_ENABLED) {
// The default watcher is ON, don't bother to stop it.
return;
@@ -16093,10 +16258,11 @@
*/
public void attachAgent(String process, String path) {
try {
- synchronized (this) {
- ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM,
+ synchronized (mProcLock) {
+ ProcessRecord proc = findProcessLOSP(process, UserHandle.USER_SYSTEM,
"attachAgent");
- if (proc == null || proc.thread == null) {
+ IApplicationThread thread;
+ if (proc == null || (thread = proc.getThread()) == null) {
throw new IllegalArgumentException("Unknown process: " + process);
}
@@ -16106,7 +16272,7 @@
}
}
- proc.thread.attachAgent(path);
+ thread.attachAgent(path);
}
} catch (RemoteException e) {
throw new IllegalStateException("Process disappeared");
@@ -16175,7 +16341,7 @@
}
// We allow delegation only to one instrumentation started from the shell
- synchronized (ActivityManagerService.this) {
+ synchronized (mProcLock) {
// If the delegate is already set up for the target UID, nothing to do.
if (mAppOpsService.getAppOpsServiceDelegate() != null) {
if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) {
@@ -16221,7 +16387,7 @@
&& UserHandle.getCallingAppId() != Process.ROOT_UID) {
throw new SecurityException("Only the shell can delegate its permissions");
}
- synchronized (ActivityManagerService.this) {
+ synchronized (mProcLock) {
mAppOpsService.setAppOpsServiceDelegate(null);
getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
}
@@ -16343,7 +16509,7 @@
if (!isCallerShell()) {
throw new SecurityException("Only shell can call it");
}
- synchronized (this) {
+ synchronized (mProcLock) {
try {
if (mLifeMonitorFds == null) {
mLifeMonitorFds = ParcelFileDescriptor.createPipe();
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index fe71fbf..c971bd2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2984,7 +2984,13 @@
pw.println("Reset all changes for " + packageName + " to default value.");
return 0;
}
- if (platformCompat.clearOverride(changeId, packageName)) {
+ boolean existed;
+ if (killPackage) {
+ existed = platformCompat.clearOverride(changeId, packageName);
+ } else {
+ existed = platformCompat.clearOverrideForTest(changeId, packageName);
+ }
+ if (existed) {
pw.println("Reset change " + changeId + " for " + packageName
+ " to default value.");
} else {
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index a7119d1..34d9a60 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -160,7 +160,7 @@
}
void appNotResponding(boolean onlyDumpSelf) {
- mApp.appNotResponding(mActivityShortComponentName, mAppInfo,
+ mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
onlyDumpSelf);
}
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index 779d378..48222cb 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -38,6 +38,7 @@
final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener {
private final ActivityManagerService mService;
+ private final ActivityManagerGlobalLock mProcLock;
private final AppErrorResult mResult;
private final ProcessRecord mProc;
private final boolean mIsRestartable;
@@ -63,6 +64,7 @@
Resources res = context.getResources();
mService = service;
+ mProcLock = service.mProcLock;
mProc = data.proc;
mResult = data.result;
mIsRestartable = (data.taskId != INVALID_TASK_ID || data.isRestartableForService)
@@ -71,7 +73,7 @@
BidiFormatter bidi = BidiFormatter.getInstance();
CharSequence name;
- if ((mProc.getPkgList().size() == 1)
+ if (mProc.getPkgList().size() == 1
&& (name = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
setTitle(res.getString(
data.repeating ? com.android.internal.R.string.aerr_application_repeated
@@ -112,7 +114,7 @@
LayoutInflater.from(context).inflate(
com.android.internal.R.layout.app_error_dialog, frame, true);
- final boolean hasReceiver = mProc.errorReportReceiver != null;
+ final boolean hasReceiver = mProc.mErrorState.getErrorReportReceiver() != null;
final TextView restart = findViewById(com.android.internal.R.id.aerr_restart);
restart.setOnClickListener(this);
@@ -166,11 +168,11 @@
}
private void setResult(int result) {
- synchronized (mService) {
+ synchronized (mProcLock) {
if (mProc != null) {
// Don't dismiss again since it leads to recursive call between dismiss and this
// method.
- mProc.getDialogController().clearCrashDialogs(false /* needDismiss */);
+ mProc.mErrorState.getDialogController().clearCrashDialogs(false /* needDismiss */);
}
}
mResult.set(result);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 7be54c2..e5a5cff 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -72,6 +72,7 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "AppErrors" : TAG_AM;
private final ActivityManagerService mService;
+ private final ActivityManagerGlobalLock mProcLock;
private final Context mContext;
private final PackageWatchdog mPackageWatchdog;
@@ -137,6 +138,7 @@
AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog) {
context.assertRuntimeOverlayThemable();
mService = service;
+ mProcLock = service.mProcLock;
mContext = context;
mPackageWatchdog = watchdog;
}
@@ -154,17 +156,48 @@
}
}
- void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) {
- synchronized (mBadProcessLock) {
- final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses;
- if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) {
- return;
+ @GuardedBy("mProcLock")
+ void dumpDebugLPr(ProtoOutputStream proto, long fieldId, String dumpPackage) {
+ final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses;
+ if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) {
+ return;
+ }
+
+ final long token = proto.start(fieldId);
+ final long now = SystemClock.uptimeMillis();
+ proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
+
+ if (!badProcesses.getMap().isEmpty()) {
+ final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap();
+ final int processCount = pmap.size();
+ for (int ip = 0; ip < processCount; ip++) {
+ final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
+ final String pname = pmap.keyAt(ip);
+ final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
+ final int uidCount = uids.size();
+
+ proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
+ for (int i = 0; i < uidCount; i++) {
+ final int puid = uids.keyAt(i);
+ final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
+ if (dumpPackage != null && (r == null
+ || !r.getPkgList().containsKey(dumpPackage))) {
+ continue;
+ }
+ final BadProcessInfo info = uids.valueAt(i);
+ final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
+ proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
+ proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
+ proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
+ proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
+ proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
+ proto.end(etoken);
+ }
+ proto.end(btoken);
}
+ }
- final long token = proto.start(fieldId);
- final long now = SystemClock.uptimeMillis();
- proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
-
+ synchronized (mBadProcessLock) {
if (!mProcessCrashTimes.getMap().isEmpty()) {
final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
final int procCount = pmap.size();
@@ -177,7 +210,7 @@
proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+ final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
if (dumpPackage != null
&& (r == null || !r.getPkgList().containsKey(dumpPackage))) {
continue;
@@ -190,44 +223,14 @@
}
proto.end(ctoken);
}
-
}
-
- if (!badProcesses.getMap().isEmpty()) {
- final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap();
- final int processCount = pmap.size();
- for (int ip = 0; ip < processCount; ip++) {
- final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
- final String pname = pmap.keyAt(ip);
- final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
- final int uidCount = uids.size();
-
- proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
- for (int i = 0; i < uidCount; i++) {
- final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.getProcessNames().get(pname, puid);
- if (dumpPackage != null && (r == null
- || !r.getPkgList().containsKey(dumpPackage))) {
- continue;
- }
- final BadProcessInfo info = uids.valueAt(i);
- final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
- proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
- proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
- proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
- proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
- proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
- proto.end(etoken);
- }
- proto.end(btoken);
- }
- }
-
- proto.end(token);
}
+
+ proto.end(token);
}
- boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) {
+ @GuardedBy("mProcLock")
+ boolean dumpLPr(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) {
final long now = SystemClock.uptimeMillis();
synchronized (mBadProcessLock) {
if (!mProcessCrashTimes.getMap().isEmpty()) {
@@ -240,9 +243,9 @@
final int uidCount = uids.size();
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.getProcessNames().get(pname, puid);
- if (dumpPackage != null && (r == null
- || !r.getPkgList().containsKey(dumpPackage))) {
+ final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
+ if (dumpPackage != null
+ && (r == null || !r.getPkgList().containsKey(dumpPackage))) {
continue;
}
if (!printed) {
@@ -271,7 +274,7 @@
final int uidCount = uids.size();
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+ final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
if (dumpPackage != null
&& (r == null || !r.getPkgList().containsKey(dumpPackage))) {
continue;
@@ -303,7 +306,7 @@
final int uidCount = uids.size();
for (int i = 0; i < uidCount; i++) {
final int puid = uids.keyAt(i);
- final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+ final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
if (dumpPackage != null && (r == null
|| !r.getPkgList().containsKey(dumpPackage))) {
continue;
@@ -330,14 +333,14 @@
for (int pos = 0; pos < info.stack.length(); pos++) {
if (info.stack.charAt(pos) == '\n') {
pw.print(" ");
- pw.write(info.stack, lastPos, pos-lastPos);
+ pw.write(info.stack, lastPos, pos - lastPos);
pw.println();
- lastPos = pos+1;
+ lastPos = pos + 1;
}
}
if (lastPos < info.stack.length()) {
pw.print(" ");
- pw.write(info.stack, lastPos, info.stack.length()-lastPos);
+ pw.write(info.stack, lastPos, info.stack.length() - lastPos);
pw.println();
}
}
@@ -437,32 +440,38 @@
}
}
+ @GuardedBy("mService")
void killAppAtUserRequestLocked(ProcessRecord app) {
- ProcessRecord.ErrorDialogController controller =
- app.getDialogController();
+ ErrorDialogController controller = app.mErrorState.getDialogController();
int reasonCode = ApplicationExitInfo.REASON_ANR;
int subReason = ApplicationExitInfo.SUBREASON_UNKNOWN;
- if (controller.hasDebugWaitingDialog()) {
- reasonCode = ApplicationExitInfo.REASON_OTHER;
- subReason = ApplicationExitInfo.SUBREASON_WAIT_FOR_DEBUGGER;
+ synchronized (mProcLock) {
+ if (controller.hasDebugWaitingDialog()) {
+ reasonCode = ApplicationExitInfo.REASON_OTHER;
+ subReason = ApplicationExitInfo.SUBREASON_WAIT_FOR_DEBUGGER;
+ }
+ controller.clearAllErrorDialogs();
+ killAppImmediateLSP(app, reasonCode, subReason,
+ "user-terminated", "user request after error");
}
-
- controller.clearAllErrorDialogs();
- killAppImmediateLocked(app, reasonCode, subReason,
- "user-terminated", "user request after error");
}
- private void killAppImmediateLocked(ProcessRecord app, int reasonCode, int subReason,
+ @GuardedBy({"mService", "mProcLock"})
+ private void killAppImmediateLSP(ProcessRecord app, int reasonCode, int subReason,
String reason, String killReason) {
- app.setCrashing(false);
- app.crashingReport = null;
- app.setNotResponding(false);
- app.notRespondingReport = null;
- if (app.pid > 0 && app.pid != MY_PID) {
- handleAppCrashLocked(app, reason,
- null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
- app.kill(killReason, reasonCode, subReason, true);
+ final ProcessErrorStateRecord errState = app.mErrorState;
+ errState.setCrashing(false);
+ errState.setCrashingReport(null);
+ errState.setNotResponding(false);
+ errState.setNotRespondingReport(null);
+ final int pid = errState.mApp.getPid();
+ if (pid > 0 && pid != MY_PID) {
+ synchronized (mBadProcessLock) {
+ handleAppCrashLSPB(app, reason,
+ null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
+ }
+ app.killLocked(killReason, reasonCode, subReason, true);
}
}
@@ -489,7 +498,7 @@
if (uid >= 0 && p.uid != uid) {
continue;
}
- if (p.pid == initialPid) {
+ if (p.getPid() == initialPid) {
proc = p;
break;
}
@@ -508,7 +517,7 @@
return;
}
- proc.scheduleCrash(message);
+ proc.scheduleCrashLocked(message);
if (force) {
// If the app is responsive, the scheduled crash will happen as expected
// and then the delayed summary kill will be a no-op.
@@ -516,9 +525,11 @@
mService.mHandler.postDelayed(
() -> {
synchronized (mService) {
- killAppImmediateLocked(p, ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_INVALID_STATE,
- "forced", "killed for invalid state");
+ synchronized (mProcLock) {
+ killAppImmediateLSP(p, ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE,
+ "forced", "killed for invalid state");
+ }
}
},
5000L);
@@ -627,52 +638,54 @@
if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
res = AppErrorDialog.FORCE_QUIT;
}
- if (res == AppErrorDialog.MUTE) {
- synchronized (mBadProcessLock) {
- stopReportingCrashesLBp(r);
- }
- }
- if (res == AppErrorDialog.RESTART) {
- synchronized (mService) {
- mService.mProcessList.removeProcessLocked(r, false, true,
- ApplicationExitInfo.REASON_CRASH, "crash");
- }
- if (taskId != INVALID_TASK_ID) {
- try {
- mService.startActivityFromRecents(taskId,
- ActivityOptions.makeBasic().toBundle());
- } catch (IllegalArgumentException e) {
- // Hmm...that didn't work. Task should either be in recents or associated
- // with a stack.
- Slog.e(TAG, "Could not restart taskId=" + taskId, e);
+ switch (res) {
+ case AppErrorDialog.MUTE:
+ synchronized (mBadProcessLock) {
+ stopReportingCrashesLBp(r);
}
- }
- }
- if (res == AppErrorDialog.FORCE_QUIT) {
- final long orig = Binder.clearCallingIdentity();
- try {
- // Kill it with fire!
- mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
- if (!r.isPersistent()) {
- synchronized (mService) {
- mService.mProcessList.removeProcessLocked(r, false, false,
- ApplicationExitInfo.REASON_CRASH, "crash");
+ break;
+ case AppErrorDialog.RESTART:
+ synchronized (mService) {
+ mService.mProcessList.removeProcessLocked(r, false, true,
+ ApplicationExitInfo.REASON_CRASH, "crash");
+ }
+ if (taskId != INVALID_TASK_ID) {
+ try {
+ mService.startActivityFromRecents(taskId,
+ ActivityOptions.makeBasic().toBundle());
+ } catch (IllegalArgumentException e) {
+ // Hmm...that didn't work. Task should either be in recents or associated
+ // with a stack.
+ Slog.e(TAG, "Could not restart taskId=" + taskId, e);
}
- mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
}
- } finally {
- Binder.restoreCallingIdentity(orig);
- }
- }
- if (res == AppErrorDialog.APP_INFO) {
- appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
- appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- }
- if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
- synchronized (mService) {
- appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
- }
+ break;
+ case AppErrorDialog.FORCE_QUIT:
+ final long orig = Binder.clearCallingIdentity();
+ try {
+ // Kill it with fire!
+ mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
+ if (!r.isPersistent()) {
+ synchronized (mService) {
+ mService.mProcessList.removeProcessLocked(r, false, false,
+ ApplicationExitInfo.REASON_CRASH, "crash");
+ }
+ mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(orig);
+ }
+ break;
+ case AppErrorDialog.APP_INFO:
+ appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
+ appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ break;
+ case AppErrorDialog.FORCE_QUIT_AND_REPORT:
+ synchronized (mProcLock) {
+ appErrorIntent = createAppErrorIntentLOSP(r, timeMillis, crashInfo);
+ }
+ break;
}
if (appErrorIntent != null) {
@@ -684,13 +697,14 @@
}
}
+ @GuardedBy("mService")
private boolean handleAppCrashInActivityController(ProcessRecord r,
ApplicationErrorReport.CrashInfo crashInfo,
String shortMsg, String longMsg,
String stackTrace, long timeMillis,
int callingPid, int callingUid) {
String name = r != null ? r.processName : null;
- int pid = r != null ? r.pid : callingPid;
+ int pid = r != null ? r.getPid() : callingPid;
int uid = r != null ? r.info.uid : callingUid;
return mService.mAtmInternal.handleAppCrashInActivityController(
@@ -703,7 +717,7 @@
Slog.w(TAG, "Force-killing crashed app " + name + " at watcher's request");
if (r != null) {
if (!makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, null)) {
- r.kill("crash", ApplicationExitInfo.REASON_CRASH, true);
+ r.killLocked("crash", ApplicationExitInfo.REASON_CRASH, true);
}
} else {
// Huh.
@@ -718,15 +732,22 @@
});
}
+ @GuardedBy("mService")
private boolean makeAppCrashingLocked(ProcessRecord app,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
- app.setCrashing(true);
- app.crashingReport = generateProcessError(app,
- ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
- app.startAppProblemLocked();
- app.getWindowProcessController().stopFreezingActivities();
- return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
- data);
+ synchronized (mProcLock) {
+ final ProcessErrorStateRecord errState = app.mErrorState;
+ errState.setCrashing(true);
+ errState.setCrashingReport(generateProcessError(app,
+ ActivityManager.ProcessErrorStateInfo.CRASHED,
+ null, shortMsg, longMsg, stackTrace));
+ errState.startAppProblemLSP();
+ app.getWindowProcessController().stopFreezingActivities();
+ synchronized (mBadProcessLock) {
+ return handleAppCrashLSPB(app, "force-crash" /*reason*/, shortMsg, longMsg,
+ stackTrace, data);
+ }
+ }
}
/**
@@ -748,7 +769,7 @@
report.condition = condition;
report.processName = app.processName;
- report.pid = app.pid;
+ report.pid = app.getPid();
report.uid = app.info.uid;
report.tag = activity;
report.shortMsg = shortMsg;
@@ -758,170 +779,157 @@
return report;
}
- Intent createAppErrorIntentLocked(ProcessRecord r,
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ Intent createAppErrorIntentLOSP(ProcessRecord r,
long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
- ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo);
+ ApplicationErrorReport report = createAppErrorReportLOSP(r, timeMillis, crashInfo);
if (report == null) {
return null;
}
Intent result = new Intent(Intent.ACTION_APP_ERROR);
- result.setComponent(r.errorReportReceiver);
+ result.setComponent(r.mErrorState.getErrorReportReceiver());
result.putExtra(Intent.EXTRA_BUG_REPORT, report);
result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return result;
}
- private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r,
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ private ApplicationErrorReport createAppErrorReportLOSP(ProcessRecord r,
long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
- if (r.errorReportReceiver == null) {
+ final ProcessErrorStateRecord errState = r.mErrorState;
+ if (errState.getErrorReportReceiver() == null) {
return null;
}
- if (!r.isCrashing() && !r.isNotResponding() && !r.forceCrashReport) {
+ if (!errState.isCrashing() && !errState.isNotResponding()
+ && !errState.isForceCrashReport()) {
return null;
}
ApplicationErrorReport report = new ApplicationErrorReport();
report.packageName = r.info.packageName;
- report.installerPackageName = r.errorReportReceiver.getPackageName();
+ report.installerPackageName = errState.getErrorReportReceiver().getPackageName();
report.processName = r.processName;
report.time = timeMillis;
report.systemApp = (r.info.flags & FLAG_SYSTEM) != 0;
- if (r.isCrashing() || r.forceCrashReport) {
+ if (errState.isCrashing() || errState.isForceCrashReport()) {
report.type = ApplicationErrorReport.TYPE_CRASH;
report.crashInfo = crashInfo;
- } else if (r.isNotResponding()) {
+ } else if (errState.isNotResponding()) {
report.type = ApplicationErrorReport.TYPE_ANR;
report.anrInfo = new ApplicationErrorReport.AnrInfo();
- report.anrInfo.activity = r.notRespondingReport.tag;
- report.anrInfo.cause = r.notRespondingReport.shortMsg;
- report.anrInfo.info = r.notRespondingReport.longMsg;
+ report.anrInfo.activity = errState.getNotRespondingReport().tag;
+ report.anrInfo.cause = errState.getNotRespondingReport().shortMsg;
+ report.anrInfo.info = errState.getNotRespondingReport().longMsg;
}
return report;
}
- private boolean handleAppCrashLocked(ProcessRecord app, String reason,
+ @GuardedBy({"mService", "mProcLock", "mBadProcessLock"})
+ private boolean handleAppCrashLSPB(ProcessRecord app, String reason,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
final long now = SystemClock.uptimeMillis();
final boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0,
mService.mUserController.getCurrentUserId()) != 0;
- final boolean procIsBoundForeground =
- (app.getCurProcState() == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-
Long crashTime;
Long crashTimePersistent;
- boolean tryAgain = false;
+ final String processName = app.processName;
+ final int uid = app.uid;
+ final int userId = app.userId;
+ final boolean isolated = app.isolated;
+ final boolean persistent = app.isPersistent();
+ final WindowProcessController proc = app.getWindowProcessController();
+ final ProcessErrorStateRecord errState = app.mErrorState;
- synchronized (mBadProcessLock) {
- if (!app.isolated) {
- crashTime = mProcessCrashTimes.get(app.processName, app.uid);
- crashTimePersistent = mProcessCrashTimesPersistent.get(app.processName, app.uid);
- } else {
- crashTime = crashTimePersistent = null;
- }
+ if (!app.isolated) {
+ crashTime = mProcessCrashTimes.get(processName, uid);
+ crashTimePersistent = mProcessCrashTimesPersistent.get(processName, uid);
+ } else {
+ crashTime = crashTimePersistent = null;
+ }
- // Bump up the crash count of any services currently running in the proc.
- for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) {
- // Any services running in the application need to be placed
- // back in the pending list.
- ServiceRecord sr = app.getRunningServiceAt(i);
- // If the service was restarted a while ago, then reset crash count,
- // else increment it.
- if (now > sr.restartTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
- sr.crashCount = 1;
- } else {
- sr.crashCount++;
- }
- // Allow restarting for started or bound foreground services that are crashing.
- // This includes wallpapers.
- if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
- && (sr.isForeground || procIsBoundForeground)) {
- tryAgain = true;
- }
- }
+ // Bump up the crash count of any services currently running in the proc.
+ boolean tryAgain = app.mServices.incServiceCrashCountLocked(now);
- final boolean quickCrash = crashTime != null
- && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
- if (quickCrash || isProcOverCrashLimitLBp(app, now)) {
- // The process either crashed again very quickly or has been crashing periodically
- // in the last few hours. If it was a bound foreground service, let's try to
- // restart again in a while, otherwise the process loses!
- Slog.w(TAG, "Process " + app.processName + " has crashed too many times, killing!"
- + " Reason: "
- + (quickCrash ? "crashed quickly" : "over process crash limit"));
- EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
- app.userId, app.processName, app.uid);
- mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
- if (!app.isPersistent()) {
- // We don't want to start this process again until the user
- // explicitly does so... but for persistent process, we really
- // need to keep it running. If a persistent process is actually
- // repeatedly crashing, then badness for everyone.
- EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
- app.processName);
- if (!app.isolated) {
- // XXX We don't have a way to mark isolated processes
- // as bad, since they don't have a persistent identity.
- markBadProcess(app.processName, app.uid,
- new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
- mProcessCrashTimes.remove(app.processName, app.uid);
- mProcessCrashCounts.remove(app.processName, app.uid);
- }
- app.bad = true;
- app.removed = true;
- // Don't let services in this process be restarted and potentially
- // annoy the user repeatedly. Unless it is persistent, since those
- // processes run critical code.
- mService.mProcessList.removeProcessLocked(app, false, tryAgain,
- ApplicationExitInfo.REASON_CRASH, "crash");
- mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
- if (!showBackground) {
- return false;
- }
+ final boolean quickCrash = crashTime != null
+ && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
+ if (quickCrash || isProcOverCrashLimitLBp(app, now)) {
+ // The process either crashed again very quickly or has been crashing periodically in
+ // the last few hours. If it was a bound foreground service, let's try to restart again
+ // in a while, otherwise the process loses!
+ Slog.w(TAG, "Process " + processName + " has crashed too many times, killing!"
+ + " Reason: " + (quickCrash ? "crashed quickly" : "over process crash limit"));
+ EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
+ userId, processName, uid);
+ mService.mAtmInternal.onHandleAppCrash(proc);
+ if (!persistent) {
+ // We don't want to start this process again until the user
+ // explicitly does so... but for persistent process, we really
+ // need to keep it running. If a persistent process is actually
+ // repeatedly crashing, then badness for everyone.
+ EventLog.writeEvent(EventLogTags.AM_PROC_BAD, userId, uid,
+ processName);
+ if (!isolated) {
+ // XXX We don't have a way to mark isolated processes
+ // as bad, since they don't have a persistent identity.
+ markBadProcess(processName, app.uid,
+ new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
+ mProcessCrashTimes.remove(processName, app.uid);
+ mProcessCrashCounts.remove(processName, app.uid);
}
+ errState.setBad(true);
+ app.setRemoved(true);
+ // Don't let services in this process be restarted and potentially
+ // annoy the user repeatedly. Unless it is persistent, since those
+ // processes run critical code.
+ mService.mProcessList.removeProcessLocked(app, false, tryAgain,
+ ApplicationExitInfo.REASON_CRASH, "crash");
mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
- } else {
- final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
- app.getWindowProcessController(), reason);
- if (data != null) {
- data.taskId = affectedTaskId;
- }
- if (data != null && crashTimePersistent != null
- && now < crashTimePersistent
- + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
- data.repeating = true;
+ if (!showBackground) {
+ return false;
}
}
-
- if (data != null && tryAgain) {
- data.isRestartableForService = true;
+ mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
+ } else {
+ final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
+ proc, reason);
+ if (data != null) {
+ data.taskId = affectedTaskId;
}
-
- // If the crashing process is what we consider to be the "home process" and it has been
- // replaced by a third-party app, clear the package preferred activities from packages
- // with a home activity running in the process to prevent a repeatedly crashing app
- // from blocking the user to manually clear the list.
- final WindowProcessController proc = app.getWindowProcessController();
- if (proc.isHomeProcess() && proc.hasActivities()
- && (app.info.flags & FLAG_SYSTEM) == 0) {
- proc.clearPackagePreferredForHomeActivities();
- }
-
- if (!app.isolated) {
- // XXX Can't keep track of crash times for isolated processes,
- // because they don't have a persistent identity.
- mProcessCrashTimes.put(app.processName, app.uid, now);
- mProcessCrashTimesPersistent.put(app.processName, app.uid, now);
- updateProcessCrashCountLBp(app.processName, app.uid, now);
+ if (data != null && crashTimePersistent != null
+ && now < crashTimePersistent + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
+ data.repeating = true;
}
}
- if (app.crashHandler != null) mService.mHandler.post(app.crashHandler);
+ if (data != null && tryAgain) {
+ data.isRestartableForService = true;
+ }
+
+ // If the crashing process is what we consider to be the "home process" and it has been
+ // replaced by a third-party app, clear the package preferred activities from packages
+ // with a home activity running in the process to prevent a repeatedly crashing app
+ // from blocking the user to manually clear the list.
+ if (proc.isHomeProcess() && proc.hasActivities() && (app.info.flags & FLAG_SYSTEM) == 0) {
+ proc.clearPackagePreferredForHomeActivities();
+ }
+
+ if (!isolated) {
+ // XXX Can't keep track of crash times for isolated processes,
+ // because they don't have a persistent identity.
+ mProcessCrashTimes.put(processName, uid, now);
+ mProcessCrashTimesPersistent.put(processName, uid, now);
+ updateProcessCrashCountLBp(processName, uid, now);
+ }
+
+ if (errState.getCrashHandler() != null) {
+ mService.mHandler.post(errState.getCrashHandler());
+ }
return true;
}
@@ -951,15 +959,16 @@
mService.mUserController.getCurrentUserId()) != 0;
final int userId;
- synchronized (mService) {
+ synchronized (mProcLock) {
final ProcessRecord proc = data.proc;
final AppErrorResult res = data.result;
if (proc == null) {
Slog.e(TAG, "handleShowAppErrorUi: proc is null");
return;
}
+ final ProcessErrorStateRecord errState = proc.mErrorState;
userId = proc.userId;
- if (proc.getDialogController().hasCrashDialogs()) {
+ if (errState.getDialogController().hasCrashDialogs()) {
Slog.e(TAG, "App already has crash dialog: " + proc);
if (res != null) {
res.set(AppErrorDialog.ALREADY_SHOWING);
@@ -968,7 +977,7 @@
}
boolean isBackground = (UserHandle.getAppId(proc.uid)
>= Process.FIRST_APPLICATION_UID
- && proc.pid != MY_PID);
+ && proc.getPid() != MY_PID);
for (int profileId : mService.mUserController.getCurrentProfileIds()) {
isBackground &= (userId != profileId);
}
@@ -1001,7 +1010,7 @@
if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
&& !crashSilenced && !shouldThottle
&& (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
- proc.getDialogController().showCrashDialogs(data);
+ errState.getDialogController().showCrashDialogs(data);
if (!proc.isolated) {
mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
}
@@ -1026,17 +1035,19 @@
void handleShowAnrUi(Message msg) {
List<VersionedPackage> packageList = null;
- synchronized (mService) {
- AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
- final ProcessRecord proc = data.proc;
- if (proc == null) {
- Slog.e(TAG, "handleShowAnrUi: proc is null");
- return;
- }
+ boolean doKill = false;
+ AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
+ final ProcessRecord proc = data.proc;
+ if (proc == null) {
+ Slog.e(TAG, "handleShowAnrUi: proc is null");
+ return;
+ }
+ synchronized (mProcLock) {
+ final ProcessErrorStateRecord errState = proc.mErrorState;
if (!proc.isPersistent()) {
packageList = proc.getPackageListWithVersionCode();
}
- if (proc.getDialogController().hasAnrDialogs()) {
+ if (errState.getDialogController().hasAnrDialogs()) {
Slog.e(TAG, "App already has anr dialog: " + proc);
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
AppNotRespondingDialog.ALREADY_SHOWING);
@@ -1047,14 +1058,17 @@
Settings.Secure.ANR_SHOW_BACKGROUND, 0,
mService.mUserController.getCurrentUserId()) != 0;
if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) {
- proc.getDialogController().showAnrDialogs(data);
+ errState.getDialogController().showAnrDialogs(data);
} else {
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
AppNotRespondingDialog.CANT_SHOW);
// Just kill the app if there is no dialog to be shown.
- mService.killAppAtUsersRequest(proc);
+ doKill = true;
}
}
+ if (doKill) {
+ mService.killAppAtUsersRequest(proc);
+ }
// Notify PackageWatchdog without the lock held
if (packageList != null) {
mPackageWatchdog.onPackageFailure(packageList,
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 48aa8be..17be210 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -920,20 +920,20 @@
info = new ApplicationExitInfo();
}
- synchronized (mService) {
- final int definingUid = app.hostingRecord != null
- ? app.hostingRecord.getDefiningUid() : 0;
- info.setPid(app.pid);
+ synchronized (mService.mProcLock) {
+ final int definingUid = app.getHostingRecord() != null
+ ? app.getHostingRecord().getDefiningUid() : 0;
+ info.setPid(app.getPid());
info.setRealUid(app.uid);
info.setPackageUid(app.info.uid);
info.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid);
info.setProcessName(app.processName);
- info.setConnectionGroup(app.connectionGroup);
+ info.setConnectionGroup(app.mServices.getConnectionGroup());
info.setPackageName(app.info.packageName);
info.setPackageList(app.getPackageList());
info.setReason(ApplicationExitInfo.REASON_UNKNOWN);
info.setStatus(0);
- info.setImportance(procStateToImportance(app.setProcState));
+ info.setImportance(procStateToImportance(app.mState.getSetProcState()));
info.setPss(app.mProfile.getLastPss());
info.setRss(app.mProfile.getLastRss());
info.setTimestamp(System.currentTimeMillis());
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 96e6f6e..77d2898 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -63,7 +63,7 @@
? data.aInfo.loadLabel(context.getPackageManager())
: null;
CharSequence name2 = null;
- if ((mProc.getPkgList().size() == 1)
+ if (mProc.getPkgList().size() == 1
&& (name2 = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
if (name1 != null) {
resid = com.android.internal.R.string.anr_activity_application;
@@ -108,7 +108,7 @@
final TextView report = findViewById(com.android.internal.R.id.aerr_report);
report.setOnClickListener(this);
- final boolean hasReceiver = mProc.errorReportReceiver != null;
+ final boolean hasReceiver = mProc.mErrorState.getErrorReportReceiver() != null;
report.setVisibility(hasReceiver ? View.VISIBLE : View.GONE);
final TextView close = findViewById(com.android.internal.R.id.aerr_close);
close.setOnClickListener(this);
@@ -152,15 +152,18 @@
// Continue waiting for the application.
synchronized (mService) {
ProcessRecord app = mProc;
+ final ProcessErrorStateRecord errState = app.mErrorState;
if (msg.what == WAIT_AND_REPORT) {
- appErrorIntent = mService.mAppErrors.createAppErrorIntentLocked(app,
+ appErrorIntent = mService.mAppErrors.createAppErrorIntentLOSP(app,
System.currentTimeMillis(), null);
}
- app.setNotResponding(false);
- app.notRespondingReport = null;
- app.getDialogController().clearAnrDialogs();
+ synchronized (mService.mProcLock) {
+ errState.setNotResponding(false);
+ errState.setNotRespondingReport(null);
+ errState.getDialogController().clearAnrDialogs();
+ }
mService.mServices.scheduleServiceTimeoutLocked(app);
}
break;
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 3df058c..8f11a5a 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -207,7 +207,6 @@
*/
private volatile boolean mTestPssMode = false;
- @GuardedBy("mService")
private final LowMemDetector mLowMemDetector;
/**
@@ -239,13 +238,13 @@
/**
* Total time spent with RAM that has been added in the past since the last idle time.
*/
- @GuardedBy("mService")
+ @GuardedBy("mProcLock")
private long mLowRamTimeSinceLastIdle = 0;
/**
* If RAM is currently low, when that horrible situation started.
*/
- @GuardedBy("mService")
+ @GuardedBy("mProcLock")
private long mLowRamStartTime = 0;
/**
@@ -331,6 +330,8 @@
*/
final Object mProfilerLock = new Object();
+ final ActivityManagerGlobalLock mProcLock;
+
/**
* Observe DeviceConfig changes to the PSS calculation interval
*/
@@ -852,8 +853,8 @@
/**
* Schedule PSS collection of all processes.
*/
- @GuardedBy("mService")
- void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
+ @GuardedBy("mProcLock")
+ void requestPssAllProcsLPr(long now, boolean always, boolean memLowered) {
synchronized (mProfilerLock) {
if (!always) {
if (now < (mLastFullPssTime
@@ -870,10 +871,9 @@
for (int i = mPendingPssProfiles.size() - 1; i >= 0; i--) {
mPendingPssProfiles.get(i).abortNextPssTime();
}
- mPendingPssProfiles.ensureCapacity(mService.mProcessList.getLruSizeLocked());
+ mPendingPssProfiles.ensureCapacity(mService.mProcessList.getLruSizeLOSP());
mPendingPssProfiles.clear();
- for (int i = mService.mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
- ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ mService.mProcessList.forEachLruProcessesLOSP(false, app -> {
final ProcessProfileRecord profile = app.mProfile;
if (profile.getThread() == null
|| profile.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
@@ -889,7 +889,7 @@
updateNextPssTimeLPf(profile.getSetProcState(), profile, now, true);
mPendingPssProfiles.add(profile);
}
- }
+ });
if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) {
mBgHandler.sendEmptyMessage(BgHandler.COLLECT_PSS_BG_MSG);
}
@@ -897,12 +897,12 @@
}
void setTestPssMode(boolean enabled) {
- synchronized (mService) {
+ synchronized (mProcLock) {
mTestPssMode = enabled;
if (enabled) {
// Whenever we enable the mode, we want to take a snapshot all of current
// process mem use.
- requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, true);
+ requestPssAllProcsLPr(SystemClock.uptimeMillis(), true, true);
}
}
}
@@ -921,8 +921,8 @@
return mLastMemoryLevel <= ADJ_MEM_FACTOR_NORMAL;
}
- @GuardedBy("mService")
- void updateLowRamTimestampLocked(long now) {
+ @GuardedBy("mProcLock")
+ void updateLowRamTimestampLPr(long now) {
mLowRamTimeSinceLastIdle = 0;
if (mLowRamStartTime != 0) {
mLowRamStartTime = now;
@@ -939,9 +939,8 @@
mMemFactorOverride = factor;
}
- @GuardedBy("mService")
- boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
- final int numOfLru = mService.mProcessList.getLruSizeLocked();
+ @GuardedBy({"mService", "mProcLock"})
+ boolean updateLowMemStateLSP(int numCached, int numEmpty, int numTrimming) {
final long now = SystemClock.uptimeMillis();
int memFactor;
if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
@@ -973,7 +972,7 @@
if (DEBUG_OOM_ADJ) {
Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor + " override=" + mMemFactorOverride
+ " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
- + " numProcs=" + mService.mProcessList.getLruSizeLocked()
+ + " numProcs=" + mService.mProcessList.getLruSizeLOSP()
+ " last=" + mLastNumProcesses);
}
boolean override;
@@ -982,7 +981,7 @@
}
if (memFactor > mLastMemoryLevel) {
if (!override && (!mAllowLowerMemLevel
- || mService.mProcessList.getLruSizeLocked() >= mLastNumProcesses)) {
+ || mService.mProcessList.getLruSizeLOSP() >= mLastNumProcesses)) {
memFactor = mLastMemoryLevel;
if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
}
@@ -992,7 +991,7 @@
FrameworkStatsLog.write(FrameworkStatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
}
mLastMemoryLevel = memFactor;
- mLastNumProcesses = mService.mProcessList.getLruSizeLocked();
+ mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();
boolean allChanged;
int trackerMemFactor;
synchronized (mService.mProcessStats.mLock) {
@@ -1004,7 +1003,6 @@
if (mLowRamStartTime == 0) {
mLowRamStartTime = now;
}
- int step = 0;
int fgTrimLevel;
switch (memFactor) {
case ADJ_MEM_FACTOR_CRITICAL:
@@ -1022,126 +1020,129 @@
if (mHasHomeProcess) minFactor++;
if (mHasPreviousProcess) minFactor++;
if (factor < minFactor) factor = minFactor;
- int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
- for (int i = 0; i < numOfLru; i++) {
- final ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ final int actualFactor = factor;
+ final int[] step = {0};
+ final int[] curLevel = {ComponentCallbacks2.TRIM_MEMORY_COMPLETE};
+ mService.mProcessList.forEachLruProcessesLOSP(true, app -> {
final ProcessProfileRecord profile = app.mProfile;
final int trimMemoryLevel = profile.getTrimMemoryLevel();
- if (allChanged || app.procStateChanged) {
- mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
- app.procStateChanged = false;
+ final ProcessStateRecord state = app.mState;
+ final int curProcState = state.getCurProcState();
+ IApplicationThread thread;
+ if (allChanged || state.hasProcStateChanged()) {
+ mService.setProcessTrackerStateLOSP(app, trackerMemFactor, now);
+ state.setProcStateChanged(false);
}
- if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
- && !app.killedByAm) {
- if (trimMemoryLevel < curLevel && app.thread != null) {
+ if (curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.isKilledByAm()) {
+ if (trimMemoryLevel < curLevel[0] && (thread = app.getThread()) != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ,
"Trimming memory of " + app.processName
- + " to " + curLevel);
+ + " to " + curLevel[0]);
}
- app.thread.scheduleTrimMemory(curLevel);
+ thread.scheduleTrimMemory(curLevel[0]);
} catch (RemoteException e) {
}
}
- profile.setTrimMemoryLevel(curLevel);
- step++;
- if (step >= factor) {
- step = 0;
- switch (curLevel) {
+ profile.setTrimMemoryLevel(curLevel[0]);
+ step[0]++;
+ if (step[0] >= actualFactor) {
+ step[0] = 0;
+ switch (curLevel[0]) {
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
+ curLevel[0] = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
break;
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+ curLevel[0] = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
break;
}
}
- } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
- && !app.killedByAm) {
+ } else if (curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+ && !app.isKilledByAm()) {
if (trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
- && app.thread != null) {
+ && (thread = app.getThread()) != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ,
"Trimming memory of heavy-weight " + app.processName
+ " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
}
- app.thread.scheduleTrimMemory(
+ thread.scheduleTrimMemory(
ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
} catch (RemoteException e) {
}
}
profile.setTrimMemoryLevel(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
} else {
- if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && profile.hasPendingUiClean()) {
+ if ((curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ || state.isSystemNoUi()) && profile.hasPendingUiClean()) {
// If this application is now in the background and it
// had done UI, then give it the special trim level to
// have it free UI resources.
final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
- if (trimMemoryLevel < level && app.thread != null) {
+ if (trimMemoryLevel < level && (thread = app.getThread()) != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
+ app.processName + " to " + level);
}
- app.thread.scheduleTrimMemory(level);
+ thread.scheduleTrimMemory(level);
} catch (RemoteException e) {
}
}
profile.setPendingUiClean(false);
}
- if (trimMemoryLevel < fgTrimLevel && app.thread != null) {
+ if (trimMemoryLevel < fgTrimLevel && (thread = app.getThread()) != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName
+ " to " + fgTrimLevel);
}
- app.thread.scheduleTrimMemory(fgTrimLevel);
+ thread.scheduleTrimMemory(fgTrimLevel);
} catch (RemoteException e) {
}
}
profile.setTrimMemoryLevel(fgTrimLevel);
}
- }
+ });
} else {
if (mLowRamStartTime != 0) {
mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
mLowRamStartTime = 0;
}
- for (int i = 0; i < numOfLru; i++) {
- final ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
+ mService.mProcessList.forEachLruProcessesLOSP(true, app -> {
final ProcessProfileRecord profile = app.mProfile;
- if (allChanged || app.procStateChanged) {
- mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
- app.procStateChanged = false;
+ final IApplicationThread thread;
+ final ProcessStateRecord state = app.mState;
+ if (allChanged || state.hasProcStateChanged()) {
+ mService.setProcessTrackerStateLOSP(app, trackerMemFactor, now);
+ state.setProcStateChanged(false);
}
- if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && profile.hasPendingUiClean()) {
+ if ((state.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ || state.isSystemNoUi()) && profile.hasPendingUiClean()) {
if (profile.getTrimMemoryLevel() < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
- && app.thread != null) {
+ && (thread = app.getThread()) != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ,
"Trimming memory of ui hidden " + app.processName
+ " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
}
- app.thread.scheduleTrimMemory(
- ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+ thread.scheduleTrimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
} catch (RemoteException e) {
}
}
profile.setPendingUiClean(false);
}
profile.setTrimMemoryLevel(0);
- }
+ });
}
return allChanged;
}
- @GuardedBy("mService")
- long getLowRamTimeSinceIdleLocked(long now) {
+ @GuardedBy("mProcLock")
+ long getLowRamTimeSinceIdleLPr(long now) {
return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now - mLowRamStartTime) : 0);
}
@@ -1262,7 +1263,7 @@
// If there are no longer any background processes running,
// and the app that died was not running instrumentation,
// then tell everyone we are now low on memory.
- if (!mService.mProcessList.haveBackgroundProcessLocked()) {
+ if (!mService.mProcessList.haveBackgroundProcessLOSP()) {
boolean doReport = Build.IS_DEBUGGABLE;
final long now = SystemClock.uptimeMillis();
if (doReport) {
@@ -1272,19 +1273,19 @@
mLastMemUsageReportTime = now;
}
}
- final int lruSize = mService.mProcessList.getLruSizeLocked();
+ final int lruSize = mService.mProcessList.getLruSizeLOSP();
final ArrayList<ProcessMemInfo> memInfos = doReport
? new ArrayList<ProcessMemInfo>(lruSize) : null;
EventLogTags.writeAmLowMemory(lruSize);
- for (int i = lruSize - 1; i >= 0; i--) {
- ProcessRecord rec = mService.mProcessList.mLruProcesses.get(i);
- if (rec == dyingProc || rec.thread == null) {
+ mService.mProcessList.forEachLruProcessesLOSP(false, rec -> {
+ if (rec == dyingProc || rec.getThread() == null) {
return;
}
+ final ProcessStateRecord state = rec.mState;
if (memInfos != null) {
- memInfos.add(new ProcessMemInfo(rec.processName, rec.pid,
- rec.setAdj, rec.setProcState,
- rec.adjType, rec.makeAdjReason()));
+ memInfos.add(new ProcessMemInfo(rec.processName, rec.getPid(),
+ state.getSetAdj(), state.getSetProcState(),
+ state.getAdjType(), state.makeAdjReason()));
}
final ProcessProfileRecord profile = rec.mProfile;
if ((profile.getLastLowMemory() + mService.mConstants.GC_MIN_INTERVAL) <= now) {
@@ -1292,7 +1293,7 @@
// state for a GC request. Make sure to do
// heavy/important/visible/foreground processes first.
synchronized (mProfilerLock) {
- if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+ if (state.getSetAdj() <= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
profile.setLastRequestedGc(0);
} else {
profile.setLastRequestedGc(profile.getLastLowMemory());
@@ -1303,7 +1304,7 @@
addProcessToGcListLPf(rec);
}
}
- }
+ });
if (doReport) {
Message msg = mService.mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos);
mService.mHandler.sendMessage(msg);
@@ -1554,7 +1555,9 @@
PrintWriter catPw = new FastPrintWriter(catSw, false, 256);
String[] emptyArgs = new String[] { };
catPw.println();
- mService.mProcessList.dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null, -1);
+ synchronized (mProcLock) {
+ mService.mProcessList.dumpProcessesLSP(null, catPw, emptyArgs, 0, false, null, -1);
+ }
catPw.println();
mService.mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0,
false, null).dumpLocked();
@@ -1575,7 +1578,7 @@
}
}
- @GuardedBy("mService")
+ @GuardedBy("mProfilerLock")
private void stopProfilerLPf(ProcessRecord proc, int profileType) {
if (proc == null || proc == mProfileData.getProfileProc()) {
proc = mProfileData.getProfileProc();
@@ -1644,7 +1647,7 @@
}
mProfileData.getProfilerInfo().profileFd = null;
- if (proc.pid == mService.MY_PID) {
+ if (proc.getPid() == mService.MY_PID) {
// When profiling the system server itself, avoid closing the file
// descriptor, as profilerControl will not create a copy.
// Note: it is also not correct to just set profileFd to null, as the
@@ -1930,6 +1933,7 @@
AppProfiler(ActivityManagerService service, Looper bgLooper, LowMemDetector detector) {
mService = service;
+ mProcLock = service.mProcLock;
mBgHandler = new BgHandler(bgLooper);
mLowMemDetector = detector;
mProcessCpuThread = new ProcessCpuThread("CpuTracker");
@@ -2027,19 +2031,21 @@
i >= 0 && app.getActiveInstrumentation() == null; i--) {
ActiveInstrumentation aInstr = mService.mActiveInstrumentation.get(i);
if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
- if (aInstr.mTargetProcesses.length == 0) {
- // This is the wildcard mode, where every process brought up for
- // the target instrumentation should be included.
- if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
- app.setActiveInstrumentation(aInstr);
- aInstr.mRunningProcesses.add(app);
- }
- } else {
- for (String proc : aInstr.mTargetProcesses) {
- if (proc.equals(app.processName)) {
+ synchronized (mProcLock) {
+ if (aInstr.mTargetProcesses.length == 0) {
+ // This is the wildcard mode, where every process brought up for
+ // the target instrumentation should be included.
+ if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
app.setActiveInstrumentation(aInstr);
aInstr.mRunningProcesses.add(app);
- break;
+ }
+ } else {
+ for (String proc : aInstr.mTargetProcesses) {
+ if (proc.equals(app.processName)) {
+ app.setActiveInstrumentation(aInstr);
+ aInstr.mRunningProcesses.add(app);
+ break;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 170c34d..c6947c2d 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -207,12 +207,13 @@
if (mPowerStatsInternal != null) {
populateEnergyConsumerSubsystemMapsLocked();
final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData();
- final boolean[] supportedBuckets = getSupportedEnergyBuckets(
- initialMeasuredEnergies);
mMeasuredEnergySnapshot = initialMeasuredEnergies == null
? null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
+ final boolean[] supportedStdBuckets
+ = getSupportedEnergyBuckets(initialMeasuredEnergies);
+ final int numCustomBuckets = 0; // TODO: Get this from initialMeasuredEnergies
synchronized (mStats) {
- mStats.initMeasuredEnergyStatsLocked(supportedBuckets);
+ mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, numCustomBuckets);
}
}
}
@@ -740,15 +741,16 @@
/**
* Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to
- * their corresponding {@link MeasuredEnergyStats.EnergyBucket}s.
+ * their corresponding {@link MeasuredEnergyStats.StandardEnergyBucket}s.
+ * Does not include custom energy buckets (which are always, by definition, supported).
*
- * @return array with true for index i if energy bucket i is supported.
+ * @return array with true for index i if standard energy bucket i is supported.
*/
private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
if (energyArray == null) {
return null;
}
- final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
+ final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS];
final int size = energyArray.size();
for (int energyIdx = 0; energyIdx < size; energyIdx++) {
switch (energyArray.getSubsystem(energyIdx)) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c287240..773e313 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -21,6 +21,10 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.State;
+import android.hardware.power.stats.StateResidency;
+import android.hardware.power.stats.StateResidencyResult;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkCapabilities;
import android.os.BatteryStats;
@@ -51,6 +55,7 @@
import android.os.health.HealthStatsParceler;
import android.os.health.HealthStatsWriter;
import android.os.health.UidHealthStats;
+import android.power.PowerStatsInternal;
import android.provider.Settings;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
@@ -87,10 +92,13 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
/**
* All information we are collecting about things that can happen that impact
@@ -112,23 +120,23 @@
private final BatteryExternalStatsWorker mWorker;
private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
- private native void getLowPowerStats(RpmStats rpmStats);
- private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
- private native int getSubsystemLowPowerStats(ByteBuffer outBuffer);
private native void getRailEnergyPowerStats(RailStats railStats);
private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8
.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith("?");
- private ByteBuffer mUtf8BufferStat = ByteBuffer.allocateDirect(MAX_LOW_POWER_STATS_SIZE);
- private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
+ private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Object mLock = new Object();
+ private PowerStatsInternal mPowerStatsInternal = null;
+ private Map<Integer, String> mEntityNames = new HashMap();
+ private Map<Integer, Map<Integer, String>> mStateNames = new HashMap();
+
@GuardedBy("mStats")
private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
@GuardedBy("mStats")
@@ -163,16 +171,57 @@
}
};
+ private void populatePowerEntityMaps() {
+ if (mPowerStatsInternal == null) {
+ // PowerStatsInternal unavailable, don't bother populating maps.
+ mEntityNames = null;
+ mStateNames = null;
+ return;
+ }
+
+ PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
+ if (entities == null) {
+ return;
+ }
+
+ for (int i = 0; i < entities.length; i++) {
+ final PowerEntity entity = entities[i];
+ Map<Integer, String> states = new HashMap();
+ for (int j = 0; j < entity.states.length; j++) {
+ final State state = entity.states[j];
+ states.put(state.id, state.name);
+ }
+
+ mEntityNames.put(entity.id, entity.name);
+ mStateNames.put(entity.id, states);
+ }
+ }
+
/**
* Replaces the information in the given rpmStats with up-to-date information.
*/
@Override
public void fillLowPowerStats(RpmStats rpmStats) {
- if (DBG) Slog.d(TAG, "begin getLowPowerStats");
+ final StateResidencyResult[] results;
try {
- getLowPowerStats(rpmStats);
- } finally {
- if (DBG) Slog.d(TAG, "end getLowPowerStats");
+ results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
+ .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to getStateResidencyAsync", e);
+ return;
+ }
+
+ for (int i = 0; i < results.length; i++) {
+ final StateResidencyResult result = results[i];
+ RpmStats.PowerStateSubsystem subsystem =
+ rpmStats.getSubsystem(mEntityNames.get(result.id));
+
+ for (int j = 0; j < result.stateResidencyData.length; j++) {
+ final StateResidency stateResidency = result.stateResidencyData[j];
+ subsystem.putState(mStateNames.get(result.id).get(stateResidency.id),
+ stateResidency.totalTimeInStateMs,
+ (int) stateResidency.totalStateEntryCount);
+ }
}
}
@@ -187,47 +236,46 @@
}
@Override
- public String getPlatformLowPowerStats() {
- if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats");
- try {
- mUtf8BufferStat.clear();
- mUtf16BufferStat.clear();
- mDecoderStat.reset();
- int bytesWritten = getPlatformLowPowerStats(mUtf8BufferStat);
- if (bytesWritten < 0) {
- return null;
- } else if (bytesWritten == 0) {
- return "Empty";
- }
- mUtf8BufferStat.limit(bytesWritten);
- mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true);
- mUtf16BufferStat.flip();
- return mUtf16BufferStat.toString();
- } finally {
- if (DBG) Slog.d(TAG, "end getPlatformLowPowerStats");
- }
- }
-
- @Override
public String getSubsystemLowPowerStats() {
- if (DBG) Slog.d(TAG, "begin getSubsystemLowPowerStats");
+ final StateResidencyResult[] results;
try {
- mUtf8BufferStat.clear();
- mUtf16BufferStat.clear();
- mDecoderStat.reset();
- int bytesWritten = getSubsystemLowPowerStats(mUtf8BufferStat);
- if (bytesWritten < 0) {
- return null;
- } else if (bytesWritten == 0) {
- return "Empty";
- }
- mUtf8BufferStat.limit(bytesWritten);
- mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true);
- mUtf16BufferStat.flip();
- return mUtf16BufferStat.toString();
- } finally {
- if (DBG) Slog.d(TAG, "end getSubsystemLowPowerStats");
+ results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
+ .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to getStateResidencyAsync", e);
+ return "Empty";
}
+
+ if (results.length == 0) return "Empty";
+
+ int charsLeft = MAX_LOW_POWER_STATS_SIZE;
+ StringBuilder builder = new StringBuilder("SubsystemPowerState");
+ for (int i = 0; i < results.length; i++) {
+ final StateResidencyResult result = results[i];
+ StringBuilder subsystemBuilder = new StringBuilder();
+ subsystemBuilder.append(" subsystem_" + i);
+ subsystemBuilder.append(" name=" + mEntityNames.get(result.id));
+
+ for (int j = 0; j < result.stateResidencyData.length; j++) {
+ final StateResidency stateResidency = result.stateResidencyData[j];
+ subsystemBuilder.append(" state_" + j);
+ subsystemBuilder.append(" name=" + mStateNames.get(result.id).get(
+ stateResidency.id));
+ subsystemBuilder.append(" time=" + stateResidency.totalTimeInStateMs);
+ subsystemBuilder.append(" count=" + stateResidency.totalStateEntryCount);
+ subsystemBuilder.append(" last entry=" + stateResidency.lastEntryTimestampMs);
+ }
+
+ if (subsystemBuilder.length() <= charsLeft) {
+ charsLeft -= subsystemBuilder.length();
+ builder.append(subsystemBuilder);
+ } else {
+ Slog.e(TAG, "getSubsystemLowPowerStats: buffer not enough");
+ break;
+ }
+ }
+
+ return builder.toString();
}
BatteryStatsService(Context context, File systemDir, Handler handler) {
@@ -274,6 +322,11 @@
} catch (RemoteException e) {
Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
}
+ mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
+ if (mPowerStatsInternal != null) {
+ populatePowerEntityMaps();
+ }
+
Watchdog.getInstance().addMonitor(this);
}
@@ -561,10 +614,10 @@
* Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
* and per-UID basis.
*/
- public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+ public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
mContext.enforceCallingPermission(
android.Manifest.permission.BATTERY_STATS, null);
- return mBatteryUsageStatsProvider.getBatteryUsageStats(query);
+ return mBatteryUsageStatsProvider.getBatteryUsageStats(queries);
}
public byte[] getStatistics() {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 4de1218..7e65434 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -26,6 +26,7 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
+import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -42,6 +43,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -221,7 +223,7 @@
}
public boolean isPendingBroadcastProcessLocked(int pid) {
- return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
+ return mPendingBroadcast != null && mPendingBroadcast.curApp.getPid() == pid;
}
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
@@ -285,19 +287,21 @@
ProcessRecord app) throws RemoteException {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " for app " + app);
- if (app.thread == null) {
+ final IApplicationThread thread = app.getThread();
+ if (thread == null) {
throw new RemoteException();
}
- if (app.inFullBackup) {
+ if (app.isInFullBackup()) {
skipReceiverLocked(r);
return;
}
- r.receiver = app.thread.asBinder();
+ r.receiver = thread.asBinder();
r.curApp = app;
- app.curReceivers.add(r);
- app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
- mService.mProcessList.updateLruProcessLocked(app, false, null);
+ final ProcessReceiverRecord prr = app.mReceivers;
+ prr.addCurReceiver(r);
+ app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
+ mService.updateLruProcessLocked(app, false, null);
// Make sure the oom adj score is updated before delivering the broadcast.
// Force an update, even if there are other pending requests, overall it still saves time,
// because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)).
@@ -314,10 +318,10 @@
+ ": " + r);
mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
- app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
+ thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
- app.getReportedProcState());
+ app.mState.getReportedProcState());
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
@@ -327,7 +331,7 @@
"Process cur broadcast " + r + ": NOT STARTED!");
r.receiver = null;
r.curApp = null;
- app.curReceivers.remove(r);
+ prr.removeCurReceiver(r);
}
}
}
@@ -335,7 +339,7 @@
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
final BroadcastRecord br = mPendingBroadcast;
- if (br != null && br.curApp.pid > 0 && br.curApp.pid == app.pid) {
+ if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
if (br.curApp != app) {
Slog.e(TAG, "App mismatch when sending pending broadcast to "
+ app.processName + ", intended target is " + br.curApp.processName);
@@ -362,7 +366,7 @@
public void skipPendingBroadcastLocked(int pid) {
final BroadcastRecord br = mPendingBroadcast;
- if (br != null && br.curApp.pid == pid) {
+ if (br != null && br.curApp.getPid() == pid) {
br.state = BroadcastRecord.IDLE;
br.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
@@ -502,8 +506,8 @@
r.receiver = null;
r.intent.setComponent(null);
- if (r.curApp != null && r.curApp.curReceivers.contains(r)) {
- r.curApp.curReceivers.remove(r);
+ if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
+ r.curApp.mReceivers.removeCurReceiver(r);
mService.enqueueOomAdjTargetLocked(r.curApp);
}
if (r.curFilter != null) {
@@ -577,12 +581,14 @@
throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
- if (app.thread != null) {
+ final IApplicationThread thread = app.getThread();
+ if (thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try {
- app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
+ thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
+ data, extras, ordered, sticky, sendingUser,
+ app.mState.getReportedProcState());
// TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
// DeadObjectException when the process isn't actually dead.
//} catch (DeadObjectException ex) {
@@ -592,8 +598,8 @@
// Failed to call into the process. It's either dying or wedged. Kill it gently.
synchronized (mService) {
Slog.w(TAG, "Can't deliver broadcast to " + app.processName
- + " (pid " + app.pid + "). Crashing it.");
- app.scheduleCrash("can't deliver broadcast");
+ + " (pid " + app.getPid() + "). Crashing it.");
+ app.scheduleCrashLocked("can't deliver broadcast");
}
throw ex;
}
@@ -723,8 +729,8 @@
skip = true;
}
- if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed
- || filter.receiverList.app.isCrashing())) {
+ if (!skip && (filter.receiverList.app == null || filter.receiverList.app.isKilled()
+ || filter.receiverList.app.mErrorState.isCrashing())) {
Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
+ " to " + filter.receiverList + ": process gone or crashing");
skip = true;
@@ -793,7 +799,7 @@
// things that directly call the IActivityManager API, which
// are already core system stuff so don't matter for this.
r.curApp = filter.receiverList.app;
- filter.receiverList.app.curReceivers.add(r);
+ filter.receiverList.app.mReceivers.addCurReceiver(r);
mService.enqueueOomAdjTargetLocked(r.curApp);
mService.updateOomAdjPendingTargetsLocked(
OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
@@ -805,7 +811,7 @@
try {
if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
"Delivering to " + filter + " : " + r);
- if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
+ if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
// Skip delivery if full backup in progress
// If it's an ordered broadcast, we need to continue to the next receiver.
if (ordered) {
@@ -832,7 +838,7 @@
if (filter.receiverList.app != null) {
filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r);
if (ordered) {
- filter.receiverList.app.curReceivers.remove(r);
+ filter.receiverList.app.mReceivers.removeCurReceiver(r);
// Something wrong, its oom adj could be downgraded, but not in a hurry.
mService.enqueueOomAdjTargetLocked(r.curApp);
}
@@ -855,8 +861,8 @@
}
final boolean callerForeground = receiverRecord.callerApp != null
- ? receiverRecord.callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND
- : true;
+ ? receiverRecord.callerApp.mState.getSetSchedGroup()
+ != ProcessList.SCHED_GROUP_BACKGROUND : true;
// Show a permission review UI only for explicit broadcast from a foreground app
if (callerForeground && receiverRecord.intent.getComponent() != null) {
@@ -897,7 +903,7 @@
}
final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r,
- @BroadcastOptions.TempAllowListType int type) {
+ @TempAllowListType int type) {
if (duration > Integer.MAX_VALUE) {
duration = Integer.MAX_VALUE;
}
@@ -1017,16 +1023,16 @@
+ mPendingBroadcast.curApp);
boolean isDead;
- if (mPendingBroadcast.curApp.pid > 0) {
+ if (mPendingBroadcast.curApp.getPid() > 0) {
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(
- mPendingBroadcast.curApp.pid);
- isDead = proc == null || proc.isCrashing();
+ mPendingBroadcast.curApp.getPid());
+ isDead = proc == null || proc.mErrorState.isCrashing();
}
} else {
- final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
+ final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
- isDead = proc == null || !proc.pendingStart;
+ isDead = proc == null || !proc.isPendingStart();
}
if (!isDead) {
// It's still alive, so keep waiting
@@ -1496,7 +1502,7 @@
+ " (uid " + r.callingUid + ")");
skip = true;
}
- if (r.curApp != null && r.curApp.isCrashing()) {
+ if (r.curApp != null && r.curApp.mErrorState.isCrashing()) {
// If the target process is crashing, just skip it.
Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
+ " to " + r.curApp + ": process crashing");
@@ -1547,14 +1553,14 @@
info.activityInfo.applicationInfo.uid, false);
if (!skip) {
- final int allowed = mService.getAppStartModeLocked(
+ final int allowed = mService.getAppStartModeLOSP(
info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
// We won't allow this receiver to be launched if the app has been
// completely disabled from launches, or it was not explicitly sent
// to it and the app is in a state that should not receive it
- // (depending on how getAppStartModeLocked has determined that).
+ // (depending on how getAppStartModeLOSP has determined that).
if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
Slog.w(TAG, "Background execution disabled: receiving "
+ r.intent + " to "
@@ -1629,7 +1635,7 @@
}
// Is this receiver's application already running?
- if (app != null && app.thread != null && !app.killed) {
+ if (app != null && app.getThread() != null && !app.isKilled()) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
@@ -1664,13 +1670,13 @@
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Need to start app ["
+ mQueueName + "] " + targetProcess + " for broadcast " + r);
- if ((r.curApp=mService.startProcessLocked(targetProcess,
+ r.curApp = mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
- new HostingRecord("broadcast", r.curComponent),
- isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
- (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
- == null) {
+ new HostingRecord("broadcast", r.curComponent), isActivityCapable
+ ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
+ (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false);
+ if (r.curApp == null) {
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
Slog.w(TAG, "Unable to launch app "
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index 45ce4c5..50278fd 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -61,6 +61,7 @@
private final Object mPhenotypeFlagLock = new Object();
private final ActivityManagerService mService;
+ private final ActivityManagerGlobalLock mProcLock;
private final Object mProfilerLock;
@GuardedBy("mPhenotypeFlagLock")
@@ -106,6 +107,7 @@
CacheOomRanker(final ActivityManagerService service) {
mService = service;
+ mProcLock = service.mProcLock;
mProfilerLock = service.mAppProfiler.mProfilerLock;
}
@@ -179,15 +181,14 @@
* Re-rank the cached processes in the lru list with a weighted ordering
* of lru, rss size and number of times the process has been put in the cache.
*/
- public void reRankLruCachedApps(ProcessList processList) {
+ @GuardedBy({"mService", "mProcLock"})
+ void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
float lruWeight;
float usesWeight;
float rssWeight;
int[] lruPositions;
RankedProcessRecord[] scoredProcessRecords;
- ArrayList<ProcessRecord> lruList = processList.mLruProcesses;
-
synchronized (mPhenotypeFlagLock) {
lruWeight = mLruWeight;
usesWeight = mUsesWeight;
@@ -204,11 +205,11 @@
// Collect the least recently used processes to re-rank, only rank cached
// processes further down the list than mLruProcessServiceStart.
int cachedProcessPos = 0;
- for (int i = 0; i < processList.mLruProcessServiceStart
+ for (int i = 0; i < lruProcessServiceStart
&& cachedProcessPos < scoredProcessRecords.length; ++i) {
ProcessRecord app = lruList.get(i);
// Processes that will be assigned a cached oom adj score.
- if (!app.killedByAm && app.thread != null && app.curAdj
+ if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
>= ProcessList.UNKNOWN_ADJ) {
scoredProcessRecords[cachedProcessPos].proc = app;
scoredProcessRecords[cachedProcessPos].score = 0.0f;
@@ -247,7 +248,8 @@
if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
boolean printedHeader = false;
for (int i = 0; i < scoredProcessRecords.length; ++i) {
- if (scoredProcessRecords[i].proc.pid != lruList.get(lruPositions[i]).pid) {
+ if (scoredProcessRecords[i].proc.getPid()
+ != lruList.get(lruPositions[i]).getPid()) {
if (!printedHeader) {
Slog.i(OomAdjuster.TAG, "reRankLruCachedApps");
printedHeader = true;
@@ -291,15 +293,15 @@
private static class LastActivityTimeComparator implements Comparator<RankedProcessRecord> {
@Override
public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
- return Long.compare(o1.proc.lastActivityTime, o2.proc.lastActivityTime);
+ return Long.compare(o1.proc.getLastActivityTime(), o2.proc.getLastActivityTime());
}
}
private static class CacheUseComparator implements Comparator<RankedProcessRecord> {
@Override
public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
- return Long.compare(o1.proc.getCacheOomRankerUseCount(),
- o2.proc.getCacheOomRankerUseCount());
+ return Long.compare(o1.proc.mState.getCacheOomRankerUseCount(),
+ o2.proc.mState.getCacheOomRankerUseCount());
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c558b3d..c18031f 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -42,7 +42,6 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
-import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
@@ -153,9 +152,14 @@
*/
final ServiceThread mCachedAppOptimizerThread;
+ @GuardedBy("mProcLock")
private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
new ArrayList<ProcessRecord>();
+
private final ActivityManagerService mAm;
+
+ private final ActivityManagerGlobalLock mProcLock;
+
private final OnPropertiesChangedListener mOnFlagsChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -234,7 +238,7 @@
@VisibleForTesting
Handler mCompactionHandler;
private Handler mFreezeHandler;
- @GuardedBy("mAm")
+ @GuardedBy("mProcLock")
private boolean mFreezerOverride = false;
// Maps process ID to last compaction statistics for processes that we've fully compacted. Used
@@ -242,6 +246,7 @@
// data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and
// facilitate removal of the oldest entry.
@VisibleForTesting
+ @GuardedBy("mProcLock")
LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats =
new LinkedHashMap<Integer, LastCompactionStats>() {
@Override
@@ -264,6 +269,7 @@
CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback,
ProcessDependencies processDependencies) {
mAm = am;
+ mProcLock = am.mProcLock;
mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
Process.THREAD_GROUP_SYSTEM, true);
mProcStateThrottle = new HashSet<>();
@@ -309,7 +315,7 @@
}
}
- @GuardedBy("mAm")
+ @GuardedBy("mProcLock")
void dump(PrintWriter pw) {
pw.println("CachedAppOptimizer settings");
synchronized (mPhenotypeFlagLock) {
@@ -350,58 +356,57 @@
}
}
- @GuardedBy("mAm")
+ @GuardedBy("mProcLock")
void compactAppSome(ProcessRecord app) {
- app.reqCompactAction = COMPACT_PROCESS_SOME;
+ app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
- COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
+ COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
}
- @GuardedBy("mAm")
+ @GuardedBy("mProcLock")
void compactAppFull(ProcessRecord app) {
- app.reqCompactAction = COMPACT_PROCESS_FULL;
+ app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
- COMPACT_PROCESS_MSG, app.setAdj, app.setProcState));
+ COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState()));
}
- @GuardedBy("mAm")
+ @GuardedBy("mProcLock")
void compactAppPersistent(ProcessRecord app) {
- app.reqCompactAction = COMPACT_PROCESS_PERSISTENT;
+ app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_PERSISTENT);
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
- COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+ COMPACT_PROCESS_MSG, app.mState.getCurAdj(), app.mState.getSetProcState()));
}
- @GuardedBy("mAm")
+ @GuardedBy("mProcLock")
boolean shouldCompactPersistent(ProcessRecord app, long now) {
- return (app.lastCompactTime == 0
- || (now - app.lastCompactTime) > mCompactThrottlePersistent);
+ return (app.mOptRecord.getLastCompactTime() == 0
+ || (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottlePersistent);
}
- @GuardedBy("mAm")
+ @GuardedBy("mProcLock")
void compactAppBfgs(ProcessRecord app) {
- app.reqCompactAction = COMPACT_PROCESS_BFGS;
+ app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_BFGS);
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
- COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
+ COMPACT_PROCESS_MSG, app.mState.getCurAdj(), app.mState.getSetProcState()));
}
- @GuardedBy("mAm")
+ @GuardedBy("mProcLock")
boolean shouldCompactBFGS(ProcessRecord app, long now) {
- return (app.lastCompactTime == 0
- || (now - app.lastCompactTime) > mCompactThrottleBFGS);
+ return (app.mOptRecord.getLastCompactTime() == 0
+ || (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottleBFGS);
}
- @GuardedBy("mAm")
void compactAllSystem() {
- if (mUseCompaction) {
+ if (useCompaction()) {
mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
COMPACT_SYSTEM_MSG));
}
@@ -468,29 +473,28 @@
// Override is applied immediately, restore is delayed
synchronized (mAm) {
- int processCount = mAm.mProcessList.mLruProcesses.size();
+ synchronized (mProcLock) {
+ mFreezerOverride = !enable;
+ Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride);
- mFreezerOverride = !enable;
- Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride);
+ mAm.mProcessList.forEachLruProcessesLOSP(true, process -> {
+ if (process == null) {
+ return;
+ }
- for (int i = 0; i < processCount; i++) {
- ProcessRecord process = mAm.mProcessList.mLruProcesses.get(i);
+ final ProcessCachedOptimizerRecord opt = process.mOptRecord;
+ if (enable && opt.hasFreezerOverride()) {
+ freezeAppAsyncLSP(process);
+ opt.setFreezerOverride(false);
+ }
- if (process == null) {
- continue;
- }
+ if (!enable && opt.isFrozen()) {
+ unfreezeAppLSP(process);
- if (enable && process.freezerOverride) {
- freezeAppAsync(process);
- process.freezerOverride = false;
- }
-
- if (!enable && process.frozen) {
- unfreezeAppLocked(process);
-
- // Set freezerOverride *after* calling unfreezeAppLocked (it resets the flag)
- process.freezerOverride = true;
- }
+ // Set freezerOverride *after* calling unfreezeAppLSP (it resets the flag)
+ opt.setFreezerOverride(true);
+ }
+ });
}
}
@@ -740,19 +744,20 @@
}
// This will ensure app will be out of the freezer for at least FREEZE_TIMEOUT_MS
+ @GuardedBy("mAm")
void unfreezeTemporarily(ProcessRecord app) {
if (mUseFreezer) {
- synchronized (mAm) {
- if (app.frozen) {
- unfreezeAppLocked(app);
- freezeAppAsync(app);
+ synchronized (mProcLock) {
+ if (app.mOptRecord.isFrozen()) {
+ unfreezeAppLSP(app);
+ freezeAppAsyncLSP(app);
}
}
}
}
- @GuardedBy("mAm")
- void freezeAppAsync(ProcessRecord app) {
+ @GuardedBy({"mAm", "mProcLock"})
+ void freezeAppAsyncLSP(ProcessRecord app) {
mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
mFreezeHandler.sendMessageDelayed(
@@ -761,16 +766,17 @@
FREEZE_TIMEOUT_MS);
}
- @GuardedBy("mAm")
- void unfreezeAppLocked(ProcessRecord app) {
+ @GuardedBy({"mAm", "mProcLock"})
+ void unfreezeAppLSP(ProcessRecord app) {
mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
- app.freezerOverride = false;
-
- if (!app.frozen) {
+ final int pid = app.getPid();
+ final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+ opt.setFreezerOverride(false);
+ if (!opt.isFrozen()) {
if (DEBUG_FREEZER) {
Slog.d(TAG_AM,
- "Skipping unfreeze for process " + app.pid + " "
+ "Skipping unfreeze for process " + pid + " "
+ app.processName + " (not frozen)");
}
return;
@@ -781,25 +787,25 @@
boolean processKilled = false;
try {
- int freezeInfo = getBinderFreezeInfo(app.pid);
+ int freezeInfo = getBinderFreezeInfo(pid);
if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
- Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+ Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " "
+ " received sync transactions while frozen, killing");
- app.kill("Sync transaction while in frozen state",
+ app.killLocked("Sync transaction while in frozen state",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
processKilled = true;
}
if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) {
- Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+ Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " "
+ " received async transactions while frozen");
}
} catch (Exception e) {
- Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + app.pid + " "
+ Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + pid + " "
+ app.processName + ". Killing it. Exception: " + e);
- app.kill("Unable to query binder frozen stats",
+ app.killLocked("Unable to query binder frozen stats",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
processKilled = true;
@@ -809,38 +815,38 @@
return;
}
- long freezeTime = app.freezeUnfreezeTime;
+ long freezeTime = opt.getFreezeUnfreezeTime();
try {
- freezeBinder(app.pid, false);
+ freezeBinder(pid, false);
} catch (RuntimeException e) {
- Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
+ Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
+ ". Killing it");
- app.kill("Unable to unfreeze",
+ app.killLocked("Unable to unfreeze",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
return;
}
try {
- Process.setProcessFrozen(app.pid, app.uid, false);
+ Process.setProcessFrozen(pid, app.uid, false);
- app.freezeUnfreezeTime = SystemClock.uptimeMillis();
- app.frozen = false;
+ opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
+ opt.setFrozen(false);
} catch (Exception e) {
- Slog.e(TAG_AM, "Unable to unfreeze " + app.pid + " " + app.processName
+ Slog.e(TAG_AM, "Unable to unfreeze " + pid + " " + app.processName
+ ". This might cause inconsistency or UI hangs.");
}
- if (!app.frozen) {
+ if (!opt.isFrozen()) {
if (DEBUG_FREEZER) {
- Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName);
+ Slog.d(TAG_AM, "sync unfroze " + pid + " " + app.processName);
}
mFreezeHandler.sendMessage(
mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG,
- app.pid,
- (int) Math.min(app.freezeUnfreezeTime - freezeTime, Integer.MAX_VALUE),
+ pid,
+ (int) Math.min(opt.getFreezeUnfreezeTime() - freezeTime, Integer.MAX_VALUE),
app.processName));
}
}
@@ -869,6 +875,7 @@
case COMPACT_PROCESS_MSG: {
long start = SystemClock.uptimeMillis();
ProcessRecord proc;
+ final ProcessCachedOptimizerRecord opt;
int pid;
String action;
final String name;
@@ -877,18 +884,19 @@
LastCompactionStats lastCompactionStats;
int lastOomAdj = msg.arg1;
int procState = msg.arg2;
- synchronized (mAm) {
+ synchronized (mProcLock) {
proc = mPendingCompactionProcesses.remove(0);
+ opt = proc.mOptRecord;
- pendingAction = proc.reqCompactAction;
- pid = proc.pid;
+ pendingAction = opt.getReqCompactAction();
+ pid = proc.getPid();
name = proc.processName;
// don't compact if the process has returned to perceptible
// and this is only a cached/home/prev compaction
if ((pendingAction == COMPACT_PROCESS_SOME
|| pendingAction == COMPACT_PROCESS_FULL)
- && (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
+ && (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
"Skipping compaction as process " + name + " is "
@@ -897,8 +905,8 @@
return;
}
- lastCompactAction = proc.lastCompactAction;
- lastCompactTime = proc.lastCompactTime;
+ lastCompactAction = opt.getLastCompactAction();
+ lastCompactTime = opt.getLastCompactTime();
lastCompactionStats = mLastCompactionStats.get(pid);
}
@@ -1076,9 +1084,9 @@
lastOomAdj, ActivityManager.processStateAmToProto(procState),
zramFreeKbBefore, zramFreeKbAfter);
}
- synchronized (mAm) {
- proc.lastCompactTime = end;
- proc.lastCompactAction = pendingAction;
+ synchronized (mProcLock) {
+ opt.setLastCompactTime(end);
+ opt.setLastCompactAction(pendingAction);
}
if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])
|| action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
@@ -1126,11 +1134,12 @@
}
}
- private void freezeProcess(ProcessRecord proc) {
- final int pid = proc.pid;
+ private void freezeProcess(final ProcessRecord proc) {
+ int pid = proc.getPid(); // Unlocked intentionally
final String name = proc.processName;
final long unfrozenDuration;
final boolean frozen;
+ final ProcessCachedOptimizerRecord opt = proc.mOptRecord;
try {
// pre-check for locks to avoid unnecessary freeze/unfreeze operations
@@ -1146,26 +1155,27 @@
return;
}
- synchronized (mAm) {
- if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ
- || proc.shouldNotFreeze) {
+ synchronized (mProcLock) {
+ pid = proc.getPid();
+ if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ
+ || opt.shouldNotFreeze()) {
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, "Skipping freeze for process " + pid
- + " " + name + " curAdj = " + proc.curAdj
- + ", shouldNotFreeze = " + proc.shouldNotFreeze);
+ + " " + name + " curAdj = " + proc.mState.getCurAdj()
+ + ", shouldNotFreeze = " + opt.shouldNotFreeze());
}
return;
}
if (mFreezerOverride) {
- proc.freezerOverride = true;
+ opt.setFreezerOverride(true);
Slog.d(TAG_AM, "Skipping freeze for process " + pid
- + " " + name + " curAdj = " + proc.curAdj
+ + " " + name + " curAdj = " + proc.mState.getCurAdj()
+ "(override)");
return;
}
- if (pid == 0 || proc.frozen) {
+ if (pid == 0 || opt.isFrozen()) {
// Already frozen or not a real process, either one being
// launched or one being killed
return;
@@ -1177,24 +1187,28 @@
freezeBinder(pid, true);
} catch (RuntimeException e) {
Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
- proc.kill("Unable to freeze binder interface",
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ mFreezeHandler.post(() -> {
+ synchronized (mAm) {
+ proc.killLocked("Unable to freeze binder interface",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ }
+ });
}
- long unfreezeTime = proc.freezeUnfreezeTime;
+ long unfreezeTime = opt.getFreezeUnfreezeTime();
try {
Process.setProcessFrozen(pid, proc.uid, true);
- proc.freezeUnfreezeTime = SystemClock.uptimeMillis();
- proc.frozen = true;
+ opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
+ opt.setFrozen(true);
} catch (Exception e) {
Slog.w(TAG_AM, "Unable to freeze " + pid + " " + name);
}
- unfrozenDuration = proc.freezeUnfreezeTime - unfreezeTime;
- frozen = proc.frozen;
+ unfrozenDuration = opt.getFreezeUnfreezeTime() - unfreezeTime;
+ frozen = opt.isFrozen();
}
if (!frozen) {
@@ -1225,13 +1239,17 @@
}
synchronized (mAm) {
- unfreezeAppLocked(proc);
+ synchronized (mProcLock) {
+ unfreezeAppLSP(proc);
+ }
}
}
} catch (IOException e) {
Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
synchronized (mAm) {
- unfreezeAppLocked(proc);
+ synchronized (mProcLock) {
+ unfreezeAppLSP(proc);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 478c512..f43c7f6 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -57,6 +57,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -148,7 +149,7 @@
ProcessRecord r = null;
if (caller != null) {
- r = mService.getRecordForAppLocked(caller);
+ r = mService.getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid() + ") when getting content provider "
@@ -183,14 +184,14 @@
ProcessRecord dyingProc = null;
if (cpr != null && cpr.proc != null) {
- providerRunning = !cpr.proc.killed;
+ providerRunning = !cpr.proc.isKilled();
// Note if killedByAm is also set, this means the provider process has just been
// killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
// yet. So we need to call appDiedLocked() here and let it clean up.
// (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
// how to test this case.)
- if (cpr.proc.killed && cpr.proc.killedByAm) {
+ if (cpr.proc.isKilled() && cpr.proc.isKilledByAm()) {
Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
// Now we are going to wait for the death before starting the new process.
dyingProc = cpr.proc;
@@ -235,7 +236,7 @@
callingTag, stable, true, startTime, mService.mProcessList);
checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
- final int verifiedAdj = cpr.proc.verifiedAdj;
+ final int verifiedAdj = cpr.proc.mState.getVerifiedAdj();
boolean success = mService.updateOomAdjLocked(cpr.proc, true,
OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
// XXX things have changed so updateOomAdjLocked doesn't actually tell us
@@ -243,7 +244,7 @@
// it, we will check whether the process still exists. Note that this doesn't
// completely get rid of races with LMK killing the process, but should make
// them much smaller.
- if (success && verifiedAdj != cpr.proc.setAdj
+ if (success && verifiedAdj != cpr.proc.mState.getSetAdj()
&& !isProcessAliveLocked(cpr.proc)) {
success = false;
}
@@ -275,7 +276,7 @@
conn = null;
dyingProc = cpr.proc;
} else {
- cpr.proc.verifiedAdj = cpr.proc.setAdj;
+ cpr.proc.mState.setVerifiedAdj(cpr.proc.mState.getSetAdj());
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -428,15 +429,18 @@
checkTime(startTime, "getContentProviderImpl: looking for process record");
ProcessRecord proc = mService.getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
- if (proc != null && proc.thread != null && !proc.killed) {
+ IApplicationThread thread;
+ if (proc != null && (thread = proc.getThread()) != null
+ && !proc.isKilled()) {
if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
Slog.d(TAG, "Installing in existing process " + proc);
}
- if (!proc.pubProviders.containsKey(cpi.name)) {
+ final ProcessProviderRecord pr = proc.mProviders;
+ if (!pr.hasProvider(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
- proc.pubProviders.put(cpi.name, cpr);
+ pr.installProvider(cpi.name, cpr);
try {
- proc.thread.scheduleInstallProvider(cpi);
+ thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
@@ -445,8 +449,8 @@
proc = mService.startProcessLocked(
cpi.processName, cpr.appInfo, false, 0,
new HostingRecord("content provider",
- new ComponentName(
- cpi.applicationInfo.packageName, cpi.name)),
+ new ComponentName(
+ cpi.applicationInfo.packageName, cpi.name)),
Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
@@ -558,9 +562,9 @@
// Note we do it after releasing the lock.
String callerName = "unknown";
if (caller != null) {
- synchronized (mService) {
+ synchronized (mService.mProcLock) {
final ProcessRecord record =
- mService.mProcessList.getLRURecordForAppLocked(caller);
+ mService.mProcessList.getLRURecordForAppLOSP(caller);
if (record != null) {
callerName = record.processName;
}
@@ -600,7 +604,7 @@
mService.enforceNotIsolatedCaller("publishContentProviders");
synchronized (mService) {
- final ProcessRecord r = mService.getRecordForAppLocked(caller);
+ final ProcessRecord r = mService.getRecordForAppLOSP(caller);
if (DEBUG_MU) {
Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
}
@@ -617,7 +621,7 @@
if (src == null || src.info == null || src.provider == null) {
continue;
}
- ContentProviderRecord dst = r.pubProviders.get(src.info.name);
+ ContentProviderRecord dst = r.mProviders.getProvider(src.info.name);
if (dst == null) {
continue;
}
@@ -808,7 +812,7 @@
}
ProcessRecord proc = conn.provider.proc;
- if (proc == null || proc.thread == null) {
+ if (proc == null || proc.getThread() == null) {
// Seems like the process is already cleaned up.
return;
}
@@ -816,7 +820,7 @@
// As far as we're concerned, this is just like receiving a
// death notification... just a bit prematurely.
mService.reportUidInfoMessageLocked(TAG, "Process " + proc.processName
- + " (pid " + proc.pid + ") early provider death", proc.info.uid);
+ + " (pid " + proc.getPid() + ") early provider death", proc.info.uid);
final long token = Binder.clearCallingIdentity();
try {
mService.appDiedLocked(proc, "unstable content provider");
@@ -1074,7 +1078,8 @@
}
int numProviders = providers.size();
- app.pubProviders.ensureCapacity(numProviders + app.pubProviders.size());
+ final ProcessProviderRecord pr = app.mProviders;
+ pr.ensureProviderCapacity(numProviders + pr.numberOfProviders());
for (int i = 0; i < numProviders; i++) {
// NOTE: keep logic in sync with installEncryptionUnawareProviders
ProviderInfo cpi = providers.get(i);
@@ -1111,7 +1116,7 @@
if (DEBUG_MU) {
Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
}
- app.pubProviders.put(cpi.name, cpr);
+ pr.installProvider(cpi.name, cpr);
if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
@@ -1162,7 +1167,8 @@
public final void installSystemProviders() {
List<ProviderInfo> providers;
synchronized (mService) {
- ProcessRecord app = mService.mProcessList.mProcessNames.get("system", SYSTEM_UID);
+ ProcessRecord app = mService.mProcessList
+ .getProcessNamesLOSP().get("system", SYSTEM_UID);
providers = generateApplicationProvidersLocked(app);
if (providers != null) {
for (int i = providers.size() - 1; i >= 0; i--) {
@@ -1205,25 +1211,29 @@
final int matchFlags =
PackageManager.GET_PROVIDERS | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- synchronized (mService) {
- final int numProc = mService.mProcessList.mProcessNames.getMap().size();
+ synchronized (mService.mProcLock) {
+ final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
+ mService.mProcessList.getProcessNamesLOSP().getMap();
+ final int numProc = pmap.size();
for (int iProc = 0; iProc < numProc; iProc++) {
- final SparseArray<ProcessRecord> apps =
- mService.mProcessList.mProcessNames.getMap().valueAt(iProc);
+ final SparseArray<ProcessRecord> apps = pmap.valueAt(iProc);
for (int iApp = 0, numApps = apps.size(); iApp < numApps; iApp++) {
final ProcessRecord app = apps.valueAt(iApp);
- if (app.userId != userId || app.thread == null || app.unlocked) continue;
+ if (app.userId != userId || app.getThread() == null || app.isUnlocked()) {
+ continue;
+ }
app.getPkgList().forEachPackage(pkgName -> {
try {
final PackageInfo pkgInfo = AppGlobals.getPackageManager()
- .getPackageInfo(pkgName, matchFlags, userId);
+ .getPackageInfo(pkgName, matchFlags, app.userId);
+ final IApplicationThread thread = app.getThread();
if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
for (ProviderInfo pi : pkgInfo.providers) {
// NOTE: keep in sync with generateApplicationProvidersLocked
final boolean processMatch =
Objects.equals(pi.processName, app.processName)
- || pi.multiprocess;
+ || pi.multiprocess;
final boolean userMatch = !mService.isSingleton(
pi.processName, pi.applicationInfo, pi.name, pi.flags)
|| app.userId == UserHandle.USER_SYSTEM;
@@ -1234,7 +1244,7 @@
if (processMatch && userMatch
&& (!isInstantApp || splitInstalled)) {
Log.v(TAG, "Installing " + pi);
- app.thread.scheduleInstallProvider(pi);
+ thread.scheduleInstallProvider(pi);
} else {
Log.v(TAG, "Skipping " + pi);
}
@@ -1259,8 +1269,9 @@
}
- for (int i = 0, size = r.conProviders.size(); i < size; i++) {
- ContentProviderConnection conn = r.conProviders.get(i);
+ final ProcessProviderRecord pr = r.mProviders;
+ for (int i = 0, size = pr.numberOfProviderConnections(); i < size; i++) {
+ ContentProviderConnection conn = pr.getProviderConnectionAt(i);
if (conn.provider == cpr) {
conn.incrementCount(stable);
return conn;
@@ -1272,10 +1283,11 @@
conn.startAssociationIfNeeded();
conn.initializeCount(stable);
cpr.connections.add(conn);
- r.conProviders.add(conn);
- mService.startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
+ pr.addProviderConnection(conn);
+ mService.startAssociationLocked(r.uid, r.processName, r.mState.getCurProcState(),
cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
- if (updateLru && cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+ if (updateLru && cpr.proc != null
+ && r.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
// If this is a perceptible app accessing the provider, make
// sure to count it as being accessed and thus back up on
// the LRU list. This is good because content providers are
@@ -1325,13 +1337,14 @@
final ContentProviderRecord cpr = conn.provider;
conn.stopAssociation();
cpr.connections.remove(conn);
- conn.client.conProviders.remove(conn);
- if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ conn.client.mProviders.removeProviderConnection(conn);
+ if (conn.client.mState.getSetProcState()
+ < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
// The client is more important than last activity -- note the time this
// is happening, so we keep the old provider process around a bit as last
// activity to avoid thrashing it.
if (cpr.proc != null) {
- cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ cpr.proc.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
}
}
mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
@@ -1429,9 +1442,9 @@
return mService.validateAssociationAllowedLocked(cpi.packageName,
cpi.applicationInfo.uid, null, callingUid) ? null : "<null>";
}
- final String r = callingApp.getPkgList().forEachPackage(pkgName -> {
+ final String r = callingApp.getPkgList().searchEachPackage(pkgName -> {
if (!mService.validateAssociationAllowedLocked(pkgName,
- callingApp.uid, cpi.packageName, cpi.applicationInfo.uid)) {
+ callingApp.uid, cpi.packageName, cpi.applicationInfo.uid)) {
return cpi.packageName;
}
return null;
@@ -1455,8 +1468,8 @@
private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
String authority) {
- if (app == null
- || app.getCurProcState() > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ if (app == null || app.mState.getCurProcState()
+ > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
return;
}
@@ -1483,31 +1496,30 @@
private final long[] mProcessStateStatsLongs = new long[1];
private boolean isProcessAliveLocked(ProcessRecord proc) {
- if (proc.pid <= 0) {
+ final int pid = proc.getPid();
+ if (pid <= 0) {
if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
Slog.d(ActivityManagerService.TAG, "Process hasn't started yet: " + proc);
}
return false;
}
- if (proc.procStatFile == null) {
- proc.procStatFile = "/proc/" + proc.pid + "/stat";
- }
+ final String procStatFile = "/proc/" + pid + "/stat";
mProcessStateStatsLongs[0] = 0;
- if (!Process.readProcFile(proc.procStatFile, PROCESS_STATE_STATS_FORMAT, null,
+ if (!Process.readProcFile(procStatFile, PROCESS_STATE_STATS_FORMAT, null,
mProcessStateStatsLongs, null)) {
if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
Slog.d(ActivityManagerService.TAG,
- "UNABLE TO RETRIEVE STATE FOR " + proc.procStatFile);
+ "UNABLE TO RETRIEVE STATE FOR " + procStatFile);
}
return false;
}
final long state = mProcessStateStatsLongs[0];
if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
Slog.d(ActivityManagerService.TAG,
- "RETRIEVED STATE FOR " + proc.procStatFile + ": " + (char) state);
+ "RETRIEVED STATE FOR " + procStatFile + ": " + (char) state);
}
if (state != 'Z' && state != 'X' && state != 'x' && state != 'K') {
- return Process.getUidForPid(proc.pid) == proc.uid;
+ return Process.getUidForPid(pid) == proc.uid;
}
return false;
}
@@ -1537,7 +1549,7 @@
}
final boolean callerForeground = r == null
- || r.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
+ || r.mState.getSetSchedGroup() != ProcessList.SCHED_GROUP_BACKGROUND;
// Show a permission review UI only for starting from a foreground app
if (!callerForeground) {
@@ -1611,26 +1623,29 @@
}
}
ProcessRecord capp = conn.client;
+ final IApplicationThread thread = capp.getThread();
conn.dead = true;
if (conn.stableCount() > 0) {
- if (!capp.isPersistent() && capp.thread != null
- && capp.pid != 0 && capp.pid != ActivityManagerService.MY_PID) {
- capp.kill("depends on provider " + cpr.name.flattenToShortString()
- + " in dying proc " + (proc != null ? proc.processName : "??")
- + " (adj " + (proc != null ? proc.setAdj : "??") + ")",
+ final int pid = capp.getPid();
+ if (!capp.isPersistent() && thread != null
+ && pid != 0 && pid != ActivityManagerService.MY_PID) {
+ capp.killLocked(
+ "depends on provider " + cpr.name.flattenToShortString()
+ + " in dying proc " + (proc != null ? proc.processName : "??")
+ + " (adj " + (proc != null ? proc.mState.getSetAdj() : "??") + ")",
ApplicationExitInfo.REASON_DEPENDENCY_DIED,
ApplicationExitInfo.SUBREASON_UNKNOWN,
true);
}
- } else if (capp.thread != null && conn.provider.provider != null) {
+ } else if (thread != null && conn.provider.provider != null) {
try {
- capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
+ thread.unstableProviderDied(conn.provider.provider.asBinder());
} catch (RemoteException e) {
}
// In the protocol here, we don't expect the client to correctly
// clean up this connection, we'll just remove it.
cpr.connections.remove(i);
- if (conn.client.conProviders.remove(conn)) {
+ if (conn.client.mProviders.removeProviderConnection(conn)) {
mService.stopAssociationLocked(capp.uid, capp.processName,
cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
}
@@ -1671,7 +1686,7 @@
// It's being launched but we've reached maximum attempts, mark it as bad
alwaysBad = true;
}
- if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
+ if (!alwaysBad && !app.mErrorState.isBad() && cpr.hasConnectionOrHandle()) {
restart = true;
} else {
removeDyingProviderLocked(app, cpr, true);
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 59a1939..b217cae 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -19,6 +19,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import android.app.ContentProviderHolder;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.IContentProvider;
import android.content.pm.ApplicationInfo;
@@ -211,9 +212,10 @@
+ " caller=" + client);
}
}
- if (client.thread != null) {
+ final IApplicationThread thread = client.getThread();
+ if (thread != null) {
try {
- client.thread.notifyContentProviderPublishStatus(
+ thread.notifyContentProviderPublishStatus(
newHolder(status ? conn : null, false),
info.authority, userId, status);
} catch (RemoteException e) {
@@ -343,8 +345,8 @@
&& mAssociation == null && provider.proc != null
&& (provider.appInfo.uid != mOwningUid
|| !provider.info.processName.equals(mOwningProcessName))) {
- ProcessStats.ProcessStateHolder holder = provider.proc.getPkgList().get(
- provider.name.getPackageName());
+ ProcessStats.ProcessStateHolder holder =
+ provider.proc.getPkgList().get(provider.name.getPackageName());
if (holder == null) {
Slog.wtf(TAG_AM, "No package in referenced provider "
+ provider.name.toShortString() + ": proc=" + provider.proc);
@@ -373,7 +375,7 @@
if (hasExternalProcessHandles() &&
externalProcessTokenToHandle.get(mToken) != null) {
removeExternalProcessHandleInternalLocked(mToken);
- }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/ErrorDialogController.java b/services/core/java/com/android/server/am/ErrorDialogController.java
new file mode 100644
index 0000000..ef135d5
--- /dev/null
+++ b/services/core/java/com/android/server/am/ErrorDialogController.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.Dialog;
+import android.content.Context;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A controller to generate error dialogs in {@link ProcessRecord}.
+ */
+final class ErrorDialogController {
+ private final ProcessRecord mApp;
+ private final ActivityManagerService mService;
+ private final ActivityManagerGlobalLock mProcLock;
+
+ /**
+ * Dialogs being displayed due to crash.
+ */
+ @GuardedBy("mProcLock")
+ private List<AppErrorDialog> mCrashDialogs;
+
+ /**
+ * Dialogs being displayed due to app not responding.
+ */
+ @GuardedBy("mProcLock")
+ private List<AppNotRespondingDialog> mAnrDialogs;
+
+ /**
+ * Dialogs displayed due to strict mode violation.
+ */
+ @GuardedBy("mProcLock")
+ private List<StrictModeViolationDialog> mViolationDialogs;
+
+ /**
+ * Current wait for debugger dialog.
+ */
+ @GuardedBy("mProcLock")
+ private AppWaitingForDebuggerDialog mWaitDialog;
+
+ @GuardedBy("mProcLock")
+ boolean hasCrashDialogs() {
+ return mCrashDialogs != null;
+ }
+
+ @GuardedBy("mProcLock")
+ List<AppErrorDialog> getCrashDialogs() {
+ return mCrashDialogs;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean hasAnrDialogs() {
+ return mAnrDialogs != null;
+ }
+
+ @GuardedBy("mProcLock")
+ List<AppNotRespondingDialog> getAnrDialogs() {
+ return mAnrDialogs;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean hasViolationDialogs() {
+ return mViolationDialogs != null;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean hasDebugWaitingDialog() {
+ return mWaitDialog != null;
+ }
+
+ @GuardedBy("mProcLock")
+ void clearAllErrorDialogs() {
+ clearCrashDialogs();
+ clearAnrDialogs();
+ clearViolationDialogs();
+ clearWaitingDialog();
+ }
+
+ @GuardedBy("mProcLock")
+ void clearCrashDialogs() {
+ clearCrashDialogs(true /* needDismiss */);
+ }
+
+ @GuardedBy("mProcLock")
+ void clearCrashDialogs(boolean needDismiss) {
+ if (mCrashDialogs == null) {
+ return;
+ }
+ if (needDismiss) {
+ forAllDialogs(mCrashDialogs, Dialog::dismiss);
+ }
+ mCrashDialogs = null;
+ }
+
+ @GuardedBy("mProcLock")
+ void clearAnrDialogs() {
+ if (mAnrDialogs == null) {
+ return;
+ }
+ forAllDialogs(mAnrDialogs, Dialog::dismiss);
+ mAnrDialogs = null;
+ }
+
+ @GuardedBy("mProcLock")
+ void clearViolationDialogs() {
+ if (mViolationDialogs == null) {
+ return;
+ }
+ forAllDialogs(mViolationDialogs, Dialog::dismiss);
+ mViolationDialogs = null;
+ }
+
+ @GuardedBy("mProcLock")
+ void clearWaitingDialog() {
+ if (mWaitDialog == null) {
+ return;
+ }
+ mWaitDialog.dismiss();
+ mWaitDialog = null;
+ }
+
+ void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) {
+ for (int i = dialogs.size() - 1; i >= 0; i--) {
+ c.accept(dialogs.get(i));
+ }
+ }
+
+ @GuardedBy("mProcLock")
+ void showCrashDialogs(AppErrorDialog.Data data) {
+ List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
+ mCrashDialogs = new ArrayList<>();
+ for (int i = contexts.size() - 1; i >= 0; i--) {
+ final Context c = contexts.get(i);
+ mCrashDialogs.add(new AppErrorDialog(c, mService, data));
+ }
+ mService.mUiHandler.post(() -> {
+ List<AppErrorDialog> dialogs;
+ synchronized (mProcLock) {
+ dialogs = mCrashDialogs;
+ }
+ if (dialogs != null) {
+ forAllDialogs(dialogs, Dialog::show);
+ }
+ });
+ }
+
+ @GuardedBy("mProcLock")
+ void showAnrDialogs(AppNotRespondingDialog.Data data) {
+ List<Context> contexts = getDisplayContexts(
+ mApp.mErrorState.isSilentAnr() /* lastUsedOnly */);
+ mAnrDialogs = new ArrayList<>();
+ for (int i = contexts.size() - 1; i >= 0; i--) {
+ final Context c = contexts.get(i);
+ mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data));
+ }
+ mService.mUiHandler.post(() -> {
+ List<AppNotRespondingDialog> dialogs;
+ synchronized (mProcLock) {
+ dialogs = mAnrDialogs;
+ }
+ if (dialogs != null) {
+ forAllDialogs(dialogs, Dialog::show);
+ }
+ });
+ }
+
+ @GuardedBy("mProcLock")
+ void showViolationDialogs(AppErrorResult res) {
+ List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
+ mViolationDialogs = new ArrayList<>();
+ for (int i = contexts.size() - 1; i >= 0; i--) {
+ final Context c = contexts.get(i);
+ mViolationDialogs.add(
+ new StrictModeViolationDialog(c, mService, res, mApp));
+ }
+ mService.mUiHandler.post(() -> {
+ List<StrictModeViolationDialog> dialogs;
+ synchronized (mProcLock) {
+ dialogs = mViolationDialogs;
+ }
+ if (dialogs != null) {
+ forAllDialogs(dialogs, Dialog::show);
+ }
+ });
+ }
+
+ @GuardedBy("mProcLock")
+ void showDebugWaitingDialogs() {
+ List<Context> contexts = getDisplayContexts(true /* lastUsedOnly */);
+ final Context c = contexts.get(0);
+ mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, mApp);
+
+ mService.mUiHandler.post(() -> {
+ Dialog dialog;
+ synchronized (mProcLock) {
+ dialog = mWaitDialog;
+ }
+ if (dialog != null) {
+ dialog.show();
+ }
+ });
+ }
+
+ /**
+ * Helper function to collect contexts from crashed app located displays.
+ *
+ * @param lastUsedOnly Sets to {@code true} to indicate to only get last used context.
+ * Sets to {@code false} to collect contexts from crashed app located
+ * displays.
+ *
+ * @return display context list.
+ */
+ private List<Context> getDisplayContexts(boolean lastUsedOnly) {
+ List<Context> displayContexts = new ArrayList<>();
+ if (!lastUsedOnly) {
+ mApp.getWindowProcessController().getDisplayContextsWithErrorDialogs(displayContexts);
+ }
+ // If there is no foreground window display, fallback to last used display.
+ if (displayContexts.isEmpty() || lastUsedOnly) {
+ displayContexts.add(mService.mWmInternal != null
+ ? mService.mWmInternal.getTopFocusedDisplayUiContext()
+ : mService.mUiContext);
+ }
+ return displayContexts;
+ }
+
+ ErrorDialogController(ProcessRecord app) {
+ mApp = app;
+ mService = app.mService;
+ mProcLock = mService.mProcLock;
+ }
+}
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index 6e42aee..94eb076 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -16,6 +16,12 @@
package com.android.server.am;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.SOCK_STREAM;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_RCVTIMEO;
+import static android.system.OsConstants.SO_SNDTIMEO;
+
import android.app.ApplicationErrorReport.CrashInfo;
import android.system.ErrnoException;
import android.system.Os;
@@ -23,8 +29,6 @@
import android.system.UnixSocketAddress;
import android.util.Slog;
-import static android.system.OsConstants.*;
-
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
@@ -259,8 +263,10 @@
// even though the process will vanish as soon as we let
// debuggerd proceed.
synchronized (mAm) {
- pr.setCrashing(true);
- pr.forceCrashReport = true;
+ synchronized (mAm.mProcLock) {
+ pr.mErrorState.setCrashing(true);
+ pr.mErrorState.setForceCrashReport(true);
+ }
}
// Crash reporting is synchronous but we want to let debuggerd
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5e146e1..d79fb8a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -51,6 +51,7 @@
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
@@ -99,6 +100,7 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.IPlatformCompat;
@@ -205,11 +207,12 @@
int mNumCachedHiddenProcs = 0;
/** Track all uids that have actively running processes. */
+ @CompositeRWLock({"mService", "mProcLock"})
ActiveUids mActiveUids;
/**
* The handler to execute {@link #setProcessGroup} (it may be heavy if the process has many
- * threads) for reducing the time spent in {@link #applyOomAdjLocked}.
+ * threads) for reducing the time spent in {@link #applyOomAdjLSP}.
*/
private final Handler mProcessGroupHandler;
@@ -217,6 +220,7 @@
private final ActivityManagerService mService;
private final ProcessList mProcessList;
+ private final ActivityManagerGlobalLock mProcLock;
private final int mNumSlots;
private final ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>();
@@ -332,6 +336,7 @@
ServiceThread adjusterThread) {
mService = service;
mProcessList = processList;
+ mProcLock = service.mProcLock;
mActiveUids = activeUids;
mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
@@ -388,23 +393,27 @@
@VisibleForTesting
@GuardedBy("mService")
void handleUserSwitchedLocked() {
+ mProcessList.forEachLruProcessesLOSP(false,
+ this::updateKeepWarmIfNecessaryForProcessLocked);
+ }
+
+ @GuardedBy("mService")
+ private void updateKeepWarmIfNecessaryForProcessLocked(final ProcessRecord app) {
final ArraySet<ComponentName> warmServices = mService.mConstants.KEEP_WARMING_SERVICES;
- final ArrayList<ProcessRecord> processes = mProcessList.mLruProcesses;
- for (int i = processes.size() - 1; i >= 0; i--) {
- final ProcessRecord app = processes.get(i);
- boolean includeWarmPkg = false;
- for (int j = warmServices.size() - 1; j >= 0; j--) {
- if (app.getPkgList().containsKey(warmServices.valueAt(j).getPackageName())) {
- includeWarmPkg = true;
- break;
- }
+ boolean includeWarmPkg = false;
+ final PackageList pkgList = app.getPkgList();
+ for (int j = warmServices.size() - 1; j >= 0; j--) {
+ if (pkgList.containsKey(warmServices.valueAt(j).getPackageName())) {
+ includeWarmPkg = true;
+ break;
}
- if (!includeWarmPkg) {
- continue;
- }
- for (int j = app.numberOfRunningServices() - 1; j >= 0; j--) {
- app.getRunningServiceAt(j).updateKeepWarmLocked();
- }
+ }
+ if (!includeWarmPkg) {
+ return;
+ }
+ final ProcessServiceRecord psr = app.mServices;
+ for (int j = psr.numberOfRunningServices() - 1; j >= 0; j--) {
+ psr.getRunningServiceAt(j).updateKeepWarmLocked();
}
}
@@ -419,11 +428,20 @@
@GuardedBy("mService")
boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll,
String oomAdjReason) {
+ synchronized (mProcLock) {
+ return updateOomAdjLSP(app, oomAdjAll, oomAdjReason);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private boolean updateOomAdjLSP(ProcessRecord app, boolean oomAdjAll,
+ String oomAdjReason) {
if (oomAdjAll && mConstants.OOMADJ_UPDATE_QUICK) {
- return updateOomAdjLocked(app, oomAdjReason);
+ return updateOomAdjLSP(app, oomAdjReason);
}
final ProcessRecord topApp = mService.getTopApp();
- final boolean wasCached = app.isCached();
+ final ProcessStateRecord state = app.mState;
+ final boolean wasCached = state.isCached();
mAdjSeq++;
@@ -431,30 +449,31 @@
// If our app is currently cached, we know it, and that is it. Otherwise,
// we don't know it yet, and it needs to now be cached we will then
// need to do a complete oom adj.
- final int cachedAdj = app.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ
- ? app.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
+ final int cachedAdj = state.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ
+ ? state.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
- boolean success = updateOomAdjLocked(app, cachedAdj, topApp, false,
+ boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false,
SystemClock.uptimeMillis());
if (oomAdjAll
- && (wasCached != app.isCached() || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
+ && (wasCached != state.isCached()
+ || state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
// Changed to/from cached state, so apps after it in the LRU
// list may also be changed.
- updateOomAdjLocked(oomAdjReason);
+ updateOomAdjLSP(oomAdjReason);
}
return success;
}
- @GuardedBy("mService")
- private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
+ @GuardedBy({"mService", "mProcLock"})
+ private boolean updateOomAdjLSP(ProcessRecord app, int cachedAdj,
ProcessRecord TOP_APP, boolean doingAll, long now) {
- if (app.thread == null) {
+ if (app.getThread() == null) {
return false;
}
- app.resetCachedInfo();
- UidRecord uidRec = app.uidRecord;
+ app.mState.resetCachedInfo();
+ UidRecord uidRec = app.getUidRecord();
if (uidRec != null) {
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
@@ -465,35 +484,23 @@
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
- computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false, true);
+ computeOomAdjLSP(app, cachedAdj, TOP_APP, doingAll, now, false, true);
- boolean success = applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
+ boolean success = applyOomAdjLSP(app, doingAll, now, SystemClock.elapsedRealtime());
if (uidRec != null) {
- // After uidRec.reset() above, for UidRecord that has multiple processes (ProcessRecord)
- // , We need to apply all ProcessRecord into UidRecord.
- final ArraySet<ProcessRecord> procRecords = app.uidRecord.procRecords;
- for (int i = procRecords.size() - 1; i >= 0; i--) {
- final ProcessRecord pr = procRecords.valueAt(i);
- if (!pr.killedByAm && pr.thread != null) {
- if (pr.isolated && pr.numberOfRunningServices() <= 0
- && pr.isolatedEntryPoint == null) {
- // No op.
- } else {
- // Keeping this process, update its uid.
- updateAppUidRecLocked(pr);
- }
- }
- }
+ // After uidRec.reset() above, for UidRecord with multiple processes (ProcessRecord),
+ // we need to apply all ProcessRecord into UidRecord.
+ uidRec.forEachProcess(this::updateAppUidRecIfNecessaryLSP);
if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
- && (uidRec.setProcState != uidRec.getCurProcState()
- || uidRec.setCapability != uidRec.curCapability
- || uidRec.mSetAllowlist != uidRec.mCurAllowlist)) {
+ && (uidRec.getSetProcState() != uidRec.getCurProcState()
+ || uidRec.getSetCapability() != uidRec.getCurCapability()
+ || uidRec.isSetAllowListed() != uidRec.isCurAllowListed())) {
ActiveUids uids = mTmpUidRecords;
uids.clear();
- uids.put(uidRec.uid, uidRec);
- updateUidsLocked(uids, SystemClock.elapsedRealtime());
- mProcessList.incrementProcStateSeqAndNotifyAppsLocked(uids);
+ uids.put(uidRec.getUid(), uidRec);
+ updateUidsLSP(uids, SystemClock.elapsedRealtime());
+ mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(uids);
}
}
@@ -505,11 +512,18 @@
*/
@GuardedBy("mService")
void updateOomAdjLocked(String oomAdjReason) {
+ synchronized (mProcLock) {
+ updateOomAdjLSP(oomAdjReason);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void updateOomAdjLSP(String oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
// Clear any pending ones because we are doing a full update now.
mPendingProcessSet.clear();
mService.mAppProfiler.mHasPreviousProcess = mService.mAppProfiler.mHasHomeProcess = false;
- updateOomAdjLockedInner(oomAdjReason, topApp , null, null, true, true);
+ updateOomAdjInnerLSP(oomAdjReason, topApp , null, null, true, true);
}
/**
@@ -522,8 +536,15 @@
*/
@GuardedBy("mService")
boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+ synchronized (mProcLock) {
+ return updateOomAdjLSP(app, oomAdjReason);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private boolean updateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
if (app == null || !mConstants.OOMADJ_UPDATE_QUICK) {
- updateOomAdjLocked(oomAdjReason);
+ updateOomAdjLSP(oomAdjReason);
return true;
}
@@ -534,20 +555,23 @@
mAdjSeq++;
// Firstly, try to see if the importance of itself gets changed
- final boolean wasCached = app.isCached();
- final int oldAdj = app.getCurRawAdj();
+ final ProcessStateRecord state = app.mState;
+ final boolean wasCached = state.isCached();
+ final int oldAdj = state.getCurRawAdj();
final int cachedAdj = oldAdj >= ProcessList.CACHED_APP_MIN_ADJ
? oldAdj : ProcessList.UNKNOWN_ADJ;
- final boolean wasBackground = ActivityManager.isProcStateBackground(app.setProcState);
- app.containsCycle = false;
- app.procStateChanged = false;
- app.resetCachedInfo();
+ final boolean wasBackground = ActivityManager.isProcStateBackground(
+ state.getSetProcState());
+ state.setContainsCycle(false);
+ state.setProcStateChanged(false);
+ state.resetCachedInfo();
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
- boolean success = updateOomAdjLocked(app, cachedAdj, topApp, false,
+ boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false,
SystemClock.uptimeMillis());
- if (!success || (wasCached == app.isCached() && oldAdj != ProcessList.INVALID_ADJ
- && wasBackground == ActivityManager.isProcStateBackground(app.setProcState))) {
+ if (!success || (wasCached == state.isCached() && oldAdj != ProcessList.INVALID_ADJ
+ && wasBackground == ActivityManager.isProcStateBackground(
+ state.getSetProcState()))) {
// Okay, it's unchanged, it won't impact any service it binds to, we're done here.
if (DEBUG_OOM_ADJ) {
Slog.i(TAG_OOM_ADJ, "No oomadj changes for " + app);
@@ -569,23 +593,25 @@
// Track if any of them reachables could include a cycle
boolean containsCycle = false;
// Scan downstreams of the process record
- app.mReachable = true;
+ state.setReachable(true);
for (ProcessRecord pr = app; pr != null; pr = queue.poll()) {
if (pr != app) {
processes.add(pr);
}
- if (pr.uidRecord != null) {
- uids.put(pr.uidRecord.uid, pr.uidRecord);
+ final UidRecord uidRec = pr.getUidRecord();
+ if (uidRec != null) {
+ uids.put(uidRec.getUid(), uidRec);
}
- for (int i = pr.connections.size() - 1; i >= 0; i--) {
- ConnectionRecord cr = pr.connections.valueAt(i);
+ final ProcessServiceRecord psr = pr.mServices;
+ for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
+ ConnectionRecord cr = psr.getConnectionAt(i);
ProcessRecord service = (cr.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
? cr.binding.service.isolatedProc : cr.binding.service.app;
if (service == null || service == pr) {
continue;
}
- containsCycle |= service.mReachable;
- if (service.mReachable) {
+ containsCycle |= service.mState.isReachable();
+ if (service.mState.isReachable()) {
continue;
}
if ((cr.flags & (Context.BIND_WAIVE_PRIORITY
@@ -595,24 +621,26 @@
continue;
}
queue.offer(service);
- service.mReachable = true;
+ service.mState.setReachable(true);
// During scanning the reachable dependants, remove them from the pending oomadj
// targets list if it's possible, as they've been added into the immediate
// oomadj targets list 'processes' above.
mPendingProcessSet.remove(service);
}
- for (int i = pr.conProviders.size() - 1; i >= 0; i--) {
- ContentProviderConnection cpc = pr.conProviders.get(i);
+ final ProcessProviderRecord ppr = pr.mProviders;
+ for (int i = ppr.numberOfProviderConnections() - 1; i >= 0; i--) {
+ ContentProviderConnection cpc = ppr.getProviderConnectionAt(i);
ProcessRecord provider = cpc.provider.proc;
- if (provider == null || provider == pr || (containsCycle |= provider.mReachable)) {
+ if (provider == null || provider == pr
+ || (containsCycle |= provider.mState.isReachable())) {
continue;
}
- containsCycle |= provider.mReachable;
- if (provider.mReachable) {
+ containsCycle |= provider.mState.isReachable();
+ if (provider.mState.isReachable()) {
continue;
}
queue.offer(provider);
- provider.mReachable = true;
+ provider.mState.setReachable(true);
// During scanning the reachable dependants, remove them from the pending oomadj
// targets list if it's possible, as they've been added into the immediate
// oomadj targets list 'processes' above.
@@ -621,10 +649,10 @@
}
// Reset the flag
- app.mReachable = false;
+ state.setReachable(false);
int size = processes.size();
if (size > 0) {
- // Reverse the process list, since the updateOomAdjLockedInner scans from the end of it.
+ // Reverse the process list, since the updateOomAdjInnerLSP scans from the end of it.
for (int l = 0, r = size - 1; l < r; l++, r--) {
ProcessRecord t = processes.get(l);
processes.set(l, processes.get(r));
@@ -632,13 +660,13 @@
}
mAdjSeq--;
// Update these reachable processes
- updateOomAdjLockedInner(oomAdjReason, topApp, processes, uids, containsCycle, false);
- } else if (app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ) {
+ updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, containsCycle, false);
+ } else if (state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ) {
// In case the app goes from non-cached to cached but it doesn't have other reachable
// processes, its adj could be still unknown as of now, assign one.
processes.add(app);
assignCachedAdjIfNecessary(processes);
- applyOomAdjLocked(app, false, SystemClock.uptimeMillis(),
+ applyOomAdjLSP(app, false, SystemClock.uptimeMillis(),
SystemClock.elapsedRealtime());
}
mTmpProcessList.clear();
@@ -683,17 +711,20 @@
final ArrayList<ProcessRecord> processes = mTmpProcessList;
final ActiveUids uids = mTmpUidRecords;
- uids.clear();
- processes.clear();
- for (int i = mPendingProcessSet.size() - 1; i >= 0; i--) {
- final ProcessRecord app = mPendingProcessSet.valueAt(i);
- if (app.uidRecord != null) {
- uids.put(app.uidRecord.uid, app.uidRecord);
+ synchronized (mProcLock) {
+ uids.clear();
+ processes.clear();
+ for (int i = mPendingProcessSet.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = mPendingProcessSet.valueAt(i);
+ final UidRecord uidRec = app.getUidRecord();
+ if (uidRec != null) {
+ uids.put(uidRec.getUid(), uidRec);
+ }
+ processes.add(app);
}
- processes.add(app);
- }
- updateOomAdjLockedInner(oomAdjReason, topApp, processes, uids, true, false);
+ updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, true, false);
+ }
processes.clear();
mPendingProcessSet.clear();
@@ -706,8 +737,8 @@
* list if the given list is null; when it's partial update, each process's client proc won't
* get evaluated recursively here.
*/
- @GuardedBy("mService")
- private void updateOomAdjLockedInner(String oomAdjReason, final ProcessRecord topApp,
+ @GuardedBy({"mService", "mProcLock"})
+ private void updateOomAdjInnerLSP(String oomAdjReason, final ProcessRecord topApp,
ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
boolean startProfiling) {
if (startProfiling) {
@@ -719,7 +750,7 @@
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
final boolean fullUpdate = processes == null;
ActiveUids activeUids = uids;
- ArrayList<ProcessRecord> activeProcesses = fullUpdate ? mProcessList.mLruProcesses
+ ArrayList<ProcessRecord> activeProcesses = fullUpdate ? mProcessList.getLruProcessesLOSP()
: processes;
final int numProc = activeProcesses.size();
@@ -728,8 +759,8 @@
activeUids = mTmpUidRecords;
activeUids.clear();
for (int i = 0; i < numUids; i++) {
- UidRecord r = mActiveUids.valueAt(i);
- activeUids.put(r.uid, r);
+ UidRecord uidRec = mActiveUids.valueAt(i);
+ activeUids.put(uidRec.getUid(), uidRec);
}
}
@@ -751,36 +782,39 @@
boolean retryCycles = false;
boolean computeClients = fullUpdate || potentialCycles;
- // need to reset cycle state before calling computeOomAdjLocked because of service conns
+ // need to reset cycle state before calling computeOomAdjLSP because of service conns
for (int i = numProc - 1; i >= 0; i--) {
ProcessRecord app = activeProcesses.get(i);
- app.mReachable = false;
+ final ProcessStateRecord state = app.mState;
+ state.setReachable(false);
// No need to compute again it has been evaluated in previous iteration
- if (app.adjSeq != mAdjSeq) {
- app.containsCycle = false;
- app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
- app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
- app.setCapability = PROCESS_CAPABILITY_NONE;
- app.resetCachedInfo();
+ if (state.getAdjSeq() != mAdjSeq) {
+ state.setContainsCycle(false);
+ state.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+ state.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
+ state.setSetCapability(PROCESS_CAPABILITY_NONE);
+ state.resetCachedInfo();
}
}
for (int i = numProc - 1; i >= 0; i--) {
ProcessRecord app = activeProcesses.get(i);
- if (!app.killedByAm && app.thread != null) {
- app.procStateChanged = false;
- computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false,
+ final ProcessStateRecord state = app.mState;
+ if (!app.isKilledByAm() && app.getThread() != null) {
+ state.setProcStateChanged(false);
+ computeOomAdjLSP(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false,
computeClients); // It won't enter cycle if not computing clients.
// if any app encountered a cycle, we need to perform an additional loop later
- retryCycles |= app.containsCycle;
+ retryCycles |= state.containsCycle();
// Keep the completedAdjSeq to up to date.
- app.completedAdjSeq = mAdjSeq;
+ state.setCompletedAdjSeq(mAdjSeq);
}
}
if (mCacheOomRanker.useOomReranking()) {
- mCacheOomRanker.reRankLruCachedApps(mProcessList);
+ mCacheOomRanker.reRankLruCachedAppsLSP(mProcessList.getLruProcessesLSP(),
+ mProcessList.getLruProcessServiceStartLOSP());
}
- assignCachedAdjIfNecessary(mProcessList.mLruProcesses);
+ assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
if (computeClients) { // There won't be cycles if we didn't compute clients above.
// Cycle strategy:
@@ -794,16 +828,18 @@
for (int i = 0; i < numProc; i++) {
ProcessRecord app = activeProcesses.get(i);
- if (!app.killedByAm && app.thread != null && app.containsCycle) {
- app.adjSeq--;
- app.completedAdjSeq--;
+ final ProcessStateRecord state = app.mState;
+ if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) {
+ state.decAdjSeq();
+ state.decCompletedAdjSeq();
}
}
for (int i = 0; i < numProc; i++) {
ProcessRecord app = activeProcesses.get(i);
- if (!app.killedByAm && app.thread != null && app.containsCycle) {
- if (computeOomAdjLocked(app, app.getCurRawAdj(), topApp, true, now,
+ final ProcessStateRecord state = app.mState;
+ if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) {
+ if (computeOomAdjLSP(app, state.getCurRawAdj(), topApp, true, now,
true, true)) {
retryCycles = true;
}
@@ -815,7 +851,7 @@
mNumNonCachedProcs = 0;
mNumCachedHiddenProcs = 0;
- boolean allChanged = updateAndTrimProcessLocked(now, nowElapsed, oldTime, activeUids);
+ boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids);
mNumServiceProcs = mNewNumServiceProcs;
if (mService.mAlwaysFinishActivities) {
@@ -825,11 +861,11 @@
}
if (allChanged) {
- mService.mAppProfiler.requestPssAllProcsLocked(now, false,
+ mService.mAppProfiler.requestPssAllProcsLPr(now, false,
mService.mProcessStats.isMemFactorLowered());
}
- updateUidsLocked(activeUids, nowElapsed);
+ updateUidsLSP(activeUids, nowElapsed);
synchronized (mService.mProcessStats.mLock) {
if (mService.mProcessStats.shouldWriteNowLocked(now)) {
@@ -856,6 +892,7 @@
}
}
+ @GuardedBy({"mService", "mProcLock"})
private void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
final int numLru = lruList.size();
@@ -899,23 +936,27 @@
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
+ final ProcessStateRecord state = app.mState;
// If we haven't yet assigned the final cached adj
// to the process, do that now.
- if (!app.killedByAm && app.thread != null && app.curAdj
+ if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
>= ProcessList.UNKNOWN_ADJ) {
- switch (app.getCurProcState()) {
+ final ProcessServiceRecord psr = app.mServices;
+ switch (state.getCurProcState()) {
case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
// Figure out the next cached level, taking into account groups.
boolean inGroup = false;
- if (app.connectionGroup != 0) {
+ final int connectionGroup = psr.getConnectionGroup();
+ if (connectionGroup != 0) {
+ final int connectionImportance = psr.getConnectionImportance();
if (lastCachedGroupUid == app.uid
- && lastCachedGroup == app.connectionGroup) {
+ && lastCachedGroup == connectionGroup) {
// This is in the same group as the last process, just tweak
// adjustment by importance.
- if (app.connectionImportance > lastCachedGroupImportance) {
- lastCachedGroupImportance = app.connectionImportance;
+ if (connectionImportance > lastCachedGroupImportance) {
+ lastCachedGroupImportance = connectionImportance;
if (curCachedAdj < nextCachedAdj
&& curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
curCachedImpAdj++;
@@ -924,8 +965,8 @@
inGroup = true;
} else {
lastCachedGroupUid = app.uid;
- lastCachedGroup = app.connectionGroup;
- lastCachedGroupImportance = app.connectionImportance;
+ lastCachedGroup = connectionGroup;
+ lastCachedGroupImportance = connectionImportance;
}
}
if (!inGroup && curCachedAdj != nextCachedAdj) {
@@ -943,11 +984,12 @@
// This process is a cached process holding activities...
// assign it the next cached value for that type, and then
// step that cached level.
- app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
- app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
+ state.setCurRawAdj(curCachedAdj + curCachedImpAdj);
+ state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj));
if (DEBUG_LRU) {
Slog.d(TAG_LRU, "Assigning activity LRU #" + i
- + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
+ + " adj: " + state.getCurAdj()
+ + " (curCachedAdj=" + curCachedAdj
+ " curCachedImpAdj=" + curCachedImpAdj + ")");
}
break;
@@ -969,11 +1011,11 @@
// long-running services that have dropped down to the
// cached level will be treated as empty (since their process
// state is still as a service), which is what we want.
- app.setCurRawAdj(curEmptyAdj);
- app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
+ state.setCurRawAdj(curEmptyAdj);
+ state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj));
if (DEBUG_LRU) {
Slog.d(TAG_LRU, "Assigning empty LRU #" + i
- + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
+ + " adj: " + state.getCurAdj() + " (curEmptyAdj=" + curEmptyAdj
+ ")");
}
break;
@@ -982,9 +1024,10 @@
}
}
- private boolean updateAndTrimProcessLocked(final long now, final long nowElapsed,
+ @GuardedBy({"mService", "mProcLock"})
+ private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed,
final long oldTime, final ActiveUids activeUids) {
- ArrayList<ProcessRecord> lruList = mProcessList.mLruProcesses;
+ ArrayList<ProcessRecord> lruList = mProcessList.getLruProcessesLOSP();
final int numLru = lruList.size();
final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
@@ -999,34 +1042,37 @@
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
- if (!app.killedByAm && app.thread != null) {
+ final ProcessStateRecord state = app.mState;
+ if (!app.isKilledByAm() && app.getThread() != null) {
// We don't need to apply the update for the process which didn't get computed
- if (app.completedAdjSeq == mAdjSeq) {
- applyOomAdjLocked(app, true, now, nowElapsed);
+ if (state.getCompletedAdjSeq() == mAdjSeq) {
+ applyOomAdjLSP(app, true, now, nowElapsed);
}
+ final ProcessServiceRecord psr = app.mServices;
// Count the number of process types.
- switch (app.getCurProcState()) {
+ switch (state.getCurProcState()) {
case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
numCached++;
- if (app.connectionGroup != 0) {
+ final int connectionGroup = psr.getConnectionGroup();
+ if (connectionGroup != 0) {
if (lastCachedGroupUid == app.info.uid
- && lastCachedGroup == app.connectionGroup) {
+ && lastCachedGroup == connectionGroup) {
// If this process is the next in the same group, we don't
// want it to count against our limit of the number of cached
// processes, so bump up the group count to account for it.
numCachedExtraGroup++;
} else {
lastCachedGroupUid = app.info.uid;
- lastCachedGroup = app.connectionGroup;
+ lastCachedGroup = connectionGroup;
}
} else {
lastCachedGroupUid = lastCachedGroup = 0;
}
if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
- app.kill("cached #" + numCached,
+ app.killLocked("cached #" + numCached,
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,
true);
@@ -1034,17 +1080,16 @@
break;
case PROCESS_STATE_CACHED_EMPTY:
if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
- && app.lastActivityTime < oldTime) {
- app.kill("empty for "
- + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
- / 1000) + "s",
+ && app.getLastActivityTime() < oldTime) {
+ app.killLocked("empty for " + ((oldTime + ProcessList.MAX_EMPTY_TIME
+ - app.getLastActivityTime()) / 1000) + "s",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TRIM_EMPTY,
true);
} else {
numEmpty++;
if (numEmpty > emptyProcessLimit) {
- app.kill("empty #" + numEmpty,
+ app.killLocked("empty #" + numEmpty,
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY,
true);
@@ -1056,8 +1101,8 @@
break;
}
- if (app.isolated && app.numberOfRunningServices() <= 0
- && app.isolatedEntryPoint == null) {
+ if (app.isolated && psr.numberOfRunningServices() <= 0
+ && app.getIsolatedEntryPoint() == null) {
// If this is an isolated process, there are no services
// running in it, and it's not a special process with a
// custom entry point, then the process is no longer
@@ -1065,40 +1110,56 @@
// definition not re-use the same process again, and it is
// good to avoid having whatever code was running in them
// left sitting around after no longer needed.
- app.kill("isolated not needed", ApplicationExitInfo.REASON_OTHER,
+ app.killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
} else {
// Keeping this process, update its uid.
- updateAppUidRecLocked(app);
+ updateAppUidRecLSP(app);
}
- if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
- && !app.killedByAm) {
+ if (state.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
+ && !app.isKilledByAm()) {
numTrimming++;
}
}
}
- mProcessList.incrementProcStateSeqAndNotifyAppsLocked(activeUids);
+ mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(activeUids);
- return mService.mAppProfiler.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
+ return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming);
}
- private void updateAppUidRecLocked(ProcessRecord app) {
- final UidRecord uidRec = app.uidRecord;
- if (uidRec != null) {
- uidRec.ephemeral = app.info.isInstantApp();
- if (uidRec.getCurProcState() > app.getCurProcState()) {
- uidRec.setCurProcState(app.getCurProcState());
+ @GuardedBy({"mService", "mProcLock"})
+ private void updateAppUidRecIfNecessaryLSP(final ProcessRecord app) {
+ if (!app.isKilledByAm() && app.getThread() != null) {
+ if (app.isolated && app.mServices.numberOfRunningServices() <= 0
+ && app.getIsolatedEntryPoint() == null) {
+ // No op.
+ } else {
+ // Keeping this process, update its uid.
+ updateAppUidRecLSP(app);
}
- if (app.hasForegroundServices()) {
- uidRec.foregroundServices = true;
- }
- uidRec.curCapability |= app.curCapability;
}
}
- private void updateUidsLocked(ActiveUids activeUids, final long nowElapsed) {
+ @GuardedBy({"mService", "mProcLock"})
+ private void updateAppUidRecLSP(ProcessRecord app) {
+ final UidRecord uidRec = app.getUidRecord();
+ if (uidRec != null) {
+ final ProcessStateRecord state = app.mState;
+ uidRec.setEphemeral(app.info.isInstantApp());
+ if (uidRec.getCurProcState() > state.getCurProcState()) {
+ uidRec.setCurProcState(state.getCurProcState());
+ }
+ if (app.mServices.hasForegroundServices()) {
+ uidRec.setForegroundServices(true);
+ }
+ uidRec.setCurCapability(uidRec.getCurCapability() | state.getCurCapability());
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void updateUidsLSP(ActiveUids activeUids, final long nowElapsed) {
ArrayList<UidRecord> becameIdle = mTmpBecameIdle;
becameIdle.clear();
@@ -1110,22 +1171,22 @@
final UidRecord uidRec = activeUids.valueAt(i);
int uidChange = UidRecord.CHANGE_PROCSTATE;
if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
- && (uidRec.setProcState != uidRec.getCurProcState()
- || uidRec.setCapability != uidRec.curCapability
- || uidRec.mSetAllowlist != uidRec.mCurAllowlist)) {
+ && (uidRec.getSetProcState() != uidRec.getCurProcState()
+ || uidRec.getSetCapability() != uidRec.getCurCapability()
+ || uidRec.isSetAllowListed() != uidRec.isCurAllowListed())) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
- + ": proc state from " + uidRec.setProcState + " to "
+ + ": proc state from " + uidRec.getSetProcState() + " to "
+ uidRec.getCurProcState() + ", capability from "
- + uidRec.setCapability + " to " + uidRec.curCapability
- + ", allowlist from " + uidRec.mSetAllowlist
- + " to " + uidRec.mCurAllowlist);
+ + uidRec.getSetCapability() + " to " + uidRec.getCurCapability()
+ + ", allowlist from " + uidRec.isSetAllowListed()
+ + " to " + uidRec.isCurAllowListed());
if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
- && !uidRec.mCurAllowlist) {
+ && !uidRec.isCurAllowListed()) {
// UID is now in the background (and not on the temp allowlist). Was it
// previously in the foreground (or on the temp allowlist)?
- if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
- || uidRec.mSetAllowlist) {
- uidRec.lastBackgroundTime = nowElapsed;
+ if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState())
+ || uidRec.isSetAllowListed()) {
+ uidRec.setLastBackgroundTime(nowElapsed);
if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
// Note: the background settle time is in elapsed realtime, while
// the handler time base is uptime. All this means is that we may
@@ -1135,38 +1196,40 @@
mConstants.BACKGROUND_SETTLE_TIME);
}
}
- if (uidRec.idle && !uidRec.setIdle) {
+ if (uidRec.isIdle() && !uidRec.isSetIdle()) {
uidChange = UidRecord.CHANGE_IDLE;
becameIdle.add(uidRec);
}
} else {
- if (uidRec.idle) {
+ if (uidRec.isIdle()) {
uidChange = UidRecord.CHANGE_ACTIVE;
- EventLogTags.writeAmUidActive(uidRec.uid);
- uidRec.idle = false;
+ EventLogTags.writeAmUidActive(uidRec.getUid());
+ uidRec.setIdle(false);
}
- uidRec.lastBackgroundTime = 0;
+ uidRec.setLastBackgroundTime(0);
}
- final boolean wasCached = uidRec.setProcState
+ final boolean wasCached = uidRec.getSetProcState()
> ActivityManager.PROCESS_STATE_RECEIVER;
final boolean isCached = uidRec.getCurProcState()
> ActivityManager.PROCESS_STATE_RECEIVER;
- if (wasCached != isCached || uidRec.setProcState == PROCESS_STATE_NONEXISTENT) {
+ if (wasCached != isCached
+ || uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
}
- uidRec.setProcState = uidRec.getCurProcState();
- uidRec.setCapability = uidRec.curCapability;
- uidRec.mSetAllowlist = uidRec.mCurAllowlist;
- uidRec.setIdle = uidRec.idle;
- mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState);
+ uidRec.setSetProcState(uidRec.getCurProcState());
+ uidRec.setSetCapability(uidRec.getCurCapability());
+ uidRec.setSetAllowListed(uidRec.isCurAllowListed());
+ uidRec.setSetIdle(uidRec.isIdle());
+ mService.mAtmInternal.onUidProcStateChanged(
+ uidRec.getUid(), uidRec.getSetProcState());
mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
- mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(),
- uidRec.curCapability);
- if (uidRec.foregroundServices) {
+ mService.noteUidProcessState(uidRec.getUid(), uidRec.getCurProcState(),
+ uidRec.getCurCapability());
+ if (uidRec.hasForegroundServices()) {
mService.mServices.foregroundServiceProcStateChangedLocked(uidRec);
}
}
- mService.mInternal.deletePendingTopUid(uidRec.uid);
+ mService.mInternal.deletePendingTopUid(uidRec.getUid());
}
if (mLocalPowerManager != null) {
mLocalPowerManager.finishUidChanges();
@@ -1177,7 +1240,7 @@
// If we have any new uids that became idle this time, we need to make sure
// they aren't left with running services.
for (int i = size - 1; i >= 0; i--) {
- mService.mServices.stopInBackgroundLocked(becameIdle.get(i).uid);
+ mService.mServices.stopInBackgroundLocked(becameIdle.get(i).getUid());
}
}
}
@@ -1185,7 +1248,7 @@
private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
new ComputeOomAdjWindowCallback();
- /** These methods are called inline during computeOomAdjLocked(), on the same thread */
+ /** These methods are called inline during computeOomAdjLSP(), on the same thread */
final class ComputeOomAdjWindowCallback
implements WindowProcessController.ComputeOomAdjCallback {
@@ -1197,6 +1260,7 @@
int appUid;
int logUid;
int processStateCurTop;
+ ProcessStateRecord mState;
void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) {
@@ -1208,6 +1272,7 @@
this.appUid = appUid;
this.logUid = logUid;
this.processStateCurTop = processStateCurTop;
+ this.mState = app.mState;
}
@Override
@@ -1215,14 +1280,14 @@
// App has a visible activity; only upgrade adjustment.
if (adj > ProcessList.VISIBLE_APP_ADJ) {
adj = ProcessList.VISIBLE_APP_ADJ;
- app.adjType = "vis-activity";
+ mState.setAdjType("vis-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
}
}
if (procState > processStateCurTop) {
procState = processStateCurTop;
- app.adjType = "vis-activity";
+ mState.setAdjType("vis-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to vis-activity (top): " + app);
@@ -1231,8 +1296,8 @@
if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
- app.setCached(false);
- app.empty = false;
+ mState.setCached(false);
+ mState.setEmpty(false);
foregroundActivities = true;
}
@@ -1240,14 +1305,14 @@
public void onPausedActivity() {
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- app.adjType = "pause-activity";
+ mState.setAdjType("pause-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: " + app);
}
}
if (procState > processStateCurTop) {
procState = processStateCurTop;
- app.adjType = "pause-activity";
+ mState.setAdjType("pause-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to pause-activity (top): " + app);
@@ -1256,8 +1321,8 @@
if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
- app.setCached(false);
- app.empty = false;
+ mState.setCached(false);
+ mState.setEmpty(false);
foregroundActivities = true;
}
@@ -1265,7 +1330,7 @@
public void onStoppingActivity(boolean finishing) {
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- app.adjType = "stop-activity";
+ mState.setAdjType("stop-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to stop-activity: " + app);
@@ -1281,15 +1346,15 @@
if (!finishing) {
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
- app.adjType = "stop-activity";
+ mState.setAdjType("stop-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to stop-activity: " + app);
}
}
}
- app.setCached(false);
- app.empty = false;
+ mState.setCached(false);
+ mState.setEmpty(false);
foregroundActivities = true;
}
@@ -1297,7 +1362,7 @@
public void onOtherActivity() {
if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
procState = PROCESS_STATE_CACHED_ACTIVITY;
- app.adjType = "cch-act";
+ mState.setAdjType("cch-act");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to cached activity: " + app);
@@ -1306,99 +1371,102 @@
}
}
- private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
+ @GuardedBy({"mService", "mProcLock"})
+ private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {
- if (mAdjSeq == app.adjSeq) {
- if (app.adjSeq == app.completedAdjSeq) {
+ final ProcessStateRecord state = app.mState;
+ if (mAdjSeq == state.getAdjSeq()) {
+ if (state.getAdjSeq() == state.getCompletedAdjSeq()) {
// This adjustment has already been computed successfully.
return false;
} else {
// The process is being computed, so there is a cycle. We cannot
// rely on this process's state.
- app.containsCycle = true;
+ state.setContainsCycle(true);
return false;
}
}
- if (app.thread == null) {
- app.adjSeq = mAdjSeq;
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
- app.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
- app.curAdj = ProcessList.CACHED_APP_MAX_ADJ;
- app.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
- app.completedAdjSeq = app.adjSeq;
- app.curCapability = PROCESS_CAPABILITY_NONE;
+ if (app.getThread() == null) {
+ state.setAdjSeq(mAdjSeq);
+ state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
+ state.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
+ state.setCurAdj(ProcessList.CACHED_APP_MAX_ADJ);
+ state.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
+ state.setCompletedAdjSeq(state.getAdjSeq());
+ state.setCurCapability(PROCESS_CAPABILITY_NONE);
return false;
}
- app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
- app.adjSource = null;
- app.adjTarget = null;
- app.empty = false;
- app.setCached(false);
- app.shouldNotFreeze = false;
-
- app.resetAllowStartFgs();
+ state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN);
+ state.setAdjSource(null);
+ state.setAdjTarget(null);
+ state.setEmpty(false);
+ state.setCached(false);
+ state.setAllowStartFgsState(PROCESS_STATE_NONEXISTENT);
+ state.resetAllowStartFgs();
+ app.mOptRecord.setShouldNotFreeze(false);
final int appUid = app.info.uid;
final int logUid = mService.mCurOomAdjUid;
- int prevAppAdj = app.curAdj;
- int prevProcState = app.getCurProcState();
- int prevCapability = app.curCapability;
+ int prevAppAdj = state.getCurAdj();
+ int prevProcState = state.getCurProcState();
+ int prevCapability = state.getCurCapability();
+ final ProcessServiceRecord psr = app.mServices;
- if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
+ if (state.getMaxAdj() <= ProcessList.FOREGROUND_APP_ADJ) {
// The max adjustment doesn't allow this app to be anything
// below foreground, so it is not worth doing work for it.
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
}
- app.adjType = "fixed";
- app.adjSeq = mAdjSeq;
- app.setCurRawAdj(app.maxAdj);
- app.setHasForegroundActivities(false);
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
- app.curCapability = PROCESS_CAPABILITY_ALL;
- app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
+ state.setAdjType("fixed");
+ state.setAdjSeq(mAdjSeq);
+ state.setCurRawAdj(state.getMaxAdj());
+ state.setHasForegroundActivities(false);
+ state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+ state.setCurCapability(PROCESS_CAPABILITY_ALL);
+ state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
// System processes can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
// facilitate this, here we need to determine whether or not it
// is currently showing UI.
- app.systemNoUi = true;
+ state.setSystemNoUi(true);
if (app == topApp) {
- app.systemNoUi = false;
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
- app.adjType = "pers-top-activity";
- } else if (app.hasTopUi()) {
+ state.setSystemNoUi(false);
+ state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+ state.setAdjType("pers-top-activity");
+ } else if (state.hasTopUi()) {
// sched group/proc state adjustment is below
- app.systemNoUi = false;
- app.adjType = "pers-top-ui";
- } else if (app.getCachedHasVisibleActivities()) {
- app.systemNoUi = false;
+ state.setSystemNoUi(false);
+ state.setAdjType("pers-top-ui");
+ } else if (state.getCachedHasVisibleActivities()) {
+ state.setSystemNoUi(false);
}
- if (!app.systemNoUi) {
+ if (!state.isSystemNoUi()) {
if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE) {
// screen on, promote UI
- app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+ state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+ state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
} else {
// screen off, restrict UI scheduling
- app.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
+ state.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
}
}
- app.setCurRawProcState(app.getCurProcState());
- app.curAdj = app.maxAdj;
- app.completedAdjSeq = app.adjSeq;
- app.bumpAllowStartFgsState(app.getCurProcState());
- app.setAllowStartFgs();
+ state.setCurRawProcState(state.getCurProcState());
+ state.setCurAdj(state.getMaxAdj());
+ state.setCompletedAdjSeq(state.getAdjSeq());
+ state.bumpAllowStartFgsState(state.getCurProcState());
+ state.setAllowStartFgs();
// if curAdj is less than prevAppAdj, then this process was promoted
- return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
+ return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState;
}
- app.systemNoUi = false;
+ state.setSystemNoUi(false);
final int PROCESS_STATE_CUR_TOP = mService.mAtmInternal.getTopProcessState();
@@ -1415,17 +1483,17 @@
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
- app.adjType = "top-activity";
+ state.setAdjType("top-activity");
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
- app.bumpAllowStartFgsState(PROCESS_STATE_TOP);
+ state.bumpAllowStartFgsState(PROCESS_STATE_TOP);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
}
- } else if (app.runningRemoteAnimation) {
+ } else if (state.isRunningRemoteAnimation()) {
adj = ProcessList.VISIBLE_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
- app.adjType = "running-remote-anim";
+ state.setAdjType("running-remote-anim");
procState = PROCESS_STATE_CUR_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
@@ -1434,12 +1502,12 @@
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- app.adjType = "instrumentation";
+ state.setAdjType("instrumentation");
procState = PROCESS_STATE_FOREGROUND_SERVICE;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
}
- } else if (app.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {
+ } else if (state.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {
// An app that is currently receiving a broadcast also
// counts as being in the foreground for OOM killer purposes.
// It's placed in a sched group based on the nature of the
@@ -1447,27 +1515,26 @@
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = (mTmpBroadcastQueue.contains(mService.mFgBroadcastQueue))
? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
- app.adjType = "broadcast";
+ state.setAdjType("broadcast");
procState = ActivityManager.PROCESS_STATE_RECEIVER;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app);
}
- } else if (app.executingServices.size() > 0) {
+ } else if (psr.numberOfExecutingServices() > 0) {
// An app that is currently executing a service callback also
// counts as being in the foreground.
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = app.execServicesFg ?
- ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
- app.adjType = "exec-service";
+ schedGroup = psr.shouldExecServicesFg()
+ ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+ state.setAdjType("exec-service");
procState = PROCESS_STATE_SERVICE;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
}
- //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
} else if (app == topApp) {
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
- app.adjType = "top-sleeping";
+ state.setAdjType("top-sleeping");
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -1480,10 +1547,10 @@
// value that the caller wants us to.
adj = cachedAdj;
procState = PROCESS_STATE_CACHED_EMPTY;
- if (!app.containsCycle) {
- app.setCached(true);
- app.empty = true;
- app.adjType = "cch-empty";
+ if (!state.containsCycle()) {
+ state.setCached(true);
+ state.setEmpty(true);
+ state.setAdjType("cch-empty");
}
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app);
@@ -1491,20 +1558,20 @@
}
// Examine all activities if not already foreground.
- if (!foregroundActivities && app.getCachedHasActivities()) {
- app.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
+ if (!foregroundActivities && state.getCachedHasActivities()) {
+ state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
adj, foregroundActivities, procState, schedGroup, appUid, logUid,
PROCESS_STATE_CUR_TOP);
- adj = app.mCachedAdj;
- foregroundActivities = app.mCachedForegroundActivities;
- procState = app.mCachedProcState;
- schedGroup = app.mCachedSchedGroup;
+ adj = state.getCachedAdj();
+ foregroundActivities = state.getCachedForegroundActivities();
+ procState = state.getCachedProcState();
+ schedGroup = state.getCachedSchedGroup();
}
- if (procState > PROCESS_STATE_CACHED_RECENT && app.getCachedHasRecentTasks()) {
+ if (procState > PROCESS_STATE_CACHED_RECENT && state.getCachedHasRecentTasks()) {
procState = PROCESS_STATE_CACHED_RECENT;
- app.adjType = "cch-rec";
+ state.setAdjType("cch-rec");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app);
}
@@ -1512,24 +1579,24 @@
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
|| procState > PROCESS_STATE_FOREGROUND_SERVICE) {
- if (app.hasForegroundServices()) {
+ if (psr.hasForegroundServices()) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
procState = PROCESS_STATE_FOREGROUND_SERVICE;
- app.bumpAllowStartFgsState(PROCESS_STATE_FOREGROUND_SERVICE);
- app.adjType = "fg-service";
- app.setCached(false);
+ state.bumpAllowStartFgsState(PROCESS_STATE_FOREGROUND_SERVICE);
+ state.setAdjType("fg-service");
+ state.setCached(false);
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + app.adjType + ": "
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + state.getAdjType() + ": "
+ app + " ");
}
- } else if (app.hasOverlayUi()) {
+ } else if (state.hasOverlayUi()) {
// The process is display an overlay UI.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
- app.setCached(false);
- app.adjType = "has-overlay-ui";
+ state.setCached(false);
+ state.setAdjType("has-overlay-ui");
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to overlay ui: " + app);
@@ -1540,11 +1607,11 @@
// If the app was recently in the foreground and moved to a foreground service status,
// allow it to get a higher rank in memory for some time, compared to other foreground
// services so that it can finish performing any persistence/processing of in-memory state.
- if (app.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
- && (app.lastTopTime + mConstants.TOP_TO_FGS_GRACE_DURATION > now
- || app.setProcState <= PROCESS_STATE_TOP)) {
+ if (psr.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
+ && (state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION > now
+ || state.getSetProcState() <= PROCESS_STATE_TOP)) {
adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
- app.adjType = "fg-service-act";
+ state.setAdjType("fg-service-act");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
}
@@ -1552,15 +1619,15 @@
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
|| procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
- if (app.forcingToImportant != null) {
+ if (state.getForcingToImportant() != null) {
// This is currently used for toasts... they are not interactive, and
// we don't want them to cause the app to become fully foreground (and
// thus out of background check), so we yes the best background level we can.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
- app.setCached(false);
- app.adjType = "force-imp";
- app.adjSource = app.forcingToImportant;
+ state.setCached(false);
+ state.setAdjType("force-imp");
+ state.setAdjSource(state.getForcingToImportant());
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app);
@@ -1568,63 +1635,63 @@
}
}
- if (app.getCachedIsHeavyWeight()) {
+ if (state.getCachedIsHeavyWeight()) {
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
- app.setCached(false);
- app.adjType = "heavy";
+ state.setCached(false);
+ state.setAdjType("heavy");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app);
}
}
if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
- app.adjType = "heavy";
+ state.setAdjType("heavy");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app);
}
}
}
- if (app.getCachedIsHomeProcess()) {
+ if (state.getCachedIsHomeProcess()) {
if (adj > ProcessList.HOME_APP_ADJ) {
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
adj = ProcessList.HOME_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
- app.setCached(false);
- app.adjType = "home";
+ state.setCached(false);
+ state.setAdjType("home");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
}
}
if (procState > ActivityManager.PROCESS_STATE_HOME) {
procState = ActivityManager.PROCESS_STATE_HOME;
- app.adjType = "home";
+ state.setAdjType("home");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app);
}
}
}
- if (app.getCachedIsPreviousProcess() && app.getCachedHasActivities()) {
+ if (state.getCachedIsPreviousProcess() && state.getCachedHasActivities()) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
// This was the previous process that showed UI to the user.
// We want to try to keep it around more aggressively, to give
// a good experience around switching between two apps.
adj = ProcessList.PREVIOUS_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
- app.setCached(false);
- app.adjType = "previous";
+ state.setCached(false);
+ state.setAdjType("previous");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
}
}
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
- app.adjType = "previous";
+ state.setAdjType("previous");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
}
@@ -1632,7 +1699,7 @@
}
if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
- + " reason=" + app.adjType);
+ + " reason=" + state.getAdjType());
// By default, we use the computed adjustment. It may be changed if
// there are applications dependent on our services or providers, but
@@ -1640,15 +1707,15 @@
// infinite recursion. If we're re-evaluating due to cycles, use the previously computed
// values.
if (cycleReEval) {
- procState = Math.min(procState, app.getCurRawProcState());
- adj = Math.min(adj, app.getCurRawAdj());
- schedGroup = Math.max(schedGroup, app.getCurrentSchedulingGroup());
+ procState = Math.min(procState, state.getCurRawProcState());
+ adj = Math.min(adj, state.getCurRawAdj());
+ schedGroup = Math.max(schedGroup, state.getCurrentSchedulingGroup());
}
- app.setCurRawAdj(adj);
- app.setCurRawProcState(procState);
+ state.setCurRawAdj(adj);
+ state.setCurRawProcState(procState);
- app.hasStartedServices = false;
- app.adjSeq = mAdjSeq;
+ state.setHasStartedServices(false);
+ state.setAdjSeq(mAdjSeq);
final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);
if (backupTarget != null && app == backupTarget.app) {
@@ -1659,15 +1726,15 @@
if (procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
}
- app.adjType = "backup";
+ state.setAdjType("backup");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app);
}
- app.setCached(false);
+ state.setCached(false);
}
if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
procState = ActivityManager.PROCESS_STATE_BACKUP;
- app.adjType = "backup";
+ state.setAdjType("backup");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app);
}
@@ -1675,29 +1742,29 @@
}
int capabilityFromFGS = 0; // capability from foreground service.
- for (int is = app.numberOfRunningServices() - 1;
+ for (int is = psr.numberOfRunningServices() - 1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
is--) {
- ServiceRecord s = app.getRunningServiceAt(is);
+ ServiceRecord s = psr.getRunningServiceAt(is);
if (s.startRequested) {
- app.hasStartedServices = true;
+ state.setHasStartedServices(true);
if (procState > PROCESS_STATE_SERVICE) {
procState = PROCESS_STATE_SERVICE;
- app.adjType = "started-services";
+ state.setAdjType("started-services");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to started service: " + app);
}
}
- if (!s.mKeepWarming && app.hasShownUi && !app.getCachedIsHomeProcess()) {
+ if (!s.mKeepWarming && state.hasShownUi() && !state.getCachedIsHomeProcess()) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
// UI stuff. We'll tag it with a label just to help
// debug and understand what is going on.
if (adj > ProcessList.SERVICE_ADJ) {
- app.adjType = "cch-started-ui-services";
+ state.setAdjType("cch-started-ui-services");
}
} else {
if (s.mKeepWarming
@@ -1707,19 +1774,19 @@
// of the background processes.
if (adj > ProcessList.SERVICE_ADJ) {
adj = ProcessList.SERVICE_ADJ;
- app.adjType = "started-services";
+ state.setAdjType("started-services");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to started service: " + app);
}
- app.setCached(false);
+ state.setCached(false);
}
}
// If we have let the service slide into the background
// state, still have some text describing what it is doing
// even though the service no longer has an impact.
if (adj > ProcessList.SERVICE_ADJ) {
- app.adjType = "cch-started-services";
+ state.setAdjType("cch-started-services");
}
}
}
@@ -1774,29 +1841,31 @@
boolean trackedProcState = false;
ProcessRecord client = cr.binding.client;
+ final ProcessStateRecord cstate = client.mState;
if (computeClients) {
- computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now,
+ computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now,
cycleReEval, true);
} else {
- client.setCurRawAdj(client.setAdj);
- client.setCurRawProcState(client.setProcState);
+ cstate.setCurRawAdj(cstate.getSetAdj());
+ cstate.setCurRawProcState(cstate.getSetProcState());
}
- int clientAdj = client.getCurRawAdj();
- int clientProcState = client.getCurRawProcState();
+ int clientAdj = cstate.getCurRawAdj();
+ int clientProcState = cstate.getCurRawProcState();
// pass client's mAllowStartFgs to the app if client is not persistent process.
- if (client.mAllowStartFgs && client.maxAdj >= ProcessList.FOREGROUND_APP_ADJ) {
- app.mAllowStartFgs = true;
+ if (cstate.getAllowedStartFgs() != FGS_FEATURE_DENIED
+ && cstate.getMaxAdj() >= ProcessList.FOREGROUND_APP_ADJ) {
+ state.setAllowStartFgs(cstate.getAllowedStartFgs());
}
if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
- if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+ if (shouldSkipDueToCycle(state, cstate, procState, adj, cycleReEval)) {
continue;
}
if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
- capability |= client.curCapability;
+ capability |= cstate.getCurCapability();
}
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
@@ -1809,7 +1878,7 @@
if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
// Not doing bind OOM management, so treat
// this guy more like a started service.
- if (app.hasShownUi && !app.getCachedIsHomeProcess()) {
+ if (state.hasShownUi() && !state.getCachedIsHomeProcess()) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
// UI stuff. We'll tag it with a label just to help
@@ -1817,7 +1886,7 @@
if (adj > clientAdj) {
adjType = "cch-bound-ui-services";
}
- app.setCached(false);
+ state.setCached(false);
clientAdj = adj;
clientProcState = procState;
} else {
@@ -1843,7 +1912,7 @@
// about letting this process get into the LRU
// list to be killed and restarted if needed for
// memory.
- if (app.hasShownUi && !app.getCachedIsHomeProcess()
+ if (state.hasShownUi() && !state.getCachedIsHomeProcess()
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
adjType = "cch-bound-ui-services";
@@ -1880,12 +1949,12 @@
newAdj = adj;
}
}
- if (!client.isCached()) {
- app.setCached(false);
+ if (!cstate.isCached()) {
+ state.setCached(false);
}
if (adj > newAdj) {
adj = newAdj;
- app.setCurRawAdj(adj);
+ state.setCurRawAdj(adj);
adjType = "service";
}
}
@@ -1895,7 +1964,7 @@
// This will treat important bound services identically to
// the top app, which may behave differently than generic
// foreground work.
- final int curSchedGroup = client.getCurrentSchedulingGroup();
+ final int curSchedGroup = cstate.getCurrentSchedulingGroup();
if (curSchedGroup > schedGroup) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
schedGroup = curSchedGroup;
@@ -1910,7 +1979,7 @@
// give them the best bound state after that.
if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) {
clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- app.bumpAllowStartFgsState(
+ state.bumpAllowStartFgsState(
PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
} else if (mService.mWakefulness.get()
== PowerManagerInternal.WAKEFULNESS_AWAKE
@@ -1925,7 +1994,7 @@
// Go at most to BOUND_TOP, unless requested to elevate
// to client's state.
clientProcState = PROCESS_STATE_BOUND_TOP;
- app.bumpAllowStartFgsState(PROCESS_STATE_BOUND_TOP);
+ state.bumpAllowStartFgsState(PROCESS_STATE_BOUND_TOP);
boolean enabled = false;
try {
enabled = mPlatformCompatCache.isChangeEnabled(
@@ -1969,7 +2038,7 @@
if (procState > clientProcState) {
procState = clientProcState;
- app.setCurRawProcState(procState);
+ state.setCurRawProcState(procState);
if (adjType == null) {
adjType = "service";
}
@@ -1979,12 +2048,12 @@
app.setPendingUiClean(true);
}
if (adjType != null) {
- app.adjType = adjType;
- app.adjTypeCode = ActivityManager.RunningAppProcessInfo
- .REASON_SERVICE_IN_USE;
- app.adjSource = cr.binding.client;
- app.adjSourceProcState = clientProcState;
- app.adjTarget = s.instanceName;
+ state.setAdjType(adjType);
+ state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE);
+ state.setAdjSource(cr.binding.client);
+ state.setAdjSourceProcState(clientProcState);
+ state.setAdjTarget(s.instanceName);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+ ": " + app + ", due to " + cr.binding.client
@@ -2003,18 +2072,18 @@
// bound by an unfrozen app via a WPRI binding has to remain
// unfrozen.
if (clientAdj < ProcessList.CACHED_APP_MIN_ADJ) {
- app.shouldNotFreeze = true;
+ app.mOptRecord.setShouldNotFreeze(true);
}
}
if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
- app.treatLikeActivity = true;
+ psr.setTreatLikeActivity(true);
}
final ActivityServiceConnectionsHolder a = cr.activity;
if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
&& a.isActivityVisible()) {
adj = ProcessList.FOREGROUND_APP_ADJ;
- app.setCurRawAdj(adj);
+ state.setCurRawAdj(adj);
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
@@ -2022,13 +2091,13 @@
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
- app.setCached(false);
- app.adjType = "service";
- app.adjTypeCode = ActivityManager.RunningAppProcessInfo
- .REASON_SERVICE_IN_USE;
- app.adjSource = a;
- app.adjSourceProcState = procState;
- app.adjTarget = s.instanceName;
+ state.setCached(false);
+ state.setAdjType("service");
+ state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE);
+ state.setAdjSource(a);
+ state.setAdjSourceProcState(procState);
+ state.setAdjTarget(s.instanceName);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise to service w/activity: " + app);
@@ -2039,12 +2108,13 @@
}
}
- for (int provi = app.pubProviders.size() - 1;
+ final ProcessProviderRecord ppr = app.mProviders;
+ for (int provi = ppr.numberOfProviders() - 1;
provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
provi--) {
- ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
+ ContentProviderRecord cpr = ppr.getProviderAt(provi);
for (int i = cpr.connections.size() - 1;
i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
@@ -2052,24 +2122,24 @@
i--) {
ContentProviderConnection conn = cpr.connections.get(i);
ProcessRecord client = conn.client;
+ final ProcessStateRecord cstate = client.mState;
if (client == app) {
// Being our own client is not interesting.
continue;
}
if (computeClients) {
- computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now, cycleReEval,
- true);
+ computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true);
} else {
- client.setCurRawAdj(client.setAdj);
- client.setCurRawProcState(client.setProcState);
+ cstate.setCurRawAdj(cstate.getSetAdj());
+ cstate.setCurRawProcState(cstate.getSetProcState());
}
- if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
+ if (shouldSkipDueToCycle(state, cstate, procState, adj, cycleReEval)) {
continue;
}
- int clientAdj = client.getCurRawAdj();
- int clientProcState = client.getCurRawProcState();
+ int clientAdj = cstate.getCurRawAdj();
+ int clientProcState = cstate.getCurRawProcState();
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
@@ -2078,16 +2148,16 @@
}
String adjType = null;
if (adj > clientAdj) {
- if (app.hasShownUi && !app.getCachedIsHomeProcess()
+ if (state.hasShownUi() && !state.getCachedIsHomeProcess()
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adjType = "cch-ui-provider";
} else {
adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
- app.setCurRawAdj(adj);
+ state.setCurRawAdj(adj);
adjType = "provider";
}
- app.setCached(app.isCached() & client.isCached());
+ state.setCached(state.isCached() & cstate.isCached());
}
if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) {
@@ -2104,18 +2174,18 @@
conn.trackProcState(clientProcState, mAdjSeq, now);
if (procState > clientProcState) {
procState = clientProcState;
- app.setCurRawProcState(procState);
+ state.setCurRawProcState(procState);
}
- if (client.getCurrentSchedulingGroup() > schedGroup) {
+ if (cstate.getCurrentSchedulingGroup() > schedGroup) {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
if (adjType != null) {
- app.adjType = adjType;
- app.adjTypeCode = ActivityManager.RunningAppProcessInfo
- .REASON_PROVIDER_IN_USE;
- app.adjSource = client;
- app.adjSourceProcState = clientProcState;
- app.adjTarget = cpr.name;
+ state.setAdjType(adjType);
+ state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+ .REASON_PROVIDER_IN_USE);
+ state.setAdjSource(client);
+ state.setAdjSourceProcState(clientProcState);
+ state.setAdjTarget(cpr.name);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+ ": " + app + ", due to " + client
@@ -2130,11 +2200,11 @@
if (cpr.hasExternalProcessHandles()) {
if (adj > ProcessList.FOREGROUND_APP_ADJ) {
adj = ProcessList.FOREGROUND_APP_ADJ;
- app.setCurRawAdj(adj);
+ state.setCurRawAdj(adj);
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- app.setCached(false);
- app.adjType = "ext-provider";
- app.adjTarget = cpr.name;
+ state.setCached(false);
+ state.setAdjType("ext-provider");
+ state.setAdjTarget(cpr.name);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to external provider: " + app);
@@ -2142,7 +2212,7 @@
}
if (procState > PROCESS_STATE_IMPORTANT_FOREGROUND) {
procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
- app.setCurRawProcState(procState);
+ state.setCurRawProcState(procState);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to external provider: " + app);
@@ -2151,13 +2221,13 @@
}
}
- if (app.lastProviderTime > 0 &&
- (app.lastProviderTime + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
+ if (ppr.getLastProviderTime() > 0
+ && (ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
adj = ProcessList.PREVIOUS_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
- app.setCached(false);
- app.adjType = "recent-provider";
+ state.setCached(false);
+ state.setAdjType("recent-provider");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to recent provider: " + app);
@@ -2165,7 +2235,7 @@
}
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
- app.adjType = "recent-provider";
+ state.setAdjType("recent-provider");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to recent provider: " + app);
@@ -2174,24 +2244,23 @@
}
if (procState >= PROCESS_STATE_CACHED_EMPTY) {
- if (app.hasClientActivities()) {
+ if (psr.hasClientActivities()) {
// This is a cached process, but with client activities. Mark it so.
procState = PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
- app.adjType = "cch-client-act";
- } else if (app.treatLikeActivity) {
+ state.setAdjType("cch-client-act");
+ } else if (psr.isTreatedLikeActivity()) {
// This is a cached process, but somebody wants us to treat it like it has
// an activity, okay!
procState = PROCESS_STATE_CACHED_ACTIVITY;
- app.adjType = "cch-as-act";
+ state.setAdjType("cch-as-act");
}
}
if (adj == ProcessList.SERVICE_ADJ) {
if (doingAll && !cycleReEval) {
- app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
+ state.setServiceB(mNewNumAServiceProcs > (mNumServiceProcs / 3));
mNewNumServiceProcs++;
- //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);
- if (!app.serviceb) {
+ if (!state.isServiceB()) {
// This service isn't far enough down on the LRU list to
// normally be a B service, but if we are low on RAM and it
// is large we want to force it down since we would prefer to
@@ -2199,29 +2268,27 @@
if (!mService.mAppProfiler.isLastMemoryLevelNormal()
&& app.mProfile.getLastPss()
>= mProcessList.getCachedRestoreThresholdKb()) {
- app.serviceHighRam = true;
- app.serviceb = true;
+ state.setServiceHighRam(true);
+ state.setServiceB(true);
//Slog.i(TAG, "ADJ " + app + " high ram!");
} else {
mNewNumAServiceProcs++;
//Slog.i(TAG, "ADJ " + app + " not high ram!");
}
} else {
- app.serviceHighRam = false;
+ state.setServiceHighRam(false);
}
}
- if (app.serviceb) {
+ if (state.isServiceB()) {
adj = ProcessList.SERVICE_B_ADJ;
}
}
- app.setCurRawAdj(adj);
+ state.setCurRawAdj(adj);
- //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
- // " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
- if (adj > app.maxAdj) {
- adj = app.maxAdj;
- if (app.maxAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+ if (adj > state.getMaxAdj()) {
+ adj = state.getMaxAdj();
+ if (adj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
@@ -2236,31 +2303,32 @@
}
// apply capability from FGS.
- if (app.hasForegroundServices()) {
+ if (psr.hasForegroundServices()) {
capability |= capabilityFromFGS;
}
- capability |= getDefaultCapability(app, procState);
+ capability |= getDefaultCapability(psr, procState);
// Do final modification to adj. Everything we do between here and applying
// the final setAdj must be done in this function, because we will also use
// it when computing the final cached adj later. Note that we don't need to
// worry about this for max adj above, since max adj will always be used to
// keep it out of the cached vaues.
- app.curAdj = app.modifyRawOomAdj(adj);
- app.curCapability = capability;
- app.setCurrentSchedulingGroup(schedGroup);
- app.setCurProcState(procState);
- app.setCurRawProcState(procState);
- app.setHasForegroundActivities(foregroundActivities);
- app.completedAdjSeq = mAdjSeq;
- app.setAllowStartFgs();
+ state.setCurAdj(psr.modifyRawOomAdj(adj));
+ state.setCurCapability(capability);
+ state.setCurrentSchedulingGroup(schedGroup);
+ state.setCurProcState(procState);
+ state.setCurRawProcState(procState);
+ state.setHasForegroundActivities(foregroundActivities);
+ state.setCompletedAdjSeq(mAdjSeq);
+ state.setAllowStartFgs();
+
// if curAdj or curProcState improved, then this process was promoted
- return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState
- || app.curCapability != prevCapability ;
+ return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState
+ || state.getCurCapability() != prevCapability;
}
- private int getDefaultCapability(ProcessRecord app, int procState) {
+ private int getDefaultCapability(ProcessServiceRecord psr, int procState) {
switch (procState) {
case PROCESS_STATE_PERSISTENT:
case PROCESS_STATE_PERSISTENT_UI:
@@ -2269,7 +2337,7 @@
case PROCESS_STATE_BOUND_TOP:
return PROCESS_CAPABILITY_NONE;
case PROCESS_STATE_FOREGROUND_SERVICE:
- if (app.hasForegroundServices()) {
+ if (psr.hasForegroundServices()) {
// Capability from FGS are conditional depending on foreground service type in
// manifest file and the mAllowWhileInUsePermissionInFgs flag.
return PROCESS_CAPABILITY_NONE;
@@ -2297,16 +2365,16 @@
* evaluation.
* @return whether to skip using the client connection at this time
*/
- private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessRecord client,
+ private boolean shouldSkipDueToCycle(ProcessStateRecord app, ProcessStateRecord client,
int procState, int adj, boolean cycleReEval) {
- if (client.containsCycle) {
- // We've detected a cycle. We should retry computeOomAdjLocked later in
+ if (client.containsCycle()) {
+ // We've detected a cycle. We should retry computeOomAdjLSP later in
// case a later-checked connection from a client would raise its
// priority legitimately.
- app.containsCycle = true;
+ app.setContainsCycle(true);
// If the client has not been completely evaluated, check if it's worth
// using the partial values.
- if (client.completedAdjSeq < mAdjSeq) {
+ if (client.getCompletedAdjSeq() < mAdjSeq) {
if (cycleReEval) {
// If the partial values are no better, skip until the next
// attempt
@@ -2325,7 +2393,8 @@
}
/** Inform the oomadj observer of changes to oomadj. Used by tests. */
- void reportOomAdjMessageLocked(String tag, String msg) {
+ @GuardedBy("mService")
+ private void reportOomAdjMessageLocked(String tag, String msg) {
Slog.d(tag, msg);
synchronized (mService.mOomAdjObserverLock) {
if (mService.mCurOomAdjObserver != null) {
@@ -2336,13 +2405,14 @@
}
/** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
- @GuardedBy("mService")
- private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
+ @GuardedBy({"mService", "mProcLock"})
+ private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
long nowElapsed) {
boolean success = true;
+ final ProcessStateRecord state = app.mState;
- if (app.getCurRawAdj() != app.setRawAdj) {
- app.setRawAdj = app.getCurRawAdj();
+ if (state.getCurRawAdj() != state.getSetRawAdj()) {
+ state.setSetRawAdj(state.getCurRawAdj());
}
int changes = 0;
@@ -2350,22 +2420,22 @@
// don't compact during bootup
if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
// Cached and prev/home compaction
- if (app.curAdj != app.setAdj) {
+ if (state.getCurAdj() != state.getSetAdj()) {
// Perform a minor compaction when a perceptible app becomes the prev/home app
// Perform a major compaction when any app enters cached
// reminder: here, setAdj is previous state, curAdj is upcoming state
- if (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&
- (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
- app.curAdj == ProcessList.HOME_APP_ADJ)) {
+ if (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ
+ && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ
+ || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) {
mCachedAppOptimizer.compactAppSome(app);
- } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
- || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
- && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
- && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
+ } else if ((state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ
+ || state.getSetAdj() > ProcessList.CACHED_APP_MAX_ADJ)
+ && state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ
+ && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) {
mCachedAppOptimizer.compactAppFull(app);
}
} else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
- && app.setAdj < ProcessList.FOREGROUND_APP_ADJ
+ && state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ
// Because these can fire independent of oom_adj/procstate changes, we need
// to throttle the actual dispatch of these requests in addition to the
// processing of the requests. As a result, there is throttling both here
@@ -2373,36 +2443,36 @@
&& mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
mCachedAppOptimizer.compactAppPersistent(app);
} else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
- && app.getCurProcState()
+ && state.getCurProcState()
== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
mCachedAppOptimizer.compactAppBfgs(app);
}
}
- if (app.curAdj != app.setAdj) {
- ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
+ if (state.getCurAdj() != state.getSetAdj()) {
+ ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
- String msg = "Set " + app.pid + " " + app.processName + " adj "
- + app.curAdj + ": " + app.adjType;
+ String msg = "Set " + app.getPid() + " " + app.processName + " adj "
+ + state.getCurAdj() + ": " + state.getAdjType();
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
- app.setAdj = app.curAdj;
- app.verifiedAdj = ProcessList.INVALID_ADJ;
+ state.setSetAdj(state.getCurAdj());
+ state.setVerifiedAdj(ProcessList.INVALID_ADJ);
}
- final int curSchedGroup = app.getCurrentSchedulingGroup();
- if (app.setSchedGroup != curSchedGroup) {
- int oldSchedGroup = app.setSchedGroup;
- app.setSchedGroup = curSchedGroup;
+ final int curSchedGroup = state.getCurrentSchedulingGroup();
+ if (state.getSetSchedGroup() != curSchedGroup) {
+ int oldSchedGroup = state.getSetSchedGroup();
+ state.setSetSchedGroup(curSchedGroup);
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
String msg = "Setting sched group of " + app.processName
- + " to " + curSchedGroup + ": " + app.adjType;
+ + " to " + curSchedGroup + ": " + state.getAdjType();
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
- if (app.waitingToKill != null && app.curReceivers.isEmpty()
- && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
- app.kill(app.waitingToKill, ApplicationExitInfo.REASON_USER_REQUESTED,
+ if (app.getWaitingToKill() != null && app.mReceivers.numberOfCurReceivers() == 0
+ && state.getSetSchedGroup() == ProcessList.SCHED_GROUP_BACKGROUND) {
+ app.killLocked(app.getWaitingToKill(), ApplicationExitInfo.REASON_USER_REQUESTED,
ApplicationExitInfo.SUBREASON_UNKNOWN, true);
success = false;
} else {
@@ -2423,22 +2493,23 @@
break;
}
mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
- 0 /* unused */, app.pid, processGroup, app.processName));
+ 0 /* unused */, app.getPid(), processGroup, app.processName));
try {
+ final int renderThreadTid = app.getRenderThreadTid();
if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
app.getWindowProcessController().onTopProcChanged();
if (mService.mUseFifoUiScheduling) {
// Switch UI pipeline for app to SCHED_FIFO
- app.savedPriority = Process.getThreadPriority(app.pid);
- mService.scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
- if (app.renderThreadTid != 0) {
- mService.scheduleAsFifoPriority(app.renderThreadTid,
+ state.setSavedPriority(Process.getThreadPriority(app.getPid()));
+ mService.scheduleAsFifoPriority(app.getPid(), true);
+ if (renderThreadTid != 0) {
+ mService.scheduleAsFifoPriority(renderThreadTid,
/* suppressLogs */true);
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Set RenderThread (TID " +
- app.renderThreadTid + ") to FIFO");
+ renderThreadTid + ") to FIFO");
}
} else {
if (DEBUG_OOM_ADJ) {
@@ -2447,10 +2518,10 @@
}
} else {
// Boost priority for top app UI and render threads
- setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
- if (app.renderThreadTid != 0) {
+ setThreadPriority(app.getPid(), TOP_APP_PRIORITY_BOOST);
+ if (renderThreadTid != 0) {
try {
- setThreadPriority(app.renderThreadTid,
+ setThreadPriority(renderThreadTid,
TOP_APP_PRIORITY_BOOST);
} catch (IllegalArgumentException e) {
// thread died, ignore
@@ -2464,10 +2535,10 @@
if (mService.mUseFifoUiScheduling) {
try {
// Reset UI pipeline to SCHED_OTHER
- setThreadScheduler(app.pid, SCHED_OTHER, 0);
- setThreadPriority(app.pid, app.savedPriority);
- if (app.renderThreadTid != 0) {
- setThreadScheduler(app.renderThreadTid,
+ setThreadScheduler(app.getPid(), SCHED_OTHER, 0);
+ setThreadPriority(app.getPid(), state.getSavedPriority());
+ if (renderThreadTid != 0) {
+ setThreadScheduler(renderThreadTid,
SCHED_OTHER, 0);
}
} catch (IllegalArgumentException e) {
@@ -2479,139 +2550,141 @@
}
} else {
// Reset priority for top app UI and render threads
- setThreadPriority(app.pid, 0);
+ setThreadPriority(app.getPid(), 0);
}
- if (app.renderThreadTid != 0) {
- setThreadPriority(app.renderThreadTid, THREAD_PRIORITY_DISPLAY);
+ if (renderThreadTid != 0) {
+ setThreadPriority(renderThreadTid, THREAD_PRIORITY_DISPLAY);
}
}
} catch (Exception e) {
if (DEBUG_ALL) {
- Slog.w(TAG, "Failed setting thread priority of " + app.pid, e);
+ Slog.w(TAG, "Failed setting thread priority of " + app.getPid(), e);
}
}
}
}
- if (app.repForegroundActivities != app.hasForegroundActivities()) {
- app.repForegroundActivities = app.hasForegroundActivities();
+ if (state.hasRepForegroundActivities() != state.hasForegroundActivities()) {
+ state.setRepForegroundActivities(state.hasForegroundActivities());
changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
}
- updateAppFreezeStateLocked(app);
+ updateAppFreezeStateLSP(app);
- if (app.getReportedProcState() != app.getCurProcState()) {
- app.setReportedProcState(app.getCurProcState());
- if (app.thread != null) {
+ if (state.getReportedProcState() != state.getCurProcState()) {
+ state.setReportedProcState(state.getCurProcState());
+ if (app.getThread() != null) {
try {
if (false) {
//RuntimeException h = new RuntimeException("here");
- Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
+ Slog.i(TAG, "Sending new process state " + state.getReportedProcState()
+ " to " + app /*, h*/);
}
- app.thread.setProcessState(app.getReportedProcState());
+ app.getThread().setProcessState(state.getReportedProcState());
} catch (RemoteException e) {
}
}
}
boolean forceUpdatePssTime = false;
- if (app.setProcState == PROCESS_STATE_NONEXISTENT
- || ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
- app.lastStateTime = now;
+ if (state.getSetProcState() == PROCESS_STATE_NONEXISTENT
+ || ProcessList.procStatesDifferForMem(
+ state.getCurProcState(), state.getSetProcState())) {
+ state.setLastStateTime(now);
forceUpdatePssTime = true;
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Process state change from "
- + ProcessList.makeProcStateString(app.setProcState) + " to "
- + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
+ + ProcessList.makeProcStateString(state.getSetProcState()) + " to "
+ + ProcessList.makeProcStateString(state.getCurProcState()) + " next pss in "
+ (app.mProfile.getNextPssTime() - now) + ": " + app);
}
}
synchronized (mService.mAppProfiler.mProfilerLock) {
- app.mProfile.updateProcState(app);
+ app.mProfile.updateProcState(app.mState);
mService.mAppProfiler.updateNextPssTimeLPf(
- app.getCurProcState(), app.mProfile, now, forceUpdatePssTime);
+ state.getCurProcState(), app.mProfile, now, forceUpdatePssTime);
}
- if (app.setProcState != app.getCurProcState()) {
+ if (state.getSetProcState() != state.getCurProcState()) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
String msg = "Proc state change of " + app.processName
- + " to " + ProcessList.makeProcStateString(app.getCurProcState())
- + " (" + app.getCurProcState() + ")" + ": " + app.adjType;
+ + " to " + ProcessList.makeProcStateString(state.getCurProcState())
+ + " (" + state.getCurProcState() + ")" + ": " + state.getAdjType();
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
- boolean setImportant = app.setProcState < PROCESS_STATE_SERVICE;
- boolean curImportant = app.getCurProcState() < PROCESS_STATE_SERVICE;
+ boolean setImportant = state.getSetProcState() < PROCESS_STATE_SERVICE;
+ boolean curImportant = state.getCurProcState() < PROCESS_STATE_SERVICE;
if (setImportant && !curImportant) {
// This app is no longer something we consider important enough to allow to use
// arbitrary amounts of battery power. Note its current CPU time to later know to
// kill it if it is not behaving well.
- app.setWhenUnimportant(now);
+ state.setWhenUnimportant(now);
app.mProfile.mLastCpuTime.set(0);
}
// Inform UsageStats of important process state change
// Must be called before updating setProcState
- maybeUpdateUsageStatsLocked(app, nowElapsed);
+ maybeUpdateUsageStatsLSP(app, nowElapsed);
- maybeUpdateLastTopTime(app, now);
+ maybeUpdateLastTopTime(state, now);
- app.setProcState = app.getCurProcState();
- if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
- app.notCachedSinceIdle = false;
+ state.setSetProcState(state.getCurProcState());
+ if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) {
+ state.setNotCachedSinceIdle(false);
}
if (!doingAll) {
- mService.setProcessTrackerStateLocked(app,
+ mService.setProcessTrackerStateLOSP(app,
mService.mProcessStats.getMemFactorLocked(), now);
} else {
- app.procStateChanged = true;
+ state.setProcStateChanged(true);
}
- } else if (app.reportedInteraction && (nowElapsed - app.getInteractionEventTime())
+ } else if (state.hasReportedInteraction() && (nowElapsed - state.getInteractionEventTime())
> mConstants.USAGE_STATS_INTERACTION_INTERVAL) {
// For apps that sit around for a long time in the interactive state, we need
// to report this at least once a day so they don't go idle.
- maybeUpdateUsageStatsLocked(app, nowElapsed);
- } else if (!app.reportedInteraction && (nowElapsed - app.getFgInteractionTime())
+ maybeUpdateUsageStatsLSP(app, nowElapsed);
+ } else if (!state.hasReportedInteraction() && (nowElapsed - state.getFgInteractionTime())
> mConstants.SERVICE_USAGE_INTERACTION_TIME) {
// For foreground services that sit around for a long time but are not interacted with.
- maybeUpdateUsageStatsLocked(app, nowElapsed);
+ maybeUpdateUsageStatsLSP(app, nowElapsed);
}
- if (app.curCapability != app.setCapability) {
+ if (state.getCurCapability() != state.getSetCapability()) {
changes |= ActivityManagerService.ProcessChangeItem.CHANGE_CAPABILITY;
- app.setCapability = app.curCapability;
+ state.setSetCapability(state.getCurCapability());
}
if (changes != 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Changes in " + app + ": " + changes);
ActivityManagerService.ProcessChangeItem item =
- mProcessList.enqueueProcessChangeItemLocked(app.pid, app.info.uid);
+ mProcessList.enqueueProcessChangeItemLocked(app.getPid(), app.info.uid);
item.changes |= changes;
- item.foregroundActivities = app.repForegroundActivities;
- item.capability = app.setCapability;
+ item.foregroundActivities = state.hasRepForegroundActivities();
+ item.capability = state.getSetCapability();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Item " + Integer.toHexString(System.identityHashCode(item))
+ " " + app.toShortString() + ": changes=" + item.changes
+ " foreground=" + item.foregroundActivities
- + " type=" + app.adjType + " source=" + app.adjSource
- + " target=" + app.adjTarget + " capability=" + item.capability);
+ + " type=" + state.getAdjType() + " source=" + state.getAdjSource()
+ + " target=" + state.getAdjTarget() + " capability=" + item.capability);
}
return success;
}
- @GuardedBy("mService")
- void setAttachingSchedGroupLocked(ProcessRecord app) {
+ @GuardedBy({"mService", "mProcLock"})
+ void setAttachingSchedGroupLSP(ProcessRecord app) {
int initialSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ final ProcessStateRecord state = app.mState;
// If the process has been marked as foreground via Zygote.START_FLAG_USE_TOP_APP_PRIORITY,
// then verify that the top priority is actually is applied.
- if (app.hasForegroundActivities()) {
+ if (state.hasForegroundActivities()) {
String fallbackReason = null;
try {
- // The priority must be the same as how does {@link #applyOomAdjLocked} set for
+ // The priority must be the same as how does {@link #applyOomAdjLSP} set for
// {@link ProcessList.SCHED_GROUP_TOP_APP}. We don't check render thread because it
// is not ready when attaching.
- if (Process.getProcessGroup(app.pid) == THREAD_GROUP_TOP_APP) {
+ if (Process.getProcessGroup(app.getPid()) == THREAD_GROUP_TOP_APP) {
app.getWindowProcessController().onTopProcChanged();
- setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
+ setThreadPriority(app.getPid(), TOP_APP_PRIORITY_BOOST);
} else {
fallbackReason = "not expected top priority";
}
@@ -2627,23 +2700,27 @@
}
}
- app.setCurrentSchedulingGroup(app.setSchedGroup = initialSchedGroup);
+ state.setSetSchedGroup(initialSchedGroup);
+ state.setCurrentSchedulingGroup(initialSchedGroup);
}
// ONLY used for unit testing in OomAdjusterTests.java
@VisibleForTesting
void maybeUpdateUsageStats(ProcessRecord app, long nowElapsed) {
synchronized (mService) {
- maybeUpdateUsageStatsLocked(app, nowElapsed);
+ synchronized (mProcLock) {
+ maybeUpdateUsageStatsLSP(app, nowElapsed);
+ }
}
}
- @GuardedBy("mService")
- private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
+ @GuardedBy({"mService", "mProcLock"})
+ private void maybeUpdateUsageStatsLSP(ProcessRecord app, long nowElapsed) {
+ final ProcessStateRecord state = app.mState;
if (DEBUG_USAGE_STATS) {
Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
- + "] state changes: old = " + app.setProcState + ", new = "
- + app.getCurProcState());
+ + "] state changes: old = " + state.getSetProcState() + ", new = "
+ + state.getCurProcState());
}
if (mService.mUsageStatsService == null) {
return;
@@ -2652,27 +2729,28 @@
// To avoid some abuse patterns, we are going to be careful about what we consider
// to be an app interaction. Being the top activity doesn't count while the display
// is sleeping, nor do short foreground services.
- if (app.getCurProcState() <= PROCESS_STATE_TOP
- || app.getCurProcState() == PROCESS_STATE_BOUND_TOP) {
+ if (state.getCurProcState() <= PROCESS_STATE_TOP
+ || state.getCurProcState() == PROCESS_STATE_BOUND_TOP) {
isInteraction = true;
- app.setFgInteractionTime(0);
- } else if (app.getCurProcState() <= PROCESS_STATE_FOREGROUND_SERVICE) {
- if (app.getFgInteractionTime() == 0) {
- app.setFgInteractionTime(nowElapsed);
+ state.setFgInteractionTime(0);
+ } else if (state.getCurProcState() <= PROCESS_STATE_FOREGROUND_SERVICE) {
+ if (state.getFgInteractionTime() == 0) {
+ state.setFgInteractionTime(nowElapsed);
isInteraction = false;
} else {
- isInteraction = nowElapsed > app.getFgInteractionTime()
+ isInteraction = nowElapsed > state.getFgInteractionTime()
+ mConstants.SERVICE_USAGE_INTERACTION_TIME;
}
} else {
isInteraction =
- app.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND;
- app.setFgInteractionTime(0);
+ state.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND;
+ state.setFgInteractionTime(0);
}
if (isInteraction
- && (!app.reportedInteraction || (nowElapsed - app.getInteractionEventTime())
- > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
- app.setInteractionEventTime(nowElapsed);
+ && (!state.hasReportedInteraction()
+ || (nowElapsed - state.getInteractionEventTime())
+ > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
+ state.setInteractionEventTime(nowElapsed);
String[] packages = app.getPackageList();
if (packages != null) {
for (int i = 0; i < packages.length; i++) {
@@ -2681,16 +2759,16 @@
}
}
}
- app.reportedInteraction = isInteraction;
+ state.setReportedInteraction(isInteraction);
if (!isInteraction) {
- app.setInteractionEventTime(0);
+ state.setInteractionEventTime(0);
}
}
- private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
- if (app.setProcState <= PROCESS_STATE_TOP
- && app.getCurProcState() > PROCESS_STATE_TOP) {
- app.lastTopTime = nowUptime;
+ private void maybeUpdateLastTopTime(ProcessStateRecord state, long nowUptime) {
+ if (state.getSetProcState() <= PROCESS_STATE_TOP
+ && state.getCurProcState() > PROCESS_STATE_TOP) {
+ state.setLastTopTime(nowUptime);
}
}
@@ -2712,13 +2790,15 @@
}
for (int i = N - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
- final long bgTime = uidRec.lastBackgroundTime;
- if (bgTime > 0 && !uidRec.idle) {
+ final long bgTime = uidRec.getLastBackgroundTime();
+ if (bgTime > 0 && !uidRec.isIdle()) {
if (bgTime <= maxBgTime) {
- EventLogTags.writeAmUidIdle(uidRec.uid);
- uidRec.idle = true;
- uidRec.setIdle = true;
- mService.doStopUidLocked(uidRec.uid, uidRec);
+ EventLogTags.writeAmUidIdle(uidRec.getUid());
+ synchronized (mProcLock) {
+ uidRec.setIdle(true);
+ uidRec.setSetIdle(true);
+ }
+ mService.doStopUidLocked(uidRec.getUid(), uidRec);
} else {
if (nextTime == 0 || nextTime > bgTime) {
nextTime = bgTime;
@@ -2736,35 +2816,35 @@
}
}
- @GuardedBy("mService")
- void setAppIdTempAllowlistStateLocked(int uid, boolean onAllowlist) {
+ @GuardedBy({"mService", "mProcLock"})
+ void setAppIdTempAllowlistStateLSP(int uid, boolean onAllowlist) {
boolean changed = false;
for (int i = mActiveUids.size() - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
- if (uidRec.uid == uid && uidRec.mCurAllowlist != onAllowlist) {
- uidRec.mCurAllowlist = onAllowlist;
+ if (uidRec.getUid() == uid && uidRec.isCurAllowListed() != onAllowlist) {
+ uidRec.setCurAllowListed(onAllowlist);
changed = true;
}
}
if (changed) {
- updateOomAdjLocked(OOM_ADJ_REASON_ALLOWLIST);
+ updateOomAdjLSP(OOM_ADJ_REASON_ALLOWLIST);
}
}
- @GuardedBy("mService")
- void setUidTempAllowlistStateLocked(int uid, boolean onAllowlist) {
+ @GuardedBy({"mService", "mProcLock"})
+ void setUidTempAllowlistStateLSP(int uid, boolean onAllowlist) {
boolean changed = false;
final UidRecord uidRec = mActiveUids.get(uid);
- if (uidRec != null && uidRec.mCurAllowlist != onAllowlist) {
- uidRec.mCurAllowlist = onAllowlist;
- updateOomAdjLocked(OOM_ADJ_REASON_ALLOWLIST);
+ if (uidRec != null && uidRec.isCurAllowListed() != onAllowlist) {
+ uidRec.setCurAllowListed(onAllowlist);
+ updateOomAdjLSP(OOM_ADJ_REASON_ALLOWLIST);
}
}
@GuardedBy("mService")
void dumpProcessListVariablesLocked(ProtoOutputStream proto) {
proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
- proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.mLruSeq);
+ proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.getLruSeqLOSP());
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS,
mNumNonCachedProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
@@ -2775,19 +2855,19 @@
@GuardedBy("mService")
void dumpSequenceNumbersLocked(PrintWriter pw) {
- pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
+ pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.getLruSeqLOSP());
}
@GuardedBy("mService")
void dumpProcCountsLocked(PrintWriter pw) {
pw.println(" mNumNonCachedProcs=" + mNumNonCachedProcs
- + " (" + mProcessList.getLruSizeLocked() + " total)"
+ + " (" + mProcessList.getLruSizeLOSP() + " total)"
+ " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
+ " mNumServiceProcs=" + mNumServiceProcs
+ " mNewNumServiceProcs=" + mNewNumServiceProcs);
}
- @GuardedBy("mService")
+ @GuardedBy("mProcLock")
void dumpCachedAppOptimizerSettings(PrintWriter pw) {
mCachedAppOptimizer.dump(pw);
}
@@ -2797,22 +2877,25 @@
mCacheOomRanker.dump(pw);
}
- @GuardedBy("mService")
- void updateAppFreezeStateLocked(ProcessRecord app) {
+ @GuardedBy({"mService", "mProcLock"})
+ private void updateAppFreezeStateLSP(ProcessRecord app) {
if (!mCachedAppOptimizer.useFreezer()) {
return;
}
+ final ProcessCachedOptimizerRecord opt = app.mOptRecord;
// if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze
- if (app.frozen && app.shouldNotFreeze) {
- mCachedAppOptimizer.unfreezeAppLocked(app);
+ if (opt.isFrozen() && opt.shouldNotFreeze()) {
+ mCachedAppOptimizer.unfreezeAppLSP(app);
}
+ final ProcessStateRecord state = app.mState;
// Use current adjustment when freezing, set adjustment when unfreezing.
- if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && !app.frozen && !app.shouldNotFreeze) {
- mCachedAppOptimizer.freezeAppAsync(app);
- } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ && app.frozen) {
- mCachedAppOptimizer.unfreezeAppLocked(app);
+ if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()
+ && !opt.shouldNotFreeze()) {
+ mCachedAppOptimizer.freezeAppAsyncLSP(app);
+ } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ && opt.isFrozen()) {
+ mCachedAppOptimizer.unfreezeAppLSP(app);
}
}
}
diff --git a/services/core/java/com/android/server/am/PackageList.java b/services/core/java/com/android/server/am/PackageList.java
index 978bcb7..aa10d52 100644
--- a/services/core/java/com/android/server/am/PackageList.java
+++ b/services/core/java/com/android/server/am/PackageList.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.annotation.NonNull;
import android.content.pm.VersionedPackage;
import android.util.ArrayMap;
@@ -73,7 +74,7 @@
}
}
- void forEachPackage(Consumer<String> callback) {
+ void forEachPackage(@NonNull Consumer<String> callback) {
synchronized (this) {
for (int i = 0, size = mPkgList.size(); i < size; i++) {
callback.accept(mPkgList.keyAt(i));
@@ -81,7 +82,7 @@
}
}
- void forEachPackage(BiConsumer<String, ProcessStats.ProcessStateHolder> callback) {
+ void forEachPackage(@NonNull BiConsumer<String, ProcessStats.ProcessStateHolder> callback) {
synchronized (this) {
for (int i = 0, size = mPkgList.size(); i < size; i++) {
callback.accept(mPkgList.keyAt(i), mPkgList.valueAt(i));
@@ -89,7 +90,16 @@
}
}
- <R> R forEachPackage(Function<String, R> callback) {
+ /**
+ * Search in the package list, invoke the given {@code callback} with each of the package names
+ * in that list; if the callback returns a non-null object, halt the search, return that
+ * object as the return value of this search function.
+ *
+ * @param callback The callback interface to accept the current package name; if it returns
+ * a non-null object, the search will be halted and this object will be used
+ * as the return value of this search function.
+ */
+ <R> R searchEachPackage(@NonNull Function<String, R> callback) {
synchronized (this) {
for (int i = 0, size = mPkgList.size(); i < size; i++) {
R r = callback.apply(mPkgList.keyAt(i));
@@ -101,7 +111,7 @@
return null;
}
- void forEachPackageProcessStats(Consumer<ProcessStats.ProcessStateHolder> callback) {
+ void forEachPackageProcessStats(@NonNull Consumer<ProcessStats.ProcessStateHolder> callback) {
synchronized (this) {
for (int i = 0, size = mPkgList.size(); i < size; i++) {
callback.accept(mPkgList.valueAt(i));
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 631b632..0eb48f6 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -24,7 +24,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
@@ -65,7 +64,8 @@
boolean canceled = false;
/**
* Map IBinder to duration specified as Pair<Long, Integer>, Long is allowlist duration in
- * milliseconds, Integer is allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * milliseconds, Integer is allowlist type defined at
+ * {@link android.os.PowerWhitelistManager.TempAllowListType}
*/
private ArrayMap<IBinder, Pair<Long, Integer>> mWhitelistDuration;
private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index 37b1741..4f3438fe 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -148,13 +148,14 @@
@GuardedBy({"mLock", "mService.mPidsSelfLocked"})
private void lookForPhantomProcessesLocked(ProcessRecord app) {
- if (app.appZygote || app.killed || app.killedByAm) {
+ if (app.appZygote || app.isKilled() || app.isKilledByAm()) {
// process forked from app zygote doesn't have its own acct entry
return;
}
- InputStream input = mCgroupProcsFds.get(app.pid);
+ final int appPid = app.getPid();
+ InputStream input = mCgroupProcsFds.get(appPid);
if (input == null) {
- final String path = getCgroupFilePath(app.info.uid, app.pid);
+ final String path = getCgroupFilePath(app.info.uid, appPid);
try {
input = mInjector.openCgroupProcs(path);
} catch (FileNotFoundException | SecurityException e) {
@@ -164,7 +165,7 @@
return;
}
// Keep the FD open for better performance
- mCgroupProcsFds.put(app.pid, input);
+ mCgroupProcsFds.put(appPid, input);
}
final byte[] buf = mDataBuffer;
try {
@@ -180,7 +181,7 @@
for (int i = 0; i < read; i++) {
final byte b = buf[i];
if (b == '\n') {
- addChildPidLocked(app, pid);
+ addChildPidLocked(app, pid, appPid);
pid = 0;
} else {
pid = pid * 10 + (b - '0');
@@ -193,14 +194,14 @@
}
} while (true);
if (pid != 0) {
- addChildPidLocked(app, pid);
+ addChildPidLocked(app, pid, appPid);
}
// rewind the fd for the next read
input.skip(-totalRead);
} catch (IOException e) {
Slog.e(TAG, "Error in reading cgroup procs from " + app, e);
IoUtils.closeQuietly(input);
- mCgroupProcsFds.delete(app.pid);
+ mCgroupProcsFds.delete(appPid);
}
}
@@ -232,8 +233,8 @@
}
@GuardedBy({"mLock", "mService.mPidsSelfLocked"})
- private void addChildPidLocked(final ProcessRecord app, final int pid) {
- if (app.pid != pid) {
+ private void addChildPidLocked(final ProcessRecord app, final int pid, final int appPid) {
+ if (appPid != pid) {
// That's something else...
final ProcessRecord r = mService.mPidsSelfLocked.get(pid);
if (r != null) {
@@ -328,15 +329,16 @@
if (r != null) {
// It's a phantom process, bookkeep it
try {
+ final int appPid = r.getPid();
final PhantomProcessRecord proc = new PhantomProcessRecord(
- processName, uid, pid, r.pid, mService,
+ processName, uid, pid, appPid, mService,
this::onPhantomProcessKilledLocked);
proc.mUpdateSeq = mUpdateSeq;
mPhantomProcesses.put(pid, proc);
- SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(r.pid);
+ SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(appPid);
if (array == null) {
array = new SparseArray<>();
- mAppPhantomProcessMap.put(r.pid, array);
+ mAppPhantomProcessMap.put(appPid, array);
}
array.put(pid, proc);
if (proc.mPidFd != null) {
@@ -414,40 +416,42 @@
* order of the oom adjs of their parent process.
*/
void trimPhantomProcessesIfNecessary() {
- synchronized (mLock) {
- mTrimPhantomProcessScheduled = false;
- if (mService.mConstants.MAX_PHANTOM_PROCESSES < mPhantomProcesses.size()) {
- for (int i = mPhantomProcesses.size() - 1; i >= 0; i--) {
- mTempPhantomProcesses.add(mPhantomProcesses.valueAt(i));
+ synchronized (mService.mProcLock) {
+ synchronized (mLock) {
+ mTrimPhantomProcessScheduled = false;
+ if (mService.mConstants.MAX_PHANTOM_PROCESSES < mPhantomProcesses.size()) {
+ for (int i = mPhantomProcesses.size() - 1; i >= 0; i--) {
+ mTempPhantomProcesses.add(mPhantomProcesses.valueAt(i));
+ }
+ synchronized (mService.mPidsSelfLocked) {
+ Collections.sort(mTempPhantomProcesses, (a, b) -> {
+ final ProcessRecord ra = mService.mPidsSelfLocked.get(a.mPpid);
+ if (ra == null) {
+ // parent is gone, this process should have been killed too
+ return 1;
+ }
+ final ProcessRecord rb = mService.mPidsSelfLocked.get(b.mPpid);
+ if (rb == null) {
+ // parent is gone, this process should have been killed too
+ return -1;
+ }
+ if (ra.mState.getCurAdj() != rb.mState.getCurAdj()) {
+ return ra.mState.getCurAdj() - rb.mState.getCurAdj();
+ }
+ if (a.mKnownSince != b.mKnownSince) {
+ // In case of identical oom adj, younger one first
+ return a.mKnownSince < b.mKnownSince ? 1 : -1;
+ }
+ return 0;
+ });
+ }
+ for (int i = mTempPhantomProcesses.size() - 1;
+ i >= mService.mConstants.MAX_PHANTOM_PROCESSES; i--) {
+ final PhantomProcessRecord proc = mTempPhantomProcesses.get(i);
+ proc.killLocked("Trimming phantom processes", true);
+ }
+ mTempPhantomProcesses.clear();
}
- synchronized (mService.mPidsSelfLocked) {
- Collections.sort(mTempPhantomProcesses, (a, b) -> {
- final ProcessRecord ra = mService.mPidsSelfLocked.get(a.mPpid);
- if (ra == null) {
- // parent is gone, this process should have been killed too
- return 1;
- }
- final ProcessRecord rb = mService.mPidsSelfLocked.get(b.mPpid);
- if (rb == null) {
- // parent is gone, this process should have been killed too
- return -1;
- }
- if (ra.curAdj != rb.curAdj) {
- return ra.curAdj - rb.curAdj;
- }
- if (a.mKnownSince != b.mKnownSince) {
- // In case of identical oom adj, younger one first
- return a.mKnownSince < b.mKnownSince ? 1 : -1;
- }
- return 0;
- });
- }
- for (int i = mTempPhantomProcesses.size() - 1;
- i >= mService.mConstants.MAX_PHANTOM_PROCESSES; i--) {
- final PhantomProcessRecord proc = mTempPhantomProcesses.get(i);
- proc.killLocked("Trimming phantom processes", true);
- }
- mTempPhantomProcesses.clear();
}
}
}
@@ -498,7 +502,7 @@
}
}
// Lastly, kill the parent process too
- app.kill("Caused by child process: " + msg, reasonCode, subReason, true);
+ app.killLocked("Caused by child process: " + msg, reasonCode, subReason, true);
}
/**
@@ -508,7 +512,7 @@
void forEachPhantomProcessOfApp(final ProcessRecord app,
final Function<PhantomProcessRecord, Boolean> callback) {
synchronized (mLock) {
- int index = mAppPhantomProcessMap.indexOfKey(app.pid);
+ int index = mAppPhantomProcessMap.indexOfKey(app.getPid());
if (index >= 0) {
final SparseArray<PhantomProcessRecord> array =
mAppPhantomProcessMap.valueAt(index);
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
new file mode 100644
index 0000000..4643610
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+
+/**
+ * The state info of app when it's cached, used by the optimizer.
+ */
+final class ProcessCachedOptimizerRecord {
+ private final ProcessRecord mApp;
+
+ private final ActivityManagerGlobalLock mProcLock;
+
+ /**
+ * The last time that this process was compacted.
+ */
+ @GuardedBy("mProcLock")
+ private long mLastCompactTime;
+
+ /**
+ * The most recent compaction action requested for this app.
+ */
+ @GuardedBy("mProcLock")
+ private int mReqCompactAction;
+
+ /**
+ * The most recent compaction action performed for this app.
+ */
+ @GuardedBy("mProcLock")
+ private int mLastCompactAction;
+
+ /**
+ * True when the process is frozen.
+ */
+ @GuardedBy("mProcLock")
+ private boolean mFrozen;
+
+ /**
+ * An override on the freeze state is in progress.
+ */
+ @GuardedBy("mProcLock")
+ boolean mFreezerOverride;
+
+ /**
+ * Last time the app was (un)frozen, 0 for never.
+ */
+ @GuardedBy("mProcLock")
+ private long mFreezeUnfreezeTime;
+
+ /**
+ * True if a process has a WPRI binding from an unfrozen process.
+ */
+ @GuardedBy("mProcLock")
+ private boolean mShouldNotFreeze;
+
+ @GuardedBy("mProcLock")
+ long getLastCompactTime() {
+ return mLastCompactTime;
+ }
+
+ @GuardedBy("mProcLock")
+ void setLastCompactTime(long lastCompactTime) {
+ mLastCompactTime = lastCompactTime;
+ }
+
+ @GuardedBy("mProcLock")
+ int getReqCompactAction() {
+ return mReqCompactAction;
+ }
+
+ @GuardedBy("mProcLock")
+ void setReqCompactAction(int reqCompactAction) {
+ mReqCompactAction = reqCompactAction;
+ }
+
+ @GuardedBy("mProcLock")
+ int getLastCompactAction() {
+ return mLastCompactAction;
+ }
+
+ @GuardedBy("mProcLock")
+ void setLastCompactAction(int lastCompactAction) {
+ mLastCompactAction = lastCompactAction;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean isFrozen() {
+ return mFrozen;
+ }
+
+ @GuardedBy("mProcLock")
+ void setFrozen(boolean frozen) {
+ mFrozen = frozen;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean hasFreezerOverride() {
+ return mFreezerOverride;
+ }
+
+ @GuardedBy("mProcLock")
+ void setFreezerOverride(boolean freezerOverride) {
+ mFreezerOverride = freezerOverride;
+ }
+
+ @GuardedBy("mProcLock")
+ long getFreezeUnfreezeTime() {
+ return mFreezeUnfreezeTime;
+ }
+
+ @GuardedBy("mProcLock")
+ void setFreezeUnfreezeTime(long freezeUnfreezeTime) {
+ mFreezeUnfreezeTime = freezeUnfreezeTime;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean shouldNotFreeze() {
+ return mShouldNotFreeze;
+ }
+
+ @GuardedBy("mProcLock")
+ void setShouldNotFreeze(boolean shouldNotFreeze) {
+ mShouldNotFreeze = shouldNotFreeze;
+ }
+
+ ProcessCachedOptimizerRecord(ProcessRecord app) {
+ mApp = app;
+ mProcLock = app.mService.mProcLock;
+ }
+
+ void init(long nowUptime) {
+ mFreezeUnfreezeTime = nowUptime;
+ }
+
+ @GuardedBy("mProcLock")
+ void dump(PrintWriter pw, String prefix, long nowUptime) {
+ pw.print(prefix); pw.print("lastCompactTime="); pw.print(mLastCompactTime);
+ pw.print(" lastCompactAction="); pw.println(mLastCompactAction);
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
new file mode 100644
index 0000000..1653123
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ProcessRecord.TAG;
+
+import android.app.ActivityManager;
+import android.app.ApplicationErrorReport;
+import android.app.ApplicationExitInfo;
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IncrementalStatesInfo;
+import android.content.pm.PackageManagerInternal;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.EventLog;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.CompositeRWLock;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.MemoryPressureUtil;
+import com.android.server.wm.WindowProcessController;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+/**
+ * The error state of the process, such as if it's crashing/ANR etc.
+ */
+class ProcessErrorStateRecord {
+ final ProcessRecord mApp;
+ private final ActivityManagerService mService;
+
+ private final ActivityManagerGlobalLock mProcLock;
+
+ /**
+ * True if disabled in the bad process list.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mBad;
+
+ /**
+ * Are we in the process of crashing?
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mCrashing;
+
+ /**
+ * Suppress normal auto-dismiss of crash dialog & report UI?
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mForceCrashReport;
+
+ /**
+ * Does the app have a not responding dialog?
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mNotResponding;
+
+ /**
+ * The report about crash of the app, generated & stored when an app gets into a crash.
+ * Will be "null" when all is OK.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private ActivityManager.ProcessErrorStateInfo mCrashingReport;
+
+ /**
+ * The report about ANR of the app, generated & stored when an app gets into an ANR.
+ * Will be "null" when all is OK.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private ActivityManager.ProcessErrorStateInfo mNotRespondingReport;
+
+ /**
+ * Controller for error dialogs.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private final ErrorDialogController mDialogController;
+
+ /**
+ * Who will be notified of the error. This is usually an activity in the
+ * app that installed the package.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private ComponentName mErrorReportReceiver;
+
+ /**
+ * Optional local handler to be invoked in the process crash.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private Runnable mCrashHandler;
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isBad() {
+ return mBad;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setBad(boolean bad) {
+ mBad = bad;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isCrashing() {
+ return mCrashing;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCrashing(boolean crashing) {
+ mCrashing = crashing;
+ mApp.getWindowProcessController().setCrashing(crashing);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isForceCrashReport() {
+ return mForceCrashReport;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setForceCrashReport(boolean forceCrashReport) {
+ mForceCrashReport = forceCrashReport;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isNotResponding() {
+ return mNotResponding;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setNotResponding(boolean notResponding) {
+ mNotResponding = notResponding;
+ mApp.getWindowProcessController().setNotResponding(notResponding);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ Runnable getCrashHandler() {
+ return mCrashHandler;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCrashHandler(Runnable crashHandler) {
+ mCrashHandler = crashHandler;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ActivityManager.ProcessErrorStateInfo getCrashingReport() {
+ return mCrashingReport;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCrashingReport(ActivityManager.ProcessErrorStateInfo crashingReport) {
+ mCrashingReport = crashingReport;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ActivityManager.ProcessErrorStateInfo getNotRespondingReport() {
+ return mNotRespondingReport;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setNotRespondingReport(ActivityManager.ProcessErrorStateInfo notRespondingReport) {
+ mNotRespondingReport = notRespondingReport;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ComponentName getErrorReportReceiver() {
+ return mErrorReportReceiver;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setErrorReportReceiver(ComponentName errorReportReceiver) {
+ mErrorReportReceiver = errorReportReceiver;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ErrorDialogController getDialogController() {
+ return mDialogController;
+ }
+
+ ProcessErrorStateRecord(ProcessRecord app) {
+ mApp = app;
+ mService = app.mService;
+ mProcLock = mService.mProcLock;
+ mDialogController = new ErrorDialogController(app);
+ }
+
+ void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
+ String parentShortComponentName, WindowProcessController parentProcess,
+ boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
+ ArrayList<Integer> firstPids = new ArrayList<>(5);
+ SparseArray<Boolean> lastPids = new SparseArray<>(20);
+
+ mApp.getWindowProcessController().appEarlyNotResponding(annotation, () -> {
+ synchronized (mService) {
+ mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
+ }
+ });
+
+ long anrTime = SystemClock.uptimeMillis();
+ if (isMonitorCpuUsage()) {
+ mService.updateCpuStatsNow();
+ }
+
+ final boolean isSilentAnr;
+ final int pid = mApp.getPid();
+ synchronized (mService) {
+ // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
+ if (mService.mAtmInternal.isShuttingDown()) {
+ Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
+ return;
+ } else if (isNotResponding()) {
+ Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
+ return;
+ } else if (isCrashing()) {
+ Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
+ return;
+ } else if (mApp.isKilledByAm()) {
+ Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
+ return;
+ } else if (mApp.isKilled()) {
+ Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
+ return;
+ }
+
+ // In case we come through here for the same app before completing
+ // this one, mark as anring now so we will bail out.
+ synchronized (mProcLock) {
+ setNotResponding(true);
+ }
+
+ // Log the ANR to the event log.
+ EventLog.writeEvent(EventLogTags.AM_ANR, mApp.userId, pid, mApp.processName,
+ mApp.info.flags, annotation);
+
+ // Dump thread traces as quickly as we can, starting with "interesting" processes.
+ firstPids.add(pid);
+
+ // Don't dump other PIDs if it's a background ANR or is requested to only dump self.
+ isSilentAnr = isSilentAnr();
+ if (!isSilentAnr && !onlyDumpSelf) {
+ int parentPid = pid;
+ if (parentProcess != null && parentProcess.getPid() > 0) {
+ parentPid = parentProcess.getPid();
+ }
+ if (parentPid != pid) firstPids.add(parentPid);
+
+ if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
+
+ final int ppid = parentPid;
+ mService.mProcessList.forEachLruProcessesLOSP(false, r -> {
+ if (r != null && r.getThread() != null) {
+ int myPid = r.getPid();
+ if (myPid > 0 && myPid != pid && myPid != ppid && myPid != MY_PID) {
+ if (r.isPersistent()) {
+ firstPids.add(myPid);
+ if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
+ } else if (r.mServices.isTreatedLikeActivity()) {
+ firstPids.add(myPid);
+ if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
+ } else {
+ lastPids.put(myPid, Boolean.TRUE);
+ if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ // Check if package is still being loaded
+ boolean isPackageLoading = false;
+ final PackageManagerInternal packageManagerInternal = mService.getPackageManagerInternal();
+ if (aInfo != null && aInfo.packageName != null) {
+ IncrementalStatesInfo incrementalStatesInfo =
+ packageManagerInternal.getIncrementalStatesInfo(
+ aInfo.packageName, mApp.uid, mApp.userId);
+ if (incrementalStatesInfo != null) {
+ isPackageLoading = incrementalStatesInfo.isLoading();
+ }
+ }
+
+ // Log the ANR to the main log.
+ StringBuilder info = new StringBuilder();
+ info.setLength(0);
+ info.append("ANR in ").append(mApp.processName);
+ if (activityShortComponentName != null) {
+ info.append(" (").append(activityShortComponentName).append(")");
+ }
+ info.append("\n");
+ info.append("PID: ").append(pid).append("\n");
+ if (annotation != null) {
+ info.append("Reason: ").append(annotation).append("\n");
+ }
+ if (parentShortComponentName != null
+ && parentShortComponentName.equals(activityShortComponentName)) {
+ info.append("Parent: ").append(parentShortComponentName).append("\n");
+ }
+
+ if (isPackageLoading) {
+ // Report in the main log that the package is still loading
+ final float loadingProgress = packageManagerInternal.getIncrementalStatesInfo(
+ aInfo.packageName, mApp.uid, mApp.userId).getProgress();
+ info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
+ }
+
+ StringBuilder report = new StringBuilder();
+ report.append(MemoryPressureUtil.currentPsiState());
+ ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
+
+ // don't dump native PIDs for background ANRs unless it is the process of interest
+ String[] nativeProcs = null;
+ if (isSilentAnr || onlyDumpSelf) {
+ for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
+ if (NATIVE_STACKS_OF_INTEREST[i].equals(mApp.processName)) {
+ nativeProcs = new String[] { mApp.processName };
+ break;
+ }
+ }
+ } else {
+ nativeProcs = NATIVE_STACKS_OF_INTEREST;
+ }
+
+ int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
+ ArrayList<Integer> nativePids = null;
+
+ if (pids != null) {
+ nativePids = new ArrayList<>(pids.length);
+ for (int i : pids) {
+ nativePids.add(i);
+ }
+ }
+
+ // For background ANRs, don't pass the ProcessCpuTracker to
+ // avoid spending 1/2 second collecting stats to rank lastPids.
+ StringWriter tracesFileException = new StringWriter();
+ // To hold the start and end offset to the ANR trace file respectively.
+ final long[] offsets = new long[2];
+ File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+ isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
+ nativePids, tracesFileException, offsets);
+
+ if (isMonitorCpuUsage()) {
+ mService.updateCpuStatsNow();
+ mService.mAppProfiler.printCurrentCpuState(report, anrTime);
+ info.append(processCpuTracker.printCurrentLoad());
+ info.append(report);
+ }
+ report.append(tracesFileException.getBuffer());
+
+ info.append(processCpuTracker.printCurrentState(anrTime));
+
+ Slog.e(TAG, info.toString());
+ if (tracesFile == null) {
+ // There is no trace file, so dump (only) the alleged culprit's threads to the log
+ Process.sendSignal(pid, Process.SIGNAL_QUIT);
+ } else if (offsets[1] > 0) {
+ // We've dumped into the trace file successfully
+ mService.mProcessList.mAppExitInfoTracker.scheduleLogAnrTrace(
+ pid, mApp.uid, mApp.getPackageList(), tracesFile, offsets[0], offsets[1]);
+ }
+
+ FrameworkStatsLog.write(FrameworkStatsLog.ANR_OCCURRED, mApp.uid, mApp.processName,
+ activityShortComponentName == null ? "unknown" : activityShortComponentName,
+ annotation,
+ (mApp.info != null) ? (mApp.info.isInstantApp()
+ ? FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
+ : FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
+ : FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
+ mApp.isInterestingToUserLocked()
+ ? FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
+ : FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND,
+ mApp.getProcessClassEnum(),
+ (mApp.info != null) ? mApp.info.packageName : "", isPackageLoading);
+ final ProcessRecord parentPr = parentProcess != null
+ ? (ProcessRecord) parentProcess.mOwner : null;
+ mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
+ parentShortComponentName, parentPr, annotation, report.toString(), tracesFile,
+ null);
+
+ if (mApp.getWindowProcessController().appNotResponding(info.toString(),
+ () -> {
+ synchronized (mService) {
+ mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
+ }
+ },
+ () -> {
+ synchronized (mService) {
+ mService.mServices.scheduleServiceTimeoutLocked(mApp);
+ }
+ })) {
+ return;
+ }
+
+ // Retrieve max ANR delay from AnrControllers without the mService lock since the
+ // controllers might in turn call into apps
+ long anrDialogDelayMs = mService.mActivityTaskManager.getMaxAnrDelayMillis(aInfo);
+ if (aInfo != null && aInfo.packageName != null && anrDialogDelayMs > 0) {
+ Slog.i(TAG, "Delaying ANR dialog for " + aInfo.packageName + " for " + anrDialogDelayMs
+ + "ms");
+ }
+
+ synchronized (mService) {
+ // mBatteryStatsService can be null if the AMS is constructed with injector only. This
+ // will only happen in tests.
+ if (mService.mBatteryStatsService != null) {
+ mService.mBatteryStatsService.noteProcessAnr(mApp.processName, mApp.uid);
+ }
+
+ if (isSilentAnr() && !mApp.isDebugging()) {
+ mApp.killLocked("bg anr", ApplicationExitInfo.REASON_ANR, true);
+ return;
+ }
+
+ synchronized (mProcLock) {
+ // Set the app's notResponding state, and look up the errorReportReceiver
+ makeAppNotRespondingLSP(activityShortComponentName,
+ annotation != null ? "ANR " + annotation : "ANR", info.toString());
+ }
+
+ // Notify package manager service to possibly update package state
+ if (aInfo != null && aInfo.packageName != null) {
+ packageManagerInternal.notifyPackageCrashOrAnr(aInfo.packageName);
+ }
+
+ // mUiHandler can be null if the AMS is constructed with injector only. This will only
+ // happen in tests.
+ if (mService.mUiHandler != null) {
+ // Bring up the infamous App Not Responding dialog
+ Message msg = Message.obtain();
+ msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
+ msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
+
+ mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
+ }
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void makeAppNotRespondingLSP(String activity, String shortMsg, String longMsg) {
+ setNotResponding(true);
+ // mAppErrors can be null if the AMS is constructed with injector only. This will only
+ // happen in tests.
+ if (mService.mAppErrors != null) {
+ mNotRespondingReport = mService.mAppErrors.generateProcessError(mApp,
+ ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
+ activity, shortMsg, longMsg, null);
+ }
+ startAppProblemLSP();
+ mApp.getWindowProcessController().stopFreezingActivities();
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void startAppProblemLSP() {
+ // If this app is not running under the current user, then we can't give it a report button
+ // because that would require launching the report UI under a different user.
+ mErrorReportReceiver = null;
+
+ for (int userId : mService.mUserController.getCurrentProfileIds()) {
+ if (mApp.userId == userId) {
+ mErrorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
+ mService.mContext, mApp.info.packageName, mApp.info.flags);
+ }
+ }
+ mService.skipCurrentReceiverLocked(mApp);
+ }
+
+ @GuardedBy("mService")
+ private boolean isInterestingForBackgroundTraces() {
+ // The system_server is always considered interesting.
+ if (mApp.getPid() == MY_PID) {
+ return true;
+ }
+
+ // A package is considered interesting if any of the following is true :
+ //
+ // - It's displaying an activity.
+ // - It's the SystemUI.
+ // - It has an overlay or a top UI visible.
+ //
+ // NOTE: The check whether a given ProcessRecord belongs to the systemui
+ // process is a bit of a kludge, but the same pattern seems repeated at
+ // several places in the system server.
+ return mApp.isInterestingToUserLocked()
+ || (mApp.info != null && "com.android.systemui".equals(mApp.info.packageName))
+ || (mApp.mState.hasTopUi() || mApp.mState.hasOverlayUi());
+ }
+
+ private boolean getShowBackground() {
+ return Settings.Secure.getInt(mService.mContext.getContentResolver(),
+ Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+ }
+
+ /**
+ * Unless configured otherwise, swallow ANRs in background processes & kill the process.
+ * Non-private access is for tests only.
+ */
+ @VisibleForTesting
+ @GuardedBy("mService")
+ boolean isSilentAnr() {
+ return !getShowBackground() && !isInterestingForBackgroundTraces();
+ }
+
+ /** Non-private access is for tests only. */
+ @VisibleForTesting
+ boolean isMonitorCpuUsage() {
+ return mService.mAppProfiler.MONITOR_CPU_USAGE;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void onCleanupApplicationRecordLSP() {
+ // Dismiss any open dialogs.
+ getDialogController().clearAllErrorDialogs();
+
+ setCrashing(false);
+ setNotResponding(false);
+ }
+
+ void dump(PrintWriter pw, String prefix, long nowUptime) {
+ synchronized (mProcLock) {
+ if (mCrashing || mDialogController.hasCrashDialogs() || mNotResponding
+ || mDialogController.hasAnrDialogs() || mBad) {
+ pw.print(prefix);
+ pw.print(" mCrashing=" + mCrashing);
+ pw.print(" " + mDialogController.getCrashDialogs());
+ pw.print(" mNotResponding=" + mNotResponding);
+ pw.print(" " + mDialogController.getAnrDialogs());
+ pw.print(" bad=" + mBad);
+
+ // mCrashing or mNotResponding is always set before errorReportReceiver
+ if (mErrorReportReceiver != null) {
+ pw.print(" errorReportReceiver=");
+ pw.print(mErrorReportReceiver.flattenToShortString());
+ }
+ pw.println();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bbf927b..0576345 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -55,6 +55,7 @@
import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
import static com.android.server.am.AppProfiler.TAG_PSS;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.AppGlobals;
@@ -114,6 +115,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
@@ -149,6 +151,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Activity manager code dealing with processes.
@@ -372,6 +376,13 @@
private static final long NATIVE_MEMTAG_SYNC = 177438394; // This is a bug id.
/**
+ * Enable automatic zero-initialization of native heap memory allocations.
+ */
+ @ChangeId
+ @Disabled
+ private static final long NATIVE_HEAP_ZERO_INIT = 178038272; // This is a bug id.
+
+ /**
* Enable sampled memory bug detection in the app.
* @see <a href="https://source.android.com/devices/tech/debug/gwp-asan">GWP-ASan</a>.
*/
@@ -466,34 +477,41 @@
* List of running applications, sorted by recent usage.
* The first entry in the list is the least recently used.
*/
- final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
+ @CompositeRWLock({"mService", "mProcLock"})
+ private final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
/**
* Where in mLruProcesses that the processes hosting activities start.
*/
- int mLruProcessActivityStart = 0;
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mLruProcessActivityStart = 0;
/**
* Where in mLruProcesses that the processes hosting services start.
* This is after (lower index) than mLruProcessesActivityStart.
*/
- int mLruProcessServiceStart = 0;
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mLruProcessServiceStart = 0;
/**
* Current sequence id for process LRU updating.
*/
- int mLruSeq = 0;
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mLruSeq = 0;
+ @CompositeRWLock({"mService", "mProcLock"})
ActiveUids mActiveUids;
/**
* The currently running isolated processes.
*/
+ @GuardedBy("mService")
final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
/**
* The currently running application zygotes.
*/
+ @GuardedBy("mService")
final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
/**
@@ -505,6 +523,7 @@
/**
* The processes that are forked off an application zygote.
*/
+ @GuardedBy("mService")
final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();
@@ -532,6 +551,8 @@
*/
private final int[] mZygoteSigChldMessage = new int[3];
+ ActivityManagerGlobalLock mProcLock;
+
final class IsolatedUidRange {
@VisibleForTesting
public final int mFirstUid;
@@ -640,6 +661,7 @@
* The available isolated UIDs for processes that are not spawned from an application zygote.
*/
@VisibleForTesting
+ @GuardedBy("mService")
IsolatedUidRange mGlobalIsolatedUids = new IsolatedUidRange(Process.FIRST_ISOLATED_UID,
Process.LAST_ISOLATED_UID);
@@ -647,6 +669,7 @@
* An allocator for isolated UID ranges for apps that use an application zygote.
*/
@VisibleForTesting
+ @GuardedBy("mService")
IsolatedUidRangeAllocator mAppIsolatedUidRangeAllocator =
new IsolatedUidRangeAllocator(Process.FIRST_APP_ZYGOTE_ISOLATED_UID,
Process.LAST_APP_ZYGOTE_ISOLATED_UID, Process.NUM_UIDS_PER_APP_ZYGOTE);
@@ -654,6 +677,7 @@
/**
* Processes that are being forcibly torn down.
*/
+ @GuardedBy("mService")
final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
// Self locked with the inner lock within the RemoteCallbackList
@@ -674,14 +698,14 @@
*/
private final Object mProcessChangeLock = new Object();
-
/**
* All of the applications we currently have running organized by name.
* The keys are strings of the application package name (as
* returned by the package manager), and the keys are ApplicationRecord
* objects.
*/
- final MyProcessMap mProcessNames = new MyProcessMap();
+ @CompositeRWLock({"mService", "mProcLock"})
+ private final MyProcessMap mProcessNames = new MyProcessMap();
final class MyProcessMap extends ProcessMap<ProcessRecord> {
@Override
@@ -749,6 +773,7 @@
mService = service;
mActiveUids = activeUids;
mPlatformCompat = platformCompat;
+ mProcLock = service.mProcLock;
// Get this after boot, and won't be changed until it's rebooted, as we don't
// want some apps enabled while some apps disabled
mAppDataIsolationEnabled =
@@ -842,13 +867,13 @@
*/
Map<Integer, String> getProcessesWithPendingBindMounts(int userId) {
final Map<Integer, String> pidPackageMap = new HashMap<>();
- synchronized (mService) {
+ synchronized (mProcLock) {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord record = mLruProcesses.get(i);
- if (record.userId != userId || !record.bindMountPending) {
+ if (record.userId != userId || !record.isBindMountPending()) {
continue;
}
- final int pid = record.pid;
+ final int pid = record.getPid();
// It can happen when app process is starting, but zygote work is not done yet so
// system does not this pid record yet.
if (pid == 0) {
@@ -857,8 +882,8 @@
}
pidPackageMap.put(pid, record.info.packageName);
}
- return pidPackageMap;
}
+ return pidPackageMap;
}
private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
@@ -1534,6 +1559,7 @@
}
}
+ @GuardedBy("mService")
final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean
keepIfLarge) {
if (uid == SYSTEM_UID) {
@@ -1554,21 +1580,19 @@
}
ProcessRecord proc = mProcessNames.get(processName, uid);
if (false && proc != null && !keepIfLarge
- && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
+ && proc.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
&& proc.mProfile.getLastCachedPss() >= 4000) {
// Turn this condition on to cause killing to happen regularly, for testing.
- final long lastCachedPss;
synchronized (mService.mAppProfiler.mProfilerLock) {
proc.mProfile.reportCachedKill();
- lastCachedPss = proc.mProfile.getLastCachedPss();
}
- proc.kill(Long.toString(lastCachedPss) + "k from cached",
+ proc.killLocked(Long.toString(proc.mProfile.getLastCachedPss()) + "k from cached",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_LARGE_CACHED,
true);
} else if (proc != null && !keepIfLarge
&& !mService.mAppProfiler.isLastMemoryLevelNormal()
- && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+ && proc.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
final long lastCachedPss;
boolean doKilling = false;
synchronized (mService.mAppProfiler.mProfilerLock) {
@@ -1582,7 +1606,7 @@
Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + lastCachedPss);
}
if (doKilling) {
- proc.kill(Long.toString(lastCachedPss) + "k from cached",
+ proc.killLocked(Long.toString(lastCachedPss) + "k from cached",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_LARGE_CACHED,
true);
@@ -1604,14 +1628,16 @@
outInfo.foregroundAppThreshold = getMemLevel(FOREGROUND_APP_ADJ);
}
- ProcessRecord findAppProcessLocked(IBinder app, String reason) {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ProcessRecord findAppProcessLOSP(IBinder app, String reason) {
final int NP = mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
ProcessRecord p = apps.valueAt(ia);
- if (p.thread != null && p.thread.asBinder() == app) {
+ final IApplicationThread thread = p.getThread();
+ if (thread != null && thread.asBinder() == app) {
return p;
}
}
@@ -1685,12 +1711,28 @@
return gidArray;
}
- // Returns the memory tagging level to be enabled. If memory tagging isn't
- // requested, returns zero.
- private int getMemtagLevel(ProcessRecord app) {
- // Ensure the hardware + kernel actually supports MTE.
- if (!Zygote.nativeSupportsMemoryTagging()) {
- return 0;
+ private int memtagModeToZygoteMemtagLevel(int memtagMode) {
+ switch (memtagMode) {
+ case ApplicationInfo.MEMTAG_ASYNC:
+ return Zygote.MEMORY_TAG_LEVEL_ASYNC;
+ case ApplicationInfo.MEMTAG_SYNC:
+ return Zygote.MEMORY_TAG_LEVEL_SYNC;
+ default:
+ return Zygote.MEMORY_TAG_LEVEL_NONE;
+ }
+ }
+
+ // Returns the requested memory tagging level.
+ private int getRequestedMemtagLevel(ProcessRecord app) {
+ // Look at the process attribute first.
+ if (app.processInfo != null
+ && app.processInfo.memtagMode != ApplicationInfo.MEMTAG_DEFAULT) {
+ return memtagModeToZygoteMemtagLevel(app.processInfo.memtagMode);
+ }
+
+ // Then at the application attribute.
+ if (app.info.getMemtagMode() != ApplicationInfo.MEMTAG_DEFAULT) {
+ return memtagModeToZygoteMemtagLevel(app.info.getMemtagMode());
}
if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_SYNC, app.info)) {
@@ -1701,40 +1743,43 @@
return Zygote.MEMORY_TAG_LEVEL_ASYNC;
}
- return 0;
- }
-
- private boolean shouldEnableTaggedPointers(ProcessRecord app) {
- // Ensure we have platform + kernel support for TBI.
- if (!Zygote.nativeSupportsTaggedPointers()) {
- return false;
- }
-
// Check to ensure the app hasn't explicitly opted-out of TBI via. the manifest attribute.
if (!app.info.allowsNativeHeapPointerTagging()) {
- return false;
+ return Zygote.MEMORY_TAG_LEVEL_NONE;
}
// Check to see that the compat feature for TBI is enabled.
- if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
- return false;
- }
-
- return true;
- }
-
- private int decideTaggingLevel(ProcessRecord app) {
- // Check MTE support first, as it should take precedence over TBI.
- int memtagLevel = getMemtagLevel(app);
- if (memtagLevel != 0) {
- return memtagLevel;
- }
-
- if (shouldEnableTaggedPointers(app)) {
+ if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
return Zygote.MEMORY_TAG_LEVEL_TBI;
}
- return 0;
+ return Zygote.MEMORY_TAG_LEVEL_NONE;
+ }
+
+ private int decideTaggingLevel(ProcessRecord app) {
+ // Get the desired tagging level (app manifest + compat features).
+ int level = getRequestedMemtagLevel(app);
+
+ // Take into account the hardware capabilities.
+ if (Zygote.nativeSupportsMemoryTagging()) {
+ // MTE devices can not do TBI, because the Zygote process already has live MTE
+ // allocations. Downgrade TBI to NONE.
+ if (level == Zygote.MEMORY_TAG_LEVEL_TBI) {
+ level = Zygote.MEMORY_TAG_LEVEL_NONE;
+ }
+ } else if (Zygote.nativeSupportsTaggedPointers()) {
+ // TBI-but-not-MTE devices downgrade MTE modes to TBI.
+ // The idea is that if an app opts into full hardware tagging (MTE), it must be ok with
+ // the "fake" pointer tagging (TBI).
+ if (level == Zygote.MEMORY_TAG_LEVEL_ASYNC || level == Zygote.MEMORY_TAG_LEVEL_SYNC) {
+ level = Zygote.MEMORY_TAG_LEVEL_TBI;
+ }
+ } else {
+ // Otherwise disable all tagging.
+ level = Zygote.MEMORY_TAG_LEVEL_NONE;
+ }
+
+ return level;
}
private int decideGwpAsanLevel(ProcessRecord app) {
@@ -1745,7 +1790,7 @@
? Zygote.GWP_ASAN_LEVEL_ALWAYS
: Zygote.GWP_ASAN_LEVEL_NEVER;
}
- // Then at the applicaton attribute.
+ // Then at the application attribute.
if (app.info.getGwpAsanMode() != ApplicationInfo.GWP_ASAN_DEFAULT) {
return app.info.getGwpAsanMode() == ApplicationInfo.GWP_ASAN_ALWAYS
? Zygote.GWP_ASAN_LEVEL_ALWAYS
@@ -1762,25 +1807,40 @@
return Zygote.GWP_ASAN_LEVEL_NEVER;
}
+ private boolean enableNativeHeapZeroInit(ProcessRecord app) {
+ // Look at the process attribute first.
+ if (app.processInfo != null && app.processInfo.nativeHeapZeroInit != null) {
+ return app.processInfo.nativeHeapZeroInit;
+ }
+ // Then at the application attribute.
+ if (app.info.isNativeHeapZeroInit() != null) {
+ return app.info.isNativeHeapZeroInit();
+ }
+ // Compat feature last.
+ if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_ZERO_INIT, app.info)) {
+ return true;
+ }
+ return false;
+ }
+
/**
* @return {@code true} if process start is successful, false otherwise.
*/
@GuardedBy("mService")
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
- int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
- String abiOverride) {
- if (app.pendingStart) {
+ int zygotePolicyFlags, boolean disableHiddenApiChecks, String abiOverride) {
+ if (app.isPendingStart()) {
return true;
}
long startTime = SystemClock.uptimeMillis();
- if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
+ if (app.getPid() > 0 && app.getPid() != ActivityManagerService.MY_PID) {
checkSlow(startTime, "startProcess: removing from pids map");
- mService.removePidLocked(app);
- app.bindMountPending = false;
+ mService.removePidLocked(app.getPid(), app);
+ app.setBindMountPending(false);
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
checkSlow(startTime, "startProcess: done removing from pids map");
app.setPid(0);
- app.startSeq = 0;
+ app.setStartSeq(0);
}
if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
@@ -1835,7 +1895,7 @@
gids = computeGidsForProcess(mountExternal, uid, permGids);
}
- app.mountMode = mountExternal;
+ app.setMountMode(mountExternal);
checkSlow(startTime, "startProcess: building args");
if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
uid = 0;
@@ -1914,10 +1974,6 @@
throw new IllegalStateException("Invalid API policy: " + policy);
}
runtimeFlags |= policyBits;
-
- if (disableTestApiChecks) {
- runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;
- }
}
String useAppImageCache = SystemProperties.get(
@@ -1955,9 +2011,9 @@
instructionSet = VMRuntime.getInstructionSet(requiredAbi);
}
- app.gids = gids;
+ app.setGids(gids);
app.setRequiredAbi(requiredAbi);
- app.instructionSet = instructionSet;
+ app.setInstructionSet(instructionSet);
// If instructionSet is non-null, this indicates that the system_server is spawning a
// process with an ISA that may be different from its own. System (kernel and hardware)
@@ -1973,6 +2029,10 @@
runtimeFlags |= decideTaggingLevel(app);
}
+ if (enableNativeHeapZeroInit(app)) {
+ runtimeFlags |= Zygote.NATIVE_HEAP_ZERO_INIT;
+ }
+
// the per-user SELinux context must be set
if (TextUtils.isEmpty(app.info.seInfoUser)) {
Slog.wtf(ActivityManagerService.TAG, "SELinux tag not defined",
@@ -2008,23 +2068,26 @@
int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
- app.pendingStart = true;
- app.killedByAm = false;
- app.removed = false;
- app.killed = false;
- if (app.startSeq != 0) {
- Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
- + " with non-zero startSeq:" + app.startSeq);
+ app.setPendingStart(true);
+ app.setRemoved(false);
+ synchronized (mProcLock) {
+ app.setKilledByAm(false);
+ app.setKilled(false);
}
- if (app.pid != 0) {
+ if (app.getStartSeq() != 0) {
Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
- + " with non-zero pid:" + app.pid);
+ + " with non-zero startSeq:" + app.getStartSeq());
}
- app.mDisabledCompatChanges = null;
+ if (app.getPid() != 0) {
+ Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+ + " with non-zero pid:" + app.getPid());
+ }
+ app.setDisabledCompatChanges(null);
if (mPlatformCompat != null) {
- app.mDisabledCompatChanges = mPlatformCompat.getDisabledChanges(app.info);
+ app.setDisabledCompatChanges(mPlatformCompat.getDisabledChanges(app.info));
}
- final long startSeq = app.startSeq = ++mProcStartSeqCounter;
+ final long startSeq = ++mProcStartSeqCounter;
+ app.setStartSeq(startSeq);
app.setStartParams(uid, hostingRecord, seInfo, startTime);
app.setUsingWrapper(invokeWith != null
|| Zygote.getWrapProperty(app.processName) != null);
@@ -2048,11 +2111,11 @@
} catch (RuntimeException e) {
Slog.e(ActivityManagerService.TAG, "Failure starting process "
+ app.processName, e);
- app.pendingStart = false;
+ app.setPendingStart(false);
mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
false, false, true, false, false, app.userId, "start failure");
}
- return app.pid > 0;
+ return app.getPid() > 0;
}
}
@@ -2065,11 +2128,11 @@
final int[] gids, final int runtimeFlags, int zygotePolicyFlags,
final int mountExternal, final String requiredAbi, final String instructionSet,
final String invokeWith, final long startSeq) {
- // If there is a precede instance of the process, wait for its death with a timeout.
+ // If there is a preceding instance of the process, wait for its death with a timeout.
// Use local reference since we are not using locks here
- final ProcessRecord precedence = app.mPrecedence;
- if (precedence != null) {
- final int pid = precedence.pid;
+ final ProcessRecord predecessor = app.mPredecessor;
+ if (predecessor != null) {
+ final int pid = predecessor.getPid();
long now = System.currentTimeMillis();
final long end = now + PROC_KILL_TIMEOUT;
final int oldPolicy = StrictMode.getThreadPolicyMask();
@@ -2077,34 +2140,34 @@
StrictMode.setThreadPolicyMask(0);
Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
// It's killed successfully, but we'd make sure the cleanup work is done.
- synchronized (precedence) {
- if (app.mPrecedence != null) {
+ synchronized (predecessor) {
+ if (app.mPredecessor != null) {
now = System.currentTimeMillis();
if (now < end) {
try {
- precedence.wait(end - now);
+ predecessor.wait(end - now);
} catch (InterruptedException e) {
}
}
}
- if (app.mPrecedence != null) {
+ if (app.mPredecessor != null) {
// The cleanup work hasn't be done yet, let's log it and continue.
- Slog.w(TAG, precedence + " has died, but its cleanup isn't done");
+ Slog.w(TAG, predecessor + " has died, but its cleanup isn't done");
}
}
} catch (Exception e) {
// It's still alive... maybe blocked at uninterruptible sleep ?
- Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
+ Slog.wtf(TAG, predecessor.toString() + " refused to die, but we need to launch "
+ app, e);
} finally {
StrictMode.setThreadPolicyMask(oldPolicy);
}
}
try {
- final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
- entryPoint, app, app.startUid, gids, runtimeFlags, zygotePolicyFlags,
- mountExternal, app.seInfo, requiredAbi, instructionSet, invokeWith,
- app.startTime);
+ final Process.ProcessStartResult startResult = startProcess(app.getHostingRecord(),
+ entryPoint, app, app.getStartUid(), gids, runtimeFlags, zygotePolicyFlags,
+ mountExternal, app.getSeInfo(), requiredAbi, instructionSet, invokeWith,
+ app.getStartTime());
synchronized (mService) {
handleProcessStartedLocked(app, startResult, startSeq);
@@ -2114,7 +2177,7 @@
Slog.e(ActivityManagerService.TAG, "Failure starting process "
+ app.processName, e);
mPendingStarts.remove(startSeq);
- app.pendingStart = false;
+ app.setPendingStart(false);
mService.forceStopPackageLocked(app.info.packageName,
UserHandle.getAppId(app.uid),
false, false, true, false, false, app.userId, "start failure");
@@ -2140,19 +2203,19 @@
// Free the isolated uid for this process
final IsolatedUidRange appUidRange =
mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info.processName,
- app.hostingRecord.getDefiningUid());
+ app.getHostingRecord().getDefiningUid());
if (appUidRange != null) {
appUidRange.freeIsolatedUidLocked(app.uid);
}
final AppZygote appZygote = mAppZygotes.get(app.info.processName,
- app.hostingRecord.getDefiningUid());
+ app.getHostingRecord().getDefiningUid());
if (appZygote != null) {
ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
zygoteProcesses.remove(app);
if (zygoteProcesses.size() == 0) {
mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG);
- if (app.removed) {
+ if (app.isRemoved()) {
// If we stopped this process because the package hosting it was removed,
// there's no point in delaying the app zygote kill.
killAppZygoteIfNeededLocked(appZygote, false /* force */);
@@ -2169,7 +2232,7 @@
synchronized (mService) {
// The UID for the app zygote should be the UID of the application hosting
// the service.
- final int uid = app.hostingRecord.getDefiningUid();
+ final int uid = app.getHostingRecord().getDefiningUid();
AppZygote appZygote = mAppZygotes.get(app.info.processName, uid);
final ArrayList<ProcessRecord> zygoteProcessList;
if (appZygote == null) {
@@ -2178,7 +2241,7 @@
}
final IsolatedUidRange uidRange =
mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(
- app.info.processName, app.hostingRecord.getDefiningUid());
+ app.info.processName, app.getHostingRecord().getDefiningUid());
final int userId = UserHandle.getUserId(uid);
// Create the app-zygote and provide it with the UID-range it's allowed
// to setresuid/setresgid to.
@@ -2191,7 +2254,7 @@
// packageName and uid of the defining application. This is because the
// preloading only makes sense in the context of the defining application,
// not the calling one.
- appInfo.packageName = app.hostingRecord.getDefiningPackageName();
+ appInfo.packageName = app.getHostingRecord().getDefiningPackageName();
appInfo.uid = uid;
appZygote = new AppZygote(appInfo, uid, firstUid, lastUid);
mAppZygotes.put(app.info.processName, uid, appZygote);
@@ -2238,14 +2301,15 @@
private boolean needsStorageDataIsolation(StorageManagerInternal storageManagerInternal,
ProcessRecord app) {
+ final int mountMode = app.getMountMode();
return mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid)
&& !storageManagerInternal.isExternalStorageService(app.uid)
// Special mounting mode doesn't need to have data isolation as they won't
// access /mnt/user anyway.
- && app.mountMode != Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
- && app.mountMode != Zygote.MOUNT_EXTERNAL_PASS_THROUGH
- && app.mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER
- && app.mountMode != Zygote.MOUNT_EXTERNAL_NONE;
+ && mountMode != Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
+ && mountMode != Zygote.MOUNT_EXTERNAL_PASS_THROUGH
+ && mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER
+ && mountMode != Zygote.MOUNT_EXTERNAL_NONE;
}
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
@@ -2261,7 +2325,7 @@
// Use has-foreground-activities as a temporary hint so the current scheduling
// group won't be lost when the process is attaching. The actual state will be
// refreshed when computing oom-adj.
- app.setHasForegroundActivities(true);
+ app.mState.setHasForegroundActivities(true);
}
Map<String, Pair<String, Long>> pkgDataInfoMap;
@@ -2311,7 +2375,7 @@
app.processName)) {
// Cannot prepare Android/app and Android/obb directory or inode == 0,
// so we won't mount it in zygote, but resume the mount after unlocking device.
- app.bindMountPending = true;
+ app.setBindMountPending(true);
bindMountAppStorageDirs = false;
}
}
@@ -2328,8 +2392,9 @@
startResult = startWebView(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
- new String[]{PROC_START_SEQ_IDENT + app.startSeq});
+ app.info.dataDir, null, app.info.packageName,
+ app.getDisabledCompatChanges(),
+ new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else if (hostingRecord.usesAppZygote()) {
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
@@ -2339,17 +2404,17 @@
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
- app.mDisabledCompatChanges, pkgDataInfoMap, allowlistedAppDataInfoMap,
+ app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
false, false,
- new String[]{PROC_START_SEQ_IDENT + app.startSeq});
+ new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
- isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
+ isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap,
allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
- new String[]{PROC_START_SEQ_IDENT + app.startSeq});
+ new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
}
checkSlow(startTime, "startProcess: returned from zygote!");
return startResult;
@@ -2364,15 +2429,14 @@
}
@GuardedBy("mService")
- final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
+ boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
int zygotePolicyFlags, String abiOverride) {
return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
- false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
- abiOverride);
+ false /* disableHiddenApiChecks */, abiOverride);
}
@GuardedBy("mService")
- final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
+ ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs,
@@ -2405,7 +2469,7 @@
info.processName);
mService.mAppErrors.clearBadProcess(processName, info.uid);
if (app != null) {
- app.bad = false;
+ app.mErrorState.setBad(false);
}
}
}
@@ -2422,11 +2486,11 @@
// already running.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
+ " app=" + app + " knownToBeDead=" + knownToBeDead
- + " thread=" + (app != null ? app.thread : null)
- + " pid=" + (app != null ? app.pid : -1));
- ProcessRecord precedence = null;
- if (app != null && app.pid > 0) {
- if ((!knownToBeDead && !app.killed) || app.thread == null) {
+ + " thread=" + (app != null ? app.getThread() : null)
+ + " pid=" + (app != null ? app.getPid() : -1));
+ ProcessRecord predecessor = null;
+ if (app != null && app.getPid() > 0) {
+ if ((!knownToBeDead && !app.isKilled()) || app.getThread() == null) {
// We already have the app running, or are waiting for it to
// come up (we have a pid but not yet its thread), so keep it.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
@@ -2440,12 +2504,12 @@
// clean it up now.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
checkSlow(startTime, "startProcess: bad proc running, killing");
- ProcessList.killProcessGroup(app.uid, app.pid);
+ ProcessList.killProcessGroup(app.uid, app.getPid());
checkSlow(startTime, "startProcess: done killing old proc");
- if (!app.killed
+ if (!app.isKilled()
|| mService.mAppProfiler.isLastMemoryLevelNormal()
- || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
+ || app.mState.getSetProcState() < ActivityManager.PROCESS_STATE_CACHED_EMPTY
|| app.mProfile.getLastCachedPss() < getCachedRestoreThresholdKb()) {
// Throw a wtf if it's not killed, or killed but not because the system was in
// memory pressure + the app was in "cch-empty" and used large amount of memory
@@ -2454,8 +2518,8 @@
Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
}
// We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
- // routine of it yet, but we'd set it as the precedence of the new process.
- precedence = app;
+ // routine of it yet, but we'd set it as the predecessor of the new process.
+ predecessor = app;
app = null;
}
@@ -2467,12 +2531,12 @@
+ processName + "/" + info.uid + " isolated=" + isolated);
return null;
}
- app.crashHandler = crashHandler;
- app.isolatedEntryPoint = entryPoint;
- app.isolatedEntryPointArgs = entryPointArgs;
- if (precedence != null) {
- app.mPrecedence = precedence;
- precedence.mSuccessor = app;
+ app.mErrorState.setCrashHandler(crashHandler);
+ app.setIsolatedEntryPoint(entryPoint);
+ app.setIsolatedEntryPointArgs(entryPointArgs);
+ if (predecessor != null) {
+ app.mPredecessor = predecessor;
+ predecessor.mSuccessor = app;
}
checkSlow(startTime, "startProcess: done creating new process record");
} else {
@@ -2505,7 +2569,7 @@
@GuardedBy("mService")
private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
StringBuilder sb = null;
- if (app.killedByAm) {
+ if (app.isKilledByAm()) {
if (sb == null) sb = new StringBuilder();
sb.append("killedByAm=true;");
}
@@ -2513,13 +2577,13 @@
if (sb == null) sb = new StringBuilder();
sb.append("No entry in mProcessNames;");
}
- if (!app.pendingStart) {
+ if (!app.isPendingStart()) {
if (sb == null) sb = new StringBuilder();
sb.append("pendingStart=false;");
}
- if (app.startSeq > expectedStartSeq) {
+ if (app.getStartSeq() > expectedStartSeq) {
if (sb == null) sb = new StringBuilder();
- sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
+ sb.append("seq=" + app.getStartSeq() + ",expected=" + expectedStartSeq + ";");
}
try {
AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, app.userId);
@@ -2542,7 +2606,7 @@
Process.ProcessStartResult startResult, long expectedStartSeq) {
// Indicates that this process start has been taken care of.
if (mPendingStarts.get(expectedStartSeq) == null) {
- if (pending.pid == startResult.pid) {
+ if (pending.getPid() == startResult.pid) {
pending.setUsingWrapper(startResult.usingWrapper);
// TODO: Update already existing clients of usingWrapper
}
@@ -2561,31 +2625,31 @@
Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" +
pid
+ ", " + reason);
- app.pendingStart = false;
+ app.setPendingStart(false);
killProcessQuiet(pid);
- Process.killProcessGroup(app.uid, app.pid);
+ Process.killProcessGroup(app.uid, app.getPid());
noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_INVALID_START, reason);
return false;
}
mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
- checkSlow(app.startTime, "startProcess: done updating battery stats");
+ checkSlow(app.getStartTime(), "startProcess: done updating battery stats");
EventLog.writeEvent(EventLogTags.AM_PROC_START,
- UserHandle.getUserId(app.startUid), pid, app.startUid,
- app.processName, app.hostingRecord.getType(),
- app.hostingRecord.getName() != null ? app.hostingRecord.getName() : "");
+ UserHandle.getUserId(app.getStartUid()), pid, app.getStartUid(),
+ app.processName, app.getHostingRecord().getType(),
+ app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : "");
try {
AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.info.packageName,
- app.processName, app.uid, app.seInfo, app.info.sourceDir, pid);
+ app.processName, app.uid, app.getSeInfo(), app.info.sourceDir, pid);
} catch (RemoteException ex) {
// Ignore
}
Watchdog.getInstance().processStarted(app.processName, pid);
- checkSlow(app.startTime, "startProcess: building log message");
+ checkSlow(app.getStartTime(), "startProcess: building log message");
StringBuilder buf = mStringBuilder;
buf.setLength(0);
buf.append("Start proc ");
@@ -2593,23 +2657,25 @@
buf.append(':');
buf.append(app.processName);
buf.append('/');
- UserHandle.formatUid(buf, app.startUid);
- if (app.isolatedEntryPoint != null) {
+ UserHandle.formatUid(buf, app.getStartUid());
+ if (app.getIsolatedEntryPoint() != null) {
buf.append(" [");
- buf.append(app.isolatedEntryPoint);
+ buf.append(app.getIsolatedEntryPoint());
buf.append("]");
}
buf.append(" for ");
- buf.append(app.hostingRecord.getType());
- if (app.hostingRecord.getName() != null) {
+ buf.append(app.getHostingRecord().getType());
+ if (app.getHostingRecord().getName() != null) {
buf.append(" ");
- buf.append(app.hostingRecord.getName());
+ buf.append(app.getHostingRecord().getName());
}
- mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
- app.setPid(pid);
- app.setUsingWrapper(usingWrapper);
- app.pendingStart = false;
- checkSlow(app.startTime, "startProcess: starting to update pids map");
+ mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.getStartUid());
+ synchronized (mProcLock) {
+ app.setPid(pid);
+ app.setUsingWrapper(usingWrapper);
+ app.setPendingStart(false);
+ }
+ checkSlow(app.getStartTime(), "startProcess: starting to update pids map");
ProcessRecord oldApp;
synchronized (mService.mPidsSelfLocked) {
oldApp = mService.mPidsSelfLocked.get(pid);
@@ -2618,11 +2684,11 @@
if (oldApp != null && !app.isolated) {
// Clean up anything relating to this pid first
Slog.wtf(TAG, "handleProcessStartedLocked process:" + app.processName
- + " startSeq:" + app.startSeq
+ + " startSeq:" + app.getStartSeq()
+ " pid:" + pid
+ " belongs to another existing app:" + oldApp.processName
- + " startSeq:" + oldApp.startSeq);
- mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
+ + " startSeq:" + oldApp.getStartSeq());
+ mService.cleanUpApplicationRecordLocked(oldApp, pid, false, false, -1,
true /*replacingPid*/);
}
mService.addPidLocked(app);
@@ -2634,43 +2700,46 @@
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
}
- checkSlow(app.startTime, "startProcess: done updating pids map");
+ checkSlow(app.getStartTime(), "startProcess: done updating pids map");
return true;
}
- final void removeLruProcessLocked(ProcessRecord app) {
+ @GuardedBy("mService")
+ void removeLruProcessLocked(ProcessRecord app) {
int lrui = mLruProcesses.lastIndexOf(app);
if (lrui >= 0) {
- if (!app.killed) {
- if (app.isPersistent()) {
- Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
- } else {
- Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
- if (app.pid > 0) {
- killProcessQuiet(app.pid);
- ProcessList.killProcessGroup(app.uid, app.pid);
- noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_REMOVE_LRU, "hasn't been killed");
+ synchronized (mProcLock) {
+ if (!app.isKilled()) {
+ if (app.isPersistent()) {
+ Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
} else {
- app.pendingStart = false;
+ Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
+ if (app.getPid() > 0) {
+ killProcessQuiet(app.getPid());
+ ProcessList.killProcessGroup(app.uid, app.getPid());
+ noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_REMOVE_LRU, "hasn't been killed");
+ } else {
+ app.setPendingStart(false);
+ }
}
}
+ if (lrui < mLruProcessActivityStart) {
+ mLruProcessActivityStart--;
+ }
+ if (lrui < mLruProcessServiceStart) {
+ mLruProcessServiceStart--;
+ }
+ mLruProcesses.remove(lrui);
}
- if (lrui < mLruProcessActivityStart) {
- mLruProcessActivityStart--;
- }
- if (lrui < mLruProcessServiceStart) {
- mLruProcessServiceStart--;
- }
- mLruProcesses.remove(lrui);
- mService.removeOomAdjTargetLocked(app, true);
}
+ mService.removeOomAdjTargetLocked(app, true);
}
- @GuardedBy("mService")
- boolean killPackageProcessesLocked(String packageName, int appId, int userId, int minOomAdj,
+ @GuardedBy({"mService", "mProcLock"})
+ boolean killPackageProcessesLSP(String packageName, int appId, int userId, int minOomAdj,
int reasonCode, int subReason, String reason) {
- return killPackageProcessesLocked(packageName, appId, userId, minOomAdj,
+ return killPackageProcessesLSP(packageName, appId, userId, minOomAdj,
false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
false /* evenPersistent */, false /* setRemoved */, reasonCode,
subReason, reason);
@@ -2703,8 +2772,8 @@
}
}
- @GuardedBy("mService")
- final boolean killPackageProcessesLocked(String packageName, int appId,
+ @GuardedBy({"mService", "mProcLock"})
+ boolean killPackageProcessesLSP(String packageName, int appId,
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
boolean doit, boolean evenPersistent, boolean setRemoved, int reasonCode,
int subReason, String reason) {
@@ -2723,7 +2792,7 @@
// we don't kill persistent processes
continue;
}
- if (app.removed) {
+ if (app.isRemoved()) {
if (doit) {
procs.add(app);
}
@@ -2731,7 +2800,7 @@
}
// Skip process if it doesn't meet our oom adj requirement.
- if (app.setAdj < minOomAdj) {
+ if (app.mState.getSetAdj() < minOomAdj) {
// Note it is still possible to have a process with oom adj 0 in the killed
// processes, but it does not mean misjudgment. E.g. a bound service process
// and its client activity process are both in the background, so they are
@@ -2753,8 +2822,8 @@
// that match it. We need to qualify this by the processes
// that are running under the specified app and user ID.
} else {
- final boolean isDep = app.pkgDeps != null
- && app.pkgDeps.contains(packageName);
+ final boolean isDep = app.getPkgDeps() != null
+ && app.getPkgDeps().contains(packageName);
if (!isDep && UserHandle.getAppId(app.uid) != appId) {
continue;
}
@@ -2771,7 +2840,7 @@
return true;
}
if (setRemoved) {
- app.removed = true;
+ app.setRemoved(true);
}
procs.add(app);
}
@@ -2812,12 +2881,12 @@
mService.mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
boolean needRestart = false;
- if ((app.pid > 0 && app.pid != ActivityManagerService.MY_PID) || (app.pid == 0 && app
- .pendingStart)) {
- int pid = app.pid;
+ final int pid = app.getPid();
+ if ((pid > 0 && pid != ActivityManagerService.MY_PID)
+ || (pid == 0 && app.isPendingStart())) {
if (pid > 0) {
- mService.removePidLocked(app);
- app.bindMountPending = false;
+ mService.removePidLocked(pid, app);
+ app.setBindMountPending(false);
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
@@ -2833,8 +2902,8 @@
needRestart = true;
}
}
- app.kill(reason, reasonCode, subReason, true);
- mService.handleAppDiedLocked(app, willRestart, allowRestart);
+ app.killLocked(reason, reasonCode, subReason, true);
+ mService.handleAppDiedLocked(app, pid, willRestart, allowRestart);
if (willRestart) {
removeLruProcessLocked(app);
mService.addAppLocked(app.info, null, false, null /* ABI override */,
@@ -2848,48 +2917,51 @@
}
@GuardedBy("mService")
- final void addProcessNameLocked(ProcessRecord proc) {
+ void addProcessNameLocked(ProcessRecord proc) {
// We shouldn't already have a process under this name, but just in case we
// need to clean up whatever may be there now.
- ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
- if (old == proc && proc.isPersistent()) {
- // We are re-adding a persistent process. Whatevs! Just leave it there.
- Slog.w(TAG, "Re-adding persistent process " + proc);
- } else if (old != null) {
- if (old.killed) {
- // The old process has been killed, we probably haven't had
- // a chance to clean up the old record, just log a warning
- Slog.w(TAG, "Existing proc " + old + " was killed "
- + (SystemClock.uptimeMillis() - old.mKillTime)
- + "ms ago when adding " + proc);
- } else {
- Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ synchronized (mProcLock) {
+ ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
+ if (old == proc && proc.isPersistent()) {
+ // We are re-adding a persistent process. Whatevs! Just leave it there.
+ Slog.w(TAG, "Re-adding persistent process " + proc);
+ } else if (old != null) {
+ if (old.isKilled()) {
+ // The old process has been killed, we probably haven't had
+ // a chance to clean up the old record, just log a warning
+ Slog.w(TAG, "Existing proc " + old + " was killed "
+ + (SystemClock.uptimeMillis() - old.getKillTime())
+ + "ms ago when adding " + proc);
+ } else {
+ Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+ }
}
- }
- UidRecord uidRec = mActiveUids.get(proc.uid);
- if (uidRec == null) {
- uidRec = new UidRecord(proc.uid);
- // This is the first appearance of the uid, report it now!
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Creating new process uid: " + uidRec);
- if (Arrays.binarySearch(mService.mDeviceIdleTempAllowlist,
- UserHandle.getAppId(proc.uid)) >= 0
- || mService.mPendingTempAllowlist.indexOfKey(proc.uid) >= 0) {
- uidRec.mSetAllowlist = uidRec.mCurAllowlist = true;
+ UidRecord uidRec = mActiveUids.get(proc.uid);
+ if (uidRec == null) {
+ uidRec = new UidRecord(proc.uid, mService);
+ // This is the first appearance of the uid, report it now!
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Creating new process uid: " + uidRec);
+ }
+ if (Arrays.binarySearch(mService.mDeviceIdleTempAllowlist,
+ UserHandle.getAppId(proc.uid)) >= 0
+ || mService.mPendingTempAllowlist.indexOfKey(proc.uid) >= 0) {
+ uidRec.setCurAllowListed(true);
+ uidRec.setSetAllowListed(true);
+ }
+ uidRec.updateHasInternetPermission();
+ mActiveUids.put(proc.uid, uidRec);
+ EventLogTags.writeAmUidRunning(uidRec.getUid());
+ mService.noteUidProcessState(uidRec.getUid(), uidRec.getCurProcState(),
+ uidRec.getCurCapability());
}
- uidRec.updateHasInternetPermission();
- mActiveUids.put(proc.uid, uidRec);
- EventLogTags.writeAmUidRunning(uidRec.uid);
- mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(),
- uidRec.curCapability);
- }
- proc.uidRecord = uidRec;
- uidRec.procRecords.add(proc);
+ proc.setUidRecord(uidRec);
+ uidRec.addProcess(proc);
- // Reset render thread tid if it was already set, so new process can set it again.
- proc.renderThreadTid = 0;
- uidRec.numProcs++;
- mProcessNames.put(proc.processName, proc.uid, proc);
+ // Reset render thread tid if it was already set, so new process can set it again.
+ proc.setRenderThreadTid(0);
+ mProcessNames.put(proc.processName, proc.uid, proc);
+ }
if (proc.isolated) {
mIsolatedProcesses.put(proc.uid, proc);
}
@@ -2908,7 +2980,7 @@
}
@GuardedBy("mService")
- final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
+ ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid, HostingRecord hostingRecord) {
String proc = customProcess != null ? customProcess : info.processName;
final int userId = UserHandle.getUserId(info.uid);
@@ -2942,58 +3014,63 @@
FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
final ProcessRecord r = new ProcessRecord(mService, info, proc, uid);
+ final ProcessStateRecord state = r.mState;
if (!mService.mBooted && !mService.mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
// The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
- r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
- r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+ state.setSetSchedGroup(ProcessList.SCHED_GROUP_DEFAULT);
r.setPersistent(true);
- r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+ state.setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ);
}
if (isolated && isolatedUid != 0) {
// Special case for startIsolatedProcess (internal only) - assume the process
// is required by the system server to prevent it being killed.
- r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
+ state.setMaxAdj(ProcessList.PERSISTENT_SERVICE_ADJ);
}
addProcessNameLocked(r);
return r;
}
@GuardedBy("mService")
- final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
+ ProcessRecord removeProcessNameLocked(final String name, final int uid) {
return removeProcessNameLocked(name, uid, null);
}
@GuardedBy("mService")
- final ProcessRecord removeProcessNameLocked(final String name, final int uid,
+ ProcessRecord removeProcessNameLocked(final String name, final int uid,
final ProcessRecord expecting) {
ProcessRecord old = mProcessNames.get(name, uid);
- // Only actually remove when the currently recorded value matches the
- // record that we expected; if it doesn't match then we raced with a
- // newly created process and we don't want to destroy the new one.
- if ((expecting == null) || (old == expecting)) {
- mProcessNames.remove(name, uid);
- }
final ProcessRecord record = expecting != null ? expecting : old;
- if (record != null && record.uidRecord != null) {
- final UidRecord uidRecord = record.uidRecord;
- uidRecord.numProcs--;
- uidRecord.procRecords.remove(record);
- if (uidRecord.numProcs == 0) {
- // No more processes using this uid, tell clients it is gone.
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "No more processes in " + uidRecord);
- mService.enqueueUidChangeLocked(uidRecord, -1,
- UidRecord.CHANGE_GONE);
- EventLogTags.writeAmUidStopped(uid);
- mActiveUids.remove(uid);
- mService.mFgsStartTempAllowList.remove(record.info.uid);
- mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,
- ActivityManager.PROCESS_CAPABILITY_NONE);
+ synchronized (mProcLock) {
+ // Only actually remove when the currently recorded value matches the
+ // record that we expected; if it doesn't match then we raced with a
+ // newly created process and we don't want to destroy the new one.
+ if ((expecting == null) || (old == expecting)) {
+ mProcessNames.remove(name, uid);
}
- record.uidRecord = null;
+ if (record != null) {
+ final UidRecord uidRecord = record.getUidRecord();
+ if (uidRecord != null) {
+ uidRecord.removeProcess(record);
+ if (uidRecord.getNumOfProcs() == 0) {
+ // No more processes using this uid, tell clients it is gone.
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "No more processes in " + uidRecord);
+ }
+ mService.enqueueUidChangeLocked(uidRecord, -1,
+ UidRecord.CHANGE_GONE);
+ EventLogTags.writeAmUidStopped(uid);
+ mActiveUids.remove(uid);
+ mService.mFgsStartTempAllowList.remove(record.info.uid);
+ mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,
+ ActivityManager.PROCESS_CAPABILITY_NONE);
+ }
+ record.setUidRecord(null);
+ }
+ }
}
mIsolatedProcesses.remove(uid);
mGlobalIsolatedUids.freeIsolatedUidLocked(uid);
@@ -3006,13 +3083,14 @@
}
/** Call setCoreSettings on all LRU processes, with the new settings. */
- @GuardedBy("mService")
- void updateCoreSettingsLocked(Bundle settings) {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ void updateCoreSettingsLOSP(Bundle settings) {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord processRecord = mLruProcesses.get(i);
+ final IApplicationThread thread = processRecord.getThread();
try {
- if (processRecord.thread != null) {
- processRecord.thread.setCoreSettings(settings);
+ if (thread != null) {
+ thread.setCoreSettings(settings);
}
} catch (RemoteException re) {
/* ignore */
@@ -3026,8 +3104,8 @@
* @param minTargetSdk
* @param maxProcState
*/
- @GuardedBy("mService")
- void killAllBackgroundProcessesExceptLocked(int minTargetSdk, int maxProcState) {
+ @GuardedBy({"mService", "mProcLock"})
+ void killAllBackgroundProcessesExceptLSP(int minTargetSdk, int maxProcState) {
final ArrayList<ProcessRecord> procs = new ArrayList<>();
final int NP = mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
@@ -3035,8 +3113,9 @@
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
- if (app.removed || ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
- && (maxProcState < 0 || app.setProcState > maxProcState))) {
+ if (app.isRemoved()
+ || ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
+ && (maxProcState < 0 || app.mState.getSetProcState() > maxProcState))) {
procs.add(app);
}
}
@@ -3053,13 +3132,14 @@
* Call updateTimePrefs on all LRU processes
* @param timePref The time pref to pass to each process
*/
- @GuardedBy("mService")
- void updateAllTimePrefsLocked(int timePref) {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ void updateAllTimePrefsLOSP(int timePref) {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
+ final IApplicationThread thread = r.getThread();
+ if (thread != null) {
try {
- r.thread.updateTimePrefs(timePref);
+ thread.updateTimePrefs(timePref);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update preferences for: "
+ r.info.processName);
@@ -3070,15 +3150,16 @@
void setAllHttpProxy() {
// Update the HTTP proxy for each application thread.
- synchronized (mService) {
+ synchronized (mProcLock) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
+ IApplicationThread thread = r.getThread();
// Don't dispatch to isolated processes as they can't access ConnectivityManager and
// don't have network privileges anyway. Exclude system server and update it
// separately outside the AMS lock, to avoid deadlock with Connectivity Service.
- if (r.pid != ActivityManagerService.MY_PID && r.thread != null && !r.isolated) {
+ if (r.getPid() != ActivityManagerService.MY_PID && thread != null && !r.isolated) {
try {
- r.thread.updateHttpProxy();
+ thread.updateHttpProxy();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update http proxy for: "
+ r.info.processName);
@@ -3089,13 +3170,14 @@
ActivityThread.updateHttpProxy(mService.mContext);
}
- @GuardedBy("mService")
- void clearAllDnsCacheLocked() {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ void clearAllDnsCacheLOSP() {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
+ final IApplicationThread thread = r.getThread();
+ if (thread != null) {
try {
- r.thread.clearDnsCache();
+ thread.clearDnsCache();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
}
@@ -3103,13 +3185,14 @@
}
}
- @GuardedBy("mService")
- void handleAllTrustStorageUpdateLocked() {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ void handleAllTrustStorageUpdateLOSP() {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
+ final IApplicationThread thread = r.getThread();
+ if (thread != null) {
try {
- r.thread.handleTrustStorageUpdate();
+ thread.handleTrustStorageUpdate();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to handle trust storage update for: " +
r.info.processName);
@@ -3118,10 +3201,10 @@
}
}
- @GuardedBy("mService")
- int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
+ @GuardedBy({"mService", "mProcLock"})
+ private int updateLruProcessInternalLSP(ProcessRecord app, long now, int index,
int lruSeq, String what, Object obj, ProcessRecord srcApp) {
- app.lastActivityTime = now;
+ app.setLastActivityTime(now);
if (app.hasActivitiesOrRecentTasks()) {
// Don't want to touch dependent processes that are hosting activities.
@@ -3153,7 +3236,7 @@
if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+ " in LRU list: " + app);
mLruProcesses.add(index, app);
- app.lruSeq = lruSeq;
+ app.setLruSeq(lruSeq);
return index;
}
@@ -3173,45 +3256,51 @@
* @param endIndex The current end of the top being processed. Typically topI - 1. That is,
* where we are going to start potentially adjusting other entries in the list.
*/
- private void updateClientActivitiesOrdering(final ProcessRecord topApp, final int topI,
+ @GuardedBy({"mService", "mProcLock"})
+ private void updateClientActivitiesOrderingLSP(final ProcessRecord topApp, final int topI,
final int bottomI, int endIndex) {
- if (topApp.hasActivitiesOrRecentTasks() || topApp.treatLikeActivity
- || !topApp.hasClientActivities()) {
+ final ProcessServiceRecord topPsr = topApp.mServices;
+ if (topApp.hasActivitiesOrRecentTasks() || topPsr.isTreatedLikeActivity()
+ || !topPsr.hasClientActivities()) {
// If this is not a special process that has client activities, then there is
// nothing to do.
return;
}
final int uid = topApp.info.uid;
- if (topApp.connectionGroup > 0) {
- int endImportance = topApp.connectionImportance;
+ final int topConnectionGroup = topPsr.getConnectionGroup();
+ if (topConnectionGroup > 0) {
+ int endImportance = topPsr.getConnectionImportance();
for (int i = endIndex; i >= bottomI; i--) {
final ProcessRecord subProc = mLruProcesses.get(i);
+ final ProcessServiceRecord subPsr = subProc.mServices;
+ final int subConnectionGroup = subPsr.getConnectionGroup();
+ final int subConnectionImportance = subPsr.getConnectionImportance();
if (subProc.info.uid == uid
- && subProc.connectionGroup == topApp.connectionGroup) {
- if (i == endIndex && subProc.connectionImportance >= endImportance) {
+ && subConnectionGroup == topConnectionGroup) {
+ if (i == endIndex && subConnectionImportance >= endImportance) {
// This process is already in the group, and its importance
// is not as strong as the process before it, so keep it
// correctly positioned in the group.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Keeping in-place above " + subProc
+ " endImportance=" + endImportance
- + " group=" + subProc.connectionGroup
- + " importance=" + subProc.connectionImportance);
+ + " group=" + subConnectionGroup
+ + " importance=" + subConnectionImportance);
endIndex--;
- endImportance = subProc.connectionImportance;
+ endImportance = subConnectionImportance;
} else {
// We want to pull this up to be with the rest of the group,
// and order within the group by importance.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Pulling up " + subProc
+ " to position in group with importance="
- + subProc.connectionImportance);
+ + subConnectionImportance);
boolean moved = false;
for (int pos = topI; pos > endIndex; pos--) {
final ProcessRecord posProc = mLruProcesses.get(pos);
- if (subProc.connectionImportance
- <= posProc.connectionImportance) {
+ if (subConnectionImportance
+ <= posProc.mServices.getConnectionImportance()) {
mLruProcesses.remove(i);
mLruProcesses.add(pos, subProc);
if (DEBUG_LRU) Slog.d(TAG_LRU,
@@ -3232,7 +3321,7 @@
+ " from position " + i + " to end of group @ "
+ endIndex);
endIndex--;
- endImportance = subProc.connectionImportance;
+ endImportance = subConnectionImportance;
}
}
}
@@ -3244,6 +3333,8 @@
int i = endIndex;
while (i >= bottomI) {
ProcessRecord subProc = mLruProcesses.get(i);
+ final ProcessServiceRecord subPsr = subProc.mServices;
+ final int subConnectionGroup = subPsr.getConnectionGroup();
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Looking to spread old procs, at " + subProc + " @ " + i);
if (subProc.info.uid != uid) {
@@ -3266,7 +3357,8 @@
subProc = mLruProcesses.get(i);
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Looking at next app at " + i + ": " + subProc);
- if (subProc.hasActivitiesOrRecentTasks() || subProc.treatLikeActivity) {
+ if (subProc.hasActivitiesOrRecentTasks()
+ || subPsr.isTreatedLikeActivity()) {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"This is hosting an activity!");
if (hasActivity) {
@@ -3276,7 +3368,7 @@
break;
}
hasActivity = true;
- } else if (subProc.hasClientActivities()) {
+ } else if (subPsr.hasClientActivities()) {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"This is a client of an activity");
if (hasActivity) {
@@ -3287,21 +3379,21 @@
"Already found a different activity: connUid="
+ connUid + " uid=" + subProc.info.uid);
break;
- } else if (connGroup == 0 || connGroup != subProc.connectionGroup) {
+ } else if (connGroup == 0 || connGroup != subConnectionGroup) {
// Previously saw a different group or not from a group,
// want to treat these as different things.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Already found a different group: connGroup="
- + connGroup + " group=" + subProc.connectionGroup);
+ + connGroup + " group=" + subConnectionGroup);
break;
}
} else {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"This is an activity client! uid="
- + subProc.info.uid + " group=" + subProc.connectionGroup);
+ + subProc.info.uid + " group=" + subConnectionGroup);
hasActivity = true;
connUid = subProc.info.uid;
- connGroup = subProc.connectionGroup;
+ connGroup = subConnectionGroup;
}
}
endIndex--;
@@ -3322,13 +3414,16 @@
}
if (endIndex >= bottomI) {
final ProcessRecord endProc = mLruProcesses.get(endIndex);
+ final ProcessServiceRecord endPsr = endProc.mServices;
+ final int endConnectionGroup = endPsr.getConnectionGroup();
for (endIndex--; endIndex >= bottomI; endIndex--) {
final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
+ final int nextConnectionGroup = nextEndProc.mServices.getConnectionGroup();
if (nextEndProc.info.uid != uid
- || nextEndProc.connectionGroup != endProc.connectionGroup) {
+ || nextConnectionGroup != endConnectionGroup) {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Found next group or app: " + nextEndProc + " @ "
- + endIndex + " group=" + nextEndProc.connectionGroup);
+ + endIndex + " group=" + nextConnectionGroup);
break;
}
}
@@ -3342,10 +3437,11 @@
}
}
- final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
- ProcessRecord client) {
- final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
- || app.treatLikeActivity;
+ @GuardedBy("mService")
+ void updateLruProcessLocked(ProcessRecord app, boolean activityChange, ProcessRecord client) {
+ final ProcessServiceRecord psr = app.mServices;
+ final boolean hasActivity = app.hasActivitiesOrRecentTasks() || psr.hasClientActivities()
+ || psr.isTreatedLikeActivity();
final boolean hasService = false; // not impl yet. app.services.size() > 0;
if (!activityChange && hasActivity) {
// The process has activities, so we are only allowing activity-based adjustments
@@ -3355,9 +3451,18 @@
return;
}
+ synchronized (mProcLock) {
+ updateLruProcessLSP(app, client, hasActivity, hasService);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void updateLruProcessLSP(ProcessRecord app, ProcessRecord client,
+ boolean hasActivity, boolean hasService) {
mLruSeq++;
final long now = SystemClock.uptimeMillis();
- app.lastActivityTime = now;
+ final ProcessServiceRecord psr = app.mServices;
+ app.setLastActivityTime(now);
// First a quick reject: if the app is already at the position we will
// put it, then there is nothing to do.
@@ -3451,14 +3556,14 @@
if (hasActivity) {
final int N = mLruProcesses.size();
nextIndex = mLruProcessServiceStart;
- if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity
+ if (!app.hasActivitiesOrRecentTasks() && !psr.isTreatedLikeActivity()
&& mLruProcessActivityStart < (N - 1)) {
// Process doesn't have activities, but has clients with
// activities... move it up, but below the app that is binding to it.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Adding to second-top of LRU activity list: " + app
- + " group=" + app.connectionGroup
- + " importance=" + app.connectionImportance);
+ + " group=" + psr.getConnectionGroup()
+ + " importance=" + psr.getConnectionImportance());
int pos = N - 1;
while (pos > mLruProcessActivityStart) {
final ProcessRecord posproc = mLruProcesses.get(pos);
@@ -3478,7 +3583,7 @@
endIndex = mLruProcessActivityStart;
}
nextActivityIndex = endIndex;
- updateClientActivitiesOrdering(app, pos, mLruProcessActivityStart, endIndex);
+ updateClientActivitiesOrderingLSP(app, pos, mLruProcessActivityStart, endIndex);
} else {
// Process has activities, put it at the very tipsy-top.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
@@ -3515,46 +3620,48 @@
mLruProcessActivityStart++;
mLruProcessServiceStart++;
if (index > 1) {
- updateClientActivitiesOrdering(app, mLruProcessServiceStart - 1, 0, index - 1);
+ updateClientActivitiesOrderingLSP(app, mLruProcessServiceStart - 1, 0, index - 1);
}
}
- app.lruSeq = mLruSeq;
+ app.setLruSeq(mLruSeq);
// If the app is currently using a content provider or service,
// bump those processes as well.
- for (int j = app.connections.size() - 1; j >= 0; j--) {
- ConnectionRecord cr = app.connections.valueAt(j);
+ for (int j = psr.numberOfConnections() - 1; j >= 0; j--) {
+ ConnectionRecord cr = psr.getConnectionAt(j);
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
- && cr.binding.service.app.lruSeq != mLruSeq
+ && cr.binding.service.app.getLruSeq() != mLruSeq
&& (cr.flags & Context.BIND_REDUCTION_FLAGS) == 0
&& !cr.binding.service.app.isPersistent()) {
- if (cr.binding.service.app.hasClientActivities()) {
+ if (cr.binding.service.app.mServices.hasClientActivities()) {
if (nextActivityIndex >= 0) {
- nextActivityIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+ nextActivityIndex = updateLruProcessInternalLSP(cr.binding.service.app,
now,
nextActivityIndex, mLruSeq,
"service connection", cr, app);
}
} else {
- nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+ nextIndex = updateLruProcessInternalLSP(cr.binding.service.app,
now,
nextIndex, mLruSeq,
"service connection", cr, app);
}
}
}
- for (int j = app.conProviders.size() - 1; j >= 0; j--) {
- ContentProviderRecord cpr = app.conProviders.get(j).provider;
- if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, mLruSeq,
+ final ProcessProviderRecord ppr = app.mProviders;
+ for (int j = ppr.numberOfProviderConnections() - 1; j >= 0; j--) {
+ ContentProviderRecord cpr = ppr.getProviderConnectionAt(j).provider;
+ if (cpr.proc != null && cpr.proc.getLruSeq() != mLruSeq && !cpr.proc.isPersistent()) {
+ nextIndex = updateLruProcessInternalLSP(cpr.proc, now, nextIndex, mLruSeq,
"provider reference", cpr, app);
}
}
}
- final ProcessRecord getLRURecordForAppLocked(IApplicationThread thread) {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ProcessRecord getLRURecordForAppLOSP(IApplicationThread thread) {
if (thread == null) {
return null;
}
@@ -3562,18 +3669,20 @@
// Find the application record.
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord rec = mLruProcesses.get(i);
- if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
+ final IApplicationThread t = rec.getThread();
+ if (t != null && t.asBinder() == threadBinder) {
return rec;
}
}
return null;
}
- boolean haveBackgroundProcessLocked() {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean haveBackgroundProcessLOSP() {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord rec = mLruProcesses.get(i);
- if (rec.thread != null
- && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
+ if (rec.getThread() != null
+ && rec.mState.getSetProcState() >= PROCESS_STATE_CACHED_ACTIVITY) {
return true;
}
}
@@ -3593,11 +3702,11 @@
return imp;
}
- @GuardedBy("mService")
- void fillInProcMemInfoLocked(ProcessRecord app,
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ void fillInProcMemInfoLOSP(ProcessRecord app,
ActivityManager.RunningAppProcessInfo outInfo,
int clientTargetSdk) {
- outInfo.pid = app.pid;
+ outInfo.pid = app.getPid();
outInfo.uid = app.info.uid;
if (app.getWindowProcessController().isHeavyWeightProcess()) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
@@ -3609,49 +3718,53 @@
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
}
outInfo.lastTrimLevel = app.mProfile.getTrimMemoryLevel();
- int adj = app.curAdj;
- int procState = app.getCurProcState();
+ final ProcessStateRecord state = app.mState;
+ int adj = state.getCurAdj();
+ int procState = state.getCurProcState();
outInfo.importance = procStateToImportance(procState, adj, outInfo,
clientTargetSdk);
- outInfo.importanceReasonCode = app.adjTypeCode;
- outInfo.processState = app.getCurProcState();
+ outInfo.importanceReasonCode = state.getAdjTypeCode();
+ outInfo.processState = procState;
outInfo.isFocused = (app == mService.getTopApp());
- outInfo.lastActivityTime = app.lastActivityTime;
+ outInfo.lastActivityTime = app.getLastActivityTime();
}
- @GuardedBy("mService")
- List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLocked(boolean allUsers,
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLOSP(boolean allUsers,
int userId, boolean allUids, int callingUid, int clientTargetSdk) {
// Lazy instantiation of list
List<ActivityManager.RunningAppProcessInfo> runList = null;
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord app = mLruProcesses.get(i);
+ final ProcessStateRecord state = app.mState;
+ final ProcessErrorStateRecord errState = app.mErrorState;
if ((!allUsers && app.userId != userId)
|| (!allUids && app.uid != callingUid)) {
continue;
}
- if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
+ if ((app.getThread() != null)
+ && (!errState.isCrashing() && !errState.isNotResponding())) {
// Generate process state info for running application
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
- app.pid, app.getPackageList());
- fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
- if (app.adjSource instanceof ProcessRecord) {
- currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
+ app.getPid(), app.getPackageList());
+ fillInProcMemInfoLOSP(app, currApp, clientTargetSdk);
+ if (state.getAdjSource() instanceof ProcessRecord) {
+ currApp.importanceReasonPid = ((ProcessRecord) state.getAdjSource()).getPid();
currApp.importanceReasonImportance =
ActivityManager.RunningAppProcessInfo.procStateToImportance(
- app.adjSourceProcState);
- } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
+ state.getAdjSourceProcState());
+ } else if (state.getAdjSource() instanceof ActivityServiceConnectionsHolder) {
final ActivityServiceConnectionsHolder r =
- (ActivityServiceConnectionsHolder) app.adjSource;
+ (ActivityServiceConnectionsHolder) state.getAdjSource();
final int pid = r.getActivityPid();
if (pid != -1) {
currApp.importanceReasonPid = pid;
}
}
- if (app.adjTarget instanceof ComponentName) {
- currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
+ if (state.getAdjTarget() instanceof ComponentName) {
+ currApp.importanceReasonComponent = (ComponentName) state.getAdjTarget();
}
//Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
// + " lru=" + currApp.lru);
@@ -3664,11 +3777,110 @@
return runList;
}
- @GuardedBy("mService")
- int getLruSizeLocked() {
+ @GuardedBy(anyOf = {"mService", "mProfileLock"})
+ int getLruSizeLOSP() {
return mLruProcesses.size();
}
+ /**
+ * Return the reference to the LRU list, call this function for read-only access
+ */
+ @GuardedBy(anyOf = {"mService", "mProfileLock"})
+ ArrayList<ProcessRecord> getLruProcessesLOSP() {
+ return mLruProcesses;
+ }
+
+ /**
+ * Return the reference to the LRU list, call this function for read/write access
+ */
+ @GuardedBy({"mService", "mProfileLock"})
+ ArrayList<ProcessRecord> getLruProcessesLSP() {
+ return mLruProcesses;
+ }
+
+ /**
+ * For test only
+ */
+ @VisibleForTesting
+ @GuardedBy({"mService", "mProfileLock"})
+ void setLruProcessServiceStartLSP(int pos) {
+ mLruProcessServiceStart = pos;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProfileLock"})
+ int getLruProcessServiceStartLOSP() {
+ return mLruProcessServiceStart;
+ }
+
+ /**
+ * Iterate the whole LRU list, invoke the given {@code callback} with each of the ProcessRecord
+ * in that list.
+ *
+ * @param iterateForward If {@code true}, iterate the LRU list from the least recent used
+ * to most recent used ProcessRecord.
+ * @param callback The callback interface to accept the current ProcessRecord.
+ */
+ @GuardedBy(anyOf = {"mService", "mProfileLock"})
+ void forEachLruProcessesLOSP(boolean iterateForward,
+ @NonNull Consumer<ProcessRecord> callback) {
+ if (iterateForward) {
+ for (int i = 0, size = mLruProcesses.size(); i < size; i++) {
+ callback.accept(mLruProcesses.get(i));
+ }
+ } else {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ callback.accept(mLruProcesses.get(i));
+ }
+ }
+ }
+
+ /**
+ * Search in the LRU list, invoke the given {@code callback} with each of the ProcessRecord
+ * in that list; if the callback returns a non-null object, halt the search, return that
+ * object as the return value of this search function.
+ *
+ * @param iterateForward If {@code true}, iterate the LRU list from the least recent used
+ * to most recent used ProcessRecord.
+ * @param callback The callback interface to accept the current ProcessRecord; if it returns
+ * a non-null object, the search will be halted and this object will be used
+ * as the return value of this search function.
+ */
+ @GuardedBy(anyOf = {"mService", "mProfileLock"})
+ <R> R searchEachLruProcessesLOSP(boolean iterateForward,
+ @NonNull Function<ProcessRecord, R> callback) {
+ if (iterateForward) {
+ for (int i = 0, size = mLruProcesses.size(); i < size; i++) {
+ R r;
+ if ((r = callback.apply(mLruProcesses.get(i))) != null) {
+ return r;
+ }
+ }
+ } else {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ R r;
+ if ((r = callback.apply(mLruProcesses.get(i))) != null) {
+ return r;
+ }
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProfileLock"})
+ boolean isInLruListLOSP(ProcessRecord app) {
+ return mLruProcesses.contains(app);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProfileLock"})
+ int getLruSeqLOSP() {
+ return mLruSeq;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProfileLock"})
+ MyProcessMap getProcessNamesLOSP() {
+ return mProcessNames;
+ }
+
@GuardedBy("mService")
void dumpLruListHeaderLocked(PrintWriter pw) {
pw.print(" Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
@@ -3679,7 +3891,8 @@
pw.println("):");
}
- void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc, String prefix) {
+ @GuardedBy("mService")
+ private void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc, String prefix) {
pw.print(prefix);
pw.print('#');
if (index < 10) {
@@ -3687,15 +3900,16 @@
}
pw.print(index);
pw.print(": ");
- pw.print(makeOomAdjString(proc.setAdj, false));
+ pw.print(makeOomAdjString(proc.mState.getSetAdj(), false));
pw.print(' ');
- pw.print(makeProcStateString(proc.getCurProcState()));
+ pw.print(makeProcStateString(proc.mState.getCurProcState()));
pw.print(' ');
- ActivityManager.printCapabilitiesSummary(pw, proc.curCapability);
+ ActivityManager.printCapabilitiesSummary(pw, proc.mState.getCurCapability());
pw.print(' ');
pw.print(proc.toShortString());
- if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities()
- || proc.treatLikeActivity) {
+ final ProcessServiceRecord psr = proc.mServices;
+ if (proc.hasActivitiesOrRecentTasks() || psr.hasClientActivities()
+ || psr.isTreatedLikeActivity()) {
pw.print(" act:");
boolean printed = false;
if (proc.hasActivities()) {
@@ -3709,14 +3923,14 @@
pw.print("recents");
printed = true;
}
- if (proc.hasClientActivities()) {
+ if (psr.hasClientActivities()) {
if (printed) {
pw.print("|");
}
pw.print("client");
printed = true;
}
- if (proc.treatLikeActivity) {
+ if (psr.isTreatedLikeActivity()) {
if (printed) {
pw.print("|");
}
@@ -3726,6 +3940,7 @@
pw.println();
}
+ @GuardedBy("mService")
boolean dumpLruLocked(PrintWriter pw, String dumpPackage, String prefix) {
final int lruSize = mLruProcesses.size();
final String innerPrefix;
@@ -3792,8 +4007,8 @@
return true;
}
- @GuardedBy("mService")
- void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ @GuardedBy({"mService", "mProcLock"})
+ void dumpProcessesLSP(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
boolean needSep = false;
int numPers = 0;
@@ -3867,7 +4082,7 @@
needSep = true;
}
- if (getLruSizeLocked() > 0) {
+ if (getLruSizeLOSP() > 0) {
if (needSep) {
pw.println();
}
@@ -3877,12 +4092,12 @@
needSep = true;
}
- mService.dumpOtherProcessesInfoLocked(fd, pw, dumpAll, dumpPackage, dumpAppId, numPers,
+ mService.dumpOtherProcessesInfoLSP(fd, pw, dumpAll, dumpPackage, dumpAppId, numPers,
needSep);
}
- @GuardedBy("this")
- void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
+ @GuardedBy({"mService", "mProcLock"})
+ void writeProcessesToProtoLSP(ProtoOutputStream proto, String dumpPackage) {
int numPers = 0;
final int numOfNames = mProcessNames.getMap().size();
@@ -3916,9 +4131,9 @@
mActiveUids.dumpProto(proto, dumpPackage, dumpAppId,
ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
- if (getLruSizeLocked() > 0) {
+ if (getLruSizeLOSP() > 0) {
long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
- int total = getLruSizeLocked();
+ int total = getLruSizeLOSP();
proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT,
total - mLruProcessActivityStart);
@@ -3930,7 +4145,7 @@
proto.end(lruToken);
}
- mService.writeOtherProcessesInfoToProtoLocked(proto, dumpPackage, dumpAppId, numPers);
+ mService.writeOtherProcessesInfoToProtoLSP(proto, dumpPackage, dumpAppId, numPers);
}
private static ArrayList<Pair<ProcessRecord, Integer>> sortProcessOomList(
@@ -3950,14 +4165,18 @@
@Override
public int compare(Pair<ProcessRecord, Integer> object1,
Pair<ProcessRecord, Integer> object2) {
- if (object1.first.setAdj != object2.first.setAdj) {
- return object1.first.setAdj > object2.first.setAdj ? -1 : 1;
+ final int adj = object2.first.mState.getSetAdj() - object1.first.mState.getSetAdj();
+ if (adj != 0) {
+ return adj;
}
- if (object1.first.setProcState != object2.first.setProcState) {
- return object1.first.setProcState > object2.first.setProcState ? -1 : 1;
+ final int procState = object2.first.mState.getSetProcState()
+ - object1.first.mState.getSetProcState();
+ if (procState != 0) {
+ return procState;
}
- if (object1.second.intValue() != object2.second.intValue()) {
- return object1.second.intValue() > object2.second.intValue() ? -1 : 1;
+ final int val = object2.second - object1.second;
+ if (val != 0) {
+ return val;
}
return 0;
}
@@ -3977,13 +4196,15 @@
for (int i = list.size() - 1; i >= 0; i--) {
ProcessRecord r = list.get(i).first;
+ final ProcessStateRecord state = r.mState;
+ final ProcessServiceRecord psr = r.mServices;
long token = proto.start(fieldId);
- String oomAdj = makeOomAdjString(r.setAdj, true);
+ String oomAdj = makeOomAdjString(state.getSetAdj(), true);
proto.write(ProcessOomProto.PERSISTENT, r.isPersistent());
proto.write(ProcessOomProto.NUM, (origList.size() - 1) - list.get(i).second);
proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
- switch (r.setSchedGroup) {
+ switch (state.getSetSchedGroup()) {
case SCHED_GROUP_BACKGROUND:
schedGroup = ProcessOomProto.SCHED_GROUP_BACKGROUND;
break;
@@ -4000,52 +4221,52 @@
if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) {
proto.write(ProcessOomProto.SCHED_GROUP, schedGroup);
}
- if (r.hasForegroundActivities()) {
+ if (state.hasForegroundActivities()) {
proto.write(ProcessOomProto.ACTIVITIES, true);
- } else if (r.hasForegroundServices()) {
+ } else if (psr.hasForegroundServices()) {
proto.write(ProcessOomProto.SERVICES, true);
}
proto.write(ProcessOomProto.STATE,
- makeProcStateProtoEnum(r.getCurProcState()));
+ makeProcStateProtoEnum(state.getCurProcState()));
proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.mProfile.getTrimMemoryLevel());
r.dumpDebug(proto, ProcessOomProto.PROC);
- proto.write(ProcessOomProto.ADJ_TYPE, r.adjType);
- if (r.adjSource != null || r.adjTarget != null) {
- if (r.adjTarget instanceof ComponentName) {
- ComponentName cn = (ComponentName) r.adjTarget;
+ proto.write(ProcessOomProto.ADJ_TYPE, state.getAdjType());
+ if (state.getAdjSource() != null || state.getAdjTarget() != null) {
+ if (state.getAdjTarget() instanceof ComponentName) {
+ ComponentName cn = (ComponentName) state.getAdjTarget();
cn.dumpDebug(proto, ProcessOomProto.ADJ_TARGET_COMPONENT_NAME);
- } else if (r.adjTarget != null) {
- proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, r.adjTarget.toString());
+ } else if (state.getAdjTarget() != null) {
+ proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, state.getAdjTarget().toString());
}
- if (r.adjSource instanceof ProcessRecord) {
- ProcessRecord p = (ProcessRecord) r.adjSource;
+ if (state.getAdjSource() instanceof ProcessRecord) {
+ ProcessRecord p = (ProcessRecord) state.getAdjSource();
p.dumpDebug(proto, ProcessOomProto.ADJ_SOURCE_PROC);
- } else if (r.adjSource != null) {
- proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, r.adjSource.toString());
+ } else if (state.getAdjSource() != null) {
+ proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, state.getAdjSource().toString());
}
}
if (inclDetails) {
long detailToken = proto.start(ProcessOomProto.DETAIL);
- proto.write(ProcessOomProto.Detail.MAX_ADJ, r.maxAdj);
- proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.getCurRawAdj());
- proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, r.setRawAdj);
- proto.write(ProcessOomProto.Detail.CUR_ADJ, r.curAdj);
- proto.write(ProcessOomProto.Detail.SET_ADJ, r.setAdj);
+ proto.write(ProcessOomProto.Detail.MAX_ADJ, state.getMaxAdj());
+ proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, state.getCurRawAdj());
+ proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, state.getSetRawAdj());
+ proto.write(ProcessOomProto.Detail.CUR_ADJ, state.getCurAdj());
+ proto.write(ProcessOomProto.Detail.SET_ADJ, state.getSetAdj());
proto.write(ProcessOomProto.Detail.CURRENT_STATE,
- makeProcStateProtoEnum(r.getCurProcState()));
+ makeProcStateProtoEnum(state.getCurProcState()));
proto.write(ProcessOomProto.Detail.SET_STATE,
- makeProcStateProtoEnum(r.setProcState));
+ makeProcStateProtoEnum(state.getSetProcState()));
proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString(
r.mProfile.getLastPss() * 1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.LAST_SWAP_PSS, DebugUtils.sizeValueToString(
r.mProfile.getLastSwapPss() * 1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.LAST_CACHED_PSS, DebugUtils.sizeValueToString(
r.mProfile.getLastCachedPss() * 1024, new StringBuilder()));
- proto.write(ProcessOomProto.Detail.CACHED, r.isCached());
- proto.write(ProcessOomProto.Detail.EMPTY, r.empty);
- proto.write(ProcessOomProto.Detail.HAS_ABOVE_CLIENT, r.hasAboveClient);
+ proto.write(ProcessOomProto.Detail.CACHED, state.isCached());
+ proto.write(ProcessOomProto.Detail.EMPTY, state.isEmpty());
+ proto.write(ProcessOomProto.Detail.HAS_ABOVE_CLIENT, psr.hasAboveClient());
- if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
+ if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
long lastCpuTime = r.mProfile.mLastCpuTime.get();
if (lastCpuTime != 0) {
long uptimeSince = curUptime - service.mLastPowerCheckUptime;
@@ -4079,9 +4300,11 @@
for (int i = list.size() - 1; i >= 0; i--) {
ProcessRecord r = list.get(i).first;
- String oomAdj = makeOomAdjString(r.setAdj, false);
+ final ProcessStateRecord state = r.mState;
+ final ProcessServiceRecord psr = r.mServices;
+ String oomAdj = makeOomAdjString(state.getSetAdj(), false);
char schedGroup;
- switch (r.setSchedGroup) {
+ switch (state.getSetSchedGroup()) {
case SCHED_GROUP_BACKGROUND:
schedGroup = 'b';
break;
@@ -4102,14 +4325,14 @@
break;
}
char foreground;
- if (r.hasForegroundActivities()) {
+ if (state.hasForegroundActivities()) {
foreground = 'A';
- } else if (r.hasForegroundServices()) {
+ } else if (psr.hasForegroundServices()) {
foreground = 'S';
} else {
foreground = ' ';
}
- String procState = makeProcStateString(r.getCurProcState());
+ String procState = makeProcStateString(state.getCurProcState());
pw.print(prefix);
pw.print(r.isPersistent() ? persistentLabel : normalLabel);
pw.print(" #");
@@ -4125,7 +4348,7 @@
pw.print('/');
pw.print(procState);
pw.print(' ');
- ActivityManager.printCapabilitiesSummary(pw, r.curCapability);
+ ActivityManager.printCapabilitiesSummary(pw, state.getCurCapability());
pw.print(' ');
pw.print(" t:");
if (r.mProfile.getTrimMemoryLevel() < 10) pw.print(' ');
@@ -4133,25 +4356,25 @@
pw.print(' ');
pw.print(r.toShortString());
pw.print(" (");
- pw.print(r.adjType);
+ pw.print(state.getAdjType());
pw.println(')');
- if (r.adjSource != null || r.adjTarget != null) {
+ if (state.getAdjSource() != null || state.getAdjTarget() != null) {
pw.print(prefix);
pw.print(" ");
- if (r.adjTarget instanceof ComponentName) {
- pw.print(((ComponentName) r.adjTarget).flattenToShortString());
- } else if (r.adjTarget != null) {
- pw.print(r.adjTarget.toString());
+ if (state.getAdjTarget() instanceof ComponentName) {
+ pw.print(((ComponentName) state.getAdjTarget()).flattenToShortString());
+ } else if (state.getAdjTarget() != null) {
+ pw.print(state.getAdjTarget().toString());
} else {
pw.print("{null}");
}
pw.print("<=");
- if (r.adjSource instanceof ProcessRecord) {
+ if (state.getAdjSource() instanceof ProcessRecord) {
pw.print("Proc{");
- pw.print(((ProcessRecord) r.adjSource).toShortString());
+ pw.print(((ProcessRecord) state.getAdjSource()).toShortString());
pw.println("}");
- } else if (r.adjSource != null) {
- pw.println(r.adjSource.toString());
+ } else if (state.getAdjSource() != null) {
+ pw.println(state.getAdjSource().toString());
} else {
pw.println("{null}");
}
@@ -4159,16 +4382,15 @@
if (inclDetails) {
pw.print(prefix);
pw.print(" ");
- pw.print("oom: max="); pw.print(r.maxAdj);
- pw.print(" curRaw="); pw.print(r.getCurRawAdj());
- pw.print(" setRaw="); pw.print(r.setRawAdj);
- pw.print(" cur="); pw.print(r.curAdj);
- pw.print(" set="); pw.println(r.setAdj);
+ pw.print("oom: max="); pw.print(state.getMaxAdj());
+ pw.print(" curRaw="); pw.print(state.getCurRawAdj());
+ pw.print(" setRaw="); pw.print(state.getSetRawAdj());
+ pw.print(" cur="); pw.print(state.getCurAdj());
+ pw.print(" set="); pw.println(state.getSetAdj());
pw.print(prefix);
pw.print(" ");
- pw.print("state: cur="); pw.print(
- makeProcStateString(r.getCurProcState()));
- pw.print(" set="); pw.print(makeProcStateString(r.setProcState));
+ pw.print("state: cur="); pw.print(makeProcStateString(state.getCurProcState()));
+ pw.print(" set="); pw.print(makeProcStateString(state.getSetProcState()));
pw.print(" lastPss=");
DebugUtils.printSizeValue(pw, r.mProfile.getLastPss() * 1024);
pw.print(" lastSwapPss=");
@@ -4178,11 +4400,11 @@
pw.println();
pw.print(prefix);
pw.print(" ");
- pw.print("cached="); pw.print(r.isCached());
- pw.print(" empty="); pw.print(r.empty);
- pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
+ pw.print("cached="); pw.print(state.isCached());
+ pw.print(" empty="); pw.print(state.isEmpty());
+ pw.print(" hasAboveClient="); pw.println(psr.hasAboveClient());
- if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
+ if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
long lastCpuTime = r.mProfile.mLastCpuTime.get();
if (lastCpuTime != 0) {
long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime;
@@ -4218,9 +4440,10 @@
pw.println(")");
}
+ @GuardedBy("mService")
boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String[] args,
int opti, boolean dumpAll, String dumpPackage, boolean inclGc) {
- if (getLruSizeLocked() > 0) {
+ if (getLruSizeLOSP() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" OOM levels:");
@@ -4241,14 +4464,14 @@
printOomLevel(pw, "CACHED_APP_MAX_ADJ", CACHED_APP_MAX_ADJ);
if (needSep) pw.println();
- pw.print(" Process OOM control ("); pw.print(getLruSizeLocked());
+ pw.print(" Process OOM control ("); pw.print(getLruSizeLOSP());
pw.print(" total, non-act at ");
- pw.print(getLruSizeLocked() - mLruProcessActivityStart);
+ pw.print(getLruSizeLOSP() - mLruProcessActivityStart);
pw.print(", non-svc at ");
- pw.print(getLruSizeLocked() - mLruProcessServiceStart);
+ pw.print(getLruSizeLOSP() - mLruProcessServiceStart);
pw.println("):");
- dumpProcessOomList(pw, mService, mLruProcesses, " ", "Proc", "PERS", true,
- dumpPackage);
+ dumpProcessOomList(pw, mService, mLruProcesses,
+ " ", "Proc", "PERS", true, dumpPackage);
needSep = true;
}
@@ -4402,8 +4625,8 @@
mProcessObservers.finishBroadcast();
}
- @GuardedBy("mService")
- ArrayList<ProcessRecord> collectProcessesLocked(int start, boolean allPkgs, String[] args) {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ArrayList<ProcessRecord> collectProcessesLOSP(int start, boolean allPkgs, String[] args) {
ArrayList<ProcessRecord> procs;
if (args != null && args.length > start
&& args[start].charAt(0) != '-') {
@@ -4415,7 +4638,7 @@
}
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord proc = mLruProcesses.get(i);
- if (proc.pid > 0 && proc.pid == pid) {
+ if (proc.getPid() > 0 && proc.getPid() == pid) {
procs.add(proc);
} else if (allPkgs && proc.getPkgList() != null
&& proc.getPkgList().containsKey(args[start])) {
@@ -4433,12 +4656,12 @@
return procs;
}
- @GuardedBy("mService")
- void updateApplicationInfoLocked(List<String> packagesToUpdate, int userId,
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ void updateApplicationInfoLOSP(List<String> packagesToUpdate, int userId,
boolean updateFrameworkRes) {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord app = mLruProcesses.get(i);
- if (app.thread == null) {
+ if (app.getThread() == null) {
continue;
}
@@ -4452,14 +4675,14 @@
final ApplicationInfo ai = AppGlobals.getPackageManager()
.getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
if (ai != null) {
- app.thread.scheduleApplicationInfoChanged(ai);
+ app.getThread().scheduleApplicationInfoChanged(ai);
if (ai.packageName.equals(app.info.packageName)) {
app.info = ai;
}
}
} catch (RemoteException e) {
Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
- packageName, app));
+ packageName, app));
}
}
});
@@ -4471,14 +4694,15 @@
boolean foundProcess = false;
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
+ final IApplicationThread thread = r.getThread();
+ if (thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
try {
for (int index = packages.length - 1; index >= 0 && !foundProcess; index--) {
if (packages[index].equals(r.info.packageName)) {
foundProcess = true;
}
}
- r.thread.dispatchPackageBroadcast(cmd, packages);
+ thread.dispatchPackageBroadcast(cmd, packages);
} catch (RemoteException ex) {
}
}
@@ -4493,15 +4717,15 @@
}
/** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */
- @GuardedBy("mService")
- int getUidProcStateLocked(int uid) {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getUidProcStateLOSP(int uid) {
UidRecord uidRec = mActiveUids.get(uid);
return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
}
/** Returns the UidRecord for the given uid, if it exists. */
- @GuardedBy("mService")
- UidRecord getUidRecordLocked(int uid) {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ UidRecord getUidRecordLOSP(int uid) {
return mActiveUids.get(uid);
}
@@ -4518,10 +4742,10 @@
continue;
}
final UidRecord uidRec = mActiveUids.valueAt(i);
- if (!uidRec.idle) {
+ if (!uidRec.isIdle()) {
continue;
}
- mService.doStopUidLocked(uidRec.uid, uidRec);
+ mService.doStopUidLocked(uidRec.getUid(), uidRec);
}
}
@@ -4535,14 +4759,16 @@
* {@link #NETWORK_STATE_NO_CHANGE}.
*/
@VisibleForTesting
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
int getBlockStateForUid(UidRecord uidRec) {
// Denotes whether uid's process state is currently allowed network access.
final boolean isAllowed =
isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState())
|| isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState());
// Denotes whether uid's process state was previously allowed network access.
- final boolean wasAllowed = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.setProcState)
- || isProcStateAllowedWhileOnRestrictBackground(uidRec.setProcState);
+ final boolean wasAllowed =
+ isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState())
+ || isProcStateAllowedWhileOnRestrictBackground(uidRec.getSetProcState());
// When the uid is coming to foreground, AMS should inform the app thread that it should
// block for the network rules to get updated before launching an activity.
@@ -4563,8 +4789,8 @@
* {@link ProcessList#mProcStateSeqCounter} and notifies the app if it needs to block.
*/
@VisibleForTesting
- @GuardedBy("mService")
- void incrementProcStateSeqAndNotifyAppsLocked(ActiveUids activeUids) {
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ void incrementProcStateSeqAndNotifyAppsLOSP(ActiveUids activeUids) {
if (mService.mWaitForNetworkTimeoutMs <= 0) {
return;
}
@@ -4573,14 +4799,14 @@
for (int i = activeUids.size() - 1; i >= 0; --i) {
final UidRecord uidRec = activeUids.valueAt(i);
// If the network is not restricted for uid, then nothing to do here.
- if (!mService.mInjector.isNetworkRestrictedForUid(uidRec.uid)) {
+ if (!mService.mInjector.isNetworkRestrictedForUid(uidRec.getUid())) {
continue;
}
- if (!UserHandle.isApp(uidRec.uid) || !uidRec.hasInternetPermission) {
+ if (!UserHandle.isApp(uidRec.getUid()) || !uidRec.hasInternetPermission) {
continue;
}
// If process state is not changed, then there's nothing to do.
- if (uidRec.setProcState == uidRec.getCurProcState()) {
+ if (uidRec.getSetProcState() == uidRec.getCurProcState()) {
continue;
}
final int blockState = getBlockStateForUid(uidRec);
@@ -4595,7 +4821,7 @@
if (blockingUids == null) {
blockingUids = new ArrayList<>();
}
- blockingUids.add(uidRec.uid);
+ blockingUids.add(uidRec.getUid());
} else {
if (DEBUG_NETWORK) {
Slog.d(TAG_NETWORK, "uid going to background, notifying all blocking"
@@ -4618,15 +4844,16 @@
if (!blockingUids.contains(app.uid)) {
continue;
}
- if (!app.killedByAm && app.thread != null) {
- final UidRecord uidRec = getUidRecordLocked(app.uid);
+ final IApplicationThread thread = app.getThread();
+ if (!app.isKilledByAm() && thread != null) {
+ final UidRecord uidRec = getUidRecordLOSP(app.uid);
try {
if (DEBUG_NETWORK) {
Slog.d(TAG_NETWORK, "Informing app thread that it needs to block: "
+ uidRec);
}
if (uidRec != null) {
- app.thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
+ thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
}
} catch (RemoteException ignored) {
}
@@ -4698,7 +4925,7 @@
Slog.i(TAG, "note: " + app + " died, saving the exit info");
}
- Watchdog.getInstance().processDied(app.processName, app.pid);
+ Watchdog.getInstance().processDied(app.processName, app.getPid());
mAppExitInfoTracker.scheduleNoteProcessDied(app);
}
@@ -4820,10 +5047,10 @@
}
final Bundle bundle = new Bundle();
- bundle.putInt(EXTRA_PID, app.pid);
+ bundle.putInt(EXTRA_PID, app.getPid());
bundle.putInt(EXTRA_UID, app.uid);
// Since the pid could be reused, let's get the actual start time of each process
- bundle.putLong(EXTRA_TIMESTAMP, app.startTime);
+ bundle.putLong(EXTRA_TIMESTAMP, app.getStartTime());
bundle.putString(EXTRA_REASON, reason);
bundle.putInt(EXTRA_REQUESTER, requester);
List<Bundle> list = mWorkItems.get(app.uid);
@@ -4915,13 +5142,14 @@
app = mService.mPidsSelfLocked.get(pid);
}
- if (app == null || app.pid != pid || app.uid != uid || app.startTime != timestamp) {
+ if (app == null || app.getPid() != pid || app.uid != uid
+ || app.getStartTime() != timestamp) {
// This process record has been reused for another process, meaning the old process
// has been gone.
return true;
}
- if (app.getPkgList().forEachPackage(pkgName -> {
+ if (app.getPkgList().searchEachPackage(pkgName -> {
if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.contains(pkgName)) {
// One of the packages in this process is exempted
return Boolean.TRUE;
@@ -4932,12 +5160,12 @@
}
if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.contains(
- app.getReportedProcState())) {
+ app.mState.getReportedProcState())) {
// We need to reschedule it.
return false;
}
- app.kill(reason, ApplicationExitInfo.REASON_OTHER,
+ app.killLocked(reason, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_IMPERCEPTIBLE, true);
if (!app.isolated) {
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index 9fd2bd7..e533cc3 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -26,6 +26,7 @@
import android.util.DebugUtils;
import android.util.TimeUtils;
+import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
@@ -141,13 +142,13 @@
/**
* Last selected memory trimming level.
*/
- @GuardedBy("mService")
+ @CompositeRWLock({"mService", "mProcLock"})
private int mTrimMemoryLevel;
/**
* Want to clean up resources from showing UI?
*/
- @GuardedBy("mService")
+ @GuardedBy("mProcLock")
private boolean mPendingUiClean;
/**
@@ -164,7 +165,7 @@
/**
* When we last told the app that memory is low.
*/
- @GuardedBy("mService")
+ @CompositeRWLock({"mService", "mProfilerLock"})
private long mLastLowMemory;
/**
@@ -193,9 +194,12 @@
@GuardedBy("mProfilerLock")
private long mLastStateTime;
+ private final ActivityManagerGlobalLock mProcLock;
+
ProcessProfileRecord(final ProcessRecord app) {
mApp = app;
mService = app.mService;
+ mProcLock = mService.mProcLock;
mProfilerLock = mService.mAppProfiler.mProfilerLock;
}
@@ -410,22 +414,22 @@
mPssStatType = pssStatType;
}
- @GuardedBy("mService")
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
int getTrimMemoryLevel() {
return mTrimMemoryLevel;
}
- @GuardedBy("mService")
+ @GuardedBy({"mService", "mProcLock"})
void setTrimMemoryLevel(int trimMemoryLevel) {
mTrimMemoryLevel = trimMemoryLevel;
}
- @GuardedBy("mService")
+ @GuardedBy("mProcLock")
boolean hasPendingUiClean() {
return mPendingUiClean;
}
- @GuardedBy("mService")
+ @GuardedBy("mProcLock")
void setPendingUiClean(boolean pendingUiClean) {
mPendingUiClean = pendingUiClean;
mApp.getWindowProcessController().setPendingUiClean(pendingUiClean);
@@ -449,12 +453,12 @@
mLastRequestedGc = lastRequestedGc;
}
- @GuardedBy("mService")
+ @GuardedBy(anyOf = {"mService", "mProfilerLock"})
long getLastLowMemory() {
return mLastLowMemory;
}
- @GuardedBy("mService")
+ @GuardedBy({"mService", "mProfilerLock"})
void setLastLowMemory(long lastLowMemory) {
mLastLowMemory = lastLowMemory;
}
@@ -593,11 +597,11 @@
}
@GuardedBy({"mService", "mProfilerLock"})
- void updateProcState(ProcessRecord app) {
- mSetProcState = app.getCurProcState();
- mSetAdj = app.curAdj;
- mCurRawAdj = app.getCurRawAdj();
- mLastStateTime = app.lastStateTime;
+ void updateProcState(ProcessStateRecord state) {
+ mSetProcState = state.getCurProcState();
+ mSetAdj = state.getCurAdj();
+ mCurRawAdj = state.getCurRawAdj();
+ mLastStateTime = state.getLastStateTime();
}
@GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/ProcessProviderRecord.java b/services/core/java/com/android/server/am/ProcessProviderRecord.java
new file mode 100644
index 0000000..751e8a82
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessProviderRecord.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.util.ArrayMap;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * The state info of all content providers in the process.
+ */
+final class ProcessProviderRecord {
+ final ProcessRecord mApp;
+ private final ActivityManagerService mService;
+
+ /**
+ * The last time someone else was using a provider in this process.
+ */
+ private long mLastProviderTime;
+
+ /**
+ * class (String) -> ContentProviderRecord.
+ */
+ private final ArrayMap<String, ContentProviderRecord> mPubProviders = new ArrayMap<>();
+
+ /**
+ * All ContentProviderRecord process is using.
+ */
+ private final ArrayList<ContentProviderConnection> mConProviders = new ArrayList<>();
+
+ long getLastProviderTime() {
+ return mLastProviderTime;
+ }
+
+ void setLastProviderTime(long lastProviderTime) {
+ mLastProviderTime = lastProviderTime;
+ }
+
+ boolean hasProvider(String name) {
+ return mPubProviders.containsKey(name);
+ }
+
+ ContentProviderRecord getProvider(String name) {
+ return mPubProviders.get(name);
+ }
+
+ int numberOfProviders() {
+ return mPubProviders.size();
+ }
+
+ ContentProviderRecord getProviderAt(int index) {
+ return mPubProviders.valueAt(index);
+ }
+
+ void installProvider(String name, ContentProviderRecord provider) {
+ mPubProviders.put(name, provider);
+ }
+
+ void removeProvider(String name) {
+ mPubProviders.remove(name);
+ }
+
+ void ensureProviderCapacity(int capacity) {
+ mPubProviders.ensureCapacity(capacity);
+ }
+
+ int numberOfProviderConnections() {
+ return mConProviders.size();
+ }
+
+ ContentProviderConnection getProviderConnectionAt(int index) {
+ return mConProviders.get(index);
+ }
+
+ void addProviderConnection(ContentProviderConnection connection) {
+ mConProviders.add(connection);
+ }
+
+ boolean removeProviderConnection(ContentProviderConnection connection) {
+ return mConProviders.remove(connection);
+ }
+
+ ProcessProviderRecord(ProcessRecord app) {
+ mApp = app;
+ mService = app.mService;
+ }
+
+ /**
+ * @return Should the process restart or not.
+ */
+ @GuardedBy("mService")
+ boolean onCleanupApplicationRecordLocked(boolean allowRestart) {
+ boolean restart = false;
+ // Remove published content providers.
+ for (int i = mPubProviders.size() - 1; i >= 0; i--) {
+ final ContentProviderRecord cpr = mPubProviders.valueAt(i);
+ if (cpr.proc != mApp) {
+ // If the hosting process record isn't really us, bail out
+ continue;
+ }
+ final boolean alwaysRemove = mApp.mErrorState.isBad() || !allowRestart;
+ final boolean inLaunching = mService.mCpHelper
+ .removeDyingProviderLocked(mApp, cpr, alwaysRemove);
+ if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
+ // We left the provider in the launching list, need to
+ // restart it.
+ restart = true;
+ }
+
+ cpr.provider = null;
+ cpr.setProcess(null);
+ }
+ mPubProviders.clear();
+
+ // Take care of any launching providers waiting for this process.
+ if (mService.mCpHelper.cleanupAppInLaunchingProvidersLocked(mApp, false)) {
+ mService.mProcessList.noteProcessDiedLocked(mApp);
+ restart = true;
+ }
+
+ // Unregister from connected content providers.
+ if (!mConProviders.isEmpty()) {
+ for (int i = mConProviders.size() - 1; i >= 0; i--) {
+ final ContentProviderConnection conn = mConProviders.get(i);
+ conn.provider.connections.remove(conn);
+ mService.stopAssociationLocked(mApp.uid, mApp.processName, conn.provider.uid,
+ conn.provider.appInfo.longVersionCode, conn.provider.name,
+ conn.provider.info.processName);
+ }
+ mConProviders.clear();
+ }
+
+ return restart;
+ }
+
+ void dump(PrintWriter pw, String prefix, long nowUptime) {
+ if (mLastProviderTime > 0) {
+ pw.print(prefix); pw.print("lastProviderTime=");
+ TimeUtils.formatDuration(mLastProviderTime, nowUptime, pw);
+ pw.println();
+ }
+ if (mPubProviders.size() > 0) {
+ pw.print(prefix); pw.println("Published Providers:");
+ for (int i = 0, size = mPubProviders.size(); i < size; i++) {
+ pw.print(prefix); pw.print(" - "); pw.println(mPubProviders.keyAt(i));
+ pw.print(prefix); pw.print(" -> "); pw.println(mPubProviders.valueAt(i));
+ }
+ }
+ if (mConProviders.size() > 0) {
+ pw.print(prefix); pw.println("Connected Providers:");
+ for (int i = 0, size = mConProviders.size(); i < size; i++) {
+ pw.print(prefix); pw.print(" - ");
+ pw.println(mConProviders.get(i).toShortString());
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessReceiverRecord.java b/services/core/java/com/android/server/am/ProcessReceiverRecord.java
new file mode 100644
index 0000000..8d3e9669
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessReceiverRecord.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+
+/**
+ * The state info of all broadcast receivers in the process.
+ */
+final class ProcessReceiverRecord {
+ final ProcessRecord mApp;
+ private final ActivityManagerService mService;
+
+ /**
+ * mReceivers currently running in the app.
+ */
+ private final ArraySet<BroadcastRecord> mCurReceivers = new ArraySet<BroadcastRecord>();
+
+ /**
+ * All IIntentReceivers that are registered from this process.
+ */
+ private final ArraySet<ReceiverList> mReceivers = new ArraySet<>();
+
+ int numberOfCurReceivers() {
+ return mCurReceivers.size();
+ }
+
+ BroadcastRecord getCurReceiverAt(int index) {
+ return mCurReceivers.valueAt(index);
+ }
+
+ boolean hasCurReceiver(BroadcastRecord receiver) {
+ return mCurReceivers.contains(receiver);
+ }
+
+ void addCurReceiver(BroadcastRecord receiver) {
+ mCurReceivers.add(receiver);
+ }
+
+ void removeCurReceiver(BroadcastRecord receiver) {
+ mCurReceivers.remove(receiver);
+ }
+
+ int numberOfReceivers() {
+ return mReceivers.size();
+ }
+
+ ReceiverList getReceiverAt(int index) {
+ return mReceivers.valueAt(index);
+ }
+
+ void addReceiver(ReceiverList receiver) {
+ mReceivers.add(receiver);
+ }
+
+ void removeReceiver(ReceiverList receiver) {
+ mReceivers.remove(receiver);
+ }
+
+ ProcessReceiverRecord(ProcessRecord app) {
+ mApp = app;
+ mService = app.mService;
+ }
+
+ @GuardedBy("mService")
+ void onCleanupApplicationRecordLocked() {
+ // Unregister any mReceivers.
+ for (int i = mReceivers.size() - 1; i >= 0; i--) {
+ mService.removeReceiverLocked(mReceivers.valueAt(i));
+ }
+ mReceivers.clear();
+ }
+
+ void dump(PrintWriter pw, String prefix, long nowUptime) {
+ if (!mCurReceivers.isEmpty()) {
+ pw.print(prefix); pw.println("Current mReceivers:");
+ for (int i = 0, size = mCurReceivers.size(); i < size; i++) {
+ pw.print(prefix); pw.print(" - "); pw.println(mCurReceivers.valueAt(i));
+ }
+ }
+ if (mReceivers.size() > 0) {
+ pw.print(prefix); pw.println("mReceivers:");
+ for (int i = 0, size = mReceivers.size(); i < size; i++) {
+ pw.print(prefix); pw.print(" - "); pw.println(mReceivers.valueAt(i));
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a1adeaa..da8aeb5 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -16,78 +16,50 @@
package com.android.server.am;
-import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
-import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
-import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.Process.NFC_UID;
-import static android.os.Process.ROOT_UID;
-import static android.os.Process.SHELL_UID;
-import static android.os.Process.SYSTEM_UID;
-
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.MY_PID;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ApplicationErrorReport;
import android.app.ApplicationExitInfo;
import android.app.ApplicationExitInfo.Reason;
import android.app.ApplicationExitInfo.SubReason;
-import android.app.Dialog;
import android.app.IApplicationThread;
-import android.content.ComponentName;
-import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IncrementalStatesInfo;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.ProcessInfo;
-import android.content.pm.ServiceInfo;
import android.content.pm.VersionedPackage;
import android.content.res.CompatibilityInfo;
import android.os.Binder;
import android.os.IBinder;
-import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.Settings;
import android.server.ServerProtoEnums;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.CompositeRWLock;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.Zygote;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.MemoryPressureUtil;
import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessListener;
-import java.io.File;
import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
-import java.util.function.Consumer;
/**
* Full information about a particular process that
@@ -97,6 +69,12 @@
static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;
final ActivityManagerService mService; // where we came from
+ private final ActivityManagerGlobalLock mProcLock;
+
+ // =========================================================
+ // Basic info of the process, immutable or semi-immutable over
+ // the lifecycle of the process
+ // =========================================================
volatile ApplicationInfo info; // all about the first app in the process
final ProcessInfo processInfo; // if non-null, process-specific manifest info
final boolean isolated; // true if this is a special isolated process
@@ -106,243 +84,288 @@
final String processName; // name of the process
/**
- * List of packages running in the process
+ * Overall state of process's uid.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private UidRecord mUidRecord;
+
+ /**
+ * List of packages running in the process.
*/
private final PackageList mPkgList = new PackageList(this);
- UidRecord uidRecord; // overall state of process's uid.
- ArraySet<String> pkgDeps; // additional packages we have a dependency on
- IApplicationThread thread; // the actual proc... may be null only if
- // 'persistent' is true (in which case we
- // are in the process of launching the app)
- int pid; // The process of this application; 0 if none
- String procStatFile; // path to /proc/<pid>/stat
- int[] gids; // The gids this process was launched with
- private String mRequiredAbi;// The ABI this process was launched with
- String instructionSet; // The instruction set this process was launched with
- boolean starting; // True if the process is being started
- long lastActivityTime; // For managing the LRU list
- long lastStateTime; // Last time setProcState changed
- int maxAdj; // Maximum OOM adjustment for this process
- private int mCurRawAdj; // Current OOM unlimited adjustment for this process
- int setRawAdj; // Last set OOM unlimited adjustment for this process
- int curAdj; // Current OOM adjustment for this process
- int setAdj; // Last set OOM adjustment for this process
- int verifiedAdj; // The last adjustment that was verified as actually being set
- int curCapability; // Current capability flags of this process. For example,
- // PROCESS_CAPABILITY_FOREGROUND_LOCATION is one capability.
- int setCapability; // Last set capability flags.
- long lastCompactTime; // The last time that this process was compacted
- int reqCompactAction; // The most recent compaction action requested for this app.
- int lastCompactAction; // The most recent compaction action performed for this app.
- boolean frozen; // True when the process is frozen.
- boolean freezerOverride; // An override on the freeze state is in progress.
- long freezeUnfreezeTime; // Last time the app was (un)frozen, 0 for never
- boolean shouldNotFreeze; // True if a process has a WPRI binding from an unfrozen process
- private int mCurSchedGroup; // Currently desired scheduling class
- int setSchedGroup; // Last set to background scheduling class
- private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
- private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
- private int mCurRawProcState = PROCESS_STATE_NONEXISTENT; // Temp state during computation
- int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
- int savedPriority; // Previous priority value if we're switching to non-SCHED_OTHER
- int renderThreadTid; // TID for RenderThread
- ServiceRecord connectionService; // Service that applied current connectionGroup/Importance
- int connectionGroup; // Last group set by a connection
- int connectionImportance; // Last importance set by a connection
- boolean serviceb; // Process currently is on the service B list
- boolean serviceHighRam; // We are forcing to service B list due to its RAM use
- boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
- private boolean mHasClientActivities; // Are there any client services with activities?
- boolean hasStartedServices; // Are there any started services running in this process?
- private boolean mHasForegroundServices; // Running any services that are foreground?
- private int mFgServiceTypes; // Type of foreground service, if there is a foreground service.
- private int mRepFgServiceTypes; // Last reported foreground service types.
- private boolean mHasForegroundActivities; // Running any activities that are foreground?
- boolean repForegroundActivities; // Last reported foreground activities.
- boolean systemNoUi; // This is a system process, but not currently showing UI.
- boolean hasShownUi; // Has UI been shown in this process since it was started?
- private boolean mHasTopUi; // Is this process currently showing a non-activity UI that the user
- // is interacting with? E.g. The status bar when it is expanded, but
- // not when it is minimized. When true the
- // process will be set to use the ProcessList#SCHED_GROUP_TOP_APP
- // scheduling group to boost performance.
- private boolean mHasOverlayUi; // Is the process currently showing a non-activity UI that
- // overlays on-top of activity UIs on screen. E.g. display a window
- // of type
- // android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY
- // When true the process will oom adj score will be set to
- // ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance
- // of the process getting killed.
- boolean runningRemoteAnimation; // Is the process currently running a RemoteAnimation? When true
- // the process will be set to use the
- // ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost
- // performance, as well as oom adj score will be set to
- // ProcessList#VISIBLE_APP_ADJ at minimum to reduce the chance
- // of the process getting killed.
- boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so want to be lower
- boolean treatLikeActivity; // Bound using BIND_TREAT_LIKE_ACTIVITY
- boolean bad; // True if disabled in the bad process list
- boolean killedByAm; // True when proc has been killed by activity manager, not for RAM
- boolean killed; // True once we know the process has been killed
- boolean procStateChanged; // Keep track of whether we changed 'setAdj'.
- boolean reportedInteraction;// Whether we have told usage stats about it being an interaction
- boolean unlocked; // True when proc was started in user unlocked state
- private long mInteractionEventTime; // The time we sent the last interaction event
- private long mFgInteractionTime; // When we became foreground for interaction purposes
- String waitingToKill; // Process is waiting to be killed when in the bg, and reason
- Object forcingToImportant; // Token that is forcing this process to be important
- int adjSeq; // Sequence id for identifying oom_adj assignment cycles
- int completedAdjSeq; // Sequence id for identifying oom_adj assignment cycles
- boolean containsCycle; // Whether this app has encountered a cycle in the most recent update
- int lruSeq; // Sequence id for identifying LRU update cycles
- CompatibilityInfo compat; // last used compatibility mode
- IBinder.DeathRecipient deathRecipient; // Who is watching for the death.
- private ActiveInstrumentation mInstr; // Set to currently active instrumentation running in
- // process.
- private boolean mUsingWrapper; // Set to true when process was launched with a wrapper attached
- final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();// receivers currently running in the app
- private long mWhenUnimportant; // When (uptime) the process last became unimportant
- long lastProviderTime; // The last time someone else was using a provider in this process.
- long lastTopTime; // The last time the process was in the TOP state or greater.
- boolean empty; // Is this an empty background process?
- private boolean mCached; // Is this a cached process?
- String adjType; // Debugging: primary thing impacting oom_adj.
- int adjTypeCode; // Debugging: adj code to report to app.
- Object adjSource; // Debugging: option dependent object.
- int adjSourceProcState; // Debugging: proc state of adjSource's process.
- Object adjTarget; // Debugging: target component impacting oom_adj.
- Runnable crashHandler; // Optional local handler to be invoked in the process crash.
- boolean bindMountPending; // True if Android/obb and Android/data need to be bind mount .
+ /**
+ * Additional packages we have a dependency on.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private ArraySet<String> mPkgDeps;
- // Controller for error dialogs
- private final ErrorDialogController mDialogController = new ErrorDialogController();
- // Controller for driving the process state on the window manager side.
+ /**
+ * The process of this application; 0 if none.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ int mPid;
+
+ /**
+ * The gids this process was launched with.
+ */
+ @GuardedBy("mService")
+ private int[] mGids;
+
+ /**
+ * The ABI this process was launched with.
+ */
+ @GuardedBy("mService")
+ private String mRequiredAbi;
+
+ /**
+ * The instruction set this process was launched with.
+ */
+ @GuardedBy("mService")
+ private String mInstructionSet;
+
+ /**
+ * The actual proc... may be null only if 'persistent' is true
+ * (in which case we are in the process of launching the app).
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private IApplicationThread mThread;
+
+ /**
+ * Always keep this application running?
+ */
+ private volatile boolean mPersistent;
+
+ /**
+ * Caching of toShortString() result.
+ * <p>Note: No lock here, it doesn't matter in case of race condition</p>
+ */
+ private String mShortStringName;
+
+ /**
+ * Caching of toString() result.
+ * <p>Note: No lock here, it doesn't matter in case of race condition</p>
+ */
+ private String mStringName;
+
+ /**
+ * Process start is pending.
+ */
+ @GuardedBy("mService")
+ private boolean mPendingStart;
+
+ /**
+ * Seq no. Indicating the latest process start associated with this process record.
+ */
+ @GuardedBy("mService")
+ private long mStartSeq;
+
+ /**
+ * Params used in starting this process.
+ */
+ private volatile HostingRecord mHostingRecord;
+
+ /**
+ * Selinux info of this process.
+ */
+ private volatile String mSeInfo;
+
+ /**
+ * When the process is started.
+ */
+ private volatile long mStartTime;
+
+ /**
+ * This will be same as {@link #uid} usually except for some apps used during factory testing.
+ */
+ private volatile int mStartUid;
+
+ /**
+ * Indicates how the external storage was mounted for this process.
+ */
+ private volatile int mMountMode;
+
+ /**
+ * True if Android/obb and Android/data need to be bind mount.
+ */
+ private volatile boolean mBindMountPending;
+
+ /**
+ * True when proc was started in user unlocked state.
+ */
+ @GuardedBy("mProcLock")
+ private boolean mUnlocked;
+
+ /**
+ * TID for RenderThread.
+ */
+ @GuardedBy("mProcLock")
+ private int mRenderThreadTid;
+
+ /**
+ * Last used compatibility mode.
+ */
+ @GuardedBy("mService")
+ private CompatibilityInfo mCompat;
+
+ /**
+ * Set of disabled compat changes for the process (all others are enabled).
+ */
+ @GuardedBy("mService")
+ private long[] mDisabledCompatChanges;
+
+ /**
+ * Who is watching for the death.
+ */
+ @GuardedBy("mService")
+ private IBinder.DeathRecipient mDeathRecipient;
+
+ /**
+ * Set to currently active instrumentation running in process.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private ActiveInstrumentation mInstr;
+
+ /**
+ * True when proc has been killed by activity manager, not for RAM.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mKilledByAm;
+
+ /**
+ * True once we know the process has been killed.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mKilled;
+
+ /**
+ * The timestamp in uptime when this process was killed.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private long mKillTime;
+
+ /**
+ * Process is waiting to be killed when in the bg, and reason.
+ */
+ @GuardedBy("mService")
+ private String mWaitingToKill;
+
+ /**
+ * Whether this process should be killed and removed from process list.
+ * It is set when the package is force-stopped or the process has crashed too many times.
+ */
+ private volatile boolean mRemoved;
+
+ /**
+ * Was app launched for debugging?
+ */
+ @GuardedBy("mService")
+ private boolean mDebugging;
+
+ /**
+ * Has process show wait for debugger dialog?
+ */
+ @GuardedBy("mProcLock")
+ private boolean mWaitedForDebugger;
+
+ /**
+ * For managing the LRU list.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private long mLastActivityTime;
+
+ /**
+ * Set to true when process was launched with a wrapper attached.
+ */
+ @GuardedBy("mService")
+ private boolean mUsingWrapper;
+
+ /**
+ * Sequence id for identifying LRU update cycles.
+ */
+ @GuardedBy("mService")
+ private int mLruSeq;
+
+ /**
+ * Class to run on start if this is a special isolated process.
+ */
+ @GuardedBy("mService")
+ private String mIsolatedEntryPoint;
+
+ /**
+ * Arguments to pass to isolatedEntryPoint's main().
+ */
+ @GuardedBy("mService")
+ private String[] mIsolatedEntryPointArgs;
+
+ /**
+ * Process is currently hosting a backup agent for backup or restore.
+ */
+ @GuardedBy("mService")
+ private boolean mInFullBackup;
+
+ /**
+ * Controller for driving the process state on the window manager side.
+ */
private final WindowProcessController mWindowProcessController;
- // all ServiceRecord running in this process
- private final ArraySet<ServiceRecord> mServices = new ArraySet<>();
- // services that are currently executing code (need to remain foreground).
- final ArraySet<ServiceRecord> executingServices = new ArraySet<>();
- // All ConnectionRecord this process holds
- final ArraySet<ConnectionRecord> connections = new ArraySet<>();
- // all IIntentReceivers that are registered from this process.
- final ArraySet<ReceiverList> receivers = new ArraySet<>();
- // class (String) -> ContentProviderRecord
- final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
- // All ContentProviderRecord process is using
- final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
- // a set of UIDs of all bound clients
- private ArraySet<Integer> mBoundClientUids = new ArraySet<>();
-
- String isolatedEntryPoint; // Class to run on start if this is a special isolated process.
- String[] isolatedEntryPointArgs; // Arguments to pass to isolatedEntryPoint's main().
-
- boolean execServicesFg; // do we need to be executing services in the foreground?
- private boolean mPersistent;// always keep this application running?
- private boolean mCrashing; // are we in the process of crashing?
- boolean forceCrashReport; // suppress normal auto-dismiss of crash dialog & report UI?
- private boolean mNotResponding; // does the app have a not responding dialog?
- volatile boolean removed; // Whether this process should be killed and removed from process
- // list. It is set when the package is force-stopped or the process
- // has crashed too many times.
- private boolean mDebugging; // was app launched for debugging?
- boolean waitedForDebugger; // has process show wait for debugger dialog?
-
- String shortStringName; // caching of toShortString() result.
- String stringName; // caching of toString() result.
- boolean pendingStart; // Process start is pending.
- long startSeq; // Seq no. indicating the latest process start associated with
- // this process record.
- int mountMode; // Indicates how the external storage was mounted for this process.
-
- // These reports are generated & stored when an app gets into an error condition.
- // They will be "null" when all is OK.
- ActivityManager.ProcessErrorStateInfo crashingReport;
- ActivityManager.ProcessErrorStateInfo notRespondingReport;
-
- // Who will be notified of the error. This is usually an activity in the
- // app that installed the package.
- ComponentName errorReportReceiver;
-
- // Process is currently hosting a backup agent for backup or restore
- public boolean inFullBackup;
- // App is allowed to manage allowlists such as temporary Power Save mode allowlist.
- boolean mAllowlistManager;
-
- // Params used in starting this process.
- HostingRecord hostingRecord;
- String seInfo;
- long startTime;
- // This will be same as {@link #uid} usually except for some apps used during factory testing.
- int startUid;
- // set of disabled compat changes for the process (all others are enabled)
- long[] mDisabledCompatChanges;
-
- // The precede instance of the process, which would exist when the previous process is killed
- // but not fully dead yet; in this case, the new instance of the process should be held until
- // this precede instance is fully dead.
- volatile ProcessRecord mPrecedence;
- // The succeeding instance of the process, which is going to be started after this process
- // is killed successfully.
- volatile ProcessRecord mSuccessor;
-
- // Cached task info for OomAdjuster
- private static final int VALUE_INVALID = -1;
- private static final int VALUE_FALSE = 0;
- private static final int VALUE_TRUE = 1;
- private int mCachedHasActivities = VALUE_INVALID;
- private int mCachedIsHeavyWeight = VALUE_INVALID;
- private int mCachedHasVisibleActivities = VALUE_INVALID;
- private int mCachedIsHomeProcess = VALUE_INVALID;
- private int mCachedIsPreviousProcess = VALUE_INVALID;
- private int mCachedHasRecentTasks = VALUE_INVALID;
- private int mCachedIsReceivingBroadcast = VALUE_INVALID;
- int mCachedAdj = ProcessList.INVALID_ADJ;
- boolean mCachedForegroundActivities = false;
- int mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- int mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
-
- // Approximates the usage count of the app, used for cache re-ranking by CacheOomRanker.
- //
- // Counts the number of times the process is re-added to the cache (i.e. setCached(false);
- // setCached(true)). This over counts, as setCached is sometimes reset while remaining in the
- // cache. However, this happens uniformly across processes, so ranking is not affected.
- private int mCacheOomRankerUseCount;
-
- boolean mReachable; // Whether or not this process is reachable from given process
-
- long mKillTime; // The timestamp in uptime when this process was killed.
-
- // If the proc state is PROCESS_STATE_BOUND_FOREGROUND_SERVICE or above, it can start FGS.
- // It must obtain the proc state from a persistent/top process or FGS, not transitive.
- int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
-
- private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>();
-
- // The list of permissions that can start FGS from background.
- private static String[] ALLOW_BG_START_FGS_PERMISSIONS =
- {START_ACTIVITIES_FROM_BACKGROUND, START_FOREGROUND_SERVICES_FROM_BACKGROUND,
- SYSTEM_ALERT_WINDOW};
- // Does the process has permission to start FGS from background.
- boolean mAllowStartFgsByPermission;
- // Can this process start FGS from background?
- // If this process has the ability to start FGS from background, this ability can be passed to
- // another process through service binding.
- boolean mAllowStartFgs;
/**
* Profiling info of the process, such as PSS, cpu, etc.
*/
final ProcessProfileRecord mProfile;
+ /**
+ * All about the services in this process.
+ */
+ final ProcessServiceRecord mServices;
+
+ /**
+ * All about the providers in this process.
+ */
+ final ProcessProviderRecord mProviders;
+
+ /**
+ * All about the receivers in this process.
+ */
+ final ProcessReceiverRecord mReceivers;
+
+ /**
+ * All about the error state(crash, ANR) in this process.
+ */
+ final ProcessErrorStateRecord mErrorState;
+
+ /**
+ * All about the process state info (proc state, oom adj score) in this process.
+ */
+ final ProcessStateRecord mState;
+
+ /**
+ * All about the state info of the optimizer when the process is cached.
+ */
+ final ProcessCachedOptimizerRecord mOptRecord;
+
+ /**
+ * The preceding instance of the process, which would exist when the previous process is killed
+ * but not fully dead yet; in this case, the new instance of the process should be held until
+ * this preceding instance is fully dead.
+ */
+ volatile ProcessRecord mPredecessor;
+
+ /**
+ * The succeeding instance of the process, which is going to be started after this process
+ * is killed successfully.
+ */
+ volatile ProcessRecord mSuccessor;
+
void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
long startTime) {
- this.startUid = startUid;
- this.hostingRecord = hostingRecord;
- this.seInfo = seInfo;
- this.startTime = startTime;
+ this.mStartUid = startUid;
+ this.mHostingRecord = hostingRecord;
+ this.mSeInfo = seInfo;
+ this.mStartTime = startTime;
}
+ @GuardedBy({"mService", "mProcLock"})
void dump(PrintWriter pw, String prefix) {
final long nowUptime = SystemClock.uptimeMillis();
@@ -352,10 +375,10 @@
pw.print(" ISOLATED uid="); pw.print(uid);
}
pw.print(" gids={");
- if (gids != null) {
- for (int gi=0; gi<gids.length; gi++) {
+ if (mGids != null) {
+ for (int gi = 0; gi < mGids.length; gi++) {
if (gi != 0) pw.print(", ");
- pw.print(gids[gi]);
+ pw.print(mGids[gi]);
}
}
@@ -371,9 +394,12 @@
if (processInfo.gwpAsanMode != ApplicationInfo.GWP_ASAN_DEFAULT) {
pw.print(prefix); pw.println(" gwpAsanMode=" + processInfo.gwpAsanMode);
}
+ if (processInfo.memtagMode != ApplicationInfo.MEMTAG_DEFAULT) {
+ pw.print(prefix); pw.println(" memtagMode=" + processInfo.memtagMode);
+ }
}
pw.print(prefix); pw.print("mRequiredAbi="); pw.print(mRequiredAbi);
- pw.print(" instructionSet="); pw.println(instructionSet);
+ pw.print(" instructionSet="); pw.println(mInstructionSet);
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
}
@@ -386,210 +412,62 @@
pw.print(" publicDir="); pw.print(info.publicSourceDir);
pw.print(" data="); pw.println(info.dataDir);
mPkgList.dump(pw, prefix);
- pw.println("}");
- if (pkgDeps != null) {
+ if (mPkgDeps != null) {
pw.print(prefix); pw.print("packageDependencies={");
- for (int i=0; i<pkgDeps.size(); i++) {
+ for (int i = 0; i < mPkgDeps.size(); i++) {
if (i > 0) pw.print(", ");
- pw.print(pkgDeps.valueAt(i));
+ pw.print(mPkgDeps.valueAt(i));
}
pw.println("}");
}
- pw.print(prefix); pw.print("compat="); pw.println(compat);
+ pw.print(prefix); pw.print("compat="); pw.println(mCompat);
if (mInstr != null) {
pw.print(prefix); pw.print("mInstr="); pw.println(mInstr);
}
- pw.print(prefix); pw.print("thread="); pw.println(thread);
- pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
- pw.println(starting);
+ pw.print(prefix); pw.print("thread="); pw.println(mThread);
+ pw.print(prefix); pw.print("pid="); pw.print(mPid);
pw.print(prefix); pw.print("lastActivityTime=");
- TimeUtils.formatDuration(lastActivityTime, nowUptime, pw);
- pw.println();
- pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
- pw.print(" lruSeq="); pw.println(lruSeq);
- pw.print(prefix); pw.print("oom adj: max="); pw.print(maxAdj);
- pw.print(" curRaw="); pw.print(mCurRawAdj);
- pw.print(" setRaw="); pw.print(setRawAdj);
- pw.print(" cur="); pw.print(curAdj);
- pw.print(" set="); pw.println(setAdj);
- pw.print(prefix); pw.print("lastCompactTime="); pw.print(lastCompactTime);
- pw.print(" lastCompactAction="); pw.println(lastCompactAction);
- pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
- pw.print(" setSchedGroup="); pw.print(setSchedGroup);
- pw.print(" systemNoUi="); pw.print(systemNoUi);
- pw.print(prefix); pw.print("curProcState="); pw.print(getCurProcState());
- pw.print(" mRepProcState="); pw.print(mRepProcState);
- pw.print(" setProcState="); pw.print(setProcState);
- pw.print(" lastStateTime=");
- TimeUtils.formatDuration(lastStateTime, nowUptime, pw);
- pw.println();
- pw.print(prefix); pw.print("curCapability=");
- ActivityManager.printCapabilitiesFull(pw, curCapability);
- pw.print(" setCapability=");
- ActivityManager.printCapabilitiesFull(pw, setCapability);
- pw.println();
- pw.print(prefix); pw.print("allowStartFgsState=");
- pw.println(mAllowStartFgsState);
- if (mAllowStartFgs) {
- pw.print(prefix); pw.print("allowStartFgs="); pw.println(mAllowStartFgs);
- }
- if (hasShownUi || mProfile.hasPendingUiClean() || hasAboveClient || treatLikeActivity) {
- pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
- pw.print(" pendingUiClean="); pw.print(mProfile.hasPendingUiClean());
- pw.print(" hasAboveClient="); pw.print(hasAboveClient);
- pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
- }
- pw.print(prefix); pw.print("cached="); pw.print(mCached);
- pw.print(" empty="); pw.println(empty);
- if (serviceb) {
- pw.print(prefix); pw.print("serviceb="); pw.print(serviceb);
- pw.print(" serviceHighRam="); pw.println(serviceHighRam);
- }
- if (notCachedSinceIdle) {
- pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(notCachedSinceIdle);
- pw.print(" initialIdlePss="); pw.println(mProfile.getInitialIdlePss());
- }
- if (connectionService != null || connectionGroup != 0) {
- pw.print(prefix); pw.print("connectionGroup="); pw.print(connectionGroup);
- pw.print(" Importance="); pw.print(connectionImportance);
- pw.print(" Service="); pw.println(connectionService);
- }
- if (hasTopUi() || hasOverlayUi() || runningRemoteAnimation) {
- pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
- pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
- pw.print(" runningRemoteAnimation="); pw.println(runningRemoteAnimation);
- }
- if (mHasForegroundServices || forcingToImportant != null) {
- pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
- pw.print(" forcingToImportant="); pw.println(forcingToImportant);
- }
- if (reportedInteraction || mFgInteractionTime != 0) {
- pw.print(prefix); pw.print("reportedInteraction=");
- pw.print(reportedInteraction);
- if (mInteractionEventTime != 0) {
- pw.print(" time=");
- TimeUtils.formatDuration(mInteractionEventTime, SystemClock.elapsedRealtime(), pw);
- }
- if (mFgInteractionTime != 0) {
- pw.print(" fgInteractionTime=");
- TimeUtils.formatDuration(mFgInteractionTime, SystemClock.elapsedRealtime(), pw);
- }
- pw.println();
- }
- if (mPersistent || removed) {
+ TimeUtils.formatDuration(mLastActivityTime, nowUptime, pw);
+ if (mPersistent || mRemoved) {
pw.print(prefix); pw.print("persistent="); pw.print(mPersistent);
- pw.print(" removed="); pw.println(removed);
+ pw.print(" removed="); pw.println(mRemoved);
}
- if (mHasClientActivities || mHasForegroundActivities || repForegroundActivities) {
- pw.print(prefix); pw.print("hasClientActivities="); pw.print(mHasClientActivities);
- pw.print(" foregroundActivities="); pw.print(mHasForegroundActivities);
- pw.print(" (rep="); pw.print(repForegroundActivities); pw.println(")");
+ if (mDebugging) {
+ pw.print(prefix); pw.print("mDebugging="); pw.println(mDebugging);
}
- if (lastProviderTime > 0) {
- pw.print(prefix); pw.print("lastProviderTime=");
- TimeUtils.formatDuration(lastProviderTime, nowUptime, pw);
- pw.println();
+ if (mPendingStart) {
+ pw.print(prefix); pw.print("pendingStart="); pw.println(mPendingStart);
}
- if (lastTopTime > 0) {
- pw.print(prefix); pw.print("lastTopTime=");
- TimeUtils.formatDuration(lastTopTime, nowUptime, pw);
- pw.println();
- }
- if (hasStartedServices) {
- pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
- }
- if (pendingStart) {
- pw.print(prefix); pw.print("pendingStart="); pw.println(pendingStart);
- }
- pw.print(prefix); pw.print("startSeq="); pw.println(startSeq);
+ pw.print(prefix); pw.print("startSeq="); pw.println(mStartSeq);
pw.print(prefix); pw.print("mountMode="); pw.println(
- DebugUtils.valueToString(Zygote.class, "MOUNT_EXTERNAL_", mountMode));
- if (setProcState > ActivityManager.PROCESS_STATE_SERVICE) {
+ DebugUtils.valueToString(Zygote.class, "MOUNT_EXTERNAL_", mMountMode));
+ if (mKilled || mKilledByAm || mWaitingToKill != null) {
+ pw.print(prefix); pw.print("killed="); pw.print(mKilled);
+ pw.print(" killedByAm="); pw.print(mKilledByAm);
+ pw.print(" waitingToKill="); pw.println(mWaitingToKill);
+ }
+ if (mIsolatedEntryPoint != null || mIsolatedEntryPointArgs != null) {
+ pw.print(prefix); pw.print("isolatedEntryPoint="); pw.println(mIsolatedEntryPoint);
+ pw.print(prefix); pw.print("isolatedEntryPointArgs=");
+ pw.println(Arrays.toString(mIsolatedEntryPointArgs));
+ }
+ if (mState.getSetProcState() > ActivityManager.PROCESS_STATE_SERVICE) {
mProfile.dumpCputime(pw, prefix);
- pw.print(" whenUnimportant=");
- TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw);
- pw.println();
}
mProfile.dumpPss(pw, prefix, nowUptime);
- if (killed || killedByAm || waitingToKill != null) {
- pw.print(prefix); pw.print("killed="); pw.print(killed);
- pw.print(" killedByAm="); pw.print(killedByAm);
- pw.print(" waitingToKill="); pw.println(waitingToKill);
- }
- if (mDebugging || mCrashing || mDialogController.hasCrashDialogs() || mNotResponding
- || mDialogController.hasAnrDialogs() || bad) {
- pw.print(prefix); pw.print("mDebugging="); pw.print(mDebugging);
- pw.print(" mCrashing=" + mCrashing);
- pw.print(" " + mDialogController.mCrashDialogs);
- pw.print(" mNotResponding=" + mNotResponding);
- pw.print(" " + mDialogController.mAnrDialogs);
- pw.print(" bad=" + bad);
-
- // mCrashing or mNotResponding is always set before errorReportReceiver
- if (errorReportReceiver != null) {
- pw.print(" errorReportReceiver=");
- pw.print(errorReportReceiver.flattenToShortString());
- }
- pw.println();
- }
- if (mAllowlistManager) {
- pw.print(prefix); pw.print("allowlistManager="); pw.println(mAllowlistManager);
- }
- if (isolatedEntryPoint != null || isolatedEntryPointArgs != null) {
- pw.print(prefix); pw.print("isolatedEntryPoint="); pw.println(isolatedEntryPoint);
- pw.print(prefix); pw.print("isolatedEntryPointArgs=");
- pw.println(Arrays.toString(isolatedEntryPointArgs));
- }
+ mState.dump(pw, prefix, nowUptime);
+ mErrorState.dump(pw, prefix, nowUptime);
+ mServices.dump(pw, prefix, nowUptime);
+ mProviders.dump(pw, prefix, nowUptime);
+ mReceivers.dump(pw, prefix, nowUptime);
+ mOptRecord.dump(pw, prefix, nowUptime);
mWindowProcessController.dump(pw, prefix);
- if (mServices.size() > 0) {
- pw.print(prefix); pw.println("Services:");
- for (int i = 0; i < mServices.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(mServices.valueAt(i));
- }
- }
- if (executingServices.size() > 0) {
- pw.print(prefix); pw.print("Executing Services (fg=");
- pw.print(execServicesFg); pw.println(")");
- for (int i=0; i<executingServices.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(executingServices.valueAt(i));
- }
- }
- if (connections.size() > 0) {
- pw.print(prefix); pw.println("Connections:");
- for (int i=0; i<connections.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(connections.valueAt(i));
- }
- }
- if (pubProviders.size() > 0) {
- pw.print(prefix); pw.println("Published Providers:");
- for (int i=0; i<pubProviders.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(pubProviders.keyAt(i));
- pw.print(prefix); pw.print(" -> "); pw.println(pubProviders.valueAt(i));
- }
- }
- if (conProviders.size() > 0) {
- pw.print(prefix); pw.println("Connected Providers:");
- for (int i=0; i<conProviders.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(conProviders.get(i).toShortString());
- }
- }
- if (!curReceivers.isEmpty()) {
- pw.print(prefix); pw.println("Current Receivers:");
- for (int i=0; i < curReceivers.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(curReceivers.valueAt(i));
- }
- }
- if (receivers.size() > 0) {
- pw.print(prefix); pw.println("Receivers:");
- for (int i=0; i<receivers.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(receivers.valueAt(i));
- }
- }
}
ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
int _uid) {
mService = _service;
+ mProcLock = _service.mProcLock;
info = _info;
ProcessInfo procInfo = null;
if (_service.mPackageManagerInt != null) {
@@ -598,9 +476,12 @@
if (processes != null) {
procInfo = processes.get(_processName);
if (procInfo != null && procInfo.deniedPermissions == null
- && procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT) {
+ && procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT
+ && procInfo.memtagMode == ApplicationInfo.MEMTAG_DEFAULT
+ && procInfo.nativeHeapZeroInit == null) {
// If this process hasn't asked for permissions to be denied, or for a
- // non-default GwpAsan mode, then we don't care about it.
+ // non-default GwpAsan mode, or any other non-default setting, then we don't
+ // care about it.
procInfo = null;
}
}
@@ -612,118 +493,393 @@
uid = _uid;
userId = UserHandle.getUserId(_uid);
processName = _processName;
- maxAdj = ProcessList.UNKNOWN_ADJ;
- mCurRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
- curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
mPersistent = false;
- removed = false;
+ mRemoved = false;
mProfile = new ProcessProfileRecord(this);
+ mServices = new ProcessServiceRecord(this);
+ mProviders = new ProcessProviderRecord(this);
+ mReceivers = new ProcessReceiverRecord(this);
+ mErrorState = new ProcessErrorStateRecord(this);
+ mState = new ProcessStateRecord(this);
+ mOptRecord = new ProcessCachedOptimizerRecord(this);
final long now = SystemClock.uptimeMillis();
- freezeUnfreezeTime = lastStateTime = now;
mProfile.init(now);
+ mOptRecord.init(now);
+ mState.init(now);
mWindowProcessController = new WindowProcessController(
mService.mActivityTaskManager, info, processName, uid, userId, this, this);
mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
- setAllowStartFgsByPermission();
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ UidRecord getUidRecord() {
+ return mUidRecord;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setUidRecord(UidRecord uidRecord) {
+ mUidRecord = uidRecord;
}
PackageList getPkgList() {
return mPkgList;
}
- public void setPid(int _pid) {
- pid = _pid;
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ArraySet<String> getPkgDeps() {
+ return mPkgDeps;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setPkgDeps(ArraySet<String> pkgDeps) {
+ mPkgDeps = pkgDeps;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getPid() {
+ return mPid;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setPid(int pid) {
+ mPid = pid;
mWindowProcessController.setPid(pid);
- procStatFile = null;
- shortStringName = null;
- stringName = null;
+ mShortStringName = null;
+ mStringName = null;
synchronized (mProfile.mProfilerLock) {
mProfile.setPid(pid);
}
}
- public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
- mProfile.onProcessActive(_thread, tracker);
- thread = _thread;
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ IApplicationThread getThread() {
+ return mThread;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ public void makeActive(IApplicationThread thread, ProcessStatsService tracker) {
+ mProfile.onProcessActive(thread, tracker);
+ mThread = thread;
mWindowProcessController.setThread(thread);
}
+ @GuardedBy({"mService", "mProcLock"})
public void makeInactive(ProcessStatsService tracker) {
- thread = null;
+ mThread = null;
mWindowProcessController.setThread(null);
mProfile.onProcessInactive(tracker);
}
- /**
- * Records a service as running in the process. Note that this method does not actually start
- * the service, but records the service as started for bookkeeping.
- *
- * @return true if the service was added, false otherwise.
- */
- boolean startService(ServiceRecord record) {
- if (record == null) {
- return false;
+ @GuardedBy("mService")
+ int[] getGids() {
+ return mGids;
+ }
+
+ @GuardedBy("mService")
+ void setGids(int[] gids) {
+ mGids = gids;
+ }
+
+ @GuardedBy("mService")
+ String getRequiredAbi() {
+ return mRequiredAbi;
+ }
+
+ @GuardedBy("mService")
+ void setRequiredAbi(String requiredAbi) {
+ mRequiredAbi = requiredAbi;
+ mWindowProcessController.setRequiredAbi(requiredAbi);
+ }
+
+ @GuardedBy("mService")
+ String getInstructionSet() {
+ return mInstructionSet;
+ }
+
+ @GuardedBy("mService")
+ void setInstructionSet(String instructionSet) {
+ mInstructionSet = instructionSet;
+ }
+
+ void setPersistent(boolean persistent) {
+ mPersistent = persistent;
+ mWindowProcessController.setPersistent(persistent);
+ }
+
+ boolean isPersistent() {
+ return mPersistent;
+ }
+
+ @GuardedBy("mService")
+ boolean isPendingStart() {
+ return mPendingStart;
+ }
+
+ @GuardedBy("mService")
+ void setPendingStart(boolean pendingStart) {
+ mPendingStart = pendingStart;
+ }
+
+ @GuardedBy("mService")
+ long getStartSeq() {
+ return mStartSeq;
+ }
+
+ @GuardedBy("mService")
+ void setStartSeq(long startSeq) {
+ mStartSeq = startSeq;
+ }
+
+ HostingRecord getHostingRecord() {
+ return mHostingRecord;
+ }
+
+ void setHostingRecord(HostingRecord hostingRecord) {
+ mHostingRecord = hostingRecord;
+ }
+
+ String getSeInfo() {
+ return mSeInfo;
+ }
+
+ void setSeInfo(String seInfo) {
+ mSeInfo = seInfo;
+ }
+
+ long getStartTime() {
+ return mStartTime;
+ }
+
+ void setStartTime(long startTime) {
+ mStartTime = startTime;
+ }
+
+ int getStartUid() {
+ return mStartUid;
+ }
+
+ void setStartUid(int startUid) {
+ mStartUid = startUid;
+ }
+
+ int getMountMode() {
+ return mMountMode;
+ }
+
+ void setMountMode(int mountMode) {
+ mMountMode = mountMode;
+ }
+
+ boolean isBindMountPending() {
+ return mBindMountPending;
+ }
+
+ void setBindMountPending(boolean bindMountPending) {
+ mBindMountPending = bindMountPending;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean isUnlocked() {
+ return mUnlocked;
+ }
+
+ @GuardedBy("mProcLock")
+ void setUnlocked(boolean unlocked) {
+ mUnlocked = unlocked;
+ }
+
+ @GuardedBy("mProcLock")
+ int getRenderThreadTid() {
+ return mRenderThreadTid;
+ }
+
+ @GuardedBy("mProcLock")
+ void setRenderThreadTid(int renderThreadTid) {
+ mRenderThreadTid = renderThreadTid;
+ }
+
+ @GuardedBy("mService")
+ CompatibilityInfo getCompat() {
+ return mCompat;
+ }
+
+ @GuardedBy("mService")
+ void setCompat(CompatibilityInfo compat) {
+ mCompat = compat;
+ }
+
+ @GuardedBy("mService")
+ long[] getDisabledCompatChanges() {
+ return mDisabledCompatChanges;
+ }
+
+ @GuardedBy("mService")
+ void setDisabledCompatChanges(long[] disabledCompatChanges) {
+ mDisabledCompatChanges = disabledCompatChanges;
+ }
+
+ @GuardedBy("mService")
+ void unlinkDeathRecipient() {
+ if (mDeathRecipient != null && mThread != null) {
+ mThread.asBinder().unlinkToDeath(mDeathRecipient, 0);
}
- boolean added = mServices.add(record);
- if (added && record.serviceInfo != null) {
- mWindowProcessController.onServiceStarted(record.serviceInfo);
- }
- return added;
+ mDeathRecipient = null;
}
- /**
- * Records a service as stopped. Note that like {@link #startService(ServiceRecord)} this method
- * does not actually stop the service, but records the service as stopped for bookkeeping.
- *
- * @return true if the service was removed, false otherwise.
- */
- boolean stopService(ServiceRecord record) {
- return mServices.remove(record);
+ @GuardedBy("mService")
+ void setDeathRecipient(IBinder.DeathRecipient deathRecipient) {
+ mDeathRecipient = deathRecipient;
}
- /**
- * The same as calling {@link #stopService(ServiceRecord)} on all current running services.
- */
- void stopAllServices() {
- mServices.clear();
+ @GuardedBy({"mService", "mProcLock"})
+ void setActiveInstrumentation(ActiveInstrumentation instr) {
+ mInstr = instr;
+ boolean isInstrumenting = instr != null;
+ mWindowProcessController.setInstrumenting(
+ isInstrumenting,
+ isInstrumenting ? instr.mSourceUid : -1,
+ isInstrumenting && instr.mHasBackgroundActivityStartsPermission);
}
- /**
- * Returns the number of services added with {@link #startService(ServiceRecord)} and not yet
- * removed by a call to {@link #stopService(ServiceRecord)} or {@link #stopAllServices()}.
- *
- * @see #startService(ServiceRecord)
- * @see #stopService(ServiceRecord)
- */
- int numberOfRunningServices() {
- return mServices.size();
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ActiveInstrumentation getActiveInstrumentation() {
+ return mInstr;
}
- /**
- * Returns the service at the specified {@code index}.
- *
- * @see #numberOfRunningServices()
- */
- ServiceRecord getRunningServiceAt(int index) {
- return mServices.valueAt(index);
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isKilledByAm() {
+ return mKilledByAm;
}
- void setCached(boolean cached) {
- if (mCached != cached) {
- mCached = cached;
- if (cached) {
- ++mCacheOomRankerUseCount;
- }
- }
+ @GuardedBy({"mService", "mProcLock"})
+ void setKilledByAm(boolean killedByAm) {
+ mKilledByAm = killedByAm;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isKilled() {
+ return mKilled;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setKilled(boolean killed) {
+ mKilled = killed;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ long getKillTime() {
+ return mKillTime;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setKillTime(long killTime) {
+ mKillTime = killTime;
+ }
+
+ @GuardedBy("mService")
+ String getWaitingToKill() {
+ return mWaitingToKill;
+ }
+
+ @GuardedBy("mService")
+ void setWaitingToKill(String waitingToKill) {
+ mWaitingToKill = waitingToKill;
+ }
+
+ @Override
+ public boolean isRemoved() {
+ return mRemoved;
+ }
+
+ void setRemoved(boolean removed) {
+ mRemoved = removed;
+ }
+
+ @GuardedBy("mService")
+ boolean isDebugging() {
+ return mDebugging;
+ }
+
+ @GuardedBy("mService")
+ void setDebugging(boolean debugging) {
+ mDebugging = debugging;
+ mWindowProcessController.setDebugging(debugging);
+ }
+
+ @GuardedBy("mProcLock")
+ boolean hasWaitedForDebugger() {
+ return mWaitedForDebugger;
+ }
+
+ @GuardedBy("mProcLock")
+ void setWaitedForDebugger(boolean waitedForDebugger) {
+ mWaitedForDebugger = waitedForDebugger;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ long getLastActivityTime() {
+ return mLastActivityTime;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setLastActivityTime(long lastActivityTime) {
+ mLastActivityTime = lastActivityTime;
+ }
+
+ @GuardedBy("mService")
+ boolean isUsingWrapper() {
+ return mUsingWrapper;
+ }
+
+ @GuardedBy("mService")
+ void setUsingWrapper(boolean usingWrapper) {
+ mUsingWrapper = usingWrapper;
+ mWindowProcessController.setUsingWrapper(usingWrapper);
+ }
+
+ @GuardedBy("mService")
+ int getLruSeq() {
+ return mLruSeq;
+ }
+
+ @GuardedBy("mService")
+ void setLruSeq(int lruSeq) {
+ mLruSeq = lruSeq;
+ }
+
+ @GuardedBy("mService")
+ String getIsolatedEntryPoint() {
+ return mIsolatedEntryPoint;
+ }
+
+ @GuardedBy("mService")
+ void setIsolatedEntryPoint(String isolatedEntryPoint) {
+ mIsolatedEntryPoint = isolatedEntryPoint;
+ }
+
+ @GuardedBy("mService")
+ String[] getIsolatedEntryPointArgs() {
+ return mIsolatedEntryPointArgs;
+ }
+
+ @GuardedBy("mService")
+ void setIsolatedEntryPointArgs(String[] isolatedEntryPointArgs) {
+ mIsolatedEntryPointArgs = isolatedEntryPointArgs;
+ }
+
+ @GuardedBy("mService")
+ boolean isInFullBackup() {
+ return mInFullBackup;
+ }
+
+ @GuardedBy("mService")
+ void setInFullBackup(boolean inFullBackup) {
+ mInFullBackup = inFullBackup;
}
@Override
public boolean isCached() {
- return mCached;
- }
-
- int getCacheOomRankerUseCount() {
- return mCacheOomRankerUseCount;
+ return mState.isCached();
}
boolean hasActivities() {
@@ -738,6 +894,22 @@
return mWindowProcessController.hasRecentTasks();
}
+ @GuardedBy({"mService", "mProcLock"})
+ boolean onCleanupApplicationRecordLSP(ProcessStatsService processStats, boolean allowRestart) {
+ mErrorState.onCleanupApplicationRecordLSP();
+
+ resetPackageList(processStats);
+ unlinkDeathRecipient();
+ makeInactive(processStats);
+ setWaitingToKill(null);
+
+ mState.onCleanupApplicationRecordLSP();
+ mServices.onCleanupApplicationRecordLocked();
+ mReceivers.onCleanupApplicationRecordLocked();
+
+ return mProviders.onCleanupApplicationRecordLocked(allowRestart);
+ }
+
/**
* This method returns true if any of the activities within the process record are interesting
* to the user. See HistoryRecord.isInterestingToUserLocked()
@@ -747,74 +919,26 @@
return true;
}
- final int servicesSize = mServices.size();
- for (int i = 0; i < servicesSize; i++) {
- ServiceRecord r = mServices.valueAt(i);
- if (r.isForeground) {
- return true;
- }
- }
- return false;
+ return mServices.hasForegroundServices();
}
- public void unlinkDeathRecipient() {
- if (deathRecipient != null && thread != null) {
- thread.asBinder().unlinkToDeath(deathRecipient, 0);
- }
- deathRecipient = null;
- }
-
- void updateHasAboveClientLocked() {
- hasAboveClient = false;
- for (int i=connections.size()-1; i>=0; i--) {
- ConnectionRecord cr = connections.valueAt(i);
- if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) {
- hasAboveClient = true;
- break;
- }
- }
- }
-
- int modifyRawOomAdj(int adj) {
- if (hasAboveClient) {
- // If this process has bound to any services with BIND_ABOVE_CLIENT,
- // then we need to drop its adjustment to be lower than the service's
- // in order to honor the request. We want to drop it by one adjustment
- // level... but there is special meaning applied to various levels so
- // we will skip some of them.
- if (adj < ProcessList.FOREGROUND_APP_ADJ) {
- // System process will not get dropped, ever
- } else if (adj < ProcessList.VISIBLE_APP_ADJ) {
- adj = ProcessList.VISIBLE_APP_ADJ;
- } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
- adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- } else if (adj < ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
- adj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
- } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
- adj = ProcessList.CACHED_APP_MIN_ADJ;
- } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
- adj++;
- }
- }
- return adj;
- }
-
- void scheduleCrash(String message) {
+ @GuardedBy("mService")
+ void scheduleCrashLocked(String message) {
// Checking killedbyAm should keep it from showing the crash dialog if the process
// was already dead for a good / normal reason.
- if (!killedByAm) {
- if (thread != null) {
- if (pid == Process.myPid()) {
+ if (!mKilledByAm) {
+ if (mThread != null) {
+ if (mPid == Process.myPid()) {
Slog.w(TAG, "scheduleCrash: trying to crash system process!");
return;
}
final long ident = Binder.clearCallingIdentity();
try {
- thread.scheduleCrash(message);
+ mThread.scheduleCrash(message);
} catch (RemoteException e) {
// If it's already dead our work is done. If it's wedged just kill it.
// We won't get the crash dialog or the error reporting.
- kill("scheduleCrash for '" + message + "' failed",
+ killLocked("scheduleCrash for '" + message + "' failed",
ApplicationExitInfo.REASON_CRASH, true);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -823,30 +947,36 @@
}
}
- void kill(String reason, @Reason int reasonCode, boolean noisy) {
- kill(reason, reasonCode, ApplicationExitInfo.SUBREASON_UNKNOWN, noisy);
+ @GuardedBy("mService")
+ void killLocked(String reason, @Reason int reasonCode, boolean noisy) {
+ killLocked(reason, reasonCode, ApplicationExitInfo.SUBREASON_UNKNOWN, noisy);
}
- void kill(String reason, @Reason int reasonCode, @SubReason int subReason, boolean noisy) {
- if (!killedByAm) {
+ @GuardedBy("mService")
+ void killLocked(String reason, @Reason int reasonCode, @SubReason int subReason,
+ boolean noisy) {
+ if (!mKilledByAm) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
if (mService != null && (noisy || info.uid == mService.mCurOomAdjUid)) {
mService.reportUidInfoMessageLocked(TAG,
- "Killing " + toShortString() + " (adj " + setAdj + "): " + reason,
- info.uid);
+ "Killing " + toShortString() + " (adj " + mState.getSetAdj()
+ + "): " + reason, info.uid);
}
- if (pid > 0) {
+ if (mPid > 0) {
mService.mProcessList.noteAppKill(this, reasonCode, subReason, reason);
- EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
- Process.killProcessQuiet(pid);
- ProcessList.killProcessGroup(uid, pid);
+ EventLog.writeEvent(EventLogTags.AM_KILL,
+ userId, mPid, processName, mState.getSetAdj(), reason);
+ Process.killProcessQuiet(mPid);
+ ProcessList.killProcessGroup(uid, mPid);
} else {
- pendingStart = false;
+ mPendingStart = false;
}
if (!mPersistent) {
- killed = true;
- killedByAm = true;
- mKillTime = SystemClock.uptimeMillis();
+ synchronized (mProcLock) {
+ mKilled = true;
+ mKilledByAm = true;
+ mKillTime = SystemClock.uptimeMillis();
+ }
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -859,7 +989,7 @@
public void dumpDebug(ProtoOutputStream proto, long fieldId, int lruIndex) {
long token = proto.start(fieldId);
- proto.write(ProcessRecordProto.PID, pid);
+ proto.write(ProcessRecordProto.PID, mPid);
proto.write(ProcessRecordProto.PROCESS_NAME, processName);
proto.write(ProcessRecordProto.UID, info.uid);
if (UserHandle.getAppId(info.uid) >= Process.FIRST_APPLICATION_UID) {
@@ -877,16 +1007,17 @@
}
public String toShortString() {
+ final String shortStringName = mShortStringName;
if (shortStringName != null) {
return shortStringName;
}
StringBuilder sb = new StringBuilder(128);
toShortString(sb);
- return shortStringName = sb.toString();
+ return mShortStringName = sb.toString();
}
void toShortString(StringBuilder sb) {
- sb.append(pid);
+ sb.append(mPid);
sb.append(':');
sb.append(processName);
sb.append('/');
@@ -911,6 +1042,7 @@
}
public String toString() {
+ final String stringName = mStringName;
if (stringName != null) {
return stringName;
}
@@ -920,33 +1052,7 @@
sb.append(' ');
toShortString(sb);
sb.append('}');
- return stringName = sb.toString();
- }
-
- public String makeAdjReason() {
- if (adjSource != null || adjTarget != null) {
- StringBuilder sb = new StringBuilder(128);
- sb.append(' ');
- if (adjTarget instanceof ComponentName) {
- sb.append(((ComponentName)adjTarget).flattenToShortString());
- } else if (adjTarget != null) {
- sb.append(adjTarget.toString());
- } else {
- sb.append("{null}");
- }
- sb.append("<=");
- if (adjSource instanceof ProcessRecord) {
- sb.append("Proc{");
- sb.append(((ProcessRecord)adjSource).toShortString());
- sb.append("}");
- } else if (adjSource != null) {
- sb.append(adjSource.toString());
- } else {
- sb.append("{null}");
- }
- return sb.toString();
- }
- return null;
+ return mStringName = sb.toString();
}
/*
@@ -976,29 +1082,6 @@
return false;
}
- public int getSetAdjWithServices() {
- if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
- if (hasStartedServices) {
- return ProcessList.SERVICE_B_ADJ;
- }
- }
- return setAdj;
- }
-
- public void forceProcessStateUpTo(int newState) {
- if (mRepProcState > newState) {
- mRepProcState = newState;
- setCurProcState(newState);
- setCurRawProcState(newState);
- getPkgList().forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgName,
- ActivityManager.processStateAmToProto(mRepProcState),
- holder.appVersion)
- );
- }
- }
-
/*
* Delete all packages from list except the package indicated in info
*/
@@ -1054,194 +1137,6 @@
return mWindowProcessController;
}
- void setCurrentSchedulingGroup(int curSchedGroup) {
- mCurSchedGroup = curSchedGroup;
- mWindowProcessController.setCurrentSchedulingGroup(curSchedGroup);
- }
-
- int getCurrentSchedulingGroup() {
- return mCurSchedGroup;
- }
-
- void setCurProcState(int curProcState) {
- mCurProcState = curProcState;
- mWindowProcessController.setCurrentProcState(mCurProcState);
- }
-
- int getCurProcState() {
- return mCurProcState;
- }
-
- void setCurRawProcState(int curRawProcState) {
- mCurRawProcState = curRawProcState;
- }
-
- int getCurRawProcState() {
- return mCurRawProcState;
- }
-
- void setReportedProcState(int repProcState) {
- mRepProcState = repProcState;
- getPkgList().forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgName,
- ActivityManager.processStateAmToProto(mRepProcState),
- holder.appVersion)
- );
- mWindowProcessController.setReportedProcState(repProcState);
- }
-
- int getReportedProcState() {
- return mRepProcState;
- }
-
- void setCrashing(boolean crashing) {
- mCrashing = crashing;
- mWindowProcessController.setCrashing(crashing);
- }
-
- boolean isCrashing() {
- return mCrashing;
- }
-
- void setNotResponding(boolean notResponding) {
- mNotResponding = notResponding;
- mWindowProcessController.setNotResponding(notResponding);
- }
-
- boolean isNotResponding() {
- return mNotResponding;
- }
-
- void setPersistent(boolean persistent) {
- mPersistent = persistent;
- mWindowProcessController.setPersistent(persistent);
- }
-
- boolean isPersistent() {
- return mPersistent;
- }
-
- public void setRequiredAbi(String requiredAbi) {
- mRequiredAbi = requiredAbi;
- mWindowProcessController.setRequiredAbi(requiredAbi);
- }
-
- String getRequiredAbi() {
- return mRequiredAbi;
- }
-
- void setHasForegroundServices(boolean hasForegroundServices, int fgServiceTypes) {
- mHasForegroundServices = hasForegroundServices;
- mFgServiceTypes = fgServiceTypes;
- mWindowProcessController.setHasForegroundServices(hasForegroundServices);
- }
-
- boolean hasForegroundServices() {
- return mHasForegroundServices;
- }
-
- boolean hasLocationForegroundServices() {
- return mHasForegroundServices
- && (mFgServiceTypes & ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION) != 0;
- }
-
- boolean hasLocationCapability() {
- return (setCapability & ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0;
- }
-
- int getForegroundServiceTypes() {
- return mHasForegroundServices ? mFgServiceTypes : 0;
- }
-
- int getReportedForegroundServiceTypes() {
- return mRepFgServiceTypes;
- }
-
- void setReportedForegroundServiceTypes(int foregroundServiceTypes) {
- mRepFgServiceTypes = foregroundServiceTypes;
- }
-
- void setHasForegroundActivities(boolean hasForegroundActivities) {
- mHasForegroundActivities = hasForegroundActivities;
- }
-
- boolean hasForegroundActivities() {
- return mHasForegroundActivities;
- }
-
- void setHasClientActivities(boolean hasClientActivities) {
- mHasClientActivities = hasClientActivities;
- mWindowProcessController.setHasClientActivities(hasClientActivities);
- }
-
- boolean hasClientActivities() {
- return mHasClientActivities;
- }
-
- void setHasTopUi(boolean hasTopUi) {
- mHasTopUi = hasTopUi;
- mWindowProcessController.setHasTopUi(hasTopUi);
- }
-
- boolean hasTopUi() {
- return mHasTopUi;
- }
-
- void setHasOverlayUi(boolean hasOverlayUi) {
- mHasOverlayUi = hasOverlayUi;
- mWindowProcessController.setHasOverlayUi(hasOverlayUi);
- }
-
- boolean hasOverlayUi() {
- return mHasOverlayUi;
- }
-
- void setInteractionEventTime(long interactionEventTime) {
- mInteractionEventTime = interactionEventTime;
- mWindowProcessController.setInteractionEventTime(interactionEventTime);
- }
-
- long getInteractionEventTime() {
- return mInteractionEventTime;
- }
-
- void setFgInteractionTime(long fgInteractionTime) {
- mFgInteractionTime = fgInteractionTime;
- mWindowProcessController.setFgInteractionTime(fgInteractionTime);
- }
-
- long getFgInteractionTime() {
- return mFgInteractionTime;
- }
-
- void setWhenUnimportant(long whenUnimportant) {
- mWhenUnimportant = whenUnimportant;
- mWindowProcessController.setWhenUnimportant(whenUnimportant);
- }
-
- long getWhenUnimportant() {
- return mWhenUnimportant;
- }
-
- void setDebugging(boolean debugging) {
- mDebugging = debugging;
- mWindowProcessController.setDebugging(debugging);
- }
-
- boolean isDebugging() {
- return mDebugging;
- }
-
- void setUsingWrapper(boolean usingWrapper) {
- mUsingWrapper = usingWrapper;
- mWindowProcessController.setUsingWrapper(usingWrapper);
- }
-
- boolean isUsingWrapper() {
- return mUsingWrapper;
- }
-
/**
* Allows background activity starts using token {@param entity}. Optionally, you can provide
* {@param originatingToken} if you have one such originating token, this is useful for tracing
@@ -1259,75 +1154,6 @@
mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity);
}
- void addBoundClientUid(int clientUid) {
- mBoundClientUids.add(clientUid);
- mWindowProcessController.setBoundClientUids(mBoundClientUids);
- }
-
- void updateBoundClientUids() {
- if (mServices.isEmpty()) {
- clearBoundClientUids();
- return;
- }
- // grab a set of clientUids of all connections of all services
- ArraySet<Integer> boundClientUids = new ArraySet<>();
- final int serviceCount = mServices.size();
- for (int j = 0; j < serviceCount; j++) {
- ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns =
- mServices.valueAt(j).getConnections();
- final int N = conns.size();
- for (int conni = 0; conni < N; conni++) {
- ArrayList<ConnectionRecord> c = conns.valueAt(conni);
- for (int i = 0; i < c.size(); i++) {
- boundClientUids.add(c.get(i).clientUid);
- }
- }
- }
- mBoundClientUids = boundClientUids;
- mWindowProcessController.setBoundClientUids(mBoundClientUids);
- }
-
- void addBoundClientUidsOfNewService(ServiceRecord sr) {
- if (sr == null) {
- return;
- }
- ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns = sr.getConnections();
- for (int conni = conns.size() - 1; conni >= 0; conni--) {
- ArrayList<ConnectionRecord> c = conns.valueAt(conni);
- for (int i = 0; i < c.size(); i++) {
- mBoundClientUids.add(c.get(i).clientUid);
- }
- }
- mWindowProcessController.setBoundClientUids(mBoundClientUids);
- }
-
- void clearBoundClientUids() {
- mBoundClientUids.clear();
- mWindowProcessController.setBoundClientUids(mBoundClientUids);
- }
-
- void setActiveInstrumentation(ActiveInstrumentation instr) {
- mInstr = instr;
- boolean isInstrumenting = instr != null;
- mWindowProcessController.setInstrumenting(
- isInstrumenting,
- isInstrumenting ? instr.mSourceUid : -1,
- isInstrumenting && instr.mHasBackgroundActivityStartsPermission);
- }
-
- ActiveInstrumentation getActiveInstrumentation() {
- return mInstr;
- }
-
- void setCurRawAdj(int curRawAdj) {
- mCurRawAdj = curRawAdj;
- mWindowProcessController.setPerceptible(curRawAdj <= ProcessList.PERCEPTIBLE_APP_ADJ);
- }
-
- int getCurRawAdj() {
- return mCurRawAdj;
- }
-
@Override
public void clearProfilerIfNeeded() {
synchronized (mService.mAppProfiler.mProfilerLock) {
@@ -1338,13 +1164,13 @@
@Override
public void updateServiceConnectionActivities() {
synchronized (mService) {
- mService.mServices.updateServiceConnectionActivitiesLocked(this);
+ mService.mServices.updateServiceConnectionActivitiesLocked(mServices);
}
}
@Override
public void setPendingUiClean(boolean pendingUiClean) {
- synchronized (mService) {
+ synchronized (mProcLock) {
mProfile.setPendingUiClean(pendingUiClean);
}
}
@@ -1353,7 +1179,7 @@
public void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
synchronized (mService) {
setPendingUiClean(true);
- forceProcessStateUpTo(newState);
+ mState.forceProcessStateUpTo(newState);
}
}
@@ -1362,40 +1188,35 @@
boolean updateOomAdj) {
synchronized (mService) {
if (updateServiceConnectionActivities) {
- mService.mServices.updateServiceConnectionActivitiesLocked(this);
+ mService.mServices.updateServiceConnectionActivitiesLocked(mServices);
}
- if (thread == null) {
+ if (mThread == null) {
// Only update lru and oom-adj if the process is alive. Because it may be called
// when cleaning up the last activity from handling process died, the dead process
// should not be added to lru list again.
return;
}
- mService.mProcessList.updateLruProcessLocked(this, activityChange, null /* client */);
+ mService.updateLruProcessLocked(this, activityChange, null /* client */);
if (updateOomAdj) {
mService.updateOomAdjLocked(this, OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
}
}
}
- @Override
- public boolean isRemoved() {
- return removed;
- }
-
/**
* Returns the total time (in milliseconds) spent executing in both user and system code.
* Safe to call without lock held.
*/
@Override
public long getCpuTime() {
- return mService.mAppProfiler.getCpuTimeForPid(pid);
+ return mService.mAppProfiler.getCpuTimeForPid(mPid);
}
@Override
public void onStartActivity(int topProcessState, boolean setProfileProc, String packageName,
long versionCode) {
synchronized (mService) {
- waitingToKill = null;
+ mWaitingToKill = null;
if (setProfileProc) {
synchronized (mService.mAppProfiler.mProfilerLock) {
mService.mAppProfiler.setProfileProcLPf(this);
@@ -1408,9 +1229,9 @@
// Update oom adj first, we don't want the additional states are involved in this round.
updateProcessInfo(false /* updateServiceConnectionActivities */,
true /* activityChange */, true /* updateOomAdj */);
- hasShownUi = true;
setPendingUiClean(true);
- forceProcessStateUpTo(topProcessState);
+ mState.setHasShownUi(true);
+ mState.forceProcessStateUpTo(topProcessState);
}
}
@@ -1423,20 +1244,12 @@
@Override
public void setRunningRemoteAnimation(boolean runningRemoteAnimation) {
- if (pid == Process.myPid()) {
+ if (mPid == Process.myPid()) {
Slog.wtf(TAG, "system can't run remote animation");
return;
}
synchronized (mService) {
- if (this.runningRemoteAnimation == runningRemoteAnimation) {
- return;
- }
- this.runningRemoteAnimation = runningRemoteAnimation;
- if (DEBUG_OOM_ADJ) {
- Slog.i(TAG, "Setting runningRemoteAnimation=" + runningRemoteAnimation
- + " for pid=" + pid);
- }
- mService.updateOomAdjLocked(this, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
+ mState.setRunningRemoteAnimation(runningRemoteAnimation);
}
}
@@ -1445,7 +1258,7 @@
}
public int getProcessClassEnum() {
- if (pid == MY_PID) {
+ if (mPid == MY_PID) {
return ServerProtoEnums.SYSTEM_SERVER;
}
if (info == null) {
@@ -1455,687 +1268,9 @@
ServerProtoEnums.DATA_APP;
}
- /**
- * Unless configured otherwise, swallow ANRs in background processes & kill the process.
- * Non-private access is for tests only.
- */
- @VisibleForTesting
- boolean isSilentAnr() {
- return !getShowBackground() && !isInterestingForBackgroundTraces();
- }
-
/** Non-private access is for tests only. */
@VisibleForTesting
List<ProcessRecord> getLruProcessList() {
- return mService.mProcessList.mLruProcesses;
- }
-
- /** Non-private access is for tests only. */
- @VisibleForTesting
- boolean isMonitorCpuUsage() {
- return mService.mAppProfiler.MONITOR_CPU_USAGE;
- }
-
- void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
- String parentShortComponentName, WindowProcessController parentProcess,
- boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
- ArrayList<Integer> firstPids = new ArrayList<>(5);
- SparseArray<Boolean> lastPids = new SparseArray<>(20);
-
- mWindowProcessController.appEarlyNotResponding(annotation, () -> kill("anr",
- ApplicationExitInfo.REASON_ANR, true));
-
- long anrTime = SystemClock.uptimeMillis();
- if (isMonitorCpuUsage()) {
- mService.updateCpuStatsNow();
- }
-
- final boolean isSilentAnr;
- synchronized (mService) {
- // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
- if (mService.mAtmInternal.isShuttingDown()) {
- Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
- return;
- } else if (isNotResponding()) {
- Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
- return;
- } else if (isCrashing()) {
- Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
- return;
- } else if (killedByAm) {
- Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
- return;
- } else if (killed) {
- Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
- return;
- }
-
- // In case we come through here for the same app before completing
- // this one, mark as anring now so we will bail out.
- setNotResponding(true);
-
- // Log the ANR to the event log.
- EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags,
- annotation);
-
- // Dump thread traces as quickly as we can, starting with "interesting" processes.
- firstPids.add(pid);
-
- // Don't dump other PIDs if it's a background ANR or is requested to only dump self.
- isSilentAnr = isSilentAnr();
- if (!isSilentAnr && !onlyDumpSelf) {
- int parentPid = pid;
- if (parentProcess != null && parentProcess.getPid() > 0) {
- parentPid = parentProcess.getPid();
- }
- if (parentPid != pid) firstPids.add(parentPid);
-
- if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
-
- for (int i = getLruProcessList().size() - 1; i >= 0; i--) {
- ProcessRecord r = getLruProcessList().get(i);
- if (r != null && r.thread != null) {
- int myPid = r.pid;
- if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
- if (r.isPersistent()) {
- firstPids.add(myPid);
- if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
- } else if (r.treatLikeActivity) {
- firstPids.add(myPid);
- if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
- } else {
- lastPids.put(myPid, Boolean.TRUE);
- if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
- }
- }
- }
- }
- }
- }
-
- // Check if package is still being loaded
- boolean isPackageLoading = false;
- final PackageManagerInternal packageManagerInternal =
- mService.getPackageManagerInternal();
- if (aInfo != null && aInfo.packageName != null) {
- IncrementalStatesInfo incrementalStatesInfo =
- packageManagerInternal.getIncrementalStatesInfo(
- aInfo.packageName, uid, userId);
- if (incrementalStatesInfo != null) {
- isPackageLoading = incrementalStatesInfo.isLoading();
- }
- }
-
- // Log the ANR to the main log.
- StringBuilder info = new StringBuilder();
- info.setLength(0);
- info.append("ANR in ").append(processName);
- if (activityShortComponentName != null) {
- info.append(" (").append(activityShortComponentName).append(")");
- }
- info.append("\n");
- info.append("PID: ").append(pid).append("\n");
- if (annotation != null) {
- info.append("Reason: ").append(annotation).append("\n");
- }
- if (parentShortComponentName != null
- && parentShortComponentName.equals(activityShortComponentName)) {
- info.append("Parent: ").append(parentShortComponentName).append("\n");
- }
-
- if (isPackageLoading) {
- // Report in the main log that the package is still loading
- final float loadingProgress = packageManagerInternal.getIncrementalStatesInfo(
- aInfo.packageName, uid, userId).getProgress();
- info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
- }
-
- StringBuilder report = new StringBuilder();
- report.append(MemoryPressureUtil.currentPsiState());
- ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
-
- // don't dump native PIDs for background ANRs unless it is the process of interest
- String[] nativeProcs = null;
- if (isSilentAnr || onlyDumpSelf) {
- for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
- if (NATIVE_STACKS_OF_INTEREST[i].equals(processName)) {
- nativeProcs = new String[] { processName };
- break;
- }
- }
- } else {
- nativeProcs = NATIVE_STACKS_OF_INTEREST;
- }
-
- int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
- ArrayList<Integer> nativePids = null;
-
- if (pids != null) {
- nativePids = new ArrayList<>(pids.length);
- for (int i : pids) {
- nativePids.add(i);
- }
- }
-
- // For background ANRs, don't pass the ProcessCpuTracker to
- // avoid spending 1/2 second collecting stats to rank lastPids.
- StringWriter tracesFileException = new StringWriter();
- // To hold the start and end offset to the ANR trace file respectively.
- final long[] offsets = new long[2];
- File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
- isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
- nativePids, tracesFileException, offsets);
-
- if (isMonitorCpuUsage()) {
- mService.updateCpuStatsNow();
- mService.mAppProfiler.printCurrentCpuState(report, anrTime);
- info.append(processCpuTracker.printCurrentLoad());
- info.append(report);
- }
- report.append(tracesFileException.getBuffer());
-
- info.append(processCpuTracker.printCurrentState(anrTime));
-
- Slog.e(TAG, info.toString());
- if (tracesFile == null) {
- // There is no trace file, so dump (only) the alleged culprit's threads to the log
- Process.sendSignal(pid, Process.SIGNAL_QUIT);
- } else if (offsets[1] > 0) {
- // We've dumped into the trace file successfully
- mService.mProcessList.mAppExitInfoTracker.scheduleLogAnrTrace(
- pid, uid, getPackageList(), tracesFile, offsets[0], offsets[1]);
- }
-
- FrameworkStatsLog.write(FrameworkStatsLog.ANR_OCCURRED, uid, processName,
- activityShortComponentName == null ? "unknown": activityShortComponentName,
- annotation,
- (this.info != null) ? (this.info.isInstantApp()
- ? FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
- : FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
- : FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
- isInterestingToUserLocked()
- ? FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
- : FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND,
- getProcessClassEnum(),
- (this.info != null) ? this.info.packageName : "", isPackageLoading);
- final ProcessRecord parentPr = parentProcess != null
- ? (ProcessRecord) parentProcess.mOwner : null;
- mService.addErrorToDropBox("anr", this, processName, activityShortComponentName,
- parentShortComponentName, parentPr, annotation, report.toString(), tracesFile,
- null);
-
- if (mWindowProcessController.appNotResponding(info.toString(), () -> kill("anr",
- ApplicationExitInfo.REASON_ANR, true),
- () -> {
- synchronized (mService) {
- mService.mServices.scheduleServiceTimeoutLocked(this);
- }
- })) {
- return;
- }
-
- synchronized (mService) {
- // mBatteryStatsService can be null if the AMS is constructed with injector only. This
- // will only happen in tests.
- if (mService.mBatteryStatsService != null) {
- mService.mBatteryStatsService.noteProcessAnr(processName, uid);
- }
-
- if (isSilentAnr() && !isDebugging()) {
- kill("bg anr", ApplicationExitInfo.REASON_ANR, true);
- return;
- }
-
- // Set the app's notResponding state, and look up the errorReportReceiver
- makeAppNotRespondingLocked(activityShortComponentName,
- annotation != null ? "ANR " + annotation : "ANR", info.toString());
-
- // Notify package manager service to possibly update package state
- if (aInfo != null && aInfo.packageName != null) {
- packageManagerInternal.notifyPackageCrashOrAnr(aInfo.packageName);
- }
-
- // mUiHandler can be null if the AMS is constructed with injector only. This will only
- // happen in tests.
- if (mService.mUiHandler != null) {
- // Bring up the infamous App Not Responding dialog
- Message msg = Message.obtain();
- msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
- msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
-
- mService.mUiHandler.sendMessage(msg);
- }
- }
- }
-
- private void makeAppNotRespondingLocked(String activity, String shortMsg, String longMsg) {
- setNotResponding(true);
- // mAppErrors can be null if the AMS is constructed with injector only. This will only
- // happen in tests.
- if (mService.mAppErrors != null) {
- notRespondingReport = mService.mAppErrors.generateProcessError(this,
- ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
- activity, shortMsg, longMsg, null);
- }
- startAppProblemLocked();
- getWindowProcessController().stopFreezingActivities();
- }
-
- void startAppProblemLocked() {
- // If this app is not running under the current user, then we can't give it a report button
- // because that would require launching the report UI under a different user.
- errorReportReceiver = null;
-
- for (int userId : mService.mUserController.getCurrentProfileIds()) {
- if (this.userId == userId) {
- errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
- mService.mContext, info.packageName, info.flags);
- }
- }
- mService.skipCurrentReceiverLocked(this);
- }
-
- private boolean isInterestingForBackgroundTraces() {
- // The system_server is always considered interesting.
- if (pid == MY_PID) {
- return true;
- }
-
- // A package is considered interesting if any of the following is true :
- //
- // - It's displaying an activity.
- // - It's the SystemUI.
- // - It has an overlay or a top UI visible.
- //
- // NOTE: The check whether a given ProcessRecord belongs to the systemui
- // process is a bit of a kludge, but the same pattern seems repeated at
- // several places in the system server.
- return isInterestingToUserLocked() ||
- (info != null && "com.android.systemui".equals(info.packageName))
- || (hasTopUi() || hasOverlayUi());
- }
-
- private boolean getShowBackground() {
- return Settings.Secure.getInt(mService.mContext.getContentResolver(),
- Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
- }
-
- void resetCachedInfo() {
- mCachedHasActivities = VALUE_INVALID;
- mCachedIsHeavyWeight = VALUE_INVALID;
- mCachedHasVisibleActivities = VALUE_INVALID;
- mCachedIsHomeProcess = VALUE_INVALID;
- mCachedIsPreviousProcess = VALUE_INVALID;
- mCachedHasRecentTasks = VALUE_INVALID;
- mCachedIsReceivingBroadcast = VALUE_INVALID;
- mCachedAdj = ProcessList.INVALID_ADJ;
- mCachedForegroundActivities = false;
- mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
- }
-
- boolean getCachedHasActivities() {
- if (mCachedHasActivities == VALUE_INVALID) {
- mCachedHasActivities = getWindowProcessController().hasActivities() ? VALUE_TRUE
- : VALUE_FALSE;
- }
- return mCachedHasActivities == VALUE_TRUE;
- }
-
- boolean getCachedIsHeavyWeight() {
- if (mCachedIsHeavyWeight == VALUE_INVALID) {
- mCachedIsHeavyWeight = getWindowProcessController().isHeavyWeightProcess()
- ? VALUE_TRUE : VALUE_FALSE;
- }
- return mCachedIsHeavyWeight == VALUE_TRUE;
- }
-
- boolean getCachedHasVisibleActivities() {
- if (mCachedHasVisibleActivities == VALUE_INVALID) {
- mCachedHasVisibleActivities = getWindowProcessController().hasVisibleActivities()
- ? VALUE_TRUE : VALUE_FALSE;
- }
- return mCachedHasVisibleActivities == VALUE_TRUE;
- }
-
- boolean getCachedIsHomeProcess() {
- if (mCachedIsHomeProcess == VALUE_INVALID) {
- if (getWindowProcessController().isHomeProcess()) {
- mCachedIsHomeProcess = VALUE_TRUE;
- mService.mAppProfiler.mHasHomeProcess = true;
- } else {
- mCachedIsHomeProcess = VALUE_FALSE;
- }
- }
- return mCachedIsHomeProcess == VALUE_TRUE;
- }
-
- boolean getCachedIsPreviousProcess() {
- if (mCachedIsPreviousProcess == VALUE_INVALID) {
- if (getWindowProcessController().isPreviousProcess()) {
- mCachedIsPreviousProcess = VALUE_TRUE;
- mService.mAppProfiler.mHasPreviousProcess = true;
- } else {
- mCachedIsPreviousProcess = VALUE_FALSE;
- }
- }
- return mCachedIsPreviousProcess == VALUE_TRUE;
- }
-
- boolean getCachedHasRecentTasks() {
- if (mCachedHasRecentTasks == VALUE_INVALID) {
- mCachedHasRecentTasks = getWindowProcessController().hasRecentTasks()
- ? VALUE_TRUE : VALUE_FALSE;
- }
- return mCachedHasRecentTasks == VALUE_TRUE;
- }
-
- boolean getCachedIsReceivingBroadcast(ArraySet<BroadcastQueue> tmpQueue) {
- if (mCachedIsReceivingBroadcast == VALUE_INVALID) {
- tmpQueue.clear();
- mCachedIsReceivingBroadcast = mService.isReceivingBroadcastLocked(this, tmpQueue)
- ? VALUE_TRUE : VALUE_FALSE;
- if (mCachedIsReceivingBroadcast == VALUE_TRUE) {
- mCachedSchedGroup = tmpQueue.contains(mService.mFgBroadcastQueue)
- ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
- }
- }
- return mCachedIsReceivingBroadcast == VALUE_TRUE;
- }
-
- void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback,
- int adj, boolean foregroundActivities, int procState, int schedGroup, int appUid,
- int logUid, int processCurTop) {
- if (mCachedAdj != ProcessList.INVALID_ADJ) {
- return;
- }
- callback.initialize(this, adj, foregroundActivities, procState, schedGroup, appUid, logUid,
- processCurTop);
- final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
- getWindowProcessController().computeOomAdjFromActivities(callback));
-
- mCachedAdj = callback.adj;
- mCachedForegroundActivities = callback.foregroundActivities;
- mCachedProcState = callback.procState;
- mCachedSchedGroup = callback.schedGroup;
-
- if (mCachedAdj == ProcessList.VISIBLE_APP_ADJ) {
- mCachedAdj += minLayer;
- }
- }
-
- public void addAllowBackgroundFgsStartsToken(Binder entity) {
- mBackgroundFgsStartTokens.add(entity);
- }
-
- public void removeAllowBackgroundFgsStartsToken(Binder entity) {
- mBackgroundFgsStartTokens.remove(entity);
- }
-
- public boolean areBackgroundFgsStartsAllowedByToken() {
- return !mBackgroundFgsStartTokens.isEmpty();
- }
-
- ErrorDialogController getDialogController() {
- return mDialogController;
- }
-
- void resetAllowStartFgs() {
- mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
- mAllowStartFgs = mAllowStartFgsByPermission;
- }
-
- void bumpAllowStartFgsState(int newProcState) {
- if (newProcState < mAllowStartFgsState) {
- mAllowStartFgsState = newProcState;
- }
- }
-
- void setAllowStartFgsByPermission() {
- boolean ret = false;
- if (!ret) {
- boolean isSystem = false;
- final int uid = UserHandle.getAppId(info.uid);
- switch (uid) {
- case ROOT_UID:
- case SYSTEM_UID:
- case NFC_UID:
- case SHELL_UID:
- isSystem = true;
- break;
- default:
- isSystem = false;
- break;
- }
-
- if (isSystem) {
- ret = true;
- }
- }
-
- if (!ret) {
- for (int i = 0; i < ALLOW_BG_START_FGS_PERMISSIONS.length; ++i) {
- if (ActivityManager.checkComponentPermission(ALLOW_BG_START_FGS_PERMISSIONS[i],
- info.uid, -1, true)
- == PERMISSION_GRANTED) {
- ret = true;
- break;
- }
- }
- }
- mAllowStartFgs = mAllowStartFgsByPermission = ret;
- }
-
- boolean isAllowedStartFgsState() {
- return mAllowStartFgsState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- }
-
- void setAllowStartFgs() {
- if (mAllowStartFgs) {
- return;
- }
- if (!mAllowStartFgs) {
- mAllowStartFgs = isAllowedStartFgsState();
- }
-
- if (!mAllowStartFgs) {
- // Is the calling UID a device owner app?
- if (mService.mInternal != null) {
- mAllowStartFgs = mService.mInternal.isDeviceOwner(info.uid);
- }
- }
-
- if (!mAllowStartFgs) {
- if (mService.mInternal != null) {
- mAllowStartFgs = mService.mInternal.isAssociatedCompanionApp(
- UserHandle.getUserId(info.uid), info.uid);
- }
- }
-
- if (!mAllowStartFgs) {
- // Is the calling UID a profile owner app?
- if (mService.mInternal != null) {
- mAllowStartFgs = mService.mInternal.isProfileOwner(info.uid);
- }
- }
-
- if (!mAllowStartFgs) {
- // uid is on DeviceIdleController's user/system allowlist
- // or AMS's FgsStartTempAllowList.
- mAllowStartFgs = mService.isAllowlistedForFgsStartLocked(info.uid);
- }
- }
-
- /** A controller to generate error dialogs in {@link ProcessRecord} */
- class ErrorDialogController {
- /** dialogs being displayed due to crash */
- private List<AppErrorDialog> mCrashDialogs;
- /** dialogs being displayed due to app not responding */
- private List<AppNotRespondingDialog> mAnrDialogs;
- /** dialogs displayed due to strict mode violation */
- private List<StrictModeViolationDialog> mViolationDialogs;
- /** current wait for debugger dialog */
- private AppWaitingForDebuggerDialog mWaitDialog;
-
- boolean hasCrashDialogs() {
- return mCrashDialogs != null;
- }
-
- boolean hasAnrDialogs() {
- return mAnrDialogs != null;
- }
-
- boolean hasViolationDialogs() {
- return mViolationDialogs != null;
- }
-
- boolean hasDebugWaitingDialog() {
- return mWaitDialog != null;
- }
-
- void clearAllErrorDialogs() {
- clearCrashDialogs();
- clearAnrDialogs();
- clearViolationDialogs();
- clearWaitingDialog();
- }
-
- void clearCrashDialogs() {
- clearCrashDialogs(true /* needDismiss */);
- }
-
- void clearCrashDialogs(boolean needDismiss) {
- if (mCrashDialogs == null) {
- return;
- }
- if (needDismiss) {
- forAllDialogs(mCrashDialogs, Dialog::dismiss);
- }
- mCrashDialogs = null;
- }
-
- void clearAnrDialogs() {
- if (mAnrDialogs == null) {
- return;
- }
- forAllDialogs(mAnrDialogs, Dialog::dismiss);
- mAnrDialogs = null;
- }
-
- void clearViolationDialogs() {
- if (mViolationDialogs == null) {
- return;
- }
- forAllDialogs(mViolationDialogs, Dialog::dismiss);
- mViolationDialogs = null;
- }
-
- void clearWaitingDialog() {
- if (mWaitDialog == null) {
- return;
- }
- mWaitDialog.dismiss();
- mWaitDialog = null;
- }
-
- void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) {
- for (int i = dialogs.size() - 1; i >= 0; i--) {
- c.accept(dialogs.get(i));
- }
- }
-
- void showCrashDialogs(AppErrorDialog.Data data) {
- List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
- mCrashDialogs = new ArrayList<>();
- for (int i = contexts.size() - 1; i >= 0; i--) {
- final Context c = contexts.get(i);
- mCrashDialogs.add(new AppErrorDialog(c, mService, data));
- }
- mService.mUiHandler.post(() -> {
- List<AppErrorDialog> dialogs;
- synchronized (mService) {
- dialogs = mCrashDialogs;
- }
- if (dialogs != null) {
- forAllDialogs(dialogs, Dialog::show);
- }
- });
- }
-
- void showAnrDialogs(AppNotRespondingDialog.Data data) {
- List<Context> contexts = getDisplayContexts(isSilentAnr() /* lastUsedOnly */);
- mAnrDialogs = new ArrayList<>();
- for (int i = contexts.size() - 1; i >= 0; i--) {
- final Context c = contexts.get(i);
- mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data));
- }
- mService.mUiHandler.post(() -> {
- List<AppNotRespondingDialog> dialogs;
- synchronized (mService) {
- dialogs = mAnrDialogs;
- }
- if (dialogs != null) {
- forAllDialogs(dialogs, Dialog::show);
- }
- });
- }
-
- void showViolationDialogs(AppErrorResult res) {
- List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
- mViolationDialogs = new ArrayList<>();
- for (int i = contexts.size() - 1; i >= 0; i--) {
- final Context c = contexts.get(i);
- mViolationDialogs.add(
- new StrictModeViolationDialog(c, mService, res, ProcessRecord.this));
- }
- mService.mUiHandler.post(() -> {
- List<StrictModeViolationDialog> dialogs;
- synchronized (mService) {
- dialogs = mViolationDialogs;
- }
- if (dialogs != null) {
- forAllDialogs(dialogs, Dialog::show);
- }
- });
- }
-
- void showDebugWaitingDialogs() {
- List<Context> contexts = getDisplayContexts(true /* lastUsedOnly */);
- final Context c = contexts.get(0);
- mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, ProcessRecord.this);
-
- mService.mUiHandler.post(() -> {
- Dialog dialog;
- synchronized (mService) {
- dialog = mWaitDialog;
- }
- if (dialog != null) {
- dialog.show();
- }
- });
- }
-
- /**
- * Helper function to collect contexts from crashed app located displays
- *
- * @param lastUsedOnly Sets to {@code true} to indicate to only get last used context.
- * Sets to {@code false} to collect contexts from crashed app located
- * displays.
- *
- * @return display context list
- */
- private List<Context> getDisplayContexts(boolean lastUsedOnly) {
- List<Context> displayContexts = new ArrayList<>();
- if (!lastUsedOnly) {
- mWindowProcessController.getDisplayContextsWithErrorDialogs(displayContexts);
- }
- // If there is no foreground window display, fallback to last used display.
- if (displayContexts.isEmpty() || lastUsedOnly) {
- displayContexts.add(mService.mWmInternal != null
- ? mService.mWmInternal.getTopFocusedDisplayUiContext()
- : mService.mUiContext);
- }
- return displayContexts;
- }
+ return mService.mProcessList.getLruProcessesLOSP();
}
}
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
new file mode 100644
index 0000000..5c3bf60
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.IBinder;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * The state info of all services in the process.
+ */
+final class ProcessServiceRecord {
+ /**
+ * Are there any client services with activities?
+ */
+ private boolean mHasClientActivities;
+
+ /**
+ * Running any services that are foreground?
+ */
+ private boolean mHasForegroundServices;
+
+ /**
+ * Service that applied current connectionGroup/Importance.
+ */
+ private ServiceRecord mConnectionService;
+
+ /**
+ * Last group set by a connection.
+ */
+ private int mConnectionGroup;
+
+ /**
+ * Last importance set by a connection.
+ */
+ private int mConnectionImportance;
+
+ /**
+ * Type of foreground service, if there is a foreground service.
+ */
+ private int mFgServiceTypes;
+
+ /**
+ * Last reported foreground service types.
+ */
+ private int mRepFgServiceTypes;
+
+ /**
+ * Bound using BIND_ABOVE_CLIENT, so want to be lower.
+ */
+ private boolean mHasAboveClient;
+
+ /**
+ * Bound using BIND_TREAT_LIKE_ACTIVITY.
+ */
+ private boolean mTreatLikeActivity;
+
+ /**
+ * Do we need to be executing services in the foreground?
+ */
+ private boolean mExecServicesFg;
+
+ /**
+ * App is allowed to manage allowlists such as temporary Power Save mode allowlist.
+ */
+ boolean mAllowlistManager;
+
+ /**
+ * All ServiceRecord running in this process.
+ */
+ private final ArraySet<ServiceRecord> mServices = new ArraySet<>();
+
+ /**
+ * Services that are currently executing code (need to remain foreground).
+ */
+ private final ArraySet<ServiceRecord> mExecutingServices = new ArraySet<>();
+
+ /**
+ * All ConnectionRecord this process holds.
+ */
+ private final ArraySet<ConnectionRecord> mConnections = new ArraySet<>();
+
+ /**
+ * A set of UIDs of all bound clients.
+ */
+ private ArraySet<Integer> mBoundClientUids = new ArraySet<>();
+
+ final ProcessRecord mApp;
+
+ private final ActivityManagerService mService;
+
+ ProcessServiceRecord(ProcessRecord app) {
+ mApp = app;
+ mService = app.mService;
+ }
+
+ void setHasClientActivities(boolean hasClientActivities) {
+ mHasClientActivities = hasClientActivities;
+ mApp.getWindowProcessController().setHasClientActivities(hasClientActivities);
+ }
+
+ boolean hasClientActivities() {
+ return mHasClientActivities;
+ }
+
+ void setHasForegroundServices(boolean hasForegroundServices, int fgServiceTypes) {
+ mHasForegroundServices = hasForegroundServices;
+ mFgServiceTypes = fgServiceTypes;
+ mApp.getWindowProcessController().setHasForegroundServices(hasForegroundServices);
+ }
+
+ boolean hasForegroundServices() {
+ return mHasForegroundServices;
+ }
+
+ int getForegroundServiceTypes() {
+ return mHasForegroundServices ? mFgServiceTypes : 0;
+ }
+
+ int getReportedForegroundServiceTypes() {
+ return mRepFgServiceTypes;
+ }
+
+ void setReportedForegroundServiceTypes(int foregroundServiceTypes) {
+ mRepFgServiceTypes = foregroundServiceTypes;
+ }
+
+ ServiceRecord getConnectionService() {
+ return mConnectionService;
+ }
+
+ void setConnectionService(ServiceRecord connectionService) {
+ mConnectionService = connectionService;
+ }
+
+ int getConnectionGroup() {
+ return mConnectionGroup;
+ }
+
+ void setConnectionGroup(int connectionGroup) {
+ mConnectionGroup = connectionGroup;
+ }
+
+ int getConnectionImportance() {
+ return mConnectionImportance;
+ }
+
+ void setConnectionImportance(int connectionImportance) {
+ mConnectionImportance = connectionImportance;
+ }
+
+ void updateHasAboveClientLocked() {
+ mHasAboveClient = false;
+ for (int i = mConnections.size() - 1; i >= 0; i--) {
+ ConnectionRecord cr = mConnections.valueAt(i);
+ if ((cr.flags & Context.BIND_ABOVE_CLIENT) != 0) {
+ mHasAboveClient = true;
+ break;
+ }
+ }
+ }
+
+ void setHasAboveClient(boolean hasAboveClient) {
+ mHasAboveClient = hasAboveClient;
+ }
+
+ boolean hasAboveClient() {
+ return mHasAboveClient;
+ }
+
+ int modifyRawOomAdj(int adj) {
+ if (mHasAboveClient) {
+ // If this process has bound to any services with BIND_ABOVE_CLIENT,
+ // then we need to drop its adjustment to be lower than the service's
+ // in order to honor the request. We want to drop it by one adjustment
+ // level... but there is special meaning applied to various levels so
+ // we will skip some of them.
+ if (adj < ProcessList.FOREGROUND_APP_ADJ) {
+ // System process will not get dropped, ever
+ } else if (adj < ProcessList.VISIBLE_APP_ADJ) {
+ adj = ProcessList.VISIBLE_APP_ADJ;
+ } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
+ adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+ } else if (adj < ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+ adj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
+ } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
+ adj = ProcessList.CACHED_APP_MIN_ADJ;
+ } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
+ adj++;
+ }
+ }
+ return adj;
+ }
+
+ boolean isTreatedLikeActivity() {
+ return mTreatLikeActivity;
+ }
+
+ void setTreatLikeActivity(boolean treatLikeActivity) {
+ mTreatLikeActivity = treatLikeActivity;
+ }
+
+ boolean shouldExecServicesFg() {
+ return mExecServicesFg;
+ }
+
+ void setExecServicesFg(boolean execServicesFg) {
+ mExecServicesFg = execServicesFg;
+ }
+
+ /**
+ * Records a service as running in the process. Note that this method does not actually start
+ * the service, but records the service as started for bookkeeping.
+ *
+ * @return true if the service was added, false otherwise.
+ */
+ boolean startService(ServiceRecord record) {
+ if (record == null) {
+ return false;
+ }
+ boolean added = mServices.add(record);
+ if (added && record.serviceInfo != null) {
+ mApp.getWindowProcessController().onServiceStarted(record.serviceInfo);
+ }
+ return added;
+ }
+
+ /**
+ * Records a service as stopped. Note that like {@link #startService(ServiceRecord)} this method
+ * does not actually stop the service, but records the service as stopped for bookkeeping.
+ *
+ * @return true if the service was removed, false otherwise.
+ */
+ boolean stopService(ServiceRecord record) {
+ return mServices.remove(record);
+ }
+
+ /**
+ * The same as calling {@link #stopService(ServiceRecord)} on all current running services.
+ */
+ void stopAllServices() {
+ mServices.clear();
+ }
+
+ /**
+ * Returns the number of services added with {@link #startService(ServiceRecord)} and not yet
+ * removed by a call to {@link #stopService(ServiceRecord)} or {@link #stopAllServices()}.
+ *
+ * @see #startService(ServiceRecord)
+ * @see #stopService(ServiceRecord)
+ */
+ int numberOfRunningServices() {
+ return mServices.size();
+ }
+
+ /**
+ * Returns the service at the specified {@code index}.
+ *
+ * @see #numberOfRunningServices()
+ */
+ ServiceRecord getRunningServiceAt(int index) {
+ return mServices.valueAt(index);
+ }
+
+ void startExecutingService(ServiceRecord service) {
+ mExecutingServices.add(service);
+ }
+
+ void stopExecutingService(ServiceRecord service) {
+ mExecutingServices.remove(service);
+ }
+
+ void stopAllExecutingServices() {
+ mExecutingServices.clear();
+ }
+
+ ServiceRecord getExecutingServiceAt(int index) {
+ return mExecutingServices.valueAt(index);
+ }
+
+ int numberOfExecutingServices() {
+ return mExecutingServices.size();
+ }
+
+ void addConnection(ConnectionRecord connection) {
+ mConnections.add(connection);
+ }
+
+ void removeConnection(ConnectionRecord connection) {
+ mConnections.remove(connection);
+ }
+
+ void removeAllConnections() {
+ mConnections.clear();
+ }
+
+ ConnectionRecord getConnectionAt(int index) {
+ return mConnections.valueAt(index);
+ }
+
+ int numberOfConnections() {
+ return mConnections.size();
+ }
+
+ void addBoundClientUid(int clientUid) {
+ mBoundClientUids.add(clientUid);
+ mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+ }
+
+ void updateBoundClientUids() {
+ if (mServices.isEmpty()) {
+ clearBoundClientUids();
+ return;
+ }
+ // grab a set of clientUids of all mConnections of all services
+ final ArraySet<Integer> boundClientUids = new ArraySet<>();
+ final int serviceCount = mServices.size();
+ for (int j = 0; j < serviceCount; j++) {
+ final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns =
+ mServices.valueAt(j).getConnections();
+ final int size = conns.size();
+ for (int conni = 0; conni < size; conni++) {
+ ArrayList<ConnectionRecord> c = conns.valueAt(conni);
+ for (int i = 0; i < c.size(); i++) {
+ boundClientUids.add(c.get(i).clientUid);
+ }
+ }
+ }
+ mBoundClientUids = boundClientUids;
+ mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+ }
+
+ void addBoundClientUidsOfNewService(ServiceRecord sr) {
+ if (sr == null) {
+ return;
+ }
+ ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns = sr.getConnections();
+ for (int conni = conns.size() - 1; conni >= 0; conni--) {
+ ArrayList<ConnectionRecord> c = conns.valueAt(conni);
+ for (int i = 0; i < c.size(); i++) {
+ mBoundClientUids.add(c.get(i).clientUid);
+ }
+ }
+ mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+ }
+
+ void clearBoundClientUids() {
+ mBoundClientUids.clear();
+ mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+ }
+
+ @GuardedBy("mService")
+ boolean incServiceCrashCountLocked(long now) {
+ final boolean procIsBoundForeground = mApp.mState.getCurProcState()
+ == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ boolean tryAgain = false;
+ // Bump up the crash count of any services currently running in the proc.
+ for (int i = numberOfRunningServices() - 1; i >= 0; i--) {
+ // Any services running in the application need to be placed
+ // back in the pending list.
+ ServiceRecord sr = getRunningServiceAt(i);
+ // If the service was restarted a while ago, then reset crash count, else increment it.
+ if (now > sr.restartTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
+ sr.crashCount = 1;
+ } else {
+ sr.crashCount++;
+ }
+ // Allow restarting for started or bound foreground services that are crashing.
+ // This includes wallpapers.
+ if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY
+ && (sr.isForeground || procIsBoundForeground)) {
+ tryAgain = true;
+ }
+ }
+ return tryAgain;
+ }
+
+ @GuardedBy("mService")
+ void onCleanupApplicationRecordLocked() {
+ mTreatLikeActivity = false;
+ mHasAboveClient = false;
+ setHasClientActivities(false);
+ }
+
+ void dump(PrintWriter pw, String prefix, long nowUptime) {
+ if (mHasForegroundServices || mApp.mState.getForcingToImportant() != null) {
+ pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
+ pw.print(" forcingToImportant="); pw.println(mApp.mState.getForcingToImportant());
+ }
+ if (mHasClientActivities || mHasAboveClient || mTreatLikeActivity) {
+ pw.print(prefix); pw.print("hasClientActivities="); pw.print(mHasClientActivities);
+ pw.print(" hasAboveClient="); pw.print(mHasAboveClient);
+ pw.print(" treatLikeActivity="); pw.println(mTreatLikeActivity);
+ }
+ if (mConnectionService != null || mConnectionGroup != 0) {
+ pw.print(prefix); pw.print("connectionGroup="); pw.print(mConnectionGroup);
+ pw.print(" Importance="); pw.print(mConnectionImportance);
+ pw.print(" Service="); pw.println(mConnectionService);
+ }
+ if (mAllowlistManager) {
+ pw.print(prefix); pw.print("allowlistManager="); pw.println(mAllowlistManager);
+ }
+ if (mServices.size() > 0) {
+ pw.print(prefix); pw.println("Services:");
+ for (int i = 0, size = mServices.size(); i < size; i++) {
+ pw.print(prefix); pw.print(" - "); pw.println(mServices.valueAt(i));
+ }
+ }
+ if (mExecutingServices.size() > 0) {
+ pw.print(prefix); pw.print("Executing Services (fg=");
+ pw.print(mExecServicesFg); pw.println(")");
+ for (int i = 0, size = mExecutingServices.size(); i < size; i++) {
+ pw.print(prefix); pw.print(" - "); pw.println(mExecutingServices.valueAt(i));
+ }
+ }
+ if (mConnections.size() > 0) {
+ pw.print(prefix); pw.println("mConnections:");
+ for (int i = 0, size = mConnections.size(); i < size; i++) {
+ pw.print(prefix); pw.print(" - "); pw.println(mConnections.valueAt(i));
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
new file mode 100644
index 0000000..e1a153d
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -0,0 +1,1337 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
+import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.NFC_UID;
+import static android.os.Process.ROOT_UID;
+import static android.os.Process.SHELL_UID;
+import static android.os.Process.SYSTEM_UID;
+
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
+import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED;
+import static com.android.server.am.ActiveServices.fgsCodeToString;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ProcessRecord.TAG;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.os.Binder;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.CompositeRWLock;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FrameworkStatsLog;
+
+
+import java.io.PrintWriter;
+
+/**
+ * The state info of the process, including proc state, oom adj score, et al.
+ */
+final class ProcessStateRecord {
+ private final ProcessRecord mApp;
+ private final ActivityManagerService mService;
+ private final ActivityManagerGlobalLock mProcLock;
+
+ /**
+ * Maximum OOM adjustment for this process.
+ */
+ @GuardedBy("mService")
+ private int mMaxAdj = ProcessList.UNKNOWN_ADJ;
+
+ /**
+ * Current OOM unlimited adjustment for this process.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mCurRawAdj = ProcessList.INVALID_ADJ;
+
+ /**
+ * Last set OOM unlimited adjustment for this process.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mSetRawAdj = ProcessList.INVALID_ADJ;
+
+ /**
+ * Current OOM adjustment for this process.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mCurAdj = ProcessList.INVALID_ADJ;
+
+ /**
+ * Last set OOM adjustment for this process.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mSetAdj = ProcessList.INVALID_ADJ;
+
+ /**
+ * The last adjustment that was verified as actually being set.
+ */
+ @GuardedBy("mService")
+ private int mVerifiedAdj = ProcessList.INVALID_ADJ;
+
+ /**
+ * Current capability flags of this process.
+ * For example, PROCESS_CAPABILITY_FOREGROUND_LOCATION is one capability.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mCurCapability = PROCESS_CAPABILITY_NONE;
+
+ /**
+ * Last set capability flags.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mSetCapability = PROCESS_CAPABILITY_NONE;
+
+ /**
+ * Currently desired scheduling class.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mCurSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+
+ /**
+ * Last set to background scheduling class.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mSetSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+
+ /**
+ * Currently computed process state.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mCurProcState = PROCESS_STATE_NONEXISTENT;
+
+ /**
+ * Last reported process state.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mRepProcState = PROCESS_STATE_NONEXISTENT;
+
+ /**
+ * Temp state during computation.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mCurRawProcState = PROCESS_STATE_NONEXISTENT;
+
+ /**
+ * Last set process state in process tracker.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mSetProcState = PROCESS_STATE_NONEXISTENT;
+
+ /**
+ * Last time mSetProcState changed.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private long mLastStateTime;
+
+ /**
+ * Previous priority value if we're switching to non-SCHED_OTHER.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mSavedPriority;
+
+ /**
+ * Process currently is on the service B list.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mServiceB;
+
+ /**
+ * We are forcing to service B list due to its RAM use.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mServiceHighRam;
+
+ /**
+ * Has this process not been in a cached state since last idle?
+ */
+ @GuardedBy("mProcLock")
+ private boolean mNotCachedSinceIdle;
+
+ /**
+ * Are there any started services running in this process?
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mHasStartedServices;
+
+ /**
+ * Running any activities that are foreground?
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mHasForegroundActivities;
+
+ /**
+ * Last reported foreground activities.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mRepForegroundActivities;
+
+ /**
+ * Has UI been shown in this process since it was started?
+ */
+ @GuardedBy("mService")
+ private boolean mHasShownUi;
+
+ /**
+ * Is this process currently showing a non-activity UI that the user
+ * is interacting with? E.g. The status bar when it is expanded, but
+ * not when it is minimized. When true the
+ * process will be set to use the ProcessList#SCHED_GROUP_TOP_APP
+ * scheduling group to boost performance.
+ */
+ @GuardedBy("mService")
+ private boolean mHasTopUi;
+
+ /**
+ * Is the process currently showing a non-activity UI that
+ * overlays on-top of activity UIs on screen. E.g. display a window
+ * of type android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY
+ * When true the process will oom adj score will be set to
+ * ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance
+ * of the process getting killed.
+ */
+ @GuardedBy("mService")
+ private boolean mHasOverlayUi;
+
+ /**
+ * Is the process currently running a RemoteAnimation? When true
+ * the process will be set to use the
+ * ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost
+ * performance, as well as oom adj score will be set to
+ * ProcessList#VISIBLE_APP_ADJ at minimum to reduce the chance
+ * of the process getting killed.
+ */
+ @GuardedBy("mService")
+ private boolean mRunningRemoteAnimation;
+
+ /**
+ * Keep track of whether we changed 'mSetAdj'.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mProcStateChanged;
+
+ /**
+ * Whether we have told usage stats about it being an interaction.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mReportedInteraction;
+
+ /**
+ * The time we sent the last interaction event.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private long mInteractionEventTime;
+
+ /**
+ * When we became foreground for interaction purposes.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private long mFgInteractionTime;
+
+ /**
+ * Token that is forcing this process to be important.
+ */
+ @GuardedBy("mService")
+ private Object mForcingToImportant;
+
+ /**
+ * Sequence id for identifying oom_adj assignment cycles.
+ */
+ @GuardedBy("mService")
+ private int mAdjSeq;
+
+ /**
+ * Sequence id for identifying oom_adj assignment cycles.
+ */
+ @GuardedBy("mService")
+ private int mCompletedAdjSeq;
+
+ /**
+ * Whether this app has encountered a cycle in the most recent update.
+ */
+ @GuardedBy("mService")
+ private boolean mContainsCycle;
+
+ /**
+ * When (uptime) the process last became unimportant.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private long mWhenUnimportant;
+
+ /**
+ * The last time the process was in the TOP state or greater.
+ */
+ @GuardedBy("mService")
+ private long mLastTopTime;
+
+ /**
+ * Is this an empty background process?
+ */
+ @GuardedBy("mService")
+ private boolean mEmpty;
+
+ /**
+ * Is this a cached process?
+ */
+ @GuardedBy("mService")
+ private boolean mCached;
+
+ /**
+ * This is a system process, but not currently showing UI.
+ */
+ @GuardedBy("mService")
+ private boolean mSystemNoUi;
+
+ /**
+ * If the proc state is PROCESS_STATE_BOUND_FOREGROUND_SERVICE or above, it can start FGS.
+ * It must obtain the proc state from a persistent/top process or FGS, not transitive.
+ */
+ @GuardedBy("mService")
+ private int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
+
+ @GuardedBy("mService")
+ private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>();
+
+ /**
+ * Does the process has permission to start FGS from background.
+ */
+ @GuardedBy("mService")
+ private @ActiveServices.FgsFeatureRetCode int mAllowStartFgsByPermission;
+
+ /**
+ * Can this process start FGS from background?
+ * If this process has the ability to start FGS from background, this ability can be passed to
+ * another process through service binding.
+ */
+ @GuardedBy("mService")
+ private @ActiveServices.FgsFeatureRetCode int mAllowStartFgs;
+
+ /**
+ * Debugging: primary thing impacting oom_adj.
+ */
+ @GuardedBy("mService")
+ private String mAdjType;
+
+ /**
+ * Debugging: adj code to report to app.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mAdjTypeCode;
+
+ /**
+ * Debugging: option dependent object.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private Object mAdjSource;
+
+ /**
+ * Debugging: proc state of mAdjSource's process.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mAdjSourceProcState;
+
+ /**
+ * Debugging: target component impacting oom_adj.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private Object mAdjTarget;
+
+ /**
+ * Approximates the usage count of the app, used for cache re-ranking by CacheOomRanker.
+ *
+ * Counts the number of times the process is re-added to the cache (i.e. setCached(false);
+ * setCached(true)). This over counts, as setCached is sometimes reset while remaining in the
+ * cache. However, this happens uniformly across processes, so ranking is not affected.
+ */
+ @GuardedBy("mService")
+ private int mCacheOomRankerUseCount;
+
+ /**
+ * Whether or not this process is reachable from given process.
+ */
+ @GuardedBy("mService")
+ private boolean mReachable;
+
+ // Below are the cached task info for OomAdjuster only
+ private static final int VALUE_INVALID = -1;
+ private static final int VALUE_FALSE = 0;
+ private static final int VALUE_TRUE = 1;
+
+ @GuardedBy("mService")
+ private int mCachedHasActivities = VALUE_INVALID;
+ @GuardedBy("mService")
+ private int mCachedIsHeavyWeight = VALUE_INVALID;
+ @GuardedBy("mService")
+ private int mCachedHasVisibleActivities = VALUE_INVALID;
+ @GuardedBy("mService")
+ private int mCachedIsHomeProcess = VALUE_INVALID;
+ @GuardedBy("mService")
+ private int mCachedIsPreviousProcess = VALUE_INVALID;
+ @GuardedBy("mService")
+ private int mCachedHasRecentTasks = VALUE_INVALID;
+ @GuardedBy("mService")
+ private int mCachedIsReceivingBroadcast = VALUE_INVALID;
+
+ @GuardedBy("mService")
+ private int mCachedAdj = ProcessList.INVALID_ADJ;
+ @GuardedBy("mService")
+ private boolean mCachedForegroundActivities = false;
+ @GuardedBy("mService")
+ private int mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ @GuardedBy("mService")
+ private int mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+
+ ProcessStateRecord(ProcessRecord app) {
+ mApp = app;
+ mService = app.mService;
+ mProcLock = mService.mProcLock;
+ setAllowStartFgsByPermission();
+ }
+
+ void init(long now) {
+ mLastStateTime = now;
+ }
+
+ @GuardedBy("mService")
+ void setMaxAdj(int maxAdj) {
+ mMaxAdj = maxAdj;
+ }
+
+ @GuardedBy("mService")
+ int getMaxAdj() {
+ return mMaxAdj;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCurRawAdj(int curRawAdj) {
+ mCurRawAdj = curRawAdj;
+ mApp.getWindowProcessController().setPerceptible(
+ curRawAdj <= ProcessList.PERCEPTIBLE_APP_ADJ);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getCurRawAdj() {
+ return mCurRawAdj;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setSetRawAdj(int setRawAdj) {
+ mSetRawAdj = setRawAdj;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getSetRawAdj() {
+ return mSetRawAdj;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCurAdj(int curAdj) {
+ mCurAdj = curAdj;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getCurAdj() {
+ return mCurAdj;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setSetAdj(int setAdj) {
+ mSetAdj = setAdj;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getSetAdj() {
+ return mSetAdj;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getSetAdjWithServices() {
+ if (mSetAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+ if (mHasStartedServices) {
+ return ProcessList.SERVICE_B_ADJ;
+ }
+ }
+ return mSetAdj;
+ }
+
+ @GuardedBy("mService")
+ void setVerifiedAdj(int verifiedAdj) {
+ mVerifiedAdj = verifiedAdj;
+ }
+
+ @GuardedBy("mService")
+ int getVerifiedAdj() {
+ return mVerifiedAdj;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCurCapability(int curCapability) {
+ mCurCapability = curCapability;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getCurCapability() {
+ return mCurCapability;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setSetCapability(int setCapability) {
+ mSetCapability = setCapability;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getSetCapability() {
+ return mSetCapability;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCurrentSchedulingGroup(int curSchedGroup) {
+ mCurSchedGroup = curSchedGroup;
+ mApp.getWindowProcessController().setCurrentSchedulingGroup(curSchedGroup);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getCurrentSchedulingGroup() {
+ return mCurSchedGroup;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setSetSchedGroup(int setSchedGroup) {
+ mSetSchedGroup = setSchedGroup;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getSetSchedGroup() {
+ return mSetSchedGroup;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCurProcState(int curProcState) {
+ mCurProcState = curProcState;
+ mApp.getWindowProcessController().setCurrentProcState(mCurProcState);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getCurProcState() {
+ return mCurProcState;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCurRawProcState(int curRawProcState) {
+ mCurRawProcState = curRawProcState;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getCurRawProcState() {
+ return mCurRawProcState;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setReportedProcState(int repProcState) {
+ mRepProcState = repProcState;
+ mApp.getPkgList().forEachPackage((pkgName, holder) ->
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ mApp.uid, mApp.processName, pkgName,
+ ActivityManager.processStateAmToProto(mRepProcState),
+ holder.appVersion)
+ );
+ mApp.getWindowProcessController().setReportedProcState(repProcState);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getReportedProcState() {
+ return mRepProcState;
+ }
+
+ @GuardedBy("mService")
+ void forceProcessStateUpTo(int newState) {
+ if (mRepProcState > newState) {
+ synchronized (mProcLock) {
+ mRepProcState = newState;
+ setCurProcState(newState);
+ setCurRawProcState(newState);
+ mApp.getPkgList().forEachPackage((pkgName, holder) ->
+ FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+ mApp.uid, mApp.processName, pkgName,
+ ActivityManager.processStateAmToProto(mRepProcState),
+ holder.appVersion)
+ );
+ }
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setSetProcState(int setProcState) {
+ mSetProcState = setProcState;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getSetProcState() {
+ return mSetProcState;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setLastStateTime(long lastStateTime) {
+ mLastStateTime = lastStateTime;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ long getLastStateTime() {
+ return mLastStateTime;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setSavedPriority(int savedPriority) {
+ mSavedPriority = savedPriority;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getSavedPriority() {
+ return mSavedPriority;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setServiceB(boolean serviceb) {
+ mServiceB = serviceb;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isServiceB() {
+ return mServiceB;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setServiceHighRam(boolean serviceHighRam) {
+ mServiceHighRam = serviceHighRam;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isServiceHighRam() {
+ return mServiceHighRam;
+ }
+
+ @GuardedBy("mProcLock")
+ void setNotCachedSinceIdle(boolean notCachedSinceIdle) {
+ mNotCachedSinceIdle = notCachedSinceIdle;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean isNotCachedSinceIdle() {
+ return mNotCachedSinceIdle;
+ }
+
+ @GuardedBy("mProcLock")
+ void setHasStartedServices(boolean hasStartedServices) {
+ mHasStartedServices = hasStartedServices;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean hasStartedServices() {
+ return mHasStartedServices;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setHasForegroundActivities(boolean hasForegroundActivities) {
+ mHasForegroundActivities = hasForegroundActivities;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean hasForegroundActivities() {
+ return mHasForegroundActivities;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setRepForegroundActivities(boolean repForegroundActivities) {
+ mRepForegroundActivities = repForegroundActivities;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean hasRepForegroundActivities() {
+ return mRepForegroundActivities;
+ }
+
+ @GuardedBy("mService")
+ void setHasShownUi(boolean hasShownUi) {
+ mHasShownUi = hasShownUi;
+ }
+
+ @GuardedBy("mService")
+ boolean hasShownUi() {
+ return mHasShownUi;
+ }
+
+ @GuardedBy("mService")
+ void setHasTopUi(boolean hasTopUi) {
+ mHasTopUi = hasTopUi;
+ mApp.getWindowProcessController().setHasTopUi(hasTopUi);
+ }
+
+ @GuardedBy("mService")
+ boolean hasTopUi() {
+ return mHasTopUi;
+ }
+
+ @GuardedBy("mService")
+ void setHasOverlayUi(boolean hasOverlayUi) {
+ mHasOverlayUi = hasOverlayUi;
+ mApp.getWindowProcessController().setHasOverlayUi(hasOverlayUi);
+ }
+
+ @GuardedBy("mService")
+ boolean hasOverlayUi() {
+ return mHasOverlayUi;
+ }
+
+ @GuardedBy("mService")
+ boolean isRunningRemoteAnimation() {
+ return mRunningRemoteAnimation;
+ }
+
+ @GuardedBy("mService")
+ void setRunningRemoteAnimation(boolean runningRemoteAnimation) {
+ if (mRunningRemoteAnimation == runningRemoteAnimation) {
+ return;
+ }
+ mRunningRemoteAnimation = runningRemoteAnimation;
+ if (DEBUG_OOM_ADJ) {
+ Slog.i(TAG, "Setting runningRemoteAnimation=" + runningRemoteAnimation
+ + " for pid=" + mApp.getPid());
+ }
+ mService.updateOomAdjLocked(mApp, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setProcStateChanged(boolean procStateChanged) {
+ mProcStateChanged = procStateChanged;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean hasProcStateChanged() {
+ return mProcStateChanged;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setReportedInteraction(boolean reportedInteraction) {
+ mReportedInteraction = reportedInteraction;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean hasReportedInteraction() {
+ return mReportedInteraction;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setInteractionEventTime(long interactionEventTime) {
+ mInteractionEventTime = interactionEventTime;
+ mApp.getWindowProcessController().setInteractionEventTime(interactionEventTime);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ long getInteractionEventTime() {
+ return mInteractionEventTime;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setFgInteractionTime(long fgInteractionTime) {
+ mFgInteractionTime = fgInteractionTime;
+ mApp.getWindowProcessController().setFgInteractionTime(fgInteractionTime);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ long getFgInteractionTime() {
+ return mFgInteractionTime;
+ }
+
+ @GuardedBy("mService")
+ void setForcingToImportant(Object forcingToImportant) {
+ mForcingToImportant = forcingToImportant;
+ }
+
+ @GuardedBy("mService")
+ Object getForcingToImportant() {
+ return mForcingToImportant;
+ }
+
+ @GuardedBy("mService")
+ void setAdjSeq(int adjSeq) {
+ mAdjSeq = adjSeq;
+ }
+
+ @GuardedBy("mService")
+ void decAdjSeq() {
+ mAdjSeq--;
+ }
+
+ @GuardedBy("mService")
+ int getAdjSeq() {
+ return mAdjSeq;
+ }
+
+ @GuardedBy("mService")
+ void setCompletedAdjSeq(int completedAdjSeq) {
+ mCompletedAdjSeq = completedAdjSeq;
+ }
+
+ @GuardedBy("mService")
+ void decCompletedAdjSeq() {
+ mCompletedAdjSeq--;
+ }
+
+ @GuardedBy("mService")
+ int getCompletedAdjSeq() {
+ return mCompletedAdjSeq;
+ }
+
+ @GuardedBy("mService")
+ void setContainsCycle(boolean containsCycle) {
+ mContainsCycle = containsCycle;
+ }
+
+ @GuardedBy("mService")
+ boolean containsCycle() {
+ return mContainsCycle;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setWhenUnimportant(long whenUnimportant) {
+ mWhenUnimportant = whenUnimportant;
+ mApp.getWindowProcessController().setWhenUnimportant(whenUnimportant);
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ long getWhenUnimportant() {
+ return mWhenUnimportant;
+ }
+
+ @GuardedBy("mService")
+ void setLastTopTime(long lastTopTime) {
+ mLastTopTime = lastTopTime;
+ }
+
+ @GuardedBy("mService")
+ long getLastTopTime() {
+ return mLastTopTime;
+ }
+
+ @GuardedBy("mService")
+ void setEmpty(boolean empty) {
+ mEmpty = empty;
+ }
+
+ @GuardedBy("mService")
+ boolean isEmpty() {
+ return mEmpty;
+ }
+
+ @GuardedBy("mService")
+ void setCached(boolean cached) {
+ if (mCached != cached) {
+ mCached = cached;
+ if (cached) {
+ ++mCacheOomRankerUseCount;
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ boolean isCached() {
+ return mCached;
+ }
+
+ @GuardedBy("mService")
+ int getCacheOomRankerUseCount() {
+ return mCacheOomRankerUseCount;
+ }
+
+ @GuardedBy("mService")
+ void setSystemNoUi(boolean systemNoUi) {
+ mSystemNoUi = systemNoUi;
+ }
+
+ @GuardedBy("mService")
+ boolean isSystemNoUi() {
+ return mSystemNoUi;
+ }
+
+ @GuardedBy("mService")
+ void setAdjType(String adjType) {
+ mAdjType = adjType;
+ }
+
+ @GuardedBy("mService")
+ String getAdjType() {
+ return mAdjType;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setAdjTypeCode(int adjTypeCode) {
+ mAdjTypeCode = adjTypeCode;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getAdjTypeCode() {
+ return mAdjTypeCode;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setAdjSource(Object adjSource) {
+ mAdjSource = adjSource;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ Object getAdjSource() {
+ return mAdjSource;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setAdjSourceProcState(int adjSourceProcState) {
+ mAdjSourceProcState = adjSourceProcState;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getAdjSourceProcState() {
+ return mAdjSourceProcState;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setAdjTarget(Object adjTarget) {
+ mAdjTarget = adjTarget;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ Object getAdjTarget() {
+ return mAdjTarget;
+ }
+
+ @GuardedBy("mService")
+ boolean isReachable() {
+ return mReachable;
+ }
+
+ @GuardedBy("mService")
+ void setReachable(boolean reachable) {
+ mReachable = reachable;
+ }
+
+ @GuardedBy("mService")
+ void resetCachedInfo() {
+ mCachedHasActivities = VALUE_INVALID;
+ mCachedIsHeavyWeight = VALUE_INVALID;
+ mCachedHasVisibleActivities = VALUE_INVALID;
+ mCachedIsHomeProcess = VALUE_INVALID;
+ mCachedIsPreviousProcess = VALUE_INVALID;
+ mCachedHasRecentTasks = VALUE_INVALID;
+ mCachedIsReceivingBroadcast = VALUE_INVALID;
+ mCachedAdj = ProcessList.INVALID_ADJ;
+ mCachedForegroundActivities = false;
+ mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+ }
+
+ @GuardedBy("mService")
+ boolean getCachedHasActivities() {
+ if (mCachedHasActivities == VALUE_INVALID) {
+ mCachedHasActivities = mApp.getWindowProcessController().hasActivities() ? VALUE_TRUE
+ : VALUE_FALSE;
+ }
+ return mCachedHasActivities == VALUE_TRUE;
+ }
+
+ @GuardedBy("mService")
+ boolean getCachedIsHeavyWeight() {
+ if (mCachedIsHeavyWeight == VALUE_INVALID) {
+ mCachedIsHeavyWeight = mApp.getWindowProcessController().isHeavyWeightProcess()
+ ? VALUE_TRUE : VALUE_FALSE;
+ }
+ return mCachedIsHeavyWeight == VALUE_TRUE;
+ }
+
+ @GuardedBy("mService")
+ boolean getCachedHasVisibleActivities() {
+ if (mCachedHasVisibleActivities == VALUE_INVALID) {
+ mCachedHasVisibleActivities = mApp.getWindowProcessController().hasVisibleActivities()
+ ? VALUE_TRUE : VALUE_FALSE;
+ }
+ return mCachedHasVisibleActivities == VALUE_TRUE;
+ }
+
+ @GuardedBy("mService")
+ boolean getCachedIsHomeProcess() {
+ if (mCachedIsHomeProcess == VALUE_INVALID) {
+ if (mApp.getWindowProcessController().isHomeProcess()) {
+ mCachedIsHomeProcess = VALUE_TRUE;
+ mService.mAppProfiler.mHasHomeProcess = true;
+ } else {
+ mCachedIsHomeProcess = VALUE_FALSE;
+ }
+ }
+ return mCachedIsHomeProcess == VALUE_TRUE;
+ }
+
+ @GuardedBy("mService")
+ boolean getCachedIsPreviousProcess() {
+ if (mCachedIsPreviousProcess == VALUE_INVALID) {
+ if (mApp.getWindowProcessController().isPreviousProcess()) {
+ mCachedIsPreviousProcess = VALUE_TRUE;
+ mService.mAppProfiler.mHasPreviousProcess = true;
+ } else {
+ mCachedIsPreviousProcess = VALUE_FALSE;
+ }
+ }
+ return mCachedIsPreviousProcess == VALUE_TRUE;
+ }
+
+ @GuardedBy("mService")
+ boolean getCachedHasRecentTasks() {
+ if (mCachedHasRecentTasks == VALUE_INVALID) {
+ mCachedHasRecentTasks = mApp.getWindowProcessController().hasRecentTasks()
+ ? VALUE_TRUE : VALUE_FALSE;
+ }
+ return mCachedHasRecentTasks == VALUE_TRUE;
+ }
+
+ @GuardedBy("mService")
+ boolean getCachedIsReceivingBroadcast(ArraySet<BroadcastQueue> tmpQueue) {
+ if (mCachedIsReceivingBroadcast == VALUE_INVALID) {
+ tmpQueue.clear();
+ mCachedIsReceivingBroadcast = mService.isReceivingBroadcastLocked(mApp, tmpQueue)
+ ? VALUE_TRUE : VALUE_FALSE;
+ if (mCachedIsReceivingBroadcast == VALUE_TRUE) {
+ mCachedSchedGroup = tmpQueue.contains(mService.mFgBroadcastQueue)
+ ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+ }
+ }
+ return mCachedIsReceivingBroadcast == VALUE_TRUE;
+ }
+
+ @GuardedBy("mService")
+ void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback,
+ int adj, boolean foregroundActivities, int procState, int schedGroup, int appUid,
+ int logUid, int processCurTop) {
+ if (mCachedAdj != ProcessList.INVALID_ADJ) {
+ return;
+ }
+ callback.initialize(mApp, adj, foregroundActivities, procState, schedGroup, appUid, logUid,
+ processCurTop);
+ final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
+ mApp.getWindowProcessController().computeOomAdjFromActivities(callback));
+
+ mCachedAdj = callback.adj;
+ mCachedForegroundActivities = callback.foregroundActivities;
+ mCachedProcState = callback.procState;
+ mCachedSchedGroup = callback.schedGroup;
+
+ if (mCachedAdj == ProcessList.VISIBLE_APP_ADJ) {
+ mCachedAdj += minLayer;
+ }
+ }
+
+ @GuardedBy("mService")
+ int getCachedAdj() {
+ return mCachedAdj;
+ }
+
+ @GuardedBy("mService")
+ boolean getCachedForegroundActivities() {
+ return mCachedForegroundActivities;
+ }
+
+ @GuardedBy("mService")
+ int getCachedProcState() {
+ return mCachedProcState;
+ }
+
+ @GuardedBy("mService")
+ int getCachedSchedGroup() {
+ return mCachedSchedGroup;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ public String makeAdjReason() {
+ if (mAdjSource != null || mAdjTarget != null) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append(' ');
+ if (mAdjTarget instanceof ComponentName) {
+ sb.append(((ComponentName) mAdjTarget).flattenToShortString());
+ } else if (mAdjTarget != null) {
+ sb.append(mAdjTarget.toString());
+ } else {
+ sb.append("{null}");
+ }
+ sb.append("<=");
+ if (mAdjSource instanceof ProcessRecord) {
+ sb.append("Proc{");
+ sb.append(((ProcessRecord) mAdjSource).toShortString());
+ sb.append("}");
+ } else if (mAdjSource != null) {
+ sb.append(mAdjSource.toString());
+ } else {
+ sb.append("{null}");
+ }
+ return sb.toString();
+ }
+ return null;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void onCleanupApplicationRecordLSP() {
+ setHasForegroundActivities(false);
+ mHasShownUi = false;
+ mForcingToImportant = null;
+ mCurRawAdj = mSetRawAdj = mCurAdj = mSetAdj = mVerifiedAdj = ProcessList.INVALID_ADJ;
+ mCurCapability = mSetCapability = PROCESS_CAPABILITY_NONE;
+ mCurSchedGroup = mSetSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+ mCurProcState = mRepProcState = mCurRawProcState = mSetProcState = mAllowStartFgsState =
+ PROCESS_STATE_NONEXISTENT;
+ }
+
+ @GuardedBy("mService")
+ void addAllowBackgroundFgsStartsToken(Binder entity) {
+ mBackgroundFgsStartTokens.add(entity);
+ }
+
+ @GuardedBy("mService")
+ void removeAllowBackgroundFgsStartsToken(Binder entity) {
+ mBackgroundFgsStartTokens.remove(entity);
+ }
+
+ @GuardedBy("mService")
+ boolean areBackgroundFgsStartsAllowedByToken() {
+ return !mBackgroundFgsStartTokens.isEmpty();
+ }
+
+ @GuardedBy("mService")
+ void resetAllowStartFgs() {
+ mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
+ mAllowStartFgs = mAllowStartFgsByPermission;
+ }
+
+ @GuardedBy("mService")
+ void bumpAllowStartFgsState(int newProcState) {
+ if (newProcState < mAllowStartFgsState) {
+ mAllowStartFgsState = newProcState;
+ }
+ }
+
+ @GuardedBy("mService")
+ void setAllowStartFgsState(int allowStartFgsState) {
+ mAllowStartFgsState = allowStartFgsState;
+ }
+
+ @GuardedBy("mService")
+ boolean isAllowedStartFgsState() {
+ return mAllowStartFgsState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ }
+
+ @GuardedBy("mService")
+ void setAllowStartFgsByPermission() {
+ int ret = FGS_FEATURE_DENIED;
+ if (ret == FGS_FEATURE_DENIED) {
+ boolean isSystem = false;
+ final int uid = UserHandle.getAppId(mApp.info.uid);
+ switch (uid) {
+ case ROOT_UID:
+ case SYSTEM_UID:
+ case NFC_UID:
+ case SHELL_UID:
+ isSystem = true;
+ break;
+ default:
+ isSystem = false;
+ break;
+ }
+
+ if (isSystem) {
+ ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID;
+ }
+ }
+
+ if (ret == FGS_FEATURE_DENIED) {
+ if (ActivityManager.checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND,
+ mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
+ ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION;
+ } else if (ActivityManager.checkComponentPermission(
+ START_FOREGROUND_SERVICES_FROM_BACKGROUND,
+ mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
+ ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION;
+ } else if (ActivityManager.checkComponentPermission(SYSTEM_ALERT_WINDOW,
+ mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
+ ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION;
+ }
+ }
+ mAllowStartFgs = mAllowStartFgsByPermission = ret;
+ }
+
+ @GuardedBy("mService")
+ void setAllowStartFgs() {
+ if (mAllowStartFgs != FGS_FEATURE_DENIED) {
+ return;
+ }
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+ if (isAllowedStartFgsState()) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROC_STATE;
+ }
+ }
+
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+ // Is the calling UID a device owner app?
+ if (mService.mInternal != null) {
+ if (mService.mInternal.isDeviceOwner(mApp.info.uid)) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER;
+ }
+ }
+ }
+
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+ if (mService.mInternal != null) {
+ final boolean isCompanionApp = mService.mInternal.isAssociatedCompanionApp(
+ UserHandle.getUserId(mApp.info.uid), mApp.info.uid);
+ if (isCompanionApp) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
+ }
+ }
+ }
+
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+ // Is the calling UID a profile owner app?
+ if (mService.mInternal != null) {
+ if (mService.mInternal.isProfileOwner(mApp.info.uid)) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
+ }
+ }
+ }
+
+ if (mAllowStartFgs == FGS_FEATURE_DENIED) {
+ // uid is on DeviceIdleController's user/system allowlist
+ // or AMS's FgsStartTempAllowList.
+ if (mService.isAllowlistedForFgsStartLOSP(mApp.info.uid)) {
+ mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST;
+ }
+ }
+ }
+
+ @GuardedBy("mService")
+ void setAllowStartFgs(@ActiveServices.FgsFeatureRetCode int allowStartFgs) {
+ mAllowStartFgs = allowStartFgs;
+ }
+
+ @GuardedBy("mService")
+ @ActiveServices.FgsFeatureRetCode int getAllowedStartFgs() {
+ return mAllowStartFgs;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void dump(PrintWriter pw, String prefix, long nowUptime) {
+ if (mReportedInteraction || mFgInteractionTime != 0) {
+ pw.print(prefix); pw.print("reportedInteraction=");
+ pw.print(mReportedInteraction);
+ if (mInteractionEventTime != 0) {
+ pw.print(" time=");
+ TimeUtils.formatDuration(mInteractionEventTime, SystemClock.elapsedRealtime(), pw);
+ }
+ if (mFgInteractionTime != 0) {
+ pw.print(" fgInteractionTime=");
+ TimeUtils.formatDuration(mFgInteractionTime, SystemClock.elapsedRealtime(), pw);
+ }
+ pw.println();
+ }
+ pw.print(prefix); pw.print("adjSeq="); pw.print(mAdjSeq);
+ pw.print(" lruSeq="); pw.println(mApp.getLruSeq());
+ pw.print(prefix); pw.print("oom adj: max="); pw.print(mMaxAdj);
+ pw.print(" curRaw="); pw.print(mCurRawAdj);
+ pw.print(" setRaw="); pw.print(mSetRawAdj);
+ pw.print(" cur="); pw.print(mCurAdj);
+ pw.print(" set="); pw.println(mSetAdj);
+ pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
+ pw.print(" setSchedGroup="); pw.print(mSetSchedGroup);
+ pw.print(" systemNoUi="); pw.print(mSystemNoUi);
+ pw.print(prefix); pw.print("curProcState="); pw.print(getCurProcState());
+ pw.print(" mRepProcState="); pw.print(mRepProcState);
+ pw.print(" setProcState="); pw.print(mSetProcState);
+ pw.print(" lastStateTime=");
+ TimeUtils.formatDuration(getLastStateTime(), nowUptime, pw);
+ pw.println();
+ pw.print(prefix); pw.print("curCapability=");
+ ActivityManager.printCapabilitiesFull(pw, mCurCapability);
+ pw.print(" setCapability=");
+ ActivityManager.printCapabilitiesFull(pw, mSetCapability);
+ pw.println();
+ pw.print(prefix); pw.print("allowStartFgsState=");
+ pw.println(mAllowStartFgsState);
+ if (mAllowStartFgs != FGS_FEATURE_DENIED) {
+ pw.print(prefix); pw.print("allowStartFgs=");
+ pw.println(fgsCodeToString(mAllowStartFgs));
+ }
+ if (mHasShownUi || mApp.mProfile.hasPendingUiClean()) {
+ pw.print(prefix); pw.print("hasShownUi="); pw.print(mHasShownUi);
+ pw.print(" pendingUiClean="); pw.print(mApp.mProfile.hasPendingUiClean());
+ }
+ pw.print(prefix); pw.print("cached="); pw.print(mCached);
+ pw.print(" empty="); pw.println(mEmpty);
+ if (mServiceB) {
+ pw.print(prefix); pw.print("serviceb="); pw.print(mServiceB);
+ pw.print(" serviceHighRam="); pw.println(mServiceHighRam);
+ }
+ if (mNotCachedSinceIdle) {
+ pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(mNotCachedSinceIdle);
+ pw.print(" initialIdlePss="); pw.println(mApp.mProfile.getInitialIdlePss());
+ }
+ if (hasTopUi() || hasOverlayUi() || mRunningRemoteAnimation) {
+ pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
+ pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
+ pw.print(" runningRemoteAnimation="); pw.println(mRunningRemoteAnimation);
+ }
+ if (mHasForegroundActivities || mRepForegroundActivities) {
+ pw.print(prefix);
+ pw.print(" foregroundActivities="); pw.print(mHasForegroundActivities);
+ pw.print(" (rep="); pw.print(mRepForegroundActivities); pw.println(")");
+ }
+ if (mSetProcState > ActivityManager.PROCESS_STATE_SERVICE) {
+ pw.print(prefix);
+ pw.print(" whenUnimportant=");
+ TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw);
+ pw.println();
+ }
+ if (mLastTopTime > 0) {
+ pw.print(prefix); pw.print("lastTopTime=");
+ TimeUtils.formatDuration(mLastTopTime, nowUptime, pw);
+ pw.println();
+ }
+ if (mHasStartedServices) {
+ pw.print(prefix); pw.print("hasStartedServices="); pw.println(mHasStartedServices);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index c10a078..34a0c1b 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -313,8 +313,8 @@
private void scheduleRequestPssAllProcs(boolean always, boolean memLowered) {
mAm.mHandler.post(() -> {
- synchronized (mAm) {
- mAm.mAppProfiler.requestPssAllProcsLocked(
+ synchronized (mAm.mProcLock) {
+ mAm.mAppProfiler.requestPssAllProcsLPr(
SystemClock.uptimeMillis(), always, memLowered);
}
});
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 29c1657..072eba5 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.ComponentName.WithComponentName;
import android.os.Binder;
@@ -23,6 +24,7 @@
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
+
import com.android.internal.os.TransferPipe;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
@@ -372,10 +374,11 @@
*/
private void dumpProvider(String prefix, FileDescriptor fd, PrintWriter pw,
final ContentProviderRecord r, String[] args, boolean dumpAll) {
+ final IApplicationThread thread = r.proc != null ? r.proc.getThread() : null;
for (String s: args) {
if (!dumpAll && s.contains("--proto")) {
- if (r.proc != null && r.proc.thread != null) {
- dumpToTransferPipe(null , fd, pw, r, args);
+ if (thread != null) {
+ dumpToTransferPipe(null , fd, pw, r, thread, args);
}
return;
}
@@ -386,7 +389,7 @@
pw.print(r);
pw.print(" pid=");
if (r.proc != null) {
- pw.println(r.proc.pid);
+ pw.println(r.proc.getPid());
} else {
pw.println("(not running)");
}
@@ -394,10 +397,10 @@
r.dump(pw, innerPrefix, true);
}
}
- if (r.proc != null && r.proc.thread != null) {
+ if (thread != null) {
pw.println(" Client:");
pw.flush();
- dumpToTransferPipe(" ", fd, pw, r, args);
+ dumpToTransferPipe(" ", fd, pw, r, thread, args);
}
}
@@ -420,8 +423,9 @@
// Only dump the first provider, since we are dumping in proto format
for (int i = 0; i < providers.size(); i++) {
final ContentProviderRecord r = providers.get(i);
- if (r.proc != null && r.proc.thread != null) {
- dumpToTransferPipe(null, fd, pw, r, newArgs);
+ IApplicationThread thread;
+ if (r.proc != null && (thread = r.proc.getThread()) != null) {
+ dumpToTransferPipe(null, fd, pw, r, thread, newArgs);
return true;
}
}
@@ -433,11 +437,11 @@
* any meta string (e.g., provider info, indentation) written to the file descriptor.
*/
private void dumpToTransferPipe(String prefix, FileDescriptor fd, PrintWriter pw,
- final ContentProviderRecord r, String[] args) {
+ final ContentProviderRecord r, final IApplicationThread thread, String[] args) {
try {
TransferPipe tp = new TransferPipe();
try {
- r.proc.thread.dumpProvider(
+ thread.dumpProvider(
tp.getWriteFd(), r.provider.asBinder(), args);
tp.setBufferPrefix(prefix);
// Short timeout, since blocking here can
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8a1b4e3..485087d 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -23,6 +23,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.Nullable;
+import android.app.IApplicationThread;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -281,7 +282,7 @@
proto.write(ServiceRecordProto.SHORT_NAME, this.shortInstanceName);
proto.write(ServiceRecordProto.IS_RUNNING, app != null);
if (app != null) {
- proto.write(ServiceRecordProto.PID, app.pid);
+ proto.write(ServiceRecordProto.PID, app.getPid());
}
if (intent != null) {
intent.getIntent().dumpDebug(proto, ServiceRecordProto.INTENT, false, true, false,
@@ -582,13 +583,14 @@
restartTracker.setRestarting(true, memFactor, now);
}
- public void setProcess(ProcessRecord _proc) {
- if (_proc != null) {
+ public void setProcess(ProcessRecord proc, IApplicationThread thread, int pid,
+ UidRecord uidRecord) {
+ if (proc != null) {
// We're starting a new process for this service, but a previous one is allowed to start
// background activities. Remove that ability now (unless the new process is the same as
// the previous one, which is a common case).
if (mAppForAllowingBgActivityStartsByStart != null) {
- if (mAppForAllowingBgActivityStartsByStart != _proc) {
+ if (mAppForAllowingBgActivityStartsByStart != proc) {
mAppForAllowingBgActivityStartsByStart
.removeAllowBackgroundActivityStartsToken(this);
ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
@@ -596,34 +598,35 @@
}
// Make sure the cleanup callback knows about the new process.
mAppForAllowingBgActivityStartsByStart = mIsAllowedBgActivityStartsByStart
- ? _proc : null;
+ ? proc : null;
if (mIsAllowedBgActivityStartsByStart
|| mIsAllowedBgActivityStartsByBinding) {
- _proc.addOrUpdateAllowBackgroundActivityStartsToken(this,
+ proc.addOrUpdateAllowBackgroundActivityStartsToken(this,
getExclusiveOriginatingToken());
} else {
- _proc.removeAllowBackgroundActivityStartsToken(this);
+ proc.removeAllowBackgroundActivityStartsToken(this);
}
if (mIsAllowedBgFgsStartsByBinding) {
- _proc.addAllowBackgroundFgsStartsToken(this);
+ proc.mState.addAllowBackgroundFgsStartsToken(this);
} else {
- _proc.removeAllowBackgroundFgsStartsToken(this);
+ proc.mState.removeAllowBackgroundFgsStartsToken(this);
}
}
- if (app != null && app != _proc) {
+ if (app != null && app != proc) {
// If the old app is allowed to start bg activities because of a service start, leave it
// that way until the cleanup callback runs. Otherwise we can remove its bg activity
// start ability immediately (it can't be bound now).
if (!mIsAllowedBgActivityStartsByStart) {
app.removeAllowBackgroundActivityStartsToken(this);
}
- app.updateBoundClientUids();
+ app.mServices.updateBoundClientUids();
}
- app = _proc;
- if (pendingConnectionGroup > 0 && _proc != null) {
- _proc.connectionService = this;
- _proc.connectionGroup = pendingConnectionGroup;
- _proc.connectionImportance = pendingConnectionImportance;
+ app = proc;
+ if (pendingConnectionGroup > 0 && proc != null) {
+ final ProcessServiceRecord psr = proc.mServices;
+ psr.setConnectionService(this);
+ psr.setConnectionGroup(pendingConnectionGroup);
+ psr.setConnectionImportance(pendingConnectionImportance);
pendingConnectionGroup = pendingConnectionImportance = 0;
}
if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) {
@@ -631,7 +634,7 @@
ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
for (int i = 0; i < cr.size(); i++) {
final ConnectionRecord conn = cr.get(i);
- if (_proc != null) {
+ if (proc != null) {
conn.startAssociationIfNeeded();
} else {
conn.stopAssociation();
@@ -639,8 +642,8 @@
}
}
}
- if (_proc != null) {
- _proc.updateBoundClientUids();
+ if (proc != null) {
+ proc.mServices.updateBoundClientUids();
}
}
@@ -658,7 +661,7 @@
// if we have a process attached, add bound client uid of this connection to it
if (app != null) {
- app.addBoundClientUid(c.clientUid);
+ app.mServices.addBoundClientUid(c.clientUid);
}
}
@@ -666,7 +669,7 @@
connections.remove(binder);
// if we have a process attached, tell it to update the state of bound clients
if (app != null) {
- app.updateBoundClientUids();
+ app.mServices.updateBoundClientUids();
}
}
@@ -820,9 +823,9 @@
return;
}
if (mIsAllowedBgFgsStartsByBinding) {
- app.addAllowBackgroundFgsStartsToken(this);
+ app.mState.addAllowBackgroundFgsStartsToken(this);
} else {
- app.removeAllowBackgroundFgsStartsToken(this);
+ app.mState.removeAllowBackgroundFgsStartsToken(this);
}
}
@@ -937,7 +940,7 @@
public void postNotification() {
final int appUid = appInfo.uid;
- final int appPid = app.pid;
+ final int appPid = app.getPid();
if (foregroundId != 0 && foregroundNoti != null) {
// Do asynchronous communication with notification manager to
// avoid deadlocks.
@@ -1057,7 +1060,7 @@
final String localPackageName = packageName;
final int localForegroundId = foregroundId;
final int appUid = appInfo.uid;
- final int appPid = app != null ? app.pid : 0;
+ final int appPid = app != null ? app.getPid() : 0;
ams.mHandler.post(new Runnable() {
public void run() {
NotificationManagerInternal nm = LocalServices.getService(
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 7299e81..e022e97 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -81,6 +81,7 @@
static final String[] sDeviceConfigScopes = new String[] {
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_CONFIGURATION,
+ DeviceConfig.NAMESPACE_CONNECTIVITY,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
index 22e7faa..4cc1fc1 100644
--- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java
+++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
@@ -49,7 +49,7 @@
mProc = app;
mResult = result;
CharSequence name;
- if ((app.getPkgList().size() == 1)
+ if (app.getPkgList().size() == 1
&& (name = context.getPackageManager().getApplicationLabel(app.info)) != null) {
setMessage(res.getString(
com.android.internal.R.string.smv_application,
@@ -67,7 +67,7 @@
res.getText(com.android.internal.R.string.dlg_ok),
mHandler.obtainMessage(ACTION_OK));
- if (app.errorReportReceiver != null) {
+ if (app.mErrorState.getErrorReportReceiver() != null) {
setButton(DialogInterface.BUTTON_NEGATIVE,
res.getText(com.android.internal.R.string.report),
mHandler.obtainMessage(ACTION_OK_AND_REPORT));
@@ -84,9 +84,9 @@
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
- synchronized (mService) {
+ synchronized (mService.mProcLock) {
if (mProc != null) {
- mProc.getDialogController().clearViolationDialogs();
+ mProc.mErrorState.getDialogController().clearViolationDialogs();
}
}
mResult.set(msg.what);
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index b3150d1..df65ee6 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -204,16 +204,18 @@
} else {
UidRecord validateUid = mValidateUids.get(item.uid);
if (validateUid == null) {
- validateUid = new UidRecord(item.uid);
+ validateUid = new UidRecord(item.uid, null);
mValidateUids.put(item.uid, validateUid);
}
if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
- validateUid.idle = true;
+ validateUid.setIdle(true);
} else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
- validateUid.idle = false;
+ validateUid.setIdle(false);
}
- validateUid.setCurProcState(validateUid.setProcState = item.procState);
- validateUid.curCapability = validateUid.setCapability = item.capability;
+ validateUid.setSetProcState(item.procState);
+ validateUid.setCurProcState(item.procState);
+ validateUid.setSetCapability(item.capability);
+ validateUid.setCurCapability(item.capability);
validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
}
}
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 36d22bf..2fb662f 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -26,27 +26,58 @@
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
+import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.server.am.UidObserverController.ChangeRecord;
+import java.util.function.Consumer;
+
/**
* Overall information about a uid that has actively running processes.
*/
public final class UidRecord {
- final int uid;
- int mCurProcState;
- int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
- int curCapability;
- int setCapability;
- long lastBackgroundTime;
- boolean ephemeral;
- boolean foregroundServices;
- boolean mCurAllowlist;
- boolean mSetAllowlist;
- boolean idle;
- boolean setIdle;
- int numProcs;
- ArraySet<ProcessRecord> procRecords = new ArraySet<>();
+ private final ActivityManagerService mService;
+ private final ActivityManagerGlobalLock mProcLock;
+ private final int mUid;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mCurProcState;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mSetProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mCurCapability;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mSetCapability;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private long mLastBackgroundTime;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mEphemeral;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mForegroundServices;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mCurAllowList;;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mSetAllowList;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mIdle;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private boolean mSetIdle;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private int mNumProcs;
+
+ @CompositeRWLock({"mService", "mProcLock"})
+ private ArraySet<ProcessRecord> mProcRecords = new ArraySet<>();
/**
* Sequence number associated with the {@link #mCurProcState}. This is incremented using
@@ -111,32 +142,168 @@
// UidObserverController is the only thing that should modify this.
final ChangeRecord pendingChange = new ChangeRecord();
- int lastReportedChange;
+ @GuardedBy("mService")
+ private int mLastReportedChange;
- public UidRecord(int _uid) {
- uid = _uid;
- idle = true;
+ public UidRecord(int uid, ActivityManagerService service) {
+ mUid = uid;
+ mService = service;
+ mProcLock = service != null ? service.mProcLock : null;
+ mIdle = true;
reset();
}
- public int getCurProcState() {
+ int getUid() {
+ return mUid;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getCurProcState() {
return mCurProcState;
}
- public void setCurProcState(int curProcState) {
+ @GuardedBy({"mService", "mProcLock"})
+ void setCurProcState(int curProcState) {
mCurProcState = curProcState;
}
- public void reset() {
- setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- foregroundServices = false;
- curCapability = 0;
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getSetProcState() {
+ return mSetProcState;
+ }
+ @GuardedBy({"mService", "mProcLock"})
+ void setSetProcState(int setProcState) {
+ mSetProcState = setProcState;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getCurCapability() {
+ return mCurCapability;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCurCapability(int curCapability) {
+ mCurCapability = curCapability;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getSetCapability() {
+ return mSetCapability;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setSetCapability(int setCapability) {
+ mSetCapability = setCapability;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ long getLastBackgroundTime() {
+ return mLastBackgroundTime;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setLastBackgroundTime(long lastBackgroundTime) {
+ mLastBackgroundTime = lastBackgroundTime;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isEphemeral() {
+ return mEphemeral;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setEphemeral(boolean ephemeral) {
+ mEphemeral = ephemeral;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean hasForegroundServices() {
+ return mForegroundServices;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setForegroundServices(boolean foregroundServices) {
+ mForegroundServices = foregroundServices;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isCurAllowListed() {
+ return mCurAllowList;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setCurAllowListed(boolean curAllowList) {
+ mCurAllowList = curAllowList;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isSetAllowListed() {
+ return mSetAllowList;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setSetAllowListed(boolean setAllowlist) {
+ mSetAllowList = setAllowlist;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isIdle() {
+ return mIdle;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setIdle(boolean idle) {
+ mIdle = idle;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ boolean isSetIdle() {
+ return mSetIdle;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void setSetIdle(boolean setIdle) {
+ mSetIdle = setIdle;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ int getNumOfProcs() {
+ return mProcRecords.size();
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ void forEachProcess(Consumer<ProcessRecord> callback) {
+ for (int i = mProcRecords.size() - 1; i >= 0; i--) {
+ callback.accept(mProcRecords.valueAt(i));
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void addProcess(ProcessRecord app) {
+ mProcRecords.add(app);
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void removeProcess(ProcessRecord app) {
+ mProcRecords.remove(app);
+ }
+
+ @GuardedBy("mService")
+ void setLastReportedChange(int lastReportedChange) {
+ mLastReportedChange = lastReportedChange;
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ void reset() {
+ setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ mForegroundServices = false;
+ mCurCapability = 0;
}
public void updateHasInternetPermission() {
hasInternetPermission = ActivityManager.checkUidPermission(Manifest.permission.INTERNET,
- uid) == PackageManager.PERMISSION_GRANTED;
+ mUid) == PackageManager.PERMISSION_GRANTED;
}
/**
@@ -153,19 +320,19 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
- proto.write(UidRecordProto.UID, uid);
+ proto.write(UidRecordProto.UID, mUid);
proto.write(UidRecordProto.CURRENT, ProcessList.makeProcStateProtoEnum(mCurProcState));
- proto.write(UidRecordProto.EPHEMERAL, ephemeral);
- proto.write(UidRecordProto.FG_SERVICES, foregroundServices);
- proto.write(UidRecordProto.WHILELIST, mCurAllowlist);
+ proto.write(UidRecordProto.EPHEMERAL, mEphemeral);
+ proto.write(UidRecordProto.FG_SERVICES, mForegroundServices);
+ proto.write(UidRecordProto.WHILELIST, mCurAllowList);
ProtoUtils.toDuration(proto, UidRecordProto.LAST_BACKGROUND_TIME,
- lastBackgroundTime, SystemClock.elapsedRealtime());
- proto.write(UidRecordProto.IDLE, idle);
- if (lastReportedChange != 0) {
+ mLastBackgroundTime, SystemClock.elapsedRealtime());
+ proto.write(UidRecordProto.IDLE, mIdle);
+ if (mLastReportedChange != 0) {
ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidRecordProto.LAST_REPORTED_CHANGES,
- lastReportedChange, ORIG_ENUMS, PROTO_ENUMS);
+ mLastReportedChange, ORIG_ENUMS, PROTO_ENUMS);
}
- proto.write(UidRecordProto.NUM_PROCS, numProcs);
+ proto.write(UidRecordProto.NUM_PROCS, mNumProcs);
long seqToken = proto.start(UidRecordProto.NETWORK_STATE_UPDATE);
proto.write(UidRecordProto.ProcStateSequence.CURURENT, curProcStateSeq);
@@ -182,54 +349,54 @@
sb.append("UidRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(' ');
- UserHandle.formatUid(sb, uid);
+ UserHandle.formatUid(sb, mUid);
sb.append(' ');
sb.append(ProcessList.makeProcStateString(mCurProcState));
- if (ephemeral) {
+ if (mEphemeral) {
sb.append(" ephemeral");
}
- if (foregroundServices) {
+ if (mForegroundServices) {
sb.append(" fgServices");
}
- if (mCurAllowlist) {
+ if (mCurAllowList) {
sb.append(" allowlist");
}
- if (lastBackgroundTime > 0) {
+ if (mLastBackgroundTime > 0) {
sb.append(" bg:");
- TimeUtils.formatDuration(SystemClock.elapsedRealtime()-lastBackgroundTime, sb);
+ TimeUtils.formatDuration(SystemClock.elapsedRealtime() - mLastBackgroundTime, sb);
}
- if (idle) {
+ if (mIdle) {
sb.append(" idle");
}
- if (lastReportedChange != 0) {
+ if (mLastReportedChange != 0) {
sb.append(" change:");
boolean printed = false;
- if ((lastReportedChange & CHANGE_GONE) != 0) {
+ if ((mLastReportedChange & CHANGE_GONE) != 0) {
printed = true;
sb.append("gone");
}
- if ((lastReportedChange & CHANGE_IDLE) != 0) {
+ if ((mLastReportedChange & CHANGE_IDLE) != 0) {
if (printed) {
sb.append("|");
}
printed = true;
sb.append("idle");
}
- if ((lastReportedChange & CHANGE_ACTIVE) != 0) {
+ if ((mLastReportedChange & CHANGE_ACTIVE) != 0) {
if (printed) {
sb.append("|");
}
printed = true;
sb.append("active");
}
- if ((lastReportedChange & CHANGE_CACHED) != 0) {
+ if ((mLastReportedChange & CHANGE_CACHED) != 0) {
if (printed) {
sb.append("|");
}
printed = true;
sb.append("cached");
}
- if ((lastReportedChange & CHANGE_UNCACHED) != 0) {
+ if ((mLastReportedChange & CHANGE_UNCACHED) != 0) {
if (printed) {
sb.append("|");
}
@@ -237,7 +404,7 @@
}
}
sb.append(" procs:");
- sb.append(numProcs);
+ sb.append(mNumProcs);
sb.append(" seq(");
sb.append(curProcStateSeq);
sb.append(",");
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index fded85c..e97f0b4 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -18,11 +18,9 @@
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-import static android.content.Intent.ACTION_USER_ADDED;
-import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS;
import static android.content.Intent.EXTRA_REPLACING;
-import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
import android.annotation.NonNull;
@@ -36,8 +34,9 @@
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
-import android.content.pm.UserInfo;
+import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.Environment;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -48,16 +47,21 @@
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;
+import java.io.File;
import java.io.FileDescriptor;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
/**
* System service that manages app hibernation state, a state apps can enter that means they are
@@ -66,6 +70,11 @@
*/
public final class AppHibernationService extends SystemService {
private static final String TAG = "AppHibernationService";
+ private static final int PACKAGE_MATCH_FLAGS =
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS;
/**
* Lock for accessing any in-memory hibernation state
@@ -76,9 +85,13 @@
private final IActivityManager mIActivityManager;
private final UserManager mUserManager;
@GuardedBy("mLock")
- private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>();
+ private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>();
+ private final SparseArray<HibernationStateDiskStore<UserLevelState>> mUserDiskStores =
+ new SparseArray<>();
@GuardedBy("mLock")
- private final Set<String> mGloballyHibernatedPackages = new ArraySet<>();
+ private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>();
+ private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore;
+ private final Injector mInjector;
/**
* Initializes the system service.
@@ -90,28 +103,22 @@
* @param context The system server context.
*/
public AppHibernationService(@NonNull Context context) {
- this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
- ActivityManager.getService(),
- context.getSystemService(UserManager.class));
+ this(new InjectorImpl(context));
}
@VisibleForTesting
- AppHibernationService(@NonNull Context context, IPackageManager packageManager,
- IActivityManager activityManager, UserManager userManager) {
- super(context);
- mContext = context;
- mIPackageManager = packageManager;
- mIActivityManager = activityManager;
- mUserManager = userManager;
+ AppHibernationService(@NonNull Injector injector) {
+ super(injector.getContext());
+ mContext = injector.getContext();
+ mIPackageManager = injector.getPackageManager();
+ mIActivityManager = injector.getActivityManager();
+ mUserManager = injector.getUserManager();
+ mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
+ mInjector = injector;
final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ACTION_USER_ADDED);
- intentFilter.addAction(ACTION_USER_REMOVED);
- userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
-
- intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_PACKAGE_ADDED);
intentFilter.addAction(ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
@@ -126,12 +133,10 @@
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_BOOT_COMPLETED) {
+ List<GlobalLevelState> states =
+ mGlobalLevelHibernationDiskStore.readHibernationStates();
synchronized (mLock) {
- final List<UserInfo> users = mUserManager.getUsers();
- // TODO: Pull from persistent disk storage. For now, just make from scratch.
- for (UserInfo user : users) {
- addUserPackageStatesL(user.id);
- }
+ initializeGlobalHibernationStates(states);
}
}
}
@@ -145,12 +150,14 @@
*/
boolean isHibernatingForUser(String packageName, int userId) {
userId = handleIncomingUser(userId, "isHibernating");
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user "
+ + userId);
+ return false;
+ }
synchronized (mLock) {
- final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
- if (packageStates == null) {
- throw new IllegalArgumentException("No user associated with user id " + userId);
- }
- final UserPackageState pkgState = packageStates.get(packageName);
+ final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+ final UserLevelState pkgState = packageStates.get(packageName);
if (pkgState == null) {
throw new IllegalArgumentException(
String.format("Package %s is not installed for user %s",
@@ -168,7 +175,12 @@
*/
boolean isHibernatingGlobally(String packageName) {
synchronized (mLock) {
- return mGloballyHibernatedPackages.contains(packageName);
+ GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+ if (state == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed", packageName));
+ }
+ return state.hibernated;
}
}
@@ -181,12 +193,14 @@
*/
void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
userId = handleIncomingUser(userId, "setHibernating");
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
+ + userId);
+ return;
+ }
synchronized (mLock) {
- if (!mUserStates.contains(userId)) {
- throw new IllegalArgumentException("No user associated with user id " + userId);
- }
- Map<String, UserPackageState> packageStates = mUserStates.get(userId);
- UserPackageState pkgState = packageStates.get(packageName);
+ final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+ final UserLevelState pkgState = packageStates.get(packageName);
if (pkgState == null) {
throw new IllegalArgumentException(
String.format("Package %s is not installed for user %s",
@@ -198,10 +212,12 @@
}
if (isHibernating) {
- hibernatePackageForUserL(packageName, userId, pkgState);
+ hibernatePackageForUser(packageName, userId, pkgState);
} else {
- unhibernatePackageForUserL(packageName, userId, pkgState);
+ unhibernatePackageForUser(packageName, userId, pkgState);
}
+ List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
+ mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
}
}
@@ -213,25 +229,32 @@
* @param isHibernating new hibernation state
*/
void setHibernatingGlobally(String packageName, boolean isHibernating) {
- if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) {
- synchronized (mLock) {
+ synchronized (mLock) {
+ GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+ if (state == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for any user", packageName));
+ }
+ if (state.hibernated != isHibernating) {
if (isHibernating) {
- hibernatePackageGloballyL(packageName);
+ hibernatePackageGlobally(packageName, state);
} else {
- unhibernatePackageGloballyL(packageName);
+ unhibernatePackageGlobally(packageName, state);
}
+ List<GlobalLevelState> states = new ArrayList<>(mGlobalHibernationStates.values());
+ mGlobalLevelHibernationDiskStore.scheduleWriteHibernationStates(states);
}
}
}
/**
* Put an app into hibernation for a given user, allowing user-level optimizations to occur.
- * The caller should hold {@link #mLock}
*
* @param pkgState package hibernation state
*/
- private void hibernatePackageForUserL(@NonNull String packageName, int userId,
- @NonNull UserPackageState pkgState) {
+ @GuardedBy("mLock")
+ private void hibernatePackageForUser(@NonNull String packageName, int userId,
+ @NonNull UserLevelState pkgState) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
final long caller = Binder.clearCallingIdentity();
try {
@@ -249,12 +272,13 @@
}
/**
- * Remove a package from hibernation for a given user. The caller should hold {@link #mLock}.
+ * Remove a package from hibernation for a given user.
*
* @param pkgState package hibernation state
*/
- private void unhibernatePackageForUserL(@NonNull String packageName, int userId,
- UserPackageState pkgState) {
+ @GuardedBy("mLock")
+ private void unhibernatePackageForUser(@NonNull String packageName, int userId,
+ UserLevelState pkgState) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
final long caller = Binder.clearCallingIdentity();
try {
@@ -271,60 +295,140 @@
/**
* Put a package into global hibernation, optimizing its storage at a package / APK level.
- * The caller should hold {@link #mLock}.
*/
- private void hibernatePackageGloballyL(@NonNull String packageName) {
+ @GuardedBy("mLock")
+ private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
// TODO(175830194): Delete vdex/odex when DexManager API is built out
- mGloballyHibernatedPackages.add(packageName);
+ state.hibernated = true;
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
/**
- * Unhibernate a package from global hibernation. The caller should hold {@link #mLock}.
+ * Unhibernate a package from global hibernation.
*/
- private void unhibernatePackageGloballyL(@NonNull String packageName) {
+ @GuardedBy("mLock")
+ private void unhibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
- mGloballyHibernatedPackages.remove(packageName);
+ state.hibernated = false;
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
/**
- * Populates {@link #mUserStates} with the users installed packages. The caller should hold
- * {@link #mLock}.
+ * Initializes in-memory store of user-level hibernation states for the given user
*
* @param userId user id to add installed packages for
+ * @param diskStates states pulled from disk, if available
*/
- private void addUserPackageStatesL(int userId) {
- Map<String, UserPackageState> packages = new ArrayMap<>();
- List<PackageInfo> packageList;
+ @GuardedBy("mLock")
+ private void initializeUserHibernationStates(int userId,
+ @Nullable List<UserLevelState> diskStates) {
+ List<PackageInfo> packages;
try {
- packageList = mIPackageManager.getInstalledPackages(MATCH_ALL, userId).getList();
+ packages = mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId).getList();
} catch (RemoteException e) {
- throw new IllegalStateException("Package manager not available.", e);
+ throw new IllegalStateException("Package manager not available", e);
}
- for (int i = 0, size = packageList.size(); i < size; i++) {
- packages.put(packageList.get(i).packageName, new UserPackageState());
+ Map<String, UserLevelState> userLevelStates = new ArrayMap<>();
+
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ String packageName = packages.get(i).packageName;
+ UserLevelState state = new UserLevelState();
+ state.packageName = packageName;
+ userLevelStates.put(packageName, state);
}
- mUserStates.put(userId, packages);
+
+ if (diskStates != null) {
+ Set<String> installedPackages = new ArraySet<>();
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ installedPackages.add(packages.get(i).packageName);
+ }
+ for (int i = 0, size = diskStates.size(); i < size; i++) {
+ String packageName = diskStates.get(i).packageName;
+ if (!installedPackages.contains(packageName)) {
+ Slog.w(TAG, String.format(
+ "No hibernation state associated with package %s user %d. Maybe"
+ + "the package was uninstalled? ", packageName, userId));
+ continue;
+ }
+ userLevelStates.put(packageName, diskStates.get(i));
+ }
+ }
+ mUserStates.put(userId, userLevelStates);
}
- private void onUserAdded(int userId) {
- synchronized (mLock) {
- addUserPackageStatesL(userId);
+ /**
+ * Initialize in-memory store of global level hibernation states.
+ *
+ * @param diskStates global level hibernation states pulled from disk, if available
+ */
+ @GuardedBy("mLock")
+ private void initializeGlobalHibernationStates(@Nullable List<GlobalLevelState> diskStates) {
+ List<PackageInfo> packages;
+ try {
+ packages = mIPackageManager.getInstalledPackages(
+ PACKAGE_MATCH_FLAGS | MATCH_ANY_USER, 0 /* userId */).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager not available", e);
+ }
+
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ String packageName = packages.get(i).packageName;
+ GlobalLevelState state = new GlobalLevelState();
+ state.packageName = packageName;
+ mGlobalHibernationStates.put(packageName, state);
+ }
+ if (diskStates != null) {
+ Set<String> installedPackages = new ArraySet<>();
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ installedPackages.add(packages.get(i).packageName);
+ }
+ for (int i = 0, size = diskStates.size(); i < size; i++) {
+ GlobalLevelState state = diskStates.get(i);
+ if (!installedPackages.contains(state.packageName)) {
+ Slog.w(TAG, String.format(
+ "No hibernation state associated with package %s. Maybe the "
+ + "package was uninstalled? ", state.packageName));
+ continue;
+ }
+ mGlobalHibernationStates.put(state.packageName, state);
+ }
}
}
- private void onUserRemoved(int userId) {
+ @Override
+ public void onUserUnlocking(@NonNull TargetUser user) {
+ int userId = user.getUserIdentifier();
+ HibernationStateDiskStore<UserLevelState> diskStore =
+ mInjector.getUserLevelDiskStore(userId);
+ mUserDiskStores.put(userId, diskStore);
+ List<UserLevelState> storedStates = diskStore.readHibernationStates();
synchronized (mLock) {
+ initializeUserHibernationStates(userId, storedStates);
+ }
+ }
+
+ @Override
+ public void onUserStopping(@NonNull TargetUser user) {
+ int userId = user.getUserIdentifier();
+ // TODO: Flush any scheduled writes to disk immediately on user stopping / power off.
+ synchronized (mLock) {
+ mUserDiskStores.remove(userId);
mUserStates.remove(userId);
}
}
private void onPackageAdded(@NonNull String packageName, int userId) {
synchronized (mLock) {
- mUserStates.get(userId).put(packageName, new UserPackageState());
+ UserLevelState userState = new UserLevelState();
+ userState.packageName = packageName;
+ mUserStates.get(userId).put(packageName, userState);
+ if (!mGlobalHibernationStates.containsKey(packageName)) {
+ GlobalLevelState globalState = new GlobalLevelState();
+ globalState.packageName = packageName;
+ mGlobalHibernationStates.put(packageName, globalState);
+ }
}
}
@@ -336,7 +440,7 @@
private void onPackageRemovedForAllUsers(@NonNull String packageName) {
synchronized (mLock) {
- mGloballyHibernatedPackages.remove(packageName);
+ mGlobalHibernationStates.remove(packageName);
}
}
@@ -395,7 +499,7 @@
}
}
- // Broadcast receiver for user and package add/removal events
+ // Broadcast receiver for package add/removal events
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -405,12 +509,6 @@
}
final String action = intent.getAction();
- if (ACTION_USER_ADDED.equals(action)) {
- onUserAdded(userId);
- }
- if (ACTION_USER_REMOVED.equals(action)) {
- onUserRemoved(userId);
- }
if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
final String packageName = intent.getData().getSchemeSpecificPart();
if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {
@@ -443,10 +541,66 @@
}
/**
- * Data class that contains hibernation state info of a package for a user.
+ * Dependency injector for {@link #AppHibernationService)}.
*/
- private static final class UserPackageState {
- public boolean hibernated;
- // TODO: Track whether hibernation is exempted by the user
+ interface Injector {
+ Context getContext();
+
+ IPackageManager getPackageManager();
+
+ IActivityManager getActivityManager();
+
+ UserManager getUserManager();
+
+ HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
+
+ HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
+ }
+
+ private static final class InjectorImpl implements Injector {
+ private static final String HIBERNATION_DIR_NAME = "hibernation";
+ private final Context mContext;
+ private final ScheduledExecutorService mScheduledExecutorService;
+ private final UserLevelHibernationProto mUserLevelHibernationProto;
+
+ InjectorImpl(Context context) {
+ mContext = context;
+ mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+ mUserLevelHibernationProto = new UserLevelHibernationProto();
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public IPackageManager getPackageManager() {
+ return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ }
+
+ @Override
+ public IActivityManager getActivityManager() {
+ return ActivityManager.getService();
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mContext.getSystemService(UserManager.class);
+ }
+
+ @Override
+ public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+ File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
+ return new HibernationStateDiskStore<>(
+ dir, new GlobalLevelHibernationProto(), mScheduledExecutorService);
+ }
+
+ @Override
+ public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+ File dir = new File(Environment.getDataSystemCeDirectory(userId), HIBERNATION_DIR_NAME);
+ return new HibernationStateDiskStore<>(
+ dir, mUserLevelHibernationProto, mScheduledExecutorService);
+ }
}
}
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
new file mode 100644
index 0000000..79e995b
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link GlobalLevelState} hiberation states.
+ */
+final class GlobalLevelHibernationProto implements ProtoReadWriter<List<GlobalLevelState>> {
+ private static final String TAG = "GlobalLevelHibernationProtoReadWriter";
+
+ @Override
+ public void writeToProto(@NonNull ProtoOutputStream stream,
+ @NonNull List<GlobalLevelState> data) {
+ for (int i = 0, size = data.size(); i < size; i++) {
+ long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+ GlobalLevelState state = data.get(i);
+ stream.write(GlobalLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+ stream.write(GlobalLevelHibernationStateProto.HIBERNATED, state.hibernated);
+ stream.end(token);
+ }
+ }
+
+ @Override
+ public @Nullable List<GlobalLevelState> readFromProto(@NonNull ProtoInputStream stream)
+ throws IOException {
+ List<GlobalLevelState> list = new ArrayList<>();
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (stream.getFieldNumber()
+ != (int) GlobalLevelHibernationStatesProto.HIBERNATION_STATE) {
+ continue;
+ }
+ GlobalLevelState state = new GlobalLevelState();
+ long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) GlobalLevelHibernationStateProto.PACKAGE_NAME:
+ state.packageName =
+ stream.readString(GlobalLevelHibernationStateProto.PACKAGE_NAME);
+ break;
+ case (int) GlobalLevelHibernationStateProto.HIBERNATED:
+ state.hibernated =
+ stream.readBoolean(GlobalLevelHibernationStateProto.HIBERNATED);
+ break;
+ default:
+ Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+ }
+ }
+ stream.end(token);
+ list.add(state);
+ }
+ return list;
+ }
+}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
similarity index 73%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/apphibernation/GlobalLevelState.java
index 19b20f2..4f75675 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
@@ -14,7 +14,12 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package com.android.server.apphibernation;
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Data class that contains global hibernation state for a package.
+ */
+final class GlobalLevelState {
+ public String packageName;
+ public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
new file mode 100644
index 0000000..c83659d
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.text.format.DateUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Disk store utility class for hibernation states.
+ *
+ * @param <T> the type of hibernation state data
+ */
+class HibernationStateDiskStore<T> {
+ private static final String TAG = "HibernationStateDiskStore";
+
+ // Time to wait before actually writing. Saves extra writes if data changes come in batches.
+ private static final long DISK_WRITE_DELAY = 1L * DateUtils.MINUTE_IN_MILLIS;
+ private static final String STATES_FILE_NAME = "states";
+
+ private final File mHibernationFile;
+ private final ScheduledExecutorService mExecutorService;
+ private final ProtoReadWriter<List<T>> mProtoReadWriter;
+ private List<T> mScheduledStatesToWrite = new ArrayList<>();
+ private ScheduledFuture<?> mFuture;
+
+ /**
+ * Initialize a disk store for hibernation states in the given directory.
+ *
+ * @param hibernationDir directory to write/read states file
+ * @param readWriter writer/reader of states proto
+ * @param executorService scheduled executor for writing data
+ */
+ HibernationStateDiskStore(@NonNull File hibernationDir,
+ @NonNull ProtoReadWriter<List<T>> readWriter,
+ @NonNull ScheduledExecutorService executorService) {
+ this(hibernationDir, readWriter, executorService, STATES_FILE_NAME);
+ }
+
+ @VisibleForTesting
+ HibernationStateDiskStore(@NonNull File hibernationDir,
+ @NonNull ProtoReadWriter<List<T>> readWriter,
+ @NonNull ScheduledExecutorService executorService,
+ @NonNull String fileName) {
+ mHibernationFile = new File(hibernationDir, fileName);
+ mExecutorService = executorService;
+ mProtoReadWriter = readWriter;
+ }
+
+ /**
+ * Schedule a full write of all the hibernation states to the file on disk. Does not run
+ * immediately and subsequent writes override previous ones.
+ *
+ * @param hibernationStates list of hibernation states to write to disk
+ */
+ void scheduleWriteHibernationStates(@NonNull List<T> hibernationStates) {
+ synchronized (this) {
+ mScheduledStatesToWrite = hibernationStates;
+ if (mExecutorService.isShutdown()) {
+ Slog.e(TAG, "Scheduled executor service is shut down.");
+ return;
+ }
+
+ // Already have write scheduled
+ if (mFuture != null) {
+ Slog.i(TAG, "Write already scheduled. Skipping schedule.");
+ return;
+ }
+
+ mFuture = mExecutorService.schedule(this::writeHibernationStates, DISK_WRITE_DELAY,
+ TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Read hibernation states from disk.
+ *
+ * @return the parsed list of hibernation states, null if file does not exist
+ */
+ @Nullable
+ List<T> readHibernationStates() {
+ synchronized (this) {
+ if (!mHibernationFile.exists()) {
+ Slog.i(TAG, "No hibernation file on disk for file " + mHibernationFile.getPath());
+ return null;
+ }
+ AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+ try {
+ FileInputStream inputStream = atomicFile.openRead();
+ ProtoInputStream protoInputStream = new ProtoInputStream(inputStream);
+ return mProtoReadWriter.readFromProto(protoInputStream);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read states protobuf.", e);
+ return null;
+ }
+ }
+ }
+
+ @WorkerThread
+ private void writeHibernationStates() {
+ synchronized (this) {
+ writeStateProto(mScheduledStatesToWrite);
+ mScheduledStatesToWrite.clear();
+ mFuture = null;
+ }
+ }
+
+ @WorkerThread
+ private void writeStateProto(List<T> states) {
+ AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+ FileOutputStream fileOutputStream;
+ try {
+ fileOutputStream = atomicFile.startWrite();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to start write to states protobuf.", e);
+ return;
+ }
+
+ try {
+ ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream);
+ mProtoReadWriter.writeToProto(protoOutputStream, states);
+ protoOutputStream.flush();
+ atomicFile.finishWrite(fileOutputStream);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to finish write to states protobuf.", e);
+ atomicFile.failWrite(fileOutputStream);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
new file mode 100644
index 0000000..0cbc09a
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+
+/**
+ * Proto utility that reads and writes proto for some data.
+ *
+ * @param <T> data that can be written and read from a proto
+ */
+interface ProtoReadWriter<T> {
+
+ /**
+ * Write data to a proto stream
+ */
+ void writeToProto(@NonNull ProtoOutputStream stream, @NonNull T data);
+
+ /**
+ * Parse data from the proto stream and return
+ */
+ @Nullable T readFromProto(@NonNull ProtoInputStream stream) throws IOException;
+}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
new file mode 100644
index 0000000..a24c4c5
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link UserLevelState} hiberation states.
+ */
+final class UserLevelHibernationProto implements ProtoReadWriter<List<UserLevelState>> {
+ private static final String TAG = "UserLevelHibernationProtoReadWriter";
+
+ @Override
+ public void writeToProto(@NonNull ProtoOutputStream stream,
+ @NonNull List<UserLevelState> data) {
+ for (int i = 0, size = data.size(); i < size; i++) {
+ long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+ UserLevelState state = data.get(i);
+ stream.write(UserLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+ stream.write(UserLevelHibernationStateProto.HIBERNATED, state.hibernated);
+ stream.end(token);
+ }
+ }
+
+ @Override
+ public @Nullable List<UserLevelState> readFromProto(@NonNull ProtoInputStream stream)
+ throws IOException {
+ List<UserLevelState> list = new ArrayList<>();
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (stream.getFieldNumber()
+ != (int) UserLevelHibernationStatesProto.HIBERNATION_STATE) {
+ continue;
+ }
+ UserLevelState state = new UserLevelState();
+ long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) UserLevelHibernationStateProto.PACKAGE_NAME:
+ state.packageName =
+ stream.readString(UserLevelHibernationStateProto.PACKAGE_NAME);
+ break;
+ case (int) UserLevelHibernationStateProto.HIBERNATED:
+ state.hibernated =
+ stream.readBoolean(UserLevelHibernationStateProto.HIBERNATED);
+ break;
+ default:
+ Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+ }
+ }
+ stream.end(token);
+ list.add(state);
+ }
+ return list;
+ }
+}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/apphibernation/UserLevelState.java
similarity index 73%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/apphibernation/UserLevelState.java
index 19b20f2..c66dad8 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -14,7 +14,12 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package com.android.server.apphibernation;
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/**
+ * Data class that contains hibernation state info of a package for a user.
+ */
+final class UserLevelState {
+ public String packageName;
+ public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index f07da8f..10fe1e1 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2796,6 +2796,8 @@
if (callback == null) {
return;
}
+ final boolean mayWatchPackageName =
+ packageName != null && !filterAppAccessUnlocked(packageName);
synchronized (this) {
int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
@@ -2824,7 +2826,7 @@
}
cbs.add(cb);
}
- if (packageName != null) {
+ if (mayWatchPackageName) {
ArraySet<ModeCallback> cbs = mPackageModeWatchers.get(packageName);
if (cbs == null) {
cbs = new ArraySet<>();
@@ -3008,13 +3010,27 @@
Objects.requireNonNull(packageName);
try {
verifyAndGetBypass(uid, packageName, null);
-
+ if (filterAppAccessUnlocked(packageName)) {
+ return AppOpsManager.MODE_ERRORED;
+ }
return AppOpsManager.MODE_ALLOWED;
} catch (SecurityException ignored) {
return AppOpsManager.MODE_ERRORED;
}
}
+ /**
+ * This method will check with PackageManager to determine if the package provided should
+ * be visible to the {@link Binder#getCallingUid()}.
+ *
+ * NOTE: This must not be called while synchronized on {@code this} to avoid dead locks
+ */
+ private boolean filterAppAccessUnlocked(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ return LocalServices.getService(PackageManagerInternal.class)
+ .filterAppAccess(packageName, callingUid, UserHandle.getUserId(callingUid));
+ }
+
@Override
public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
String proxiedAttributionTag, int proxyUid, String proxyPackageName,
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9aea7c4..f72fb1f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -765,10 +765,6 @@
return mAudioService.getVssVolumeForDevice(streamType, device);
}
- /*package*/ int getModeOwnerPid() {
- return mModeOwnerPid;
- }
-
/*package*/ int getDeviceForStream(int streamType) {
return mAudioService.getDeviceForStream(streamType);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d575963..6f625a7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -95,6 +95,7 @@
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMetrics;
+import android.media.MediaRecorder.AudioSource;
import android.media.PlayerBase;
import android.media.VolumePolicy;
import android.media.audiofx.AudioEffect;
@@ -161,9 +162,11 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -300,6 +303,10 @@
private static final int MSG_STREAM_DEVICES_CHANGED = 32;
private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
private static final int MSG_REINIT_VOLUMES = 34;
+ private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
+ private static final int MSG_UPDATE_AUDIO_MODE = 36;
+ private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
+
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -699,8 +706,9 @@
private long mLoweredFromNormalToVibrateTime;
// Array of Uids of valid accessibility services to check if caller is one of them
- private int[] mAccessibilityServiceUids;
private final Object mAccessibilityServiceUidsLock = new Object();
+ @GuardedBy("mAccessibilityServiceUidsLock")
+ private int[] mAccessibilityServiceUids;
// Uid of the active input method service to check if caller is the one or not.
private int mInputMethodServiceUid = android.os.Process.INVALID_UID;
@@ -939,6 +947,7 @@
mDeviceBroker = new AudioDeviceBroker(mContext, this);
mRecordMonitor = new RecordingActivityMonitor(mContext);
+ mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
// must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[]
// array initialized by updateStreamVolumeAlias()
@@ -948,6 +957,8 @@
mPlaybackMonitor =
new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
+ mPlaybackMonitor.registerPlaybackCallback(mVoicePlaybackActivityMonitor, true);
+
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
readAndSetLowRamDevice();
@@ -1076,7 +1087,6 @@
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
synchronized (mHdmiClientLock) {
- mHdmiCecSink = false;
mHdmiManager = mContext.getSystemService(HdmiControlManager.class);
if (mHdmiManager != null) {
mHdmiManager.addHdmiControlStatusChangeListener(
@@ -1197,12 +1207,8 @@
// Restore call state
synchronized (mDeviceBroker.mSetModeLock) {
- if (mAudioSystem.setPhoneState(mMode, getModeOwnerUid())
- == AudioSystem.AUDIO_STATUS_OK) {
- mModeLogger.log(new AudioEventLogger.StringEvent(
- "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
- + ", uid=" + getModeOwnerUid() + ")"));
- }
+ onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
+ mContext.getPackageName());
}
final int forSys;
synchronized (mSettingsLock) {
@@ -1508,7 +1514,8 @@
if (isPlatformTelevision()) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null && mHdmiPlaybackClient != null) {
- updateHdmiCecSinkLocked(mHdmiCecSink | false);
+ updateHdmiCecSinkLocked(
+ mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI));
}
}
}
@@ -1518,7 +1525,8 @@
if (isPlatformTelevision()) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
- updateHdmiCecSinkLocked(mHdmiCecSink | false);
+ updateHdmiCecSinkLocked(
+ mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI));
}
}
}
@@ -2310,11 +2318,9 @@
/** @see AudioManager#adjustVolume(int, int) */
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
- boolean hasModifyAudioSettings =
- mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED;
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
- caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ caller, Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+ VOL_ADJUST_NORMAL);
}
public void setFastScrollSoundEffectsEnabled(boolean enabled) {
@@ -2441,13 +2447,12 @@
+ "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
return;
}
- final boolean hasModifyAudioSettings =
- mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED;
+
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
direction/*val1*/, flags/*val2*/, callingPackage));
adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+ VOL_ADJUST_NORMAL);
}
protected void adjustStreamVolume(int streamType, int direction, int flags,
@@ -2670,8 +2675,7 @@
if (adjustVolume) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
- // mHdmiCecSink true => mHdmiPlaybackClient != null
- if (mHdmiCecSink
+ if (mHdmiPlaybackClient != null
&& mHdmiCecVolumeControlEnabled
&& streamTypeAlias == AudioSystem.STREAM_MUSIC
// vol change on a full volume device
@@ -2966,13 +2970,11 @@
+ " MODIFY_AUDIO_ROUTING callingPackage=" + callingPackage);
return;
}
- final boolean hasModifyAudioSettings =
- mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED;
+
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
index/*val1*/, flags/*val2*/, callingPackage));
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), hasModifyAudioSettings);
+ Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
}
private boolean canChangeAccessibilityVolume() {
@@ -2994,7 +2996,7 @@
}
/*package*/ int getHearingAidStreamType() {
- return getHearingAidStreamType(mMode);
+ return getHearingAidStreamType(getMode());
}
private int getHearingAidStreamType(int mode) {
@@ -3007,15 +3009,15 @@
// other conditions will influence the stream type choice, read on...
break;
}
- if (mVoiceActive.get()) {
+ if (mVoicePlaybackActive.get()) {
return AudioSystem.STREAM_VOICE_CALL;
}
return AudioSystem.STREAM_MUSIC;
}
- private AtomicBoolean mVoiceActive = new AtomicBoolean(false);
+ private AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false);
- private final IPlaybackConfigDispatcher mVoiceActivityMonitor =
+ private final IPlaybackConfigDispatcher mVoicePlaybackActivityMonitor =
new IPlaybackConfigDispatcher.Stub() {
@Override
public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
@@ -3037,16 +3039,126 @@
break;
}
}
- if (mVoiceActive.getAndSet(voiceActive) != voiceActive) {
+ if (mVoicePlaybackActive.getAndSet(voiceActive) != voiceActive) {
updateHearingAidVolumeOnVoiceActivityUpdate();
}
+
+ // Update playback active state for all apps in audio mode stack.
+ // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
+ // and request an audio mode update immediately. Upon any other change, queue the message
+ // and request an audio mode update after a grace period.
+ synchronized (mDeviceBroker.mSetModeLock) {
+ boolean updateAudioMode = false;
+ int existingMsgPolicy = SENDMSG_QUEUE;
+ int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ boolean wasActive = h.isActive();
+ h.setPlaybackActive(false);
+ for (AudioPlaybackConfiguration config : configs) {
+ final int usage = config.getAudioAttributes().getUsage();
+ if (config.getClientUid() == h.getUid()
+ && (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
+ || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
+ && config.getPlayerState()
+ == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ h.setPlaybackActive(true);
+ break;
+ }
+ }
+ if (wasActive != h.isActive()) {
+ updateAudioMode = true;
+ if (h.isActive() && h == getAudioModeOwnerHandler()) {
+ existingMsgPolicy = SENDMSG_REPLACE;
+ delay = 0;
+ }
+ }
+ }
+ if (updateAudioMode) {
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ existingMsgPolicy,
+ AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(),
+ mContext.getPackageName(),
+ delay);
+ }
+ }
+ }
+
+ private final IRecordingConfigDispatcher mVoiceRecordingActivityMonitor =
+ new IRecordingConfigDispatcher.Stub() {
+ @Override
+ public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+ sendMsg(mAudioHandler, MSG_RECORDING_CONFIG_CHANGE, SENDMSG_REPLACE,
+ 0 /*arg1 ignored*/, 0 /*arg2 ignored*/,
+ configs /*obj*/, 0 /*delay*/);
+ }
+ };
+
+ private void onRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+ // Update recording active state for all apps in audio mode stack.
+ // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
+ // and request an audio mode update immediately. Upon any other change, queue the message
+ // and request an audio mode update after a grace period.
+ synchronized (mDeviceBroker.mSetModeLock) {
+ boolean updateAudioMode = false;
+ int existingMsgPolicy = SENDMSG_QUEUE;
+ int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ boolean wasActive = h.isActive();
+ h.setRecordingActive(false);
+ for (AudioRecordingConfiguration config : configs) {
+ if (config.getClientUid() == h.getUid()
+ && config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) {
+ h.setRecordingActive(true);
+ break;
+ }
+ }
+ if (wasActive != h.isActive()) {
+ updateAudioMode = true;
+ if (h.isActive() && h == getAudioModeOwnerHandler()) {
+ existingMsgPolicy = SENDMSG_REPLACE;
+ delay = 0;
+ }
+ }
+ }
+ if (updateAudioMode) {
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ existingMsgPolicy,
+ AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(),
+ mContext.getPackageName(),
+ delay);
+ }
+ }
+ }
+
+ private void dumpAudioMode(PrintWriter pw) {
+ pw.println("\nAudio mode: ");
+ pw.println("- Current mode = " + AudioSystem.modeToString(getMode()));
+ pw.println("- Mode owner: ");
+ SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+ if (hdlr != null) {
+ hdlr.dump(pw, -1);
+ } else {
+ pw.println(" None");
+ }
+ pw.println("- Mode owner stack: ");
+ if (mSetModeDeathHandlers.isEmpty()) {
+ pw.println(" Empty");
+ } else {
+ for (int i = 0; i < mSetModeDeathHandlers.size(); i++) {
+ mSetModeDeathHandlers.get(i).dump(pw, i);
+ }
+ }
}
private void updateHearingAidVolumeOnVoiceActivityUpdate() {
final int streamType = getHearingAidStreamType();
final int index = getStreamVolume(streamType);
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
- mVoiceActive.get(), streamType, index));
+ mVoicePlaybackActive.get(), streamType, index));
mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
}
@@ -3639,8 +3751,7 @@
ensureValidStreamType(streamType);
final boolean isPrivileged =
Binder.getCallingUid() == Process.SYSTEM_UID
- || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED)
+ || callingHasAudioSettingsPermission()
|| (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
== PackageManager.PERMISSION_GRANTED);
return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
@@ -4064,65 +4175,46 @@
}
- /**
- * Return the pid of the current audio mode owner
- * @return 0 if nobody owns the mode
- */
- /*package*/ int getModeOwnerPid() {
- int modeOwnerPid = 0;
- try {
- modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
- } catch (Exception e) {
- // nothing to do, modeOwnerPid is not modified
- }
- return modeOwnerPid;
- }
-
- /**
- * Return the uid of the current audio mode owner
- * @return 0 if nobody owns the mode
- */
- /*package*/ int getModeOwnerUid() {
- int modeOwnerUid = 0;
- try {
- modeOwnerUid = mSetModeDeathHandlers.get(0).getUid();
- } catch (Exception e) {
- // nothing to do, modeOwnerUid is not modified
- }
- return modeOwnerUid;
- }
-
private class SetModeDeathHandler implements IBinder.DeathRecipient {
private final IBinder mCb; // To be notified of client's death
private final int mPid;
private final int mUid;
private final boolean mIsPrivileged;
private final String mPackage;
- private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
+ private int mMode;
+ private long mUpdateTime;
+ private boolean mPlaybackActive = false;
+ private boolean mRecordingActive = false;
- SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
+ SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged,
+ String caller, int mode) {
+ mMode = mode;
mCb = cb;
mPid = pid;
mUid = uid;
- mIsPrivileged = isPrivileged;
mPackage = caller;
+ mIsPrivileged = isPrivileged;
+ mUpdateTime = java.lang.System.currentTimeMillis();
}
public void binderDied() {
- int newModeOwnerPid = 0;
synchronized (mDeviceBroker.mSetModeLock) {
- Log.w(TAG, "setMode() client died");
+ Log.w(TAG, "SetModeDeathHandler client died");
int index = mSetModeDeathHandlers.indexOf(this);
if (index < 0) {
- Log.w(TAG, "unregistered setMode() client died");
+ Log.w(TAG, "unregistered SetModeDeathHandler client died");
} else {
- newModeOwnerPid = setModeInt(
- AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG);
+ SetModeDeathHandler h = mSetModeDeathHandlers.get(index);
+ mSetModeDeathHandlers.remove(index);
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ SENDMSG_QUEUE,
+ AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(),
+ mContext.getPackageName(),
+ 0);
}
}
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode());
}
public int getPid() {
@@ -4131,6 +4223,7 @@
public void setMode(int mode) {
mMode = mode;
+ mUpdateTime = java.lang.System.currentTimeMillis();
}
public int getMode() {
@@ -4152,25 +4245,131 @@
public boolean isPrivileged() {
return mIsPrivileged;
}
+
+ public long getUpdateTime() {
+ return mUpdateTime;
+ }
+
+ public void setPlaybackActive(boolean active) {
+ mPlaybackActive = active;
+ }
+
+ public void setRecordingActive(boolean active) {
+ mRecordingActive = active;
+ }
+
+ /**
+ * An app is considered active if:
+ * - It is privileged (has MODIFY_PHONE_STATE permission)
+ * or
+ * - It requests mode MODE_IN_COMMUNICATION, and it is either playing
+ * or recording for VOICE_COMMUNICATION.
+ * or
+ * - It requests a mode different from MODE_IN_COMMUNICATION or MODE_NORMAL
+ */
+ public boolean isActive() {
+ return mIsPrivileged
+ || ((mMode == AudioSystem.MODE_IN_COMMUNICATION)
+ && (mRecordingActive || mPlaybackActive))
+ || mMode == AudioSystem.MODE_IN_CALL
+ || mMode == AudioSystem.MODE_RINGTONE
+ || mMode == AudioSystem.MODE_CALL_SCREENING;
+ }
+
+ public void dump(PrintWriter pw, int index) {
+ SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+
+ if (index >= 0) {
+ pw.println(" Requester # " + (index + 1) + ":");
+ }
+ pw.println(" - Mode: " + AudioSystem.modeToString(mMode));
+ pw.println(" - Binder: " + mCb);
+ pw.println(" - Pid: " + mPid);
+ pw.println(" - Uid: " + mUid);
+ pw.println(" - Package: " + mPackage);
+ pw.println(" - Privileged: " + mIsPrivileged);
+ pw.println(" - Active: " + isActive());
+ pw.println(" Playback active: " + mPlaybackActive);
+ pw.println(" Recording active: " + mRecordingActive);
+ pw.println(" - update time: " + format.format(new Date(mUpdateTime)));
+ }
+ }
+
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ private SetModeDeathHandler getAudioModeOwnerHandler() {
+ // The Audio mode owner is:
+ // 1) the most recent privileged app in the stack
+ // 2) the most recent active app in the tack
+ SetModeDeathHandler modeOwner = null;
+ SetModeDeathHandler privilegedModeOwner = null;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ if (h.isActive()) {
+ // privileged apps are always active
+ if (h.isPrivileged()) {
+ if (privilegedModeOwner == null
+ || h.getUpdateTime() > privilegedModeOwner.getUpdateTime()) {
+ privilegedModeOwner = h;
+ }
+ } else {
+ if (modeOwner == null
+ || h.getUpdateTime() > modeOwner.getUpdateTime()) {
+ modeOwner = h;
+ }
+ }
+ }
+ }
+ return privilegedModeOwner != null ? privilegedModeOwner : modeOwner;
+ }
+
+ /**
+ * Return the pid of the current audio mode owner
+ * @return 0 if nobody owns the mode
+ */
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ /*package*/ int getModeOwnerPid() {
+ SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+ if (hdlr != null) {
+ return hdlr.getPid();
+ }
+ return 0;
+ }
+
+ /**
+ * Return the uid of the current audio mode owner
+ * @return 0 if nobody owns the mode
+ */
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ /*package*/ int getModeOwnerUid() {
+ SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+ if (hdlr != null) {
+ return hdlr.getUid();
+ }
+ return 0;
}
/** @see AudioManager#setMode(int) */
public void setMode(int mode, IBinder cb, String callingPackage) {
+ int pid = Binder.getCallingPid();
+ int uid = Binder.getCallingUid();
if (DEBUG_MODE) {
- Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
+ Log.v(TAG, "setMode(mode=" + mode + ", pid=" + pid
+ + ", uid=" + uid + ", caller=" + callingPackage + ")");
}
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
- final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
- == PackageManager.PERMISSION_GRANTED;
- final int callingPid = Binder.getCallingPid();
- if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
- Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
- + callingPid + ", uid=" + Binder.getCallingUid());
+ if (cb == null) {
+ Log.e(TAG, "setMode() called with null binder");
return;
}
+ if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
+ Log.w(TAG, "setMode() invalid mode: " + mode);
+ return;
+ }
+
+ if (mode == AudioSystem.MODE_CURRENT) {
+ mode = getMode();
+ }
if (mode == AudioSystem.MODE_CALL_SCREENING && !mIsCallScreeningModeSupported) {
Log.w(TAG, "setMode(MODE_CALL_SCREENING) not permitted "
@@ -4178,171 +4377,152 @@
return;
}
- if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
+ final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
+ if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
+ Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
+ + pid + ", uid=" + Binder.getCallingUid());
return;
}
- int newModeOwnerPid;
+ SetModeDeathHandler currentModeHandler = null;
synchronized (mDeviceBroker.mSetModeLock) {
- if (mode == AudioSystem.MODE_CURRENT) {
- mode = mMode;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ if (h.getPid() == pid) {
+ currentModeHandler = h;
+ break;
+ }
}
- int oldModeOwnerPid = getModeOwnerPid();
- // Do not allow changing mode if a call is active and the requester
- // does not have permission to modify phone state or is not the mode owner,
- // unless returning to NORMAL mode (will not change current mode owner) or
- // not changing mode in which case the mode owner will reflect the last
- // requester of current mode
- if (!((mode == mMode) || (mode == AudioSystem.MODE_NORMAL))
- && ((mMode == AudioSystem.MODE_IN_CALL)
- || (mMode == AudioSystem.MODE_IN_COMMUNICATION))
- && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) {
- Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid
- + ", uid=" + Binder.getCallingUid()
- + ", cannot change mode from " + mMode
- + " without permission or being mode owner");
- return;
+
+ if (mode == AudioSystem.MODE_NORMAL) {
+ if (currentModeHandler != null) {
+ if (!currentModeHandler.isPrivileged()
+ && currentModeHandler.getMode() == AudioSystem.MODE_IN_COMMUNICATION) {
+ mAudioHandler.removeEqualMessages(
+ MSG_CHECK_MODE_FOR_UID, currentModeHandler);
+ }
+ mSetModeDeathHandlers.remove(currentModeHandler);
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(" + mode + ") removing hldr for pid: " + pid);
+ }
+ try {
+ currentModeHandler.getBinder().unlinkToDeath(currentModeHandler, 0);
+ } catch (NoSuchElementException e) {
+ Log.w(TAG, "setMode link does not exist ...");
+ }
+ }
+ } else {
+ if (currentModeHandler != null) {
+ currentModeHandler.setMode(mode);
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(" + mode + ") updating hldr for pid: " + pid);
+ }
+ } else {
+ currentModeHandler = new SetModeDeathHandler(cb, pid, uid,
+ hasModifyPhoneStatePermission, callingPackage, mode);
+ // Register for client death notification
+ try {
+ cb.linkToDeath(currentModeHandler, 0);
+ } catch (RemoteException e) {
+ // Client has died!
+ Log.w(TAG, "setMode() could not link to " + cb + " binder death");
+ return;
+ }
+ mSetModeDeathHandlers.add(currentModeHandler);
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(" + mode + ") adding handler for pid=" + pid);
+ }
+ }
+ if (mode == AudioSystem.MODE_IN_COMMUNICATION) {
+ // Force active state when entering/updating the stack to avoid glitches when
+ // an app starts playing/recording after settng the audio mode,
+ // and send a reminder to check activity after a grace period.
+ if (!currentModeHandler.isPrivileged()) {
+ currentModeHandler.setPlaybackActive(true);
+ currentModeHandler.setRecordingActive(true);
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MODE_FOR_UID,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ currentModeHandler,
+ CHECK_MODE_FOR_UID_PERIOD_MS);
+ }
+ }
}
- newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(),
- hasModifyPhoneStatePermission, callingPackage);
+
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ SENDMSG_REPLACE,
+ mode,
+ pid,
+ callingPackage,
+ 0);
}
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode());
}
- // setModeInt() returns a valid PID if the audio mode was successfully set to
- // any mode other than NORMAL.
@GuardedBy("mDeviceBroker.mSetModeLock")
- private int setModeInt(
- int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
+ void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage) {
+ if (requestedMode == AudioSystem.MODE_CURRENT) {
+ requestedMode = getMode();
+ }
+ int mode = AudioSystem.MODE_NORMAL;
+ int uid = 0;
+ int pid = 0;
+ SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler();
+ if (currentModeHandler != null) {
+ mode = currentModeHandler.getMode();
+ uid = currentModeHandler.getUid();
+ pid = currentModeHandler.getPid();
+ }
if (DEBUG_MODE) {
- Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid
- + ", uid=" + uid + ", caller=" + caller + ")");
+ Log.v(TAG, "onUpdateAudioMode() mode: " + mode + ", mMode: " + mMode
+ + " requestedMode: " + requestedMode);
}
- int newModeOwnerPid = 0;
- if (cb == null) {
- Log.e(TAG, "setModeInt() called with null binder");
- return newModeOwnerPid;
- }
+ if (mode != mMode) {
+ final long identity = Binder.clearCallingIdentity();
+ int status = mAudioSystem.setPhoneState(mode, uid);
+ Binder.restoreCallingIdentity(identity);
+ if (status == AudioSystem.AUDIO_STATUS_OK) {
+ if (DEBUG_MODE) {
+ Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode);
+ }
+ int previousMode = mMode;
+ mMode = mode;
+ // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
+ mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid,
+ requestedMode, pid, mode));
- SetModeDeathHandler hdlr = null;
- Iterator iter = mSetModeDeathHandlers.iterator();
- while (iter.hasNext()) {
- SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
- if (h.getPid() == pid) {
- hdlr = h;
- // Remove from client list so that it is re-inserted at top of list
- iter.remove();
- if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) {
- mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr);
- }
- try {
- hdlr.getBinder().unlinkToDeath(hdlr, 0);
- if (cb != hdlr.getBinder()){
- hdlr = null;
- }
- } catch (NoSuchElementException e) {
- hdlr = null;
- Log.w(TAG, "link does not exist ...");
- }
- break;
- }
- }
- final int oldMode = mMode;
- int status = AudioSystem.AUDIO_STATUS_OK;
- int actualMode;
- do {
- actualMode = mode;
- if (mode == AudioSystem.MODE_NORMAL) {
- // get new mode from client at top the list if any
- if (!mSetModeDeathHandlers.isEmpty()) {
- hdlr = mSetModeDeathHandlers.get(0);
- cb = hdlr.getBinder();
- actualMode = hdlr.getMode();
- if (DEBUG_MODE) {
- Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid="
- + hdlr.mPid);
- }
- }
+ int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
+ int device = getDeviceForStream(streamType);
+ int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
+ setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true,
+ requesterPackage, true /*hasModifyAudioSettings*/);
+
+ updateStreamVolumeAlias(true /*updateVolumes*/, requesterPackage);
+
+ // change of mode may require volume to be re-applied on some devices
+ updateAbsVolumeMultiModeDevices(previousMode, mode);
+
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
+ // connections not started by the application changing the mode when pid changes
+ mDeviceBroker.postSetModeOwnerPid(pid, mode);
} else {
- if (hdlr == null) {
- hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller);
- }
- // Register for client death notification
- try {
- cb.linkToDeath(hdlr, 0);
- } catch (RemoteException e) {
- // Client has died!
- Log.w(TAG, "setMode() could not link to "+cb+" binder death");
- }
-
- // Last client to call setMode() is always at top of client list
- // as required by SetModeDeathHandler.binderDied()
- mSetModeDeathHandlers.add(0, hdlr);
- hdlr.setMode(mode);
- }
-
- if (actualMode != mMode) {
- final long identity = Binder.clearCallingIdentity();
- status = mAudioSystem.setPhoneState(actualMode, getModeOwnerUid());
- Binder.restoreCallingIdentity(identity);
- if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
- mMode = actualMode;
- } else {
- if (hdlr != null) {
- mSetModeDeathHandlers.remove(hdlr);
- cb.unlinkToDeath(hdlr, 0);
- }
- // force reading new top of mSetModeDeathHandlers stack
- if (DEBUG_MODE) { Log.w(TAG, " mode set to MODE_NORMAL after phoneState pb"); }
- mode = AudioSystem.MODE_NORMAL;
- }
- } else {
- status = AudioSystem.AUDIO_STATUS_OK;
- }
- } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
-
- if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (actualMode != AudioSystem.MODE_NORMAL) {
- newModeOwnerPid = getModeOwnerPid();
- if (newModeOwnerPid == 0) {
- Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
- }
- }
- // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
- mModeLogger.log(
- new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode));
-
- int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
- int device = getDeviceForStream(streamType);
- int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
- setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, caller,
- true /*hasModifyAudioSettings*/);
-
- updateStreamVolumeAlias(true /*updateVolumes*/, caller);
-
- // change of mode may require volume to be re-applied on some devices
- updateAbsVolumeMultiModeDevices(oldMode, actualMode);
-
- if (actualMode == AudioSystem.MODE_IN_COMMUNICATION
- && !hdlr.isPrivileged()) {
- sendMsg(mAudioHandler,
- MSG_CHECK_MODE_FOR_UID,
- SENDMSG_QUEUE,
- 0,
- 0,
- hdlr,
- CHECK_MODE_FOR_UID_PERIOD_MS);
+ Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode);
}
}
- return newModeOwnerPid;
}
/** @see AudioManager#getMode() */
public int getMode() {
- return mMode;
+ synchronized (mDeviceBroker.mSetModeLock) {
+ SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler();
+ if (currentModeHandler != null) {
+ return currentModeHandler.getMode();
+ }
+ return AudioSystem.MODE_NORMAL;
+ }
}
/** cached value read from audiopolicy manager after initialization. */
@@ -4383,13 +4563,10 @@
throw new SecurityException("Should only be called from system process");
}
- final boolean hasModifyAudioSettings =
- mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
// direction and stream type swap here because the public
// adjustSuggested has a different order than the other methods.
adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, uid,
- hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
}
/** @see AudioManager#adjustStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4407,11 +4584,9 @@
new StringBuilder(packageName).append(" uid:").append(uid)
.toString()));
}
- final boolean hasModifyAudioSettings =
- mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
+
adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
- hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
}
/** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4423,11 +4598,8 @@
throw new SecurityException("Should only be called from system process");
}
- final boolean hasModifyAudioSettings =
- mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
setStreamVolume(streamType, index, flags, packageName, packageName, uid,
- hasModifyAudioSettings);
+ hasAudioSettingsPermission(uid, pid));
}
//==========================================================================================
@@ -5393,8 +5565,7 @@
}
boolean checkAudioSettingsPermission(String method) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED) {
+ if (callingOrSelfHasAudioSettingsPermission()) {
return true;
}
String msg = "Audio Settings Permission Denial: " + method + " from pid="
@@ -5404,6 +5575,21 @@
return false;
}
+ private boolean callingOrSelfHasAudioSettingsPermission() {
+ return mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean callingHasAudioSettingsPermission() {
+ return mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean hasAudioSettingsPermission(int uid, int pid) {
+ return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
/**
* Minimum attenuation that can be set for alarms over speaker by an application that
* doesn't have the MODIFY_AUDIO_SETTINGS permission.
@@ -5683,11 +5869,6 @@
throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
+ " (dis)connection, got " + state);
}
- if (state == BluetoothProfile.STATE_CONNECTED) {
- mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true);
- } else {
- mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor);
- }
mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
device, state, suppressNoisyIntent, musicDevice, "AudioService");
}
@@ -7032,6 +7213,9 @@
case MSG_PLAYBACK_CONFIG_CHANGE:
onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj);
break;
+ case MSG_RECORDING_CONFIG_CHANGE:
+ onRecordingConfigChange((List<AudioRecordingConfiguration>) msg.obj);
+ break;
case MSG_BROADCAST_MICROPHONE_MUTE:
mSystemServer.sendMicrophoneMuteChangedIntent();
@@ -7042,30 +7226,19 @@
if (msg.obj == null) {
break;
}
- // If no other app is currently owning the audio mode and
- // the app corresponding to this mode death handler object is still in the
- // mode owner stack but not capturing or playing audio after 3 seconds,
- // remove it from the stack.
- // Otherwise, check again in 3 seconds.
+ // Update active playback/recording for apps requesting IN_COMMUNICATION
+ // mode after a grace period following the mode change
SetModeDeathHandler h = (SetModeDeathHandler) msg.obj;
if (mSetModeDeathHandlers.indexOf(h) < 0) {
break;
}
- if (getModeOwnerUid() != h.getUid()
- || mRecordMonitor.isRecordingActiveForUid(h.getUid())
- || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) {
- sendMsg(mAudioHandler,
- MSG_CHECK_MODE_FOR_UID,
- SENDMSG_QUEUE,
- 0,
- 0,
- h,
- CHECK_MODE_FOR_UID_PERIOD_MS);
- break;
+ boolean wasActive = h.isActive();
+ h.setPlaybackActive(mPlaybackMonitor.isPlaybackActiveForUid(h.getUid()));
+ h.setRecordingActive(mRecordMonitor.isRecordingActiveForUid(h.getUid()));
+ if (wasActive != h.isActive()) {
+ onUpdateAudioMode(AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(), mContext.getPackageName());
}
- setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(),
- h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID");
- mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid()));
}
break;
@@ -7082,6 +7255,16 @@
case MSG_REINIT_VOLUMES:
onReinitVolumes((String) msg.obj);
break;
+
+ case MSG_UPDATE_A11Y_SERVICE_UIDS:
+ onUpdateAccessibilityServiceUids();
+ break;
+
+ case MSG_UPDATE_AUDIO_MODE:
+ synchronized (mDeviceBroker.mSetModeLock) {
+ onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj);
+ }
+ break;
}
}
}
@@ -7825,9 +8008,8 @@
@GuardedBy("mHdmiClientLock")
private void updateHdmiCecSinkLocked(boolean hdmiCecSink) {
- mHdmiCecSink = hdmiCecSink;
if (!hasDeviceVolumeBehavior(AudioSystem.DEVICE_OUT_HDMI)) {
- if (mHdmiCecSink) {
+ if (hdmiCecSink) {
if (DEBUG_VOL) {
Log.d(TAG, "CEC sink: setting HDMI as full vol device");
}
@@ -7885,9 +8067,6 @@
// Set only when device is a set-top box.
@GuardedBy("mHdmiClientLock")
private HdmiPlaybackClient mHdmiPlaybackClient;
- // true if we are a set-top box, an HDMI sink is connected and it supports CEC.
- @GuardedBy("mHdmiClientLock")
- private boolean mHdmiCecSink;
// Set only when device is an audio system.
@GuardedBy("mHdmiClientLock")
private HdmiAudioSystemClient mHdmiAudioSystemClient;
@@ -8116,6 +8295,7 @@
dumpStreamStates(pw);
dumpVolumeGroups(pw);
dumpRingerMode(pw);
+ dumpAudioMode(pw);
pw.println("\nAudio routes:");
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(
mDeviceBroker.getCurAudioRoutes().mainType));
@@ -8142,7 +8322,6 @@
pw.print(" mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
pw.print(" mFullVolumeDevices="); pw.println(dumpDeviceTypes(mFullVolumeDevices));
pw.print(" mExtVolumeController="); pw.println(mExtVolumeController);
- pw.print(" mHdmiCecSink="); pw.println(mHdmiCecSink);
pw.print(" mHdmiAudioSystemClient="); pw.println(mHdmiAudioSystemClient);
pw.print(" mHdmiPlaybackClient="); pw.println(mHdmiPlaybackClient);
pw.print(" mHdmiTvClient="); pw.println(mHdmiTvClient);
@@ -8153,6 +8332,9 @@
+ " FromRestrictions=" + mMicMuteFromRestrictions
+ " FromApi=" + mMicMuteFromApi
+ " from system=" + mMicMuteFromSystemCached);
+ pw.print("\n mAssistantUid="); pw.println(mAssistantUid);
+ pw.print(" mCurrentImeUid="); pw.println(mCurrentImeUid);
+ dumpAccessibilityServiceUids(pw);
dumpAudioPolicies(pw);
mDynPolicyLogger.dump(pw);
@@ -8186,6 +8368,19 @@
}
}
+ private void dumpAccessibilityServiceUids(PrintWriter pw) {
+ synchronized (mSupportedSystemUsagesLock) {
+ if (mAccessibilityServiceUids != null && mAccessibilityServiceUids.length > 0) {
+ pw.println(" Accessibility service Uids:");
+ for (int uid : mAccessibilityServiceUids) {
+ pw.println(" - " + uid);
+ }
+ } else {
+ pw.println(" No accessibility service Uids.");
+ }
+ }
+ }
+
/**
* Audio Analytics ids.
*/
@@ -8489,7 +8684,8 @@
mAccessibilityServiceUids = uids.toArray();
}
}
- AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
+ sendMsg(mAudioHandler, MSG_UPDATE_A11Y_SERVICE_UIDS, SENDMSG_REPLACE,
+ 0, 0, null, 0);
}
}
@@ -8507,6 +8703,14 @@
}
}
+ private void onUpdateAccessibilityServiceUids() {
+ int[] accessibilityServiceUids;
+ synchronized (mAccessibilityServiceUidsLock) {
+ accessibilityServiceUids = mAccessibilityServiceUids;
+ }
+ AudioSystem.setA11yServicesUids(accessibilityServiceUids);
+ }
+
//==========================================================================================
// Audio policy management
//==========================================================================================
@@ -9062,6 +9266,18 @@
}
/**
+ * Update player session ID
+ * @param piid Player id to update
+ * @param sessionId The new audio session ID
+ */
+ public void playerSessionId(int piid, int sessionId) {
+ if (sessionId <= AudioSystem.AUDIO_SESSION_ALLOCATE) {
+ throw new IllegalArgumentException("invalid session Id " + sessionId);
+ }
+ mPlaybackMonitor.playerSessionId(piid, sessionId, Binder.getCallingUid());
+ }
+
+ /**
* Update player event
* @param piid Player id to update
* @param event The new player event
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 36c67cd..68a084e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -197,6 +197,28 @@
}
}
+ /**
+ * Update player session ID
+ * @param piid Player id to update
+ * @param sessionId The new audio session ID
+ * @param binderUid Calling binder uid
+ */
+ public void playerSessionId(int piid, int sessionId, int binderUid) {
+ final boolean change;
+ synchronized (mPlayerLock) {
+ final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
+ if (checkConfigurationCaller(piid, apc, binderUid)) {
+ change = apc.handleSessionIdEvent(sessionId);
+ } else {
+ Log.e(TAG, "Error updating audio session");
+ change = false;
+ }
+ }
+ if (change) {
+ dispatchPlaybackChange(false);
+ }
+ }
+
private static final int FLAGS_FOR_SILENCE_OVERRIDE =
AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
AudioAttributes.FLAG_BYPASS_MUTE;
@@ -921,6 +943,7 @@
private final int mClientUid;
private final int mClientPid;
private final AudioAttributes mPlayerAttr;
+ private final int mSessionId;
NewPlayerEvent(AudioPlaybackConfiguration apc) {
mPlayerIId = apc.getPlayerInterfaceId();
@@ -928,6 +951,7 @@
mClientUid = apc.getClientUid();
mClientPid = apc.getClientPid();
mPlayerAttr = apc.getAudioAttributes();
+ mSessionId = apc.getSessionId();
}
@Override
@@ -935,7 +959,8 @@
return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/"
+ mClientPid + " type:"
+ AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
- + " attr:" + mPlayerAttr);
+ + " attr:" + mPlayerAttr
+ + " session:" + mSessionId);
}
}
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 6905b3d..6851d71 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -90,6 +90,7 @@
int userId, PromptInfo promptInfo, String opPackageName,
boolean checkDevicePolicyManager)
throws RemoteException {
+
final boolean confirmationRequested = promptInfo.isConfirmationRequested();
final boolean biometricRequested = Utils.isBiometricRequested(promptInfo);
final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo);
@@ -111,7 +112,7 @@
@AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
devicePolicyManager, settingObserver, sensor, userId, opPackageName,
- checkDevicePolicyManager, requestedStrength);
+ checkDevicePolicyManager, requestedStrength, promptInfo.getSensorId());
Slog.d(TAG, "Package: " + opPackageName
+ " Sensor ID: " + sensor.id
@@ -141,7 +142,11 @@
DevicePolicyManager devicePolicyManager,
BiometricService.SettingObserver settingObserver,
BiometricSensor sensor, int userId, String opPackageName,
- boolean checkDevicePolicyManager, int requestedStrength) {
+ boolean checkDevicePolicyManager, int requestedStrength, int requestedSensorId) {
+
+ if (requestedSensorId != BiometricManager.SENSOR_ID_ANY && sensor.id != requestedSensorId) {
+ return BIOMETRIC_NO_HARDWARE;
+ }
final boolean wasStrongEnough =
Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength);
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index d87af42..5cd0bbf 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -399,10 +399,15 @@
}
}
- public static boolean isKeyguard(Context context, String clientPackage) {
- final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
- == PackageManager.PERMISSION_GRANTED;
-
+ /**
+ * Checks if a client package matches Keyguard and can perform internal biometric operations.
+ *
+ * @param context The system context.
+ * @param clientPackage The name of the package to be checked against Keyguard.
+ * @return Whether the given package matches Keyguard.
+ */
+ public static boolean isKeyguard(@NonNull Context context, @Nullable String clientPackage) {
+ final boolean hasPermission = hasInternalPermission(context);
final ComponentName keyguardComponent = ComponentName.unflattenFromString(
context.getResources().getString(R.string.config_keyguardComponent));
final String keyguardPackage = keyguardComponent != null
@@ -410,6 +415,34 @@
return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
}
+ /**
+ * Checks if a client package matches the Android system and can perform internal biometric
+ * operations.
+ *
+ * @param context The system context.
+ * @param clientPackage The name of the package to be checked against the Android system.
+ * @return Whether the given package matches the Android system.
+ */
+ public static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) {
+ return hasInternalPermission(context) && "android".equals(clientPackage);
+ }
+
+ /**
+ * Checks if a client package matches Settings and can perform internal biometric operations.
+ *
+ * @param context The system context.
+ * @param clientPackage The name of the package to be checked against Settings.
+ * @return Whether the given package matches Settings.
+ */
+ public static boolean isSettings(@NonNull Context context, @Nullable String clientPackage) {
+ return hasInternalPermission(context) && "com.android.settings".equals(clientPackage);
+ }
+
+ private static boolean hasInternalPermission(@NonNull Context context) {
+ return context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
public static String getClientName(@Nullable BaseClientMonitor client) {
return client != null ? client.getClass().getSimpleName() : "null";
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 14433fb..0536e78 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -149,9 +149,10 @@
pm.incrementAuthForUser(getTargetUserId(), authenticated);
}
- // Ensure authentication only succeeds if the client activity is on top or is keyguard.
+ // Ensure authentication only succeeds if the client activity is on top.
boolean isBackgroundAuth = false;
- if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) {
+ if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())
+ && !Utils.isSystem(getContext(), getOwnerString())) {
final List<ActivityManager.RunningTaskInfo> tasks =
mActivityTaskManager.getTasks(1);
if (tasks == null || tasks.isEmpty()) {
@@ -166,7 +167,7 @@
final String topPackage = topActivity.getPackageName();
if (!topPackage.contentEquals(getOwnerString())) {
Slog.e(TAG, "Background authentication detected, top: " + topPackage
- + ", client: " + this);
+ + ", client: " + getOwnerString());
isBackgroundAuth = true;
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
new file mode 100644
index 0000000..769c47a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.face.AuthenticationFrame;
+import android.hardware.biometrics.face.BaseFrame;
+import android.hardware.biometrics.face.Cell;
+import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceDataFrame;
+import android.hardware.face.FaceEnrollCell;
+import android.hardware.face.FaceEnrollFrame;
+
+/**
+ * Utilities for converting between hardware and framework-defined AIDL models.
+ */
+final class AidlConversionUtils {
+ // Prevent instantiation.
+ private AidlConversionUtils() {}
+
+ @NonNull
+ public static FaceAuthenticationFrame convert(@NonNull AuthenticationFrame frame) {
+ return new FaceAuthenticationFrame(convert(frame.data));
+ }
+
+ @NonNull
+ public static AuthenticationFrame convert(@NonNull FaceAuthenticationFrame frame) {
+ final AuthenticationFrame convertedFrame = new AuthenticationFrame();
+ convertedFrame.data = convert(frame.getData());
+ return convertedFrame;
+ }
+
+ @NonNull
+ public static FaceEnrollFrame convert(@NonNull EnrollmentFrame frame) {
+ return new FaceEnrollFrame(convert(frame.cell), frame.stage, convert(frame.data));
+ }
+
+ @NonNull
+ public static EnrollmentFrame convert(@NonNull FaceEnrollFrame frame) {
+ final EnrollmentFrame convertedFrame = new EnrollmentFrame();
+ convertedFrame.cell = convert(frame.getCell());
+ convertedFrame.stage = (byte) frame.getStage();
+ convertedFrame.data = convert(frame.getData());
+ return convertedFrame;
+ }
+
+ @NonNull
+ public static FaceDataFrame convert(@NonNull BaseFrame frame) {
+ return new FaceDataFrame(
+ frame.acquiredInfo,
+ frame.vendorCode,
+ frame.pan,
+ frame.tilt,
+ frame.distance,
+ frame.isCancellable);
+ }
+
+ @NonNull
+ public static BaseFrame convert(@NonNull FaceDataFrame frame) {
+ final BaseFrame convertedFrame = new BaseFrame();
+ convertedFrame.acquiredInfo = (byte) frame.getAcquiredInfo();
+ convertedFrame.vendorCode = frame.getVendorCode();
+ convertedFrame.pan = frame.getPan();
+ convertedFrame.tilt = frame.getTilt();
+ convertedFrame.distance = frame.getDistance();
+ convertedFrame.isCancellable = frame.isCancellable();
+ return convertedFrame;
+ }
+
+ @Nullable
+ public static FaceEnrollCell convert(@Nullable Cell cell) {
+ return cell == null ? null : new FaceEnrollCell(cell.x, cell.y, cell.z);
+ }
+
+ @Nullable
+ public static Cell convert(@Nullable FaceEnrollCell cell) {
+ if (cell == null) {
+ return null;
+ }
+
+ final Cell convertedCell = new Cell();
+ convertedCell.x = cell.getX();
+ convertedCell.y = cell.getY();
+ convertedCell.z = cell.getZ();
+ return convertedCell;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index a7bfc4b..3057766 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -28,6 +28,8 @@
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceDataFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
@@ -193,6 +195,17 @@
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
+ /**
+ * Called each time a new frame is received during face authentication.
+ *
+ * @param frame Information about the current frame.
+ */
+ public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) {
+ // TODO(b/178414967): Send additional frame data to the client callback.
+ final FaceDataFrame data = frame.getData();
+ onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+ }
+
@Override public void onLockoutTimed(long durationMillis) {
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
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 afc7f64..da657b9 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
@@ -27,6 +27,8 @@
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
+import android.hardware.face.FaceDataFrame;
+import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.NativeHandle;
@@ -110,6 +112,17 @@
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
+ /**
+ * Called each time a new frame is received during face enrollment.
+ *
+ * @param frame Information about the current frame.
+ */
+ public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) {
+ // TODO(b/178414967): Send additional frame data to the client callback.
+ final FaceDataFrame data = frame.getData();
+ onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+ }
+
@Override
protected void startHalOperation() {
final ArrayList<Byte> token = new ArrayList<>();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index f49601a..640838c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -45,7 +45,6 @@
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -170,33 +169,39 @@
@Override
public void onAuthenticationFrame(AuthenticationFrame frame) {
- // TODO(b/174619156): propagate the frame to an AuthenticationClient
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(mTag, "onAcquired for non-acquisition client: "
+ if (!(client instanceof FaceAuthenticationClient)) {
+ Slog.e(mTag, "onAuthenticationFrame for incompatible client: "
+ + Utils.getClientName(client));
+ return;
+
+ }
+ if (frame == null) {
+ Slog.e(mTag, "Received null authentication frame for client: "
+ Utils.getClientName(client));
return;
}
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+ ((FaceAuthenticationClient) client).onAuthenticationFrame(
+ AidlConversionUtils.convert(frame));
});
}
@Override
public void onEnrollmentFrame(EnrollmentFrame frame) {
- // TODO(b/174619156): propagate the frame to an EnrollmentClient
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(mTag, "onAcquired for non-acquisition client: "
+ if (!(client instanceof FaceEnrollClient)) {
+ Slog.e(mTag, "onEnrollmentFrame for incompatible client: "
+ Utils.getClientName(client));
return;
}
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+ if (frame == null) {
+ Slog.e(mTag, "Received null enrollment frame for client: "
+ + Utils.getClientName(client));
+ return;
+ }
+ ((FaceEnrollClient) client).onEnrollmentFrame(AidlConversionUtils.convert(frame));
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 0265cb9..b0e42cd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -25,6 +25,10 @@
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,6 +36,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
@@ -49,6 +54,7 @@
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
import android.os.Build;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -80,6 +86,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -190,7 +197,7 @@
@Override // Binder call
public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId,
final IFingerprintServiceReceiver receiver, final String opPackageName,
- boolean shouldLogMetrics) {
+ @FingerprintManager.EnrollReason int enrollReason) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -200,7 +207,7 @@
}
provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
- receiver, opPackageName, shouldLogMetrics);
+ receiver, opPackageName, enrollReason);
}
@Override // Binder call
@@ -219,8 +226,8 @@
@SuppressWarnings("deprecation")
@Override // Binder call
public void authenticate(final IBinder token, final long operationId,
- @FingerprintManager.SensorId final int sensorId, final int userId,
- final IFingerprintServiceReceiver receiver, final String opPackageName) {
+ final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
+ final String opPackageName) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -236,7 +243,7 @@
final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
// Clear calling identity when checking LockPatternUtils for StrongAuth flags.
- final long identity = Binder.clearCallingIdentity();
+ long identity = Binder.clearCallingIdentity();
try {
if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
// If this happens, something in KeyguardUpdateMonitor is wrong.
@@ -266,9 +273,101 @@
return;
}
- provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
- 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
- restricted, statsClient, isKeyguard);
+ final FingerprintSensorPropertiesInternal sensorProps =
+ provider.second.getSensorProperties(sensorId);
+ if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
+ && sensorProps != null && sensorProps.isAnyUdfpsType()) {
+ identity = Binder.clearCallingIdentity();
+ try {
+ authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else {
+ provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+ 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
+ restricted, statsClient, isKeyguard);
+ }
+ }
+
+ private void authenticateWithPrompt(
+ final long operationId,
+ @NonNull final FingerprintSensorPropertiesInternal props,
+ final int userId,
+ final IFingerprintServiceReceiver receiver) {
+
+ final Context context = getUiContext();
+ final Executor executor = context.getMainExecutor();
+
+ final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context)
+ .setTitle(context.getString(R.string.biometric_dialog_default_title))
+ .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle))
+ .setNegativeButton(
+ context.getString(R.string.cancel),
+ executor,
+ (dialog, which) -> {
+ try {
+ receiver.onError(
+ FINGERPRINT_ERROR_USER_CANCELED, 0 /* vendorCode */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in negative button onClick()", e);
+ }
+ })
+ .setSensorId(props.sensorId)
+ .build();
+
+ final BiometricPrompt.AuthenticationCallback promptCallback =
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ try {
+ if (FingerprintUtils.isKnownErrorCode(errorCode)) {
+ receiver.onError(errorCode, 0 /* vendorCode */);
+ } else {
+ receiver.onError(FINGERPRINT_ERROR_VENDOR, errorCode);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in onAuthenticationError()", e);
+ }
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(
+ BiometricPrompt.AuthenticationResult result) {
+ final Fingerprint fingerprint = new Fingerprint("", 0, 0L);
+ final boolean isStrong = props.sensorStrength == STRENGTH_STRONG;
+ try {
+ receiver.onAuthenticationSucceeded(fingerprint, userId, isStrong);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in onAuthenticationSucceeded()", e);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ try {
+ receiver.onAuthenticationFailed();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in onAuthenticationFailed()", e);
+ }
+ }
+
+ @Override
+ public void onAuthenticationAcquired(int acquireInfo) {
+ try {
+ if (FingerprintUtils.isKnownAcquiredCode(acquireInfo)) {
+ receiver.onAcquired(acquireInfo, 0 /* vendorCode */);
+ } else {
+ receiver.onAcquired(FINGERPRINT_ACQUIRED_VENDOR, acquireInfo);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in onAuthenticationAcquired()", e);
+ }
+ }
+ };
+
+ biometricPrompt.authenticateUserForOperation(
+ new CancellationSignal(), executor, promptCallback, userId, operationId);
}
@Override
@@ -374,6 +473,7 @@
@Override // Binder call
public void cancelAuthenticationFromService(final int sensorId, final IBinder token,
final String opPackageName) {
+
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final ServiceProvider provider = getProviderForSensor(sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index dc6fd3a..d69151d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -16,8 +16,18 @@
package com.android.server.biometrics.sensors.fingerprint;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMAGER_DIRTY;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_INSUFFICIENT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_FAST;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_SLOW;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
+
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.fingerprint.V2_1.FingerprintError;
import android.hardware.fingerprint.Fingerprint;
import android.text.TextUtils;
import android.util.SparseArray;
@@ -138,5 +148,51 @@
return state;
}
}
+
+ /**
+ * Checks if the given error code corresponds to a known fingerprint error.
+ *
+ * @param errorCode The error code to be checked.
+ * @return Whether the error code corresponds to a known error.
+ */
+ public static boolean isKnownErrorCode(int errorCode) {
+ switch (errorCode) {
+ case FingerprintError.ERROR_HW_UNAVAILABLE:
+ case FingerprintError.ERROR_UNABLE_TO_PROCESS:
+ case FingerprintError.ERROR_TIMEOUT:
+ case FingerprintError.ERROR_NO_SPACE:
+ case FingerprintError.ERROR_CANCELED:
+ case FingerprintError.ERROR_UNABLE_TO_REMOVE:
+ case FingerprintError.ERROR_LOCKOUT:
+ case FingerprintError.ERROR_VENDOR:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the given acquired code corresponds to a known fingerprint error.
+ *
+ * @param acquiredCode The acquired code to be checked.
+ * @return Whether the acquired code corresponds to a known error.
+ */
+ public static boolean isKnownAcquiredCode(int acquiredCode) {
+ switch (acquiredCode) {
+ case FINGERPRINT_ACQUIRED_GOOD:
+ case FINGERPRINT_ACQUIRED_PARTIAL:
+ case FINGERPRINT_ACQUIRED_INSUFFICIENT:
+ case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
+ case FINGERPRINT_ACQUIRED_TOO_SLOW:
+ case FINGERPRINT_ACQUIRED_TOO_FAST:
+ case FINGERPRINT_ACQUIRED_VENDOR:
+ case FINGERPRINT_ACQUIRED_START:
+ return true;
+
+ default:
+ return false;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 303c080..f672ae5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -78,7 +78,7 @@
void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- boolean shouldLogMetrics);
+ @FingerprintManager.EnrollReason int enrollReason);
void cancelEnrollment(int sensorId, @NonNull IBinder token);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
index d092e86..37f8e8c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -65,6 +65,17 @@
}
}
+ public static int getReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
+ switch (reason) {
+ case FingerprintManager.ENROLL_FIND_SENSOR:
+ return IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR;
+ case FingerprintManager.ENROLL_ENROLL:
+ return IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
+ default:
+ return IUdfpsOverlayController.REASON_UNKNOWN;
+ }
+ }
+
public static void showUdfpsOverlay(int sensorId, int reason,
@Nullable IUdfpsOverlayController udfpsOverlayController) {
if (udfpsOverlayController == null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index c2a30be..ea9c709 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.util.Slog;
@@ -131,7 +132,7 @@
Utils.checkPermission(mContext, TEST_BIOMETRIC);
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), true /* shouldLogMetrics */);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 08cc464..ae64c77 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -25,6 +25,7 @@
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
@@ -43,6 +44,7 @@
private static final String TAG = "FingerprintEnrollClient";
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ private final @FingerprintManager.EnrollReason int mEnrollReason;
@Nullable private ICancellationSignal mCancellationSignal;
private final int mMaxTemplatesPerUser;
@@ -52,13 +54,17 @@
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser,
- boolean shouldLogMetrics) {
+ @FingerprintManager.EnrollReason int enrollReason) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
true /* shouldVibrate */);
mUdfpsOverlayController = udfpsOvelayController;
mMaxTemplatesPerUser = maxTemplatesPerUser;
- setShouldLog(shouldLogMetrics);
+
+ mEnrollReason = enrollReason;
+ if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
+ setShouldLog(false);
+ }
}
@Override
@@ -72,6 +78,7 @@
}
}
+
@Override
public void onAcquired(int acquiredInfo, int vendorCode) {
super.onAcquired(acquiredInfo, vendorCode);
@@ -112,7 +119,8 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL,
+ UdfpsHelper.showUdfpsOverlay(getSensorId(),
+ UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
mUdfpsOverlayController);
try {
mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index f845024..ced46e1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -28,6 +28,7 @@
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -96,7 +97,8 @@
Slog.e(getTag(), "Task stack changed for client: " + client);
continue;
}
- if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+ if (Utils.isKeyguard(mContext, client.getOwnerString())
+ || Utils.isSystem(mContext, client.getOwnerString())) {
continue; // Keyguard is always allowed
}
@@ -365,7 +367,7 @@
@Override
public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken,
int userId, @NonNull IFingerprintServiceReceiver receiver,
- @NonNull String opPackageName, boolean shouldLogMetrics) {
+ @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
mHandler.post(() -> {
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
@@ -387,7 +389,7 @@
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
- mUdfpsOverlayController, maxTemplatesPerUser, shouldLogMetrics);
+ mUdfpsOverlayController, maxTemplatesPerUser, enrollReason);
scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index 6893e72..312ee0a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.util.Slog;
@@ -132,7 +133,7 @@
Utils.checkPermission(mContext, TEST_BIOMETRIC);
mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), true/* shouldLogMetrics */);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 54f8df2..7a74c6a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -33,6 +33,7 @@
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -49,6 +50,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
@@ -113,6 +115,7 @@
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
private int mCurrentUserId = UserHandle.USER_NULL;
+ private final boolean mIsUdfps;
private final int mSensorId;
private final class BiometricTaskStackListener extends TaskStackListener {
@@ -124,7 +127,8 @@
Slog.e(TAG, "Task stack changed for client: " + client);
return;
}
- if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+ if (Utils.isKeyguard(mContext, client.getOwnerString())
+ || Utils.isSystem(mContext, client.getOwnerString())) {
return; // Keyguard is always allowed
}
@@ -145,11 +149,11 @@
private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback =
new LockoutFrameworkImpl.LockoutResetCallback() {
- @Override
- public void onLockoutReset(int userId) {
- mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
- }
- };
+ @Override
+ public void onLockoutReset(int userId) {
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
+ }
+ };
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
@@ -334,29 +338,20 @@
Slog.e(TAG, "Unable to register user switch observer");
}
- final IBiometricsFingerprint daemon = getDaemon();
- boolean isUdfps = false;
- android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
- android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
- daemon);
- if (extension != null) {
- try {
- isUdfps = extension.isUdfps(sensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception while quering udfps", e);
- isUdfps = false;
- }
- }
+ // TODO(b/179175438): Remove this code block after transition to AIDL.
+ // The existence of config_udfps_sensor_props indicates that the sensor is UDFPS.
+ mIsUdfps = !ArrayUtils.isEmpty(
+ mContext.getResources().getIntArray(R.array.config_udfps_sensor_props));
final @FingerprintSensorProperties.SensorType int sensorType =
- isUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+ mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
: FingerprintSensorProperties.TYPE_REAR;
// resetLockout is controlled by the framework, so hardwareAuthToken is not required
final boolean resetLockoutRequiresHardwareAuthToken = false;
final int maxEnrollmentsPerUser = mContext.getResources()
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
- mSensorProperties = new FingerprintSensorPropertiesInternal(sensorId,
+ mSensorProperties = new FingerprintSensorPropertiesInternal(context, sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
sensorType, resetLockoutRequiresHardwareAuthToken);
}
@@ -553,7 +548,7 @@
public void scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- boolean shouldLogMetrics) {
+ @FingerprintManager.EnrollReason int enrollReason) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -561,7 +556,7 @@
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
- shouldLogMetrics);
+ enrollReason);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -631,13 +626,13 @@
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
@NonNull String opPackageName) {
mHandler.post(() -> {
- scheduleUpdateActiveUserWithoutHandler(userId);
+ scheduleUpdateActiveUserWithoutHandler(userId);
- final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
- userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client);
+ final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
+ userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
+ mSensorProperties.sensorId, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
});
}
@@ -799,6 +794,7 @@
JSONObject dump = new JSONObject();
try {
dump.put("service", TAG);
+ dump.put("isUdfps", mIsUdfps);
JSONArray sets = new JSONArray();
for (UserInfo user : UserManager.get(mContext).getUsers()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index d927aa7..33db64c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -24,6 +24,7 @@
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
@@ -46,6 +47,7 @@
private static final String TAG = "FingerprintEnrollClient";
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ private final @FingerprintManager.EnrollReason int mEnrollReason;
FingerprintEnrollClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@@ -53,12 +55,16 @@
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
@Nullable IUdfpsOverlayController udfpsOverlayController,
- boolean shouldLogMetrics) {
+ @FingerprintManager.EnrollReason int enrollReason) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
true /* shouldVibrate */);
mUdfpsOverlayController = udfpsOverlayController;
- setShouldLog(shouldLogMetrics);
+
+ mEnrollReason = enrollReason;
+ if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
+ setShouldLog(false);
+ }
}
@Override
@@ -76,7 +82,8 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL,
+ UdfpsHelper.showUdfpsOverlay(getSensorId(),
+ UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
mUdfpsOverlayController);
try {
// GroupId was never used. In fact, groupId is always the same as userId.
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6b2a1c9..51ba5f7 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -211,9 +211,9 @@
}
@Override
- public void clearOverrideForTest(long changeId, String packageName) {
+ public boolean clearOverrideForTest(long changeId, String packageName) {
checkCompatChangeOverridePermission();
- mCompatConfig.removeOverride(changeId, packageName);
+ return mCompatConfig.removeOverride(changeId, packageName);
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index c70bb08..43d9ade 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.IDnsResolver;
+import android.net.InetAddresses;
import android.net.LinkProperties;
import android.net.Network;
import android.net.ResolverOptionsParcel;
@@ -190,7 +191,7 @@
for (String ipAddress : ipAddresses) {
try {
latestDnses.add(new Pair(hostname,
- InetAddress.parseNumericAddress(ipAddress)));
+ InetAddresses.parseNumericAddress(ipAddress)));
} catch (IllegalArgumentException e) {}
}
// Remove <hostname, ipAddress> pairs that should not be tracked.
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 952193b..46c49e7 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -34,9 +34,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkStackConstants;
import com.android.server.net.BaseNetworkObserver;
-import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.Objects;
@@ -433,7 +433,7 @@
// clat IPv4 address itself (for those apps, it doesn't matter what
// the IP of the gateway is, only that there is one).
RouteInfo ipv4Default = new RouteInfo(
- new LinkAddress(Inet4Address.ANY, 0),
+ new LinkAddress(NetworkStackConstants.IPV4_ADDR_ANY, 0),
clatAddress.getAddress(), mIface);
stacked.addRoute(ipv4Default);
stacked.addLinkAddress(clatAddress);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1a4f20c7..bff1a5c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -122,6 +122,13 @@
//
// When ConnectivityService disconnects a network:
// -----------------------------------------------
+// If a network is just connected, ConnectivityService will think it will be used soon, but might
+// not be used. Thus, a 5s timer will be held to prevent the network being torn down immediately.
+// This "nascent" state is implemented by the "lingering" logic below without relating to any
+// request, and is used in some cases where network requests race with network establishment. The
+// nascent state ends when the 5-second timer fires, or as soon as the network satisfies a
+// request, whichever is earlier. In this state, the network is considered in the background.
+//
// If a network has no chance of satisfying any requests (even if it were to become validated
// and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
//
@@ -210,23 +217,23 @@
// network is taken down. This usually only happens to the default network. Lingering ends with
// either the linger timeout expiring and the network being taken down, or the network
// satisfying a request again.
- public static class LingerTimer implements Comparable<LingerTimer> {
+ public static class InactivityTimer implements Comparable<InactivityTimer> {
public final int requestId;
public final long expiryMs;
- public LingerTimer(int requestId, long expiryMs) {
+ public InactivityTimer(int requestId, long expiryMs) {
this.requestId = requestId;
this.expiryMs = expiryMs;
}
public boolean equals(Object o) {
- if (!(o instanceof LingerTimer)) return false;
- LingerTimer other = (LingerTimer) o;
+ if (!(o instanceof InactivityTimer)) return false;
+ InactivityTimer other = (InactivityTimer) o;
return (requestId == other.requestId) && (expiryMs == other.expiryMs);
}
public int hashCode() {
return Objects.hash(requestId, expiryMs);
}
- public int compareTo(LingerTimer other) {
+ public int compareTo(InactivityTimer other) {
return (expiryMs != other.expiryMs) ?
Long.compare(expiryMs, other.expiryMs) :
Integer.compare(requestId, other.requestId);
@@ -269,30 +276,32 @@
*/
public static final int ARG_AGENT_SUCCESS = 1;
- // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
+ // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
// a request is moved to a network with a better score, regardless of whether the network is or
- // was lingering or not.
+ // was lingering or not. An inactivity timer is also added when a network connects
+ // without immediately satisfying any requests.
// TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
// SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
- private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
+ private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
- // For fast lookups. Indexes into mLingerTimers by request ID.
- private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
+ // For fast lookups. Indexes into mInactivityTimers by request ID.
+ private final SparseArray<InactivityTimer> mInactivityTimerForRequest = new SparseArray<>();
- // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
- // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
- // When the timer fires, all linger state is cleared, and if the network has no requests, it is
- // torn down.
- private WakeupMessage mLingerMessage;
+ // Inactivity expiry timer. Armed whenever mInactivityTimers is non-empty, regardless of
+ // whether the network is inactive or not. Always set to the expiry of the mInactivityTimers
+ // that expires last. When the timer fires, all inactivity state is cleared, and if the network
+ // has no requests, it is torn down.
+ private WakeupMessage mInactivityMessage;
- // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
- private long mLingerExpiryMs;
+ // Inactivity expiry. Holds the expiry time of the inactivity timer, or 0 if the timer is not
+ // armed.
+ private long mInactivityExpiryMs;
- // Whether the network is lingering or not. Must be maintained separately from the above because
+ // Whether the network is inactive or not. Must be maintained separately from the above because
// it depends on the state of other networks and requests, which only ConnectivityService knows.
// (Example: we don't linger a network if it would become the best for a NetworkRequest if it
// validated).
- private boolean mLingering;
+ private boolean mInactive;
// This represents the quality of the network with no clear scale.
private int mScore;
@@ -895,20 +904,25 @@
/**
* Sets the specified requestId to linger on this network for the specified time. Called by
- * ConnectivityService when the request is moved to another network with a higher score.
+ * ConnectivityService when the request is moved to another network with a higher score, or
+ * when a network is newly created.
+ *
+ * @param requestId The requestId of the request that no longer need to be served by this
+ * network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the
+ * {@code LingerTimer} for a newly created network.
*/
public void lingerRequest(int requestId, long now, long duration) {
- if (mLingerTimerForRequest.get(requestId) != null) {
+ if (mInactivityTimerForRequest.get(requestId) != null) {
// Cannot happen. Once a request is lingering on a particular network, we cannot
// re-linger it unless that network becomes the best for that request again, in which
// case we should have unlingered it.
Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered");
}
final long expiryMs = now + duration;
- LingerTimer timer = new LingerTimer(requestId, expiryMs);
- if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
- mLingerTimers.add(timer);
- mLingerTimerForRequest.put(requestId, timer);
+ InactivityTimer timer = new InactivityTimer(requestId, expiryMs);
+ if (VDBG) Log.d(TAG, "Adding InactivityTimer " + timer + " to " + toShortString());
+ mInactivityTimers.add(timer);
+ mInactivityTimerForRequest.put(requestId, timer);
}
/**
@@ -916,23 +930,25 @@
* Returns true if the given requestId was lingering on this network, false otherwise.
*/
public boolean unlingerRequest(int requestId) {
- LingerTimer timer = mLingerTimerForRequest.get(requestId);
+ InactivityTimer timer = mInactivityTimerForRequest.get(requestId);
if (timer != null) {
- if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
- mLingerTimers.remove(timer);
- mLingerTimerForRequest.remove(requestId);
+ if (VDBG) {
+ Log.d(TAG, "Removing InactivityTimer " + timer + " from " + toShortString());
+ }
+ mInactivityTimers.remove(timer);
+ mInactivityTimerForRequest.remove(requestId);
return true;
}
return false;
}
- public long getLingerExpiry() {
- return mLingerExpiryMs;
+ public long getInactivityExpiry() {
+ return mInactivityExpiryMs;
}
- public void updateLingerTimer() {
- long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
- if (newExpiry == mLingerExpiryMs) return;
+ public void updateInactivityTimer() {
+ long newExpiry = mInactivityTimers.isEmpty() ? 0 : mInactivityTimers.last().expiryMs;
+ if (newExpiry == mInactivityExpiryMs) return;
// Even if we're going to reschedule the timer, cancel it first. This is because the
// semantics of WakeupMessage guarantee that if cancel is called then the alarm will
@@ -940,49 +956,65 @@
// WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
// has already been dispatched, rescheduling to some time in the future won't stop it
// from calling its callback immediately.
- if (mLingerMessage != null) {
- mLingerMessage.cancel();
- mLingerMessage = null;
+ if (mInactivityMessage != null) {
+ mInactivityMessage.cancel();
+ mInactivityMessage = null;
}
if (newExpiry > 0) {
- mLingerMessage = new WakeupMessage(
+ mInactivityMessage = new WakeupMessage(
mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
this /* obj (NetworkAgentInfo) */);
- mLingerMessage.schedule(newExpiry);
+ mInactivityMessage.schedule(newExpiry);
}
- mLingerExpiryMs = newExpiry;
+ mInactivityExpiryMs = newExpiry;
}
- public void linger() {
- mLingering = true;
+ public void setInactive() {
+ mInactive = true;
}
- public void unlinger() {
- mLingering = false;
+ public void unsetInactive() {
+ mInactive = false;
+ }
+
+ public boolean isInactive() {
+ return mInactive;
}
public boolean isLingering() {
- return mLingering;
+ return mInactive && !isNascent();
}
- public void clearLingerState() {
- if (mLingerMessage != null) {
- mLingerMessage.cancel();
- mLingerMessage = null;
+ /**
+ * Return whether the network is just connected and about to be torn down because of not
+ * satisfying any request.
+ */
+ public boolean isNascent() {
+ return mInactive && mInactivityTimers.size() == 1
+ && mInactivityTimers.first().requestId == NetworkRequest.REQUEST_ID_NONE;
+ }
+
+ public void clearInactivityState() {
+ if (mInactivityMessage != null) {
+ mInactivityMessage.cancel();
+ mInactivityMessage = null;
}
- mLingerTimers.clear();
- mLingerTimerForRequest.clear();
- updateLingerTimer(); // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
- mLingering = false;
+ mInactivityTimers.clear();
+ mInactivityTimerForRequest.clear();
+ // Sets mInactivityExpiryMs, cancels and nulls out mInactivityMessage.
+ updateInactivityTimer();
+ mInactive = false;
}
- public void dumpLingerTimers(PrintWriter pw) {
- for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
+ public void dumpInactivityTimers(PrintWriter pw) {
+ for (InactivityTimer timer : mInactivityTimers) {
+ pw.println(timer);
+ }
}
/**
@@ -1016,7 +1048,7 @@
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
+ networkInfo.toShortString() + "} "
+ " Score{" + getCurrentScore() + "} "
- + (isLingering() ? " lingering" : "")
+ + (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
+ (everValidated ? " everValidated" : "")
+ (lastValidated ? " lastValidated" : "")
+ (partialConnectivity ? " partialConnectivity" : "")
diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
index 5dc8c1a..aadaf4d 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
@@ -16,7 +16,6 @@
package com.android.server.connectivity;
-import android.annotation.NonNull;
import android.annotation.WorkerThread;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -72,6 +71,10 @@
private static final int DELAY_LONG = 4;
private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
+ // Return values for #setCurrentProxyScriptUrl
+ public static final boolean DONT_SEND_BROADCAST = false;
+ public static final boolean DO_SEND_BROADCAST = true;
+
private String mCurrentPac;
@GuardedBy("mProxyLock")
private volatile Uri mPacUrl = Uri.EMPTY;
@@ -90,7 +93,7 @@
private volatile boolean mHasSentBroadcast;
private volatile boolean mHasDownloaded;
- private final Handler mConnectivityHandler;
+ private Handler mConnectivityHandler;
private final int mProxyMessage;
/**
@@ -99,13 +102,6 @@
private final Object mProxyLock = new Object();
/**
- * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
- * last URL and port, and the broadcast message being sent with the correct arguments.
- * TODO : this should probably protect all instances of these variables
- */
- private final Object mBroadcastStateLock = new Object();
-
- /**
* Runnable to download PAC script.
* The behavior relies on the assumption it always runs on mNetThread to guarantee that the
* latest data fetched from mPacUrl is stored in mProxyService.
@@ -150,7 +146,7 @@
}
}
- public PacProxyInstaller(@NonNull Context context, @NonNull Handler handler, int proxyMessage) {
+ public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
mContext = context;
mLastPort = -1;
final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
@@ -180,27 +176,31 @@
* PacProxyInstaller will trigger a new broadcast when it is ready.
*
* @param proxy Proxy information that is about to be broadcast.
+ * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
*/
- public void setCurrentProxyScriptUrl(@NonNull ProxyInfo proxy) {
- synchronized (mBroadcastStateLock) {
- if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
- if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
- mPacUrl = proxy.getPacFileUrl();
- mCurrentDelay = DELAY_1;
- mHasSentBroadcast = false;
- mHasDownloaded = false;
- getAlarmManager().cancel(mPacRefreshIntent);
- bind();
- } else {
- getAlarmManager().cancel(mPacRefreshIntent);
- synchronized (mProxyLock) {
- mPacUrl = Uri.EMPTY;
- mCurrentPac = null;
- if (mProxyService != null) {
- unbind();
- }
+ public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+ if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+ if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
+ // Allow to send broadcast, nothing to do.
+ return DO_SEND_BROADCAST;
+ }
+ mPacUrl = proxy.getPacFileUrl();
+ mCurrentDelay = DELAY_1;
+ mHasSentBroadcast = false;
+ mHasDownloaded = false;
+ getAlarmManager().cancel(mPacRefreshIntent);
+ bind();
+ return DONT_SEND_BROADCAST;
+ } else {
+ getAlarmManager().cancel(mPacRefreshIntent);
+ synchronized (mProxyLock) {
+ mPacUrl = Uri.EMPTY;
+ mCurrentPac = null;
+ if (mProxyService != null) {
+ unbind();
}
}
+ return DO_SEND_BROADCAST;
}
}
@@ -275,7 +275,6 @@
getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
}
- @GuardedBy("mProxyLock")
private void setCurrentProxyScript(String script) {
if (mProxyService == null) {
Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -348,9 +347,6 @@
public void setProxyPort(int port) {
if (mLastPort != -1) {
// Always need to send if port changed
- // TODO: Here lacks synchronization because this write cannot
- // guarantee that it's visible from sendProxyIfNeeded() when
- // it's called by a Runnable which is post by mNetThread.
mHasSentBroadcast = false;
}
mLastPort = port;
@@ -390,15 +386,13 @@
mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
}
- private void sendProxyIfNeeded() {
- synchronized (mBroadcastStateLock) {
- if (!mHasDownloaded || (mLastPort == -1)) {
- return;
- }
- if (!mHasSentBroadcast) {
- sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
- mHasSentBroadcast = true;
- }
+ private synchronized void sendProxyIfNeeded() {
+ if (!mHasDownloaded || (mLastPort == -1)) {
+ return;
+ }
+ if (!mHasSentBroadcast) {
+ sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+ mHasSentBroadcast = true;
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index d507b5f..8d21f6f 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -265,7 +265,10 @@
for (Entry<Integer, Boolean> app : apps.entrySet()) {
List<Integer> list = app.getValue() ? system : network;
for (int user : users) {
- list.add(UserHandle.getUid(user, app.getKey()));
+ final UserHandle handle = UserHandle.of(user);
+ if (handle == null) continue;
+
+ list.add(UserHandle.getUid(handle, app.getKey()));
}
}
try {
@@ -550,7 +553,10 @@
for (UidRange range : ranges) {
for (int userId = range.getStartUser(); userId <= range.getEndUser(); userId++) {
for (int appId : appIds) {
- final int uid = UserHandle.getUid(userId, appId);
+ final UserHandle handle = UserHandle.of(userId);
+ if (handle == null) continue;
+
+ final int uid = UserHandle.getUid(handle, appId);
if (range.contains(uid)) {
result.add(uid);
}
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index b618d2b..d83ff83 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -226,9 +226,9 @@
final ProxyInfo defaultProxy = getDefaultProxy();
final ProxyInfo proxyInfo = null != defaultProxy ?
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
- mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo);
- if (!shouldSendBroadcast(proxyInfo)) {
+ if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
+ == PacProxyInstaller.DONT_SEND_BROADCAST) {
return;
}
if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,13 +244,6 @@
}
}
- private boolean shouldSendBroadcast(ProxyInfo proxy) {
- if (Uri.EMPTY.equals(proxy.getPacFileUrl())) return false;
- if (proxy.getPacFileUrl().equals(proxy.getPacFileUrl())
- && (proxy.getPort() > 0)) return true;
- return true;
- }
-
/**
* Sets the global proxy in memory. Also writes the values to the global settings of the device.
*
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index b455a3f..fc2c7e0 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -51,6 +51,7 @@
import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
import android.net.IpSecManager.IpSecTunnelInterface;
@@ -111,6 +112,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.net.module.util.NetworkStackConstants;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
@@ -203,6 +205,7 @@
protected final NetworkCapabilities mNetworkCapabilities;
private final SystemServices mSystemServices;
private final Ikev2SessionCreator mIkev2SessionCreator;
+ private final UserManager mUserManager;
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -277,6 +280,10 @@
return LocalServices.getService(DeviceIdleInternal.class);
}
+ public PendingIntent getIntentForStatusPanel(Context context) {
+ return VpnConfig.getIntentForStatusPanel(context);
+ }
+
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final RetryScheduler retryScheduler) throws IOException, InterruptedException {
@@ -327,7 +334,7 @@
public InetAddress resolve(final String endpoint)
throws ExecutionException, InterruptedException {
try {
- return InetAddress.parseNumericAddress(endpoint);
+ return InetAddresses.parseNumericAddress(endpoint);
} catch (IllegalArgumentException e) {
// Endpoint is not numeric : fall through and resolve
}
@@ -405,6 +412,7 @@
mLooper = looper;
mSystemServices = systemServices;
mIkev2SessionCreator = ikev2SessionCreator;
+ mUserManager = mContext.getSystemService(UserManager.class);
mPackage = VpnConfig.LEGACY_VPN;
mOwnerUID = getAppUid(mPackage, mUserId);
@@ -1119,7 +1127,7 @@
if (mConfig.dnsServers != null) {
for (String dnsServer : mConfig.dnsServers) {
- InetAddress address = InetAddress.parseNumericAddress(dnsServer);
+ InetAddress address = InetAddresses.parseNumericAddress(dnsServer);
lp.addDnsServer(address);
allowIPv4 |= address instanceof Inet4Address;
allowIPv6 |= address instanceof Inet6Address;
@@ -1129,10 +1137,12 @@
lp.setHttpProxy(mConfig.proxyInfo);
if (!allowIPv4) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV4_ADDR_ANY, 0), RTN_UNREACHABLE));
}
if (!allowIPv6) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV6_ADDR_ANY, 0), RTN_UNREACHABLE));
}
// Concatenate search domains into a string.
@@ -1431,7 +1441,7 @@
final long token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
- users = UserManager.get(mContext).getAliveUsers();
+ users = mUserManager.getAliveUsers();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1515,7 +1525,7 @@
*/
public void onUserAdded(int userId) {
// If the user is restricted tie them to the parent user's VPN
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1543,7 +1553,7 @@
*/
public void onUserRemoved(int userId) {
// clean up if restricted
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1768,7 +1778,7 @@
private void prepareStatusIntent() {
final long token = Binder.clearCallingIdentity();
try {
- mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+ mStatusIntent = mDeps.getIntentForStatusPanel(mContext);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1968,8 +1978,7 @@
private void enforceNotRestrictedUser() {
Binder.withCleanCallingIdentity(() -> {
- final UserManager mgr = UserManager.get(mContext);
- final UserInfo user = mgr.getUserInfo(mUserId);
+ final UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
@@ -1982,30 +1991,30 @@
* secondary thread to perform connection work, returning quickly.
*
* Should only be called to respond to Binder requests as this enforces caller permission. Use
- * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
+ * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, Network, LinkProperties)} to skip the
* permission check only when the caller is trusted (or the call is initiated by the system).
*/
- public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
+ public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, @Nullable Network underlying,
+ LinkProperties egress) {
enforceControlPermission();
final long token = Binder.clearCallingIdentity();
try {
- startLegacyVpnPrivileged(profile, keyStore, egress);
+ startLegacyVpnPrivileged(profile, keyStore, underlying, egress);
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
- * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
- * permissions under the assumption that the caller is the system.
+ * Like {@link #startLegacyVpn(VpnProfile, KeyStore, Network, LinkProperties)}, but does not
+ * check permissions under the assumption that the caller is the system.
*
* Callers are responsible for checking permissions if needed.
*/
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
- LinkProperties egress) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(mUserId);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ @Nullable Network underlying, @NonNull LinkProperties egress) {
+ UserInfo user = mUserManager.getUserInfo(mUserId);
+ if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
new UserHandle(mUserId))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
@@ -2128,6 +2137,9 @@
config.session = profile.name;
config.isMetered = false;
config.proxyInfo = profile.proxy;
+ if (underlying != null) {
+ config.underlyingNetworks = new Network[] { underlying };
+ }
config.addLegacyRoutes(profile.routes);
if (!profile.dnsServers.isEmpty()) {
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
new file mode 100644
index 0000000..e496d77
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicestate;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A state of the device defined by the {@link DeviceStateProvider} and managed by the
+ * {@link DeviceStateManagerService}.
+ * <p>
+ * Device state is an abstract concept that allows mapping the current state of the device to the
+ * state of the system. This is useful for variable-state devices, like foldable or rollable
+ * devices, that can be configured by users into differing hardware states, which each may have a
+ * different expected use case.
+ *
+ * @see DeviceStateProvider
+ * @see DeviceStateManagerService
+ */
+public final class DeviceState {
+ /** Unique identifier for the device state. */
+ @IntRange(from = 0)
+ private final int mIdentifier;
+
+ /** String description of the device state. */
+ @NonNull
+ private final String mName;
+
+ public DeviceState(@IntRange(from = 0) int identifier,
+ @NonNull String name) {
+ if (identifier < 0) {
+ throw new IllegalArgumentException("Identifier must be greater than or equal to zero.");
+ }
+ mIdentifier = identifier;
+ mName = name;
+ }
+
+ /** Returns the unique identifier for the device state. */
+ @IntRange(from = 0)
+ public int getIdentifier() {
+ return mIdentifier;
+ }
+
+ /** Returns a string description of the device state. */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DeviceState that = (DeviceState) o;
+ return mIdentifier == that.mIdentifier
+ && Objects.equals(mName, that.mName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIdentifier, mName);
+ }
+}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index d7dcbde..984a176 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -17,11 +17,13 @@
package com.android.server.devicestate;
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES;
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.PackageManager;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.IDeviceStateManager;
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.Binder;
@@ -29,7 +31,8 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
-import android.util.IntArray;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -42,7 +45,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Optional;
/**
* A system service that manages the state of a device with user-configurable hardware like a
@@ -61,8 +64,12 @@
* the {@link DeviceStateProvider} to modify the current device state and communicating with the
* {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state.
* </p>
+ * The service also provides the {@link DeviceStateManager} API allowing clients to listen for
+ * changes in device state and submit requests to override the device state provided by the
+ * {@link DeviceStateProvider}.
*
* @see DeviceStatePolicy
+ * @see DeviceStateManager
*/
public final class DeviceStateManagerService extends SystemService {
private static final String TAG = "DeviceStateManagerService";
@@ -74,30 +81,39 @@
@NonNull
private final BinderService mBinderService;
+ // All supported device states keyed by identifier.
@GuardedBy("mLock")
- private IntArray mSupportedDeviceStates;
+ private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
- // The current committed device state.
+ // The current committed device state. The default of UNSET will be replaced by
+ // the current state after the initial callback from the DeviceStateProvider.
@GuardedBy("mLock")
- private int mCommittedState = INVALID_DEVICE_STATE;
+ @NonNull
+ private DeviceState mCommittedState = new DeviceState(0, "UNSET");
// The device state that is currently awaiting callback from the policy to be committed.
@GuardedBy("mLock")
- private int mPendingState = INVALID_DEVICE_STATE;
+ @NonNull
+ private Optional<DeviceState> mPendingState = Optional.empty();
// Whether or not the policy is currently waiting to be notified of the current pending state.
@GuardedBy("mLock")
private boolean mIsPolicyWaitingForState = false;
- // The device state that is currently requested and is next to be configured and committed.
- // Can be overwritten by an override state value if requested.
- @GuardedBy("mLock")
- private int mRequestedState = INVALID_DEVICE_STATE;
- // The most recently requested override state, or INVALID_DEVICE_STATE if no override is
- // requested.
- @GuardedBy("mLock")
- private int mRequestedOverrideState = INVALID_DEVICE_STATE;
- // List of registered callbacks indexed by process id.
+ // The device state that is set by the device state provider.
@GuardedBy("mLock")
- private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();
+ @NonNull
+ private Optional<DeviceState> mBaseState = Optional.empty();
+
+ // List of processes registered to receive notifications about changes to device state and
+ // request status indexed by process id.
+ @GuardedBy("mLock")
+ private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
+ // List of override requests with the highest precedence request at the end.
+ @GuardedBy("mLock")
+ private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>();
+ // Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified
+ // of a change in status.
+ @GuardedBy("mLock")
+ private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>();
public DeviceStateManagerService(@NonNull Context context) {
this(context, new DeviceStatePolicyImpl(context));
@@ -122,79 +138,74 @@
*
* @see #getPendingState()
*/
- int getCommittedState() {
+ @NonNull
+ DeviceState getCommittedState() {
synchronized (mLock) {
return mCommittedState;
}
}
/**
- * Returns the state the system is currently configuring, or {@link #INVALID_DEVICE_STATE} if
- * the system is not in the process of configuring a state.
+ * Returns the state the system is currently configuring, or {@link Optional#empty()} if the
+ * system is not in the process of configuring a state.
*/
@VisibleForTesting
- int getPendingState() {
+ @NonNull
+ Optional<DeviceState> getPendingState() {
synchronized (mLock) {
return mPendingState;
}
}
/**
- * Returns the requested state. The service will configure the device to match the requested
- * state when possible.
+ * Returns the base state. The service will configure the device to match the base state when
+ * there is no active request to override the base state.
+ *
+ * @see #getOverrideState()
*/
- int getRequestedState() {
+ @NonNull
+ Optional<DeviceState> getBaseState() {
synchronized (mLock) {
- return mRequestedState;
+ return mBaseState;
}
}
/**
- * Overrides the current device state with the provided state.
- *
- * @return {@code true} if the override state is valid and supported, {@code false} otherwise.
+ * Returns the current override state, or {@link Optional#empty()} if no override state is
+ * requested. If an override states is present, the returned state will take precedence over
+ * the base state returned from {@link #getBaseState()}.
*/
- boolean setOverrideState(int overrideState) {
- if (getContext().checkCallingOrSelfPermission(CONTROL_DEVICE_STATE)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Must hold permission " + CONTROL_DEVICE_STATE);
- }
-
+ @NonNull
+ Optional<DeviceState> getOverrideState() {
synchronized (mLock) {
- if (overrideState != INVALID_DEVICE_STATE && !isSupportedStateLocked(overrideState)) {
- return false;
+ if (mRequestRecords.isEmpty()) {
+ return Optional.empty();
}
- mRequestedOverrideState = overrideState;
- updatePendingStateLocked();
- }
-
- notifyPolicyIfNeeded();
- return true;
- }
-
- /**
- * Clears an override state set with {@link #setOverrideState(int)}.
- */
- void clearOverrideState() {
- setOverrideState(INVALID_DEVICE_STATE);
- }
-
- /**
- * Returns the current requested override state, or {@link #INVALID_DEVICE_STATE} is no override
- * state is requested.
- */
- int getOverrideState() {
- synchronized (mLock) {
- return mRequestedOverrideState;
+ OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1);
+ return Optional.of(topRequest.mRequestedState);
}
}
/** Returns the list of currently supported device states. */
- int[] getSupportedStates() {
+ DeviceState[] getSupportedStates() {
synchronized (mLock) {
- // Copy array to prevent external modification of internal state.
- return Arrays.copyOf(mSupportedDeviceStates.toArray(), mSupportedDeviceStates.size());
+ DeviceState[] supportedStates = new DeviceState[mDeviceStates.size()];
+ for (int i = 0; i < supportedStates.length; i++) {
+ supportedStates[i] = mDeviceStates.valueAt(i);
+ }
+ return supportedStates;
+ }
+ }
+
+ /** Returns the list of currently supported device state identifiers. */
+ private int[] getSupportedStateIdentifiers() {
+ synchronized (mLock) {
+ int[] supportedStates = new int[mDeviceStates.size()];
+ for (int i = 0; i < supportedStates.length; i++) {
+ supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier();
+ }
+ return supportedStates;
}
}
@@ -203,53 +214,86 @@
return mBinderService;
}
- private void updateSupportedStates(int[] supportedDeviceStates) {
- // Must ensure sorted as isSupportedStateLocked() impl uses binary search.
- Arrays.sort(supportedDeviceStates, 0, supportedDeviceStates.length);
+ private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
synchronized (mLock) {
- mSupportedDeviceStates = IntArray.wrap(supportedDeviceStates);
+ mDeviceStates.clear();
+ for (int i = 0; i < supportedDeviceStates.length; i++) {
+ DeviceState state = supportedDeviceStates[i];
+ mDeviceStates.put(state.getIdentifier(), state);
+ }
- if (mRequestedState != INVALID_DEVICE_STATE
- && !isSupportedStateLocked(mRequestedState)) {
- // The current requested state is no longer valid. We'll clear it here, though
+ if (mBaseState.isPresent()
+ && !isSupportedStateLocked(mBaseState.get().getIdentifier())) {
+ // The current base state is no longer valid. We'll clear it here, though
// we won't actually update the current state until a callback comes from the
// provider with the most recent state.
- mRequestedState = INVALID_DEVICE_STATE;
+ mBaseState = Optional.empty();
}
- if (mRequestedOverrideState != INVALID_DEVICE_STATE
- && !isSupportedStateLocked(mRequestedOverrideState)) {
- // The current override state is no longer valid. We'll clear it here and update
- // the committed state if necessary.
- mRequestedOverrideState = INVALID_DEVICE_STATE;
+
+ final int requestSize = mRequestRecords.size();
+ for (int i = 0; i < requestSize; i++) {
+ OverrideRequestRecord request = mRequestRecords.get(i);
+ if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) {
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+ }
}
+
updatePendingStateLocked();
}
+ notifyRequestsOfStatusChangeIfNeeded();
notifyPolicyIfNeeded();
}
/**
* Returns {@code true} if the provided state is supported. Requires that
- * {@link #mSupportedDeviceStates} is sorted prior to calling.
+ * {@link #mDeviceStates} is sorted prior to calling.
*/
- private boolean isSupportedStateLocked(int state) {
- return mSupportedDeviceStates.binarySearch(state) >= 0;
+ private boolean isSupportedStateLocked(int identifier) {
+ return mDeviceStates.contains(identifier);
}
/**
- * Requests that the system enter the provided {@code state}. The request may not be honored
- * under certain conditions, for example if the provided state is not supported.
+ * Returns the {@link DeviceState} with the supplied {@code identifier}, or {@code null} if
+ * there is no device state with the identifier.
+ */
+ @Nullable
+ private Optional<DeviceState> getStateLocked(int identifier) {
+ return Optional.ofNullable(mDeviceStates.get(identifier));
+ }
+
+ /**
+ * Requests to set the base state. The request may not be honored under certain conditions, for
+ * example if the provided state is not supported.
*
* @see #isSupportedStateLocked(int)
*/
- private void requestState(int state) {
+ private void setBaseState(int identifier) {
synchronized (mLock) {
- if (isSupportedStateLocked(state)) {
- mRequestedState = state;
+ if (mBaseState.isPresent() && mBaseState.get().getIdentifier() == identifier) {
+ // Base state hasn't changed. Nothing to do.
+ return;
}
+
+ final Optional<DeviceState> baseState = getStateLocked(identifier);
+ if (!baseState.isPresent()) {
+ throw new IllegalArgumentException("Base state is not supported");
+ }
+
+ mBaseState = baseState;
+
+ final int requestSize = mRequestRecords.size();
+ for (int i = 0; i < requestSize; i++) {
+ OverrideRequestRecord request = mRequestRecords.get(i);
+ if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) {
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+ }
+ }
+
updatePendingStateLocked();
}
+ notifyRequestsOfStatusChangeIfNeeded();
notifyPolicyIfNeeded();
}
@@ -259,19 +303,19 @@
* changed.
*/
private void updatePendingStateLocked() {
- if (mPendingState != INVALID_DEVICE_STATE) {
+ if (mPendingState.isPresent()) {
// Have pending state, can not configure a new state until the state is committed.
return;
}
- int stateToConfigure;
- if (mRequestedOverrideState != INVALID_DEVICE_STATE) {
- stateToConfigure = mRequestedOverrideState;
+ final DeviceState stateToConfigure;
+ if (!mRequestRecords.isEmpty()) {
+ stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
} else {
- stateToConfigure = mRequestedState;
+ stateToConfigure = mBaseState.orElse(null);
}
- if (stateToConfigure == INVALID_DEVICE_STATE) {
+ if (stateToConfigure == null) {
// No currently requested state.
return;
}
@@ -281,7 +325,7 @@
return;
}
- mPendingState = stateToConfigure;
+ mPendingState = Optional.of(stateToConfigure);
mIsPolicyWaitingForState = true;
}
@@ -302,7 +346,7 @@
return;
}
mIsPolicyWaitingForState = false;
- state = mPendingState;
+ state = mPendingState.get().getIdentifier();
}
if (DEBUG) {
@@ -333,15 +377,25 @@
if (DEBUG) {
Slog.d(TAG, "Committing state: " + mPendingState);
}
- mCommittedState = mPendingState;
- newState = mCommittedState;
- mPendingState = INVALID_DEVICE_STATE;
+ mCommittedState = mPendingState.get();
+ newState = mCommittedState.getIdentifier();
+
+ if (!mRequestRecords.isEmpty()) {
+ final OverrideRequestRecord topRequest =
+ mRequestRecords.get(mRequestRecords.size() - 1);
+ topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
+ }
+
+ mPendingState = Optional.empty();
updatePendingStateLocked();
}
// Notify callbacks of a change.
notifyDeviceStateChanged(newState);
+ // Notify the top request that it's active.
+ notifyRequestsOfStatusChangeIfNeeded();
+
// Try to configure the next state if needed.
notifyPolicyIfNeeded();
}
@@ -352,114 +406,221 @@
"Attempting to notify callbacks with service lock held.");
}
- // Grab the lock and copy the callbacks.
- ArrayList<CallbackRecord> callbacks;
+ // Grab the lock and copy the process records.
+ ArrayList<ProcessRecord> registeredProcesses;
synchronized (mLock) {
- if (mCallbacks.size() == 0) {
+ if (mProcessRecords.size() == 0) {
return;
}
- callbacks = new ArrayList<>();
- for (int i = 0; i < mCallbacks.size(); i++) {
- callbacks.add(mCallbacks.valueAt(i));
+ registeredProcesses = new ArrayList<>();
+ for (int i = 0; i < mProcessRecords.size(); i++) {
+ registeredProcesses.add(mProcessRecords.valueAt(i));
}
}
// After releasing the lock, send the notifications out.
- for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).notifyDeviceStateAsync(deviceState);
+ for (int i = 0; i < registeredProcesses.size(); i++) {
+ registeredProcesses.get(i).notifyDeviceStateAsync(deviceState);
}
}
- private void registerCallbackInternal(IDeviceStateManagerCallback callback, int callingPid) {
+ /**
+ * Notifies all dirty requests (requests that have a change in status, but have not yet been
+ * notified) that their status has changed.
+ */
+ private void notifyRequestsOfStatusChangeIfNeeded() {
+ if (Thread.holdsLock(mLock)) {
+ throw new IllegalStateException(
+ "Attempting to notify requests with service lock held.");
+ }
+
+ ArraySet<OverrideRequestRecord> dirtyRequests;
+ synchronized (mLock) {
+ if (mRequestsPendingStatusChange.isEmpty()) {
+ return;
+ }
+
+ dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange);
+ mRequestsPendingStatusChange.clear();
+ }
+
+ // After releasing the lock, send the notifications out.
+ for (int i = 0; i < dirtyRequests.size(); i++) {
+ dirtyRequests.valueAt(i).notifyStatusIfNeeded();
+ }
+ }
+
+ private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
int currentState;
- CallbackRecord record;
+ ProcessRecord record;
// Grab the lock to register the callback and get the current state.
synchronized (mLock) {
- if (mCallbacks.contains(callingPid)) {
+ if (mProcessRecords.contains(pid)) {
throw new SecurityException("The calling process has already registered an"
+ " IDeviceStateManagerCallback.");
}
- record = new CallbackRecord(callback, callingPid);
+ record = new ProcessRecord(callback, pid);
try {
callback.asBinder().linkToDeath(record, 0);
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
- mCallbacks.put(callingPid, record);
- currentState = mCommittedState;
+ mProcessRecords.put(pid, record);
+ currentState = mCommittedState.getIdentifier();
}
// Notify the callback of the state at registration.
record.notifyDeviceStateAsync(currentState);
}
- private void unregisterCallbackInternal(CallbackRecord record) {
+ private void handleProcessDied(ProcessRecord processRecord) {
synchronized (mLock) {
- mCallbacks.remove(record.mPid);
+ // Cancel all requests from this process.
+ final int requestCount = processRecord.mRequestRecords.size();
+ for (int i = 0; i < requestCount; i++) {
+ final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i);
+ // Cancel the request but don't mark it as dirty since there's no need to send
+ // notifications if the process has died.
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED,
+ false /* markDirty */);
+ }
+
+ mProcessRecords.remove(processRecord.mPid);
+
+ updatePendingStateLocked();
}
+
+ notifyPolicyIfNeeded();
+ }
+
+ private void requestStateInternal(int state, int flags, int callingPid,
+ @NonNull IBinder token) {
+ synchronized (mLock) {
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+
+ if (processRecord.mRequestRecords.get(token) != null) {
+ throw new IllegalStateException("Request has already been made for the supplied"
+ + " token: " + token);
+ }
+
+ final Optional<DeviceState> deviceState = getStateLocked(state);
+ if (!deviceState.isPresent()) {
+ throw new IllegalArgumentException("Requested state: " + state
+ + " is not supported.");
+ }
+
+ OverrideRequestRecord topRecord = mRequestRecords.isEmpty()
+ ? null : mRequestRecords.get(mRequestRecords.size() - 1);
+ if (topRecord != null) {
+ topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED);
+ }
+
+ final OverrideRequestRecord request =
+ new OverrideRequestRecord(processRecord, token, deviceState.get(), flags);
+ mRequestRecords.add(request);
+ processRecord.mRequestRecords.put(request.mToken, request);
+ // We don't set the status of the new request to ACTIVE here as it will be set in
+ // commitPendingState().
+
+ updatePendingStateLocked();
+ }
+
+ notifyRequestsOfStatusChangeIfNeeded();
+ notifyPolicyIfNeeded();
+ }
+
+ private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
+ synchronized (mLock) {
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+
+ OverrideRequestRecord request = processRecord.mRequestRecords.get(token);
+ if (request == null) {
+ throw new IllegalStateException("No known request for the given token");
+ }
+
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+
+ updatePendingStateLocked();
+ }
+
+ notifyRequestsOfStatusChangeIfNeeded();
+ notifyPolicyIfNeeded();
}
private void dumpInternal(PrintWriter pw) {
pw.println("DEVICE STATE MANAGER (dumpsys device_state)");
synchronized (mLock) {
- pw.println(" mCommittedState=" + toString(mCommittedState));
- pw.println(" mPendingState=" + toString(mPendingState));
- pw.println(" mRequestedState=" + toString(mRequestedState));
- pw.println(" mRequestedOverrideState=" + toString(mRequestedOverrideState));
+ pw.println(" mCommittedState=" + mCommittedState);
+ pw.println(" mPendingState=" + mPendingState);
+ pw.println(" mBaseState=" + mBaseState);
+ pw.println(" mOverrideState=" + getOverrideState());
- final int callbackCount = mCallbacks.size();
+ final int processCount = mProcessRecords.size();
pw.println();
- pw.println("Callbacks: size=" + callbackCount);
- for (int i = 0; i < callbackCount; i++) {
- CallbackRecord callback = mCallbacks.valueAt(i);
- pw.println(" " + i + ": mPid=" + callback.mPid);
+ pw.println("Registered processes: size=" + processCount);
+ for (int i = 0; i < processCount; i++) {
+ ProcessRecord processRecord = mProcessRecords.valueAt(i);
+ pw.println(" " + i + ": mPid=" + processRecord.mPid);
+ }
+
+ final int requestCount = mRequestRecords.size();
+ pw.println();
+ pw.println("Override requests: size=" + requestCount);
+ for (int i = 0; i < requestCount; i++) {
+ OverrideRequestRecord requestRecord = mRequestRecords.get(i);
+ pw.println(" " + i + ": mPid=" + requestRecord.mProcessRecord.mPid
+ + ", mRequestedState=" + requestRecord.mRequestedState
+ + ", mFlags=" + requestRecord.mFlags
+ + ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus));
}
}
}
- private String toString(int state) {
- return state == INVALID_DEVICE_STATE ? "(none)" : String.valueOf(state);
- }
-
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
@Override
- public void onSupportedDeviceStatesChanged(int[] newDeviceStates) {
- for (int i = 0; i < newDeviceStates.length; i++) {
- if (newDeviceStates[i] < 0) {
- throw new IllegalArgumentException("Supported device states includes invalid"
- + " value: " + newDeviceStates[i]);
- }
+ public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
+ if (newDeviceStates.length == 0) {
+ throw new IllegalArgumentException("Supported device states must not be empty");
}
-
updateSupportedStates(newDeviceStates);
}
@Override
- public void onStateChanged(int state) {
- if (state < 0) {
- throw new IllegalArgumentException("Invalid state: " + state);
+ public void onStateChanged(@IntRange(from = 0) int identifier) {
+ if (identifier < 0) {
+ throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
- requestState(state);
+ setBaseState(identifier);
}
}
- private final class CallbackRecord implements IBinder.DeathRecipient {
+ private final class ProcessRecord implements IBinder.DeathRecipient {
private final IDeviceStateManagerCallback mCallback;
private final int mPid;
- CallbackRecord(IDeviceStateManagerCallback callback, int pid) {
+ private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>();
+
+ ProcessRecord(IDeviceStateManagerCallback callback, int pid) {
mCallback = callback;
mPid = pid;
}
@Override
public void binderDied() {
- unregisterCallbackInternal(this);
+ handleProcessDied(this);
}
public void notifyDeviceStateAsync(int devicestate) {
@@ -470,6 +631,119 @@
ex);
}
}
+
+ public void notifyRequestActiveAsync(OverrideRequestRecord request) {
+ try {
+ mCallback.onRequestActive(request.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+ ex);
+ }
+ }
+
+ public void notifyRequestSuspendedAsync(OverrideRequestRecord request) {
+ try {
+ mCallback.onRequestSuspended(request.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+ ex);
+ }
+ }
+
+ public void notifyRequestCanceledAsync(OverrideRequestRecord request) {
+ try {
+ mCallback.onRequestCanceled(request.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+ ex);
+ }
+ }
+ }
+
+ /** A record describing a request to override the state of the device. */
+ private final class OverrideRequestRecord {
+ public static final int STATUS_UNKNOWN = 0;
+ public static final int STATUS_ACTIVE = 1;
+ public static final int STATUS_SUSPENDED = 2;
+ public static final int STATUS_CANCELED = 3;
+
+ @Nullable
+ public String statusToString(int status) {
+ switch (status) {
+ case STATUS_ACTIVE:
+ return "ACTIVE";
+ case STATUS_SUSPENDED:
+ return "SUSPENDED";
+ case STATUS_CANCELED:
+ return "CANCELED";
+ case STATUS_UNKNOWN:
+ return "UNKNOWN";
+ default:
+ return null;
+ }
+ }
+
+ private final ProcessRecord mProcessRecord;
+ @NonNull
+ private final IBinder mToken;
+ @NonNull
+ private final DeviceState mRequestedState;
+ private final int mFlags;
+
+ private int mStatus = STATUS_UNKNOWN;
+ private int mLastNotifiedStatus = STATUS_UNKNOWN;
+
+ OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token,
+ @NonNull DeviceState requestedState, int flags) {
+ mProcessRecord = processRecord;
+ mToken = token;
+ mRequestedState = requestedState;
+ mFlags = flags;
+ }
+
+ public void setStatusLocked(int status) {
+ setStatusLocked(status, true /* markDirty */);
+ }
+
+ public void setStatusLocked(int status, boolean markDirty) {
+ if (mStatus != status) {
+ if (mStatus == STATUS_CANCELED) {
+ throw new IllegalStateException(
+ "Can not alter the status of a request after set to CANCELED.");
+ }
+
+ mStatus = status;
+
+ if (mStatus == STATUS_CANCELED) {
+ mRequestRecords.remove(this);
+ mProcessRecord.mRequestRecords.remove(mToken);
+ }
+
+ if (markDirty) {
+ mRequestsPendingStatusChange.add(this);
+ }
+ }
+ }
+
+ public void notifyStatusIfNeeded() {
+ int stateToReport;
+ synchronized (mLock) {
+ if (mLastNotifiedStatus == mStatus) {
+ return;
+ }
+
+ stateToReport = mStatus;
+ mLastNotifiedStatus = mStatus;
+ }
+
+ if (stateToReport == STATUS_ACTIVE) {
+ mProcessRecord.notifyRequestActiveAsync(this);
+ } else if (stateToReport == STATUS_SUSPENDED) {
+ mProcessRecord.notifyRequestSuspendedAsync(this);
+ } else if (stateToReport == STATUS_CANCELED) {
+ mProcessRecord.notifyRequestCanceledAsync(this);
+ }
+ }
}
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
@@ -483,13 +757,59 @@
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
- registerCallbackInternal(callback, callingPid);
+ registerProcess(callingPid, callback);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
+ public int[] getSupportedDeviceStates() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getSupportedStateIdentifiers();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void requestState(IBinder token, int state, int flags) {
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to request device state.");
+
+ if (token == null) {
+ throw new IllegalArgumentException("Request token must not be null.");
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ requestStateInternal(state, flags, callingPid, token);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelRequest(IBinder token) {
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to clear requested device state.");
+
+ if (token == null) {
+ throw new IllegalArgumentException("Request token must not be null.");
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ cancelRequestInternal(callingPid, token);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver result) {
new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index cf3b297..6cc55a6 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,11 +16,17 @@
package com.android.server.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static android.Manifest.permission.CONTROL_DEVICE_STATE;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateRequest;
+import android.os.Binder;
import android.os.ShellCommand;
import java.io.PrintWriter;
+import java.util.Optional;
/**
* ShellCommands for {@link DeviceStateManagerService}.
@@ -28,10 +34,15 @@
* Use with {@code adb shell cmd device_state ...}.
*/
public class DeviceStateManagerShellCommand extends ShellCommand {
- private final DeviceStateManagerService mInternal;
+ @Nullable
+ private static DeviceStateRequest sLastRequest;
+
+ private final DeviceStateManagerService mService;
+ private final DeviceStateManager mClient;
public DeviceStateManagerShellCommand(DeviceStateManagerService service) {
- mInternal = service;
+ mService = service;
+ mClient = service.getContext().getSystemService(DeviceStateManager.class);
}
@Override
@@ -52,24 +63,15 @@
}
private void printState(PrintWriter pw) {
- int committedState = mInternal.getCommittedState();
- int requestedState = mInternal.getRequestedState();
- int requestedOverrideState = mInternal.getOverrideState();
+ DeviceState committedState = mService.getCommittedState();
+ Optional<DeviceState> baseState = mService.getBaseState();
+ Optional<DeviceState> overrideState = mService.getOverrideState();
- if (committedState == INVALID_DEVICE_STATE) {
- pw.println("Device state: (invalid)");
- } else {
- pw.println("Device state: " + committedState);
- }
-
- if (requestedOverrideState != INVALID_DEVICE_STATE) {
+ pw.println("Committed state: " + committedState);
+ if (overrideState.isPresent()) {
pw.println("----------------------");
- if (requestedState == INVALID_DEVICE_STATE) {
- pw.println("Base state: (invalid)");
- } else {
- pw.println("Base state: " + requestedState);
- }
- pw.println("Override state: " + committedState);
+ pw.println("Base state: " + baseState.orElse(null));
+ pw.println("Override state: " + overrideState.get());
}
}
@@ -77,40 +79,56 @@
final String nextArg = getNextArg();
if (nextArg == null) {
printState(pw);
- } else if ("reset".equals(nextArg)) {
- mInternal.clearOverrideState();
- } else {
- int requestedState;
- try {
- requestedState = Integer.parseInt(nextArg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: requested state should be an integer");
- return -1;
- }
-
- boolean success = mInternal.setOverrideState(requestedState);
- if (!success) {
- getErrPrintWriter().println("Error: failed to set override state. Run:");
- getErrPrintWriter().println("");
- getErrPrintWriter().println(" print-states");
- getErrPrintWriter().println("");
- getErrPrintWriter().println("to get the list of currently supported device states");
- return -1;
- }
}
+
+ final Context context = mService.getContext();
+ context.enforceCallingOrSelfPermission(
+ CONTROL_DEVICE_STATE,
+ "Permission required to request device state.");
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ if ("reset".equals(nextArg)) {
+ if (sLastRequest != null) {
+ mClient.cancelRequest(sLastRequest);
+ sLastRequest = null;
+ }
+ } else {
+ int requestedState = Integer.parseInt(nextArg);
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();
+
+ mClient.requestState(request, null /* executor */, null /* callback */);
+ if (sLastRequest != null) {
+ mClient.cancelRequest(sLastRequest);
+ }
+
+ sLastRequest = request;
+ }
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: requested state should be an integer");
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e.getMessage());
+ getErrPrintWriter().println("-------------------");
+ getErrPrintWriter().println("Run:");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println(" print-states");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println("to get the list of currently supported device states");
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+
return 0;
}
private int runPrintStates(PrintWriter pw) {
- int[] states = mInternal.getSupportedStates();
- pw.print("Supported states: [ ");
+ DeviceState[] states = mService.getSupportedStates();
+ pw.print("Supported states: [\n");
for (int i = 0; i < states.length; i++) {
- pw.print(states[i]);
- if (i < states.length - 1) {
- pw.print(", ");
- }
+ pw.print(" " + states[i] + ",\n");
}
- pw.println(" ]");
+ pw.println("]");
return 0;
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 0e8bf9b..2d4377f 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -16,9 +16,11 @@
package com.android.server.devicestate;
+import android.annotation.IntRange;
+
/**
- * Responsible for providing the set of currently supported device states and well as the current
- * device state.
+ * Responsible for providing the set of supported {@link DeviceState device states} as well as the
+ * current device state.
*
* @see DeviceStatePolicy
*/
@@ -26,8 +28,8 @@
/**
* Registers a listener for changes in provider state.
* <p>
- * It is <b>required</b> that {@link Listener#onSupportedDeviceStatesChanged(int[])} be called
- * followed by {@link Listener#onStateChanged(int)} with the initial values on successful
+ * It is <b>required</b> that {@link Listener#onSupportedDeviceStatesChanged(DeviceState[])} be
+ * called followed by {@link Listener#onStateChanged(int)} with the initial values on successful
* registration of the listener.
*/
void setListener(Listener listener);
@@ -35,35 +37,36 @@
/** Callback for changes in {@link DeviceStateProvider} state. */
interface Listener {
/**
- * Called to notify the listener of a change in supported device states. Required to be
- * called once on successful registration of the listener and then once on every
- * subsequent change in supported device states.
+ * Called to notify the listener of a change in supported {@link DeviceState device states}.
+ * Required to be called once on successful registration of the listener and then once on
+ * every subsequent change in supported device states.
* <p>
* The set of device states can change based on the current hardware state of the device.
* For example, if a device state depends on a particular peripheral device (display, etc)
* it would only be reported as supported when the device is plugged. Otherwise, it should
* not be included in the set of supported states.
* <p>
- * All values provided must be greater than or equal to zero and there must always be at
- * least one supported device state.
+ * The identifier for every provided device state must be unique and greater than or equal
+ * to zero and there must always be at least one supported device state.
*
* @param newDeviceStates array of supported device states.
*
* @throws IllegalArgumentException if the list of device states is empty or if one of the
- * provided states is less than 0.
+ * provided states contains an invalid identifier.
*/
- void onSupportedDeviceStatesChanged(int[] newDeviceStates);
+ void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates);
/**
* Called to notify the listener of a change in current device state. Required to be called
* once on successful registration of the listener and then once on every subsequent change
* in device state. Value must have been included in the set of supported device states
- * provided in the most recent call to {@link #onSupportedDeviceStatesChanged(int[])}.
+ * provided in the most recent call to
+ * {@link #onSupportedDeviceStatesChanged(DeviceState[])}.
*
- * @param state the new device state.
+ * @param identifier the identifier of the new device state.
*
* @throws IllegalArgumentException if the state is less than 0.
*/
- void onStateChanged(int state);
+ void onStateChanged(@IntRange(from = 0) int identifier);
}
}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index b8e579d..4832e46 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -300,6 +300,8 @@
/** @return The default brightness configuration. */
public abstract BrightnessConfiguration getDefaultConfig();
+ /** Recalculates the backlight-to-nits and nits-to-backlight splines. */
+ public abstract void recalculateSplines(boolean applyAdjustment, float[] adjustment);
/**
* Returns the timeout for the short term model
@@ -658,6 +660,11 @@
}
@Override
+ public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
+ // Do nothing.
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("SimpleMappingStrategy");
pw.println(" mSpline=" + mSpline);
@@ -696,7 +703,7 @@
// A spline mapping from nits to the corresponding backlight value, normalized to the range
// [0, 1.0].
- private final Spline mNitsToBacklightSpline;
+ private Spline mNitsToBacklightSpline;
// The default brightness configuration.
private final BrightnessConfiguration mDefaultConfig;
@@ -705,6 +712,11 @@
// a brightness in nits.
private Spline mBacklightToNitsSpline;
+ private float[] mNits;
+ private int[] mBacklight;
+
+ private boolean mBrightnessRangeAdjustmentApplied;
+
private float mMaxGamma;
private float mAutoBrightnessAdjustment;
private float mUserLux;
@@ -726,15 +738,9 @@
mUserLux = -1;
mUserBrightness = -1;
- // Setup the backlight spline
- final int N = nits.length;
- float[] normalizedBacklight = new float[N];
- for (int i = 0; i < N; i++) {
- normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
- }
-
- mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
- mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
+ mNits = nits;
+ mBacklight = backlight;
+ computeNitsBrightnessSplines(mNits);
mDefaultConfig = config;
if (mLoggingEnabled) {
@@ -868,6 +874,12 @@
}
@Override
+ public void recalculateSplines(boolean applyAdjustment, float[] adjustedNits) {
+ mBrightnessRangeAdjustmentApplied = applyAdjustment;
+ computeNitsBrightnessSplines(mBrightnessRangeAdjustmentApplied ? adjustedNits : mNits);
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("PhysicalMappingStrategy");
pw.println(" mConfig=" + mConfig);
@@ -878,6 +890,18 @@
pw.println(" mUserLux=" + mUserLux);
pw.println(" mUserBrightness=" + mUserBrightness);
pw.println(" mDefaultConfig=" + mDefaultConfig);
+ pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
+ }
+
+ private void computeNitsBrightnessSplines(float[] nits) {
+ final int len = nits.length;
+ float[] normalizedBacklight = new float[len];
+ for (int i = 0; i < len; i++) {
+ normalizedBacklight[i] = normalizeAbsoluteBrightness(mBacklight[i]);
+ }
+
+ mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
+ mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
}
private void computeSpline() {
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 2375f74..a186e33 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -63,7 +63,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.RingBuffer;
import com.android.server.LocalServices;
@@ -71,7 +70,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
@@ -80,7 +78,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -118,6 +115,9 @@
private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
private static final String ATTR_NIGHT_MODE = "nightMode";
private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
+ private static final String ATTR_REDUCE_BRIGHT_COLORS = "reduceBrightColors";
+ private static final String ATTR_REDUCE_BRIGHT_COLORS_STRENGTH = "reduceBrightColorsStrength";
+ private static final String ATTR_REDUCE_BRIGHT_COLORS_OFFSET = "reduceBrightColorsOffset";
private static final String ATTR_LAST_NITS = "lastNits";
private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
private static final String ATTR_POWER_SAVE = "powerSaveFactor";
@@ -398,6 +398,10 @@
builder.setNightMode(mInjector.isNightDisplayActivated(mContext));
builder.setColorTemperature(mInjector.getNightDisplayColorTemperature(mContext));
+ builder.setReduceBrightColors(mInjector.isReduceBrightColorsActivated(mContext));
+ builder.setReduceBrightColorsStrength(mInjector.getReduceBrightColorsStrength(mContext));
+ builder.setReduceBrightColorsOffset(mInjector.getReduceBrightColorsOffsetFactor(mContext)
+ * brightness);
if (mColorSamplingEnabled) {
DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample);
@@ -563,6 +567,12 @@
out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode);
out.attributeInt(null, ATTR_COLOR_TEMPERATURE,
toWrite[i].colorTemperature);
+ out.attributeBoolean(null, ATTR_REDUCE_BRIGHT_COLORS,
+ toWrite[i].reduceBrightColors);
+ out.attributeInt(null, ATTR_REDUCE_BRIGHT_COLORS_STRENGTH,
+ toWrite[i].reduceBrightColorsStrength);
+ out.attributeFloat(null, ATTR_REDUCE_BRIGHT_COLORS_OFFSET,
+ toWrite[i].reduceBrightColorsOffset);
out.attributeFloat(null, ATTR_LAST_NITS,
toWrite[i].lastBrightness);
out.attributeBoolean(null, ATTR_DEFAULT_CONFIG,
@@ -641,6 +651,12 @@
builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE));
builder.setColorTemperature(
parser.getAttributeInt(null, ATTR_COLOR_TEMPERATURE));
+ builder.setReduceBrightColors(
+ parser.getAttributeBoolean(null, ATTR_REDUCE_BRIGHT_COLORS));
+ builder.setReduceBrightColorsStrength(
+ parser.getAttributeInt(null, ATTR_REDUCE_BRIGHT_COLORS_STRENGTH));
+ builder.setReduceBrightColorsOffset(
+ parser.getAttributeFloat(null, ATTR_REDUCE_BRIGHT_COLORS_OFFSET));
builder.setLastBrightness(parser.getAttributeFloat(null, ATTR_LAST_NITS));
String luxValue = parser.getAttributeValue(null, ATTR_LUX);
@@ -1114,6 +1130,21 @@
return context.getSystemService(ColorDisplayManager.class).isNightDisplayActivated();
}
+ public int getReduceBrightColorsStrength(Context context) {
+ return context.getSystemService(ColorDisplayManager.class)
+ .getReduceBrightColorsStrength();
+ }
+
+ public float getReduceBrightColorsOffsetFactor(Context context) {
+ return context.getSystemService(ColorDisplayManager.class)
+ .getReduceBrightColorsOffsetFactor();
+ }
+
+ public boolean isReduceBrightColorsActivated(Context context) {
+ return context.getSystemService(ColorDisplayManager.class)
+ .isReduceBrightColorsActivated();
+ }
+
public DisplayedContentSample sampleColor(int noFramesToSample) {
final DisplayManagerInternal displayManagerInternal =
LocalServices.getService(DisplayManagerInternal.class);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 2641ee7..bf16a6d 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -23,6 +23,7 @@
import android.view.DisplayAddress;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
+import android.view.RoundedCorners;
import android.view.Surface;
import com.android.internal.BrightnessSynchronizer;
@@ -281,6 +282,11 @@
public DisplayCutout displayCutout;
/**
+ * The {@link RoundedCorners} if present or {@code null} otherwise.
+ */
+ public RoundedCorners roundedCorners;
+
+ /**
* The touch attachment, per {@link DisplayViewport#touch}.
*/
public int touch;
@@ -401,7 +407,8 @@
|| !BrightnessSynchronizer.floatEquals(brightnessMinimum, other.brightnessMinimum)
|| !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
|| !BrightnessSynchronizer.floatEquals(brightnessDefault,
- other.brightnessDefault)) {
+ other.brightnessDefault)
+ || !Objects.equals(roundedCorners, other.roundedCorners)) {
diff |= DIFF_OTHER;
}
return diff;
@@ -444,6 +451,7 @@
brightnessMinimum = other.brightnessMinimum;
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
+ roundedCorners = other.roundedCorners;
}
// For debugging purposes
@@ -487,6 +495,9 @@
sb.append(", brightnessMinimum ").append(brightnessMinimum);
sb.append(", brightnessMaximum ").append(brightnessMaximum);
sb.append(", brightnessDefault ").append(brightnessDefault);
+ if (roundedCorners != null) {
+ sb.append(", roundedCorners ").append(roundedCorners);
+ }
sb.append(flagsToString(flags));
sb.append("}");
return sb.toString();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e0baee7..0950d5d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -181,8 +181,8 @@
private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable";
private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
-
private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
+ private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.1f;
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
@@ -371,7 +371,7 @@
private final boolean mAllowNonNativeRefreshRateOverride;
- private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.1f;
+ private final BrightnessSynchronizer mBrightnessSynchronizer;
/**
* Applications use {@link android.view.Display#getRefreshRate} and
@@ -408,6 +408,7 @@
mLogicalDisplayMapper = new LogicalDisplayMapper(context, mDisplayDeviceRepo,
new LogicalDisplayListener());
mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
+ mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
Resources resources = mContext.getResources();
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
@@ -513,8 +514,8 @@
DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
- deviceStateManager.registerDeviceStateListener(new DeviceStateListener(),
- new HandlerExecutor(mHandler));
+ deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler),
+ new DeviceStateListener());
scheduleTraversalLocked(false);
}
@@ -542,6 +543,7 @@
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
mSettingsObserver = new SettingsObserver();
+ mBrightnessSynchronizer.startSynchronizing();
}
@VisibleForTesting
@@ -2761,22 +2763,8 @@
public boolean requestPowerState(int groupId, DisplayPowerRequest request,
boolean waitForNegativeProximity) {
synchronized (mSyncRoot) {
- final DisplayGroup displayGroup = mLogicalDisplayMapper.getDisplayGroupLocked(
- groupId);
- if (displayGroup == null) {
- return true;
- }
-
- final int size = displayGroup.getSizeLocked();
- boolean ready = true;
- for (int i = 0; i < size; i++) {
- final DisplayPowerController displayPowerController =
- mDisplayPowerControllers.get(displayGroup.getIdLocked(i));
- ready &= displayPowerController.requestPowerState(request,
- waitForNegativeProximity);
- }
-
- return ready;
+ return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
+ .requestPowerState(request, waitForNegativeProximity);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 13dc0b9..2df33652 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -47,6 +47,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
import android.util.TimeUtils;
@@ -58,6 +59,8 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
+import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
@@ -152,6 +155,7 @@
private final DisplayPowerCallbacks mCallbacks;
// Battery stats.
+ @Nullable
private final IBatteryStats mBatteryStats;
// The sensor manager.
@@ -170,6 +174,7 @@
private final int mDisplayId;
// Tracker for brightness changes.
+ @Nullable
private final BrightnessTracker mBrightnessTracker;
// Tracker for brightness settings changes.
@@ -346,6 +351,9 @@
@Nullable
private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
+ private final ColorDisplayServiceInternal mCdsi;
+ private final float[] mNitsRange;
+
// A record of state for skipping brightness ramps.
private int mSkipRampState = RAMP_STATE_SKIP_NONE;
@@ -401,30 +409,30 @@
private ObjectAnimator mColorFadeOffAnimator;
private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
- // The brightness synchronizer to allow changes in the int brightness value to be reflected in
- // the float brightness value and vice versa.
- @Nullable
- private final BrightnessSynchronizer mBrightnessSynchronizer;
-
/**
* Creates the display power controller.
*/
public DisplayPowerController(Context context,
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay) {
+ mLogicalDisplay = logicalDisplay;
+ mDisplayId = mLogicalDisplay.getDisplayIdLocked();
mHandler = new DisplayControllerHandler(handler.getLooper());
- mBrightnessTracker = new BrightnessTracker(context, null);
+
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mBrightnessTracker = new BrightnessTracker(context, null);
+ mBatteryStats = BatteryStatsService.getService();
+ } else {
+ mBrightnessTracker = null;
+ mBatteryStats = null;
+ }
+
mSettingsObserver = new SettingsObserver(mHandler);
mCallbacks = callbacks;
- mBatteryStats = BatteryStatsService.getService();
mSensorManager = sensorManager;
mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
mBlanker = blanker;
mContext = context;
- mBrightnessSynchronizer = new BrightnessSynchronizer(context);
- mBrightnessSynchronizer.startSynchronizing();
- mLogicalDisplay = logicalDisplay;
- mDisplayId = mLogicalDisplay.getDisplayIdLocked();
PowerManager pm = context.getSystemService(PowerManager.class);
@@ -455,8 +463,12 @@
mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(
pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR));
+ // Check the setting, but also verify that it is the default display. Only the default
+ // display has an automatic brightness controller running.
+ // TODO: b/179021925 - Fix to work with multiple displays
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
- com.android.internal.R.bool.config_automatic_brightness_available);
+ com.android.internal.R.bool.config_automatic_brightness_available)
+ && mDisplayId == Display.DEFAULT_DISPLAY;
mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
@@ -552,7 +564,9 @@
mBrightnessBucketsInDozeConfig = resources.getBoolean(
com.android.internal.R.bool.config_displayBrightnessBucketsInDoze);
- if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
+ if (mDisplayId == Display.DEFAULT_DISPLAY && !DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
+ // TODO: b/178385123 Once there are sensor associations, we can enable proximity for
+ // non-default displays.
mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (mProximitySensor != null) {
mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
@@ -580,6 +594,42 @@
}
mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
mDisplayWhiteBalanceController = displayWhiteBalanceController;
+
+ if (displayDeviceConfig != null && displayDeviceConfig.getNits() != null) {
+ mNitsRange = displayDeviceConfig.getNits();
+ } else {
+ Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back");
+ mNitsRange = BrightnessMappingStrategy.getFloatArray(context.getResources()
+ .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
+ }
+ mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
+ boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
+ @Override
+ public void onReduceBrightColorsActivationChanged(boolean activated) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+
+ @Override
+ public void onReduceBrightColorsStrengthChanged(int strength) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ });
+ if (active) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ }
+
+ private void applyReduceBrightColorsSplineAdjustment() {
+ if (mBrightnessMapper == null) {
+ Log.w(TAG, "No brightness mapping available to recalculate splines");
+ return;
+ }
+
+ float[] adjustedNits = new float[mNitsRange.length];
+ for (int i = 0; i < mNitsRange.length; i++) {
+ adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
+ }
+ mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits);
}
private Sensor findDisplayLightSensor(String sensorType) {
@@ -607,18 +657,28 @@
* @param userId userId to fetch data for
* @param includePackage if false will null out the package name in events
*/
+ @Nullable
public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
@UserIdInt int userId, boolean includePackage) {
+ if (mBrightnessTracker == null) {
+ return null;
+ }
return mBrightnessTracker.getEvents(userId, includePackage);
}
public void onSwitchUser(@UserIdInt int newUserId) {
handleSettingsChange(true /* userSwitch */);
- mBrightnessTracker.onSwitchUser(newUserId);
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.onSwitchUser(newUserId);
+ }
}
+ @Nullable
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@UserIdInt int userId) {
+ if (mBrightnessTracker == null) {
+ return null;
+ }
return mBrightnessTracker.getAmbientBrightnessStats(userId);
}
@@ -626,7 +686,9 @@
* Persist the brightness slider events and ambient brightness stats to disk.
*/
public void persistBrightnessTrackerState() {
- mBrightnessTracker.persistBrightnessTrackerState();
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.persistBrightnessTrackerState();
+ }
}
/**
@@ -730,20 +792,16 @@
mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT);
mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
- // Initialize screen state for battery stats.
- try {
- mBatteryStats.noteScreenState(mPowerState.getScreenState());
- mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(
- mPowerState.getScreenBrightness()));
- } catch (RemoteException ex) {
- // same process
- }
+ noteScreenState(mPowerState.getScreenState());
+ noteScreenBrightness(mPowerState.getScreenBrightness());
+
// Initialize all of the brightness tracking state
final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt(
mPowerState.getScreenBrightness()));
if (brightness >= 0.0f) {
mBrightnessTracker.start(brightness);
}
+
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT),
false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
@@ -1330,11 +1388,7 @@
SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
mPowerState.setScreenState(state);
// Tell battery stats about the transition.
- try {
- mBatteryStats.noteScreenState(state);
- } catch (RemoteException ex) {
- // same process
- }
+ noteScreenState(state);
}
}
@@ -1406,13 +1460,7 @@
Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
// TODO(b/153319140) remove when we can get this from the above trace invocation
SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
- try {
- // TODO(brightnessfloat): change BatteryStats to use float
- mBatteryStats.noteScreenBrightness(
- BrightnessSynchronizer.brightnessFloatToInt(target));
- } catch (RemoteException ex) {
- // same process
- }
+ noteScreenBrightness(target);
}
}
@@ -1731,15 +1779,21 @@
}
private void putScreenBrightnessSetting(float brightnessValue) {
- mCurrentScreenBrightnessSetting = brightnessValue;
- Settings.System.putFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessValue, UserHandle.USER_CURRENT);
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mCurrentScreenBrightnessSetting = brightnessValue;
+ Settings.System.putFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessValue,
+ UserHandle.USER_CURRENT);
+ }
}
private void putAutoBrightnessAdjustmentSetting(float adjustment) {
- mAutoBrightnessAdjustment = adjustment;
- Settings.System.putFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment, UserHandle.USER_CURRENT);
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mAutoBrightnessAdjustment = adjustment;
+ Settings.System.putFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment,
+ UserHandle.USER_CURRENT);
+ }
}
private boolean updateAutoBrightnessAdjustment() {
@@ -2020,6 +2074,29 @@
return MathUtils.constrain(value, -1.0f, 1.0f);
}
+ private void noteScreenState(int screenState) {
+ if (mBatteryStats != null) {
+ try {
+ // TODO(multi-display): make this multi-display
+ mBatteryStats.noteScreenState(screenState);
+ } catch (RemoteException e) {
+ // same process
+ }
+ }
+ }
+
+ private void noteScreenBrightness(float brightness) {
+ if (mBatteryStats != null) {
+ try {
+ // TODO(brightnessfloat): change BatteryStats to use float
+ mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(
+ brightness));
+ } catch (RemoteException e) {
+ // same process
+ }
+ }
+ }
+
private final class DisplayControllerHandler extends Handler {
public DisplayControllerHandler(Looper looper) {
super(looper, null, true /*async*/);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 8198e51..7e6a137 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -35,6 +35,7 @@
import android.view.DisplayAddress;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
+import android.view.RoundedCorners;
import android.view.SurfaceControl;
import com.android.internal.BrightnessSynchronizer;
@@ -604,6 +605,8 @@
}
mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
mInfo.width, mInfo.height);
+ mInfo.roundedCorners = RoundedCorners.fromResources(
+ res, mInfo.width, mInfo.height);
} else {
if (!res.getBoolean(
com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 86de159..80781d2 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -195,6 +195,7 @@
info.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
+ info.roundedCorners = mOverrideDisplayInfo.roundedCorners;
}
mInfo.set(info);
}
@@ -386,6 +387,7 @@
mBaseDisplayInfo.brightnessMinimum = deviceInfo.brightnessMinimum;
mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
mBaseDisplayInfo.brightnessDefault = deviceInfo.brightnessDefault;
+ mBaseDisplayInfo.roundedCorners = deviceInfo.roundedCorners;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 8ee01be..16c4b26 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -107,7 +107,7 @@
private final DisplayDeviceRepository mDisplayDeviceRepo;
private final Listener mListener;
- private final int mFoldedDeviceState;
+ private final int[] mFoldedDeviceStates;
LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener) {
mDisplayDeviceRepo = repo;
@@ -115,8 +115,8 @@
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
mDisplayDeviceRepo.addListener(this);
- mFoldedDeviceState = context.getResources().getInteger(
- com.android.internal.R.integer.config_foldedDeviceState);
+ mFoldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
loadFoldedDisplayConfig(context);
}
@@ -232,7 +232,14 @@
}
void setDeviceStateLocked(int state) {
- setDeviceFoldedLocked(state == mFoldedDeviceState);
+ boolean folded = false;
+ for (int i = 0; i < mFoldedDeviceStates.length; i++) {
+ if (state == mFoldedDeviceStates[i]) {
+ folded = true;
+ break;
+ }
+ }
+ setDeviceFoldedLocked(folded);
}
void setDeviceFoldedLocked(boolean isFolded) {
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 4706edc..88b2668 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -173,6 +173,7 @@
private ContentObserver mContentObserver;
private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
+ private ReduceBrightColorsListener mReduceBrightColorsListener;
private NightDisplayAutoMode mNightDisplayAutoMode;
@@ -617,18 +618,24 @@
if (mCurrentUser == UserHandle.USER_NULL) {
return;
}
- mReduceBrightColorsTintController.setActivated(
- Secure.getIntForUser(getContext().getContentResolver(),
- Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1);
+ final boolean activated = Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1;
+ mReduceBrightColorsTintController.setActivated(activated);
+ if (mReduceBrightColorsListener != null) {
+ mReduceBrightColorsListener.onReduceBrightColorsActivationChanged(activated);
+ }
}
private void onReduceBrightColorsStrengthLevelChanged() {
if (mCurrentUser == UserHandle.USER_NULL) {
return;
}
- mReduceBrightColorsTintController.setMatrix(
- Secure.getIntForUser(getContext().getContentResolver(),
- Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mCurrentUser));
+ final int strength = Secure.getIntForUser(getContext().getContentResolver(),
+ Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mCurrentUser);
+ mReduceBrightColorsTintController.setMatrix(strength);
+ if (mReduceBrightColorsListener != null) {
+ mReduceBrightColorsListener.onReduceBrightColorsStrengthChanged(strength);
+ }
}
/**
@@ -762,6 +769,22 @@
mCurrentUser) == 1;
}
+ private boolean setReduceBrightColorsActivatedInternal(boolean activated) {
+ if (mCurrentUser == UserHandle.USER_NULL) {
+ return false;
+ }
+ return Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, activated ? 1 : 0, mCurrentUser);
+ }
+
+ private boolean setReduceBrightColorsStrengthInternal(int strength) {
+ if (mCurrentUser == UserHandle.USER_NULL) {
+ return false;
+ }
+ return Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.REDUCE_BRIGHT_COLORS_LEVEL, strength, mCurrentUser);
+ }
+
private boolean isDeviceColorManagedInternal() {
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
return dtm.isDeviceColorManaged();
@@ -1469,6 +1492,31 @@
}
/**
+ * Sets the listener and returns whether reduce bright colors is currently enabled.
+ */
+ public boolean setReduceBrightColorsListener(ReduceBrightColorsListener listener) {
+ mReduceBrightColorsListener = listener;
+ return mReduceBrightColorsTintController.isActivated();
+ }
+
+ /**
+ * Returns whether reduce bright colors is currently active.
+ */
+ public boolean isReduceBrightColorsActivated() {
+ return mReduceBrightColorsTintController.isActivated();
+ }
+
+ /**
+ * Gets the computed brightness, in nits, when the reduce bright colors feature is applied
+ * at the current strength.
+ *
+ * @hide
+ */
+ public float getReduceBrightColorsAdjustedBrightnessNits(float nits) {
+ return mReduceBrightColorsTintController.getAdjustedBrightness(nits);
+ }
+
+ /**
* Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
* invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
*/
@@ -1491,6 +1539,22 @@
void onDisplayWhiteBalanceStatusChanged(boolean activated);
}
+ /**
+ * Listener for changes in reduce bright colors status.
+ */
+ public interface ReduceBrightColorsListener {
+
+ /**
+ * Notify that the reduce bright colors activation status has changed.
+ */
+ void onReduceBrightColorsActivationChanged(boolean activated);
+
+ /**
+ * Notify that the reduce bright colors strength has changed.
+ */
+ void onReduceBrightColorsStrengthChanged(int strength);
+ }
+
private final class TintHandler extends Handler {
private TintHandler(Looper looper) {
@@ -1787,6 +1851,62 @@
}
@Override
+ public boolean isReduceBrightColorsActivated() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mReduceBrightColorsTintController.isActivated();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean setReduceBrightColorsActivated(boolean activated) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set reduce bright colors activation state");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return setReduceBrightColorsActivatedInternal(activated);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public int getReduceBrightColorsStrength() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mReduceBrightColorsTintController.getStrength();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public float getReduceBrightColorsOffsetFactor() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mReduceBrightColorsTintController.getOffsetFactor();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean setReduceBrightColorsStrength(int strength) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to set reduce bright colors strength");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return setReduceBrightColorsStrengthInternal(strength);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
diff --git a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
index 7e120c9..cb93cc8 100644
--- a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
+++ b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
@@ -34,7 +34,7 @@
public class ReduceBrightColorsTintController extends TintController {
private final float[] mMatrix = new float[16];
- private final float[] mCoefficients = new float[9];
+ private final float[] mCoefficients = new float[3];
private int mStrength;
@@ -42,8 +42,8 @@
public void setUp(Context context, boolean needsLinear) {
final String[] coefficients = context.getResources().getStringArray(
needsLinear ? R.array.config_reduceBrightColorsCoefficients
- : R.array.config_reduceBrightColorsCoefficientsNative);
- for (int i = 0; i < 9 && i < coefficients.length; i++) {
+ : R.array.config_reduceBrightColorsCoefficientsNonlinear);
+ for (int i = 0; i < 3 && i < coefficients.length; i++) {
mCoefficients[i] = Float.parseFloat(coefficients[i]);
}
}
@@ -67,20 +67,11 @@
Matrix.setIdentityM(mMatrix, 0);
- final float percentageStrength = strengthLevel / 100f;
- final float squaredPercentageStrength = percentageStrength * percentageStrength;
- final float red =
- squaredPercentageStrength * mCoefficients[0] + percentageStrength * mCoefficients[1]
- + mCoefficients[2];
- final float green =
- squaredPercentageStrength * mCoefficients[3] + percentageStrength * mCoefficients[4]
- + mCoefficients[5];
- final float blue =
- squaredPercentageStrength * mCoefficients[6] + percentageStrength * mCoefficients[7]
- + mCoefficients[8];
- mMatrix[0] = clamp(red);
- mMatrix[5] = clamp(green);
- mMatrix[10] = clamp(blue);
+ // All three (r,g,b) components are equal and calculated with the same formula.
+ final float componentValue = computeComponentValue(strengthLevel);
+ mMatrix[0] = componentValue;
+ mMatrix[5] = componentValue;
+ mMatrix[10] = componentValue;
}
private float clamp(float value) {
@@ -110,4 +101,26 @@
public int getStrength() {
return mStrength;
}
+
+ /** Returns the offset factor at Ymax. */
+ public float getOffsetFactor() {
+ // Strength terms drop out as strength --> 1, leaving the coefficients.
+ return mCoefficients[0] + mCoefficients[1] + mCoefficients[2];
+ }
+
+ /**
+ * Returns the effective brightness (in nits), which has been adjusted to account for the effect
+ * of the bright color reduction.
+ */
+ public float getAdjustedBrightness(float nits) {
+ return computeComponentValue(mStrength) * nits;
+ }
+
+ private float computeComponentValue(int strengthLevel) {
+ final float percentageStrength = strengthLevel / 100f;
+ final float squaredPercentageStrength = percentageStrength * percentageStrength;
+ return clamp(
+ squaredPercentageStrength * mCoefficients[0] + percentageStrength * mCoefficients[1]
+ + mCoefficients[2]);
+ }
}
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 8e5215b..1b27572 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -24,9 +24,8 @@
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontFileUtil;
import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontUpdateRequest;
import android.graphics.fonts.SystemFonts;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SharedMemory;
import android.os.ShellCallback;
@@ -55,6 +54,7 @@
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -67,20 +67,20 @@
private static final String CRASH_MARKER_FILE = "/data/fonts/config/crash.txt";
@Override
- public FontConfig getFontConfig() throws RemoteException {
+ public FontConfig getFontConfig() {
return getSystemFontConfig();
}
@Override
- public int updateFont(ParcelFileDescriptor fd, byte[] signature, int baseVersion)
- throws RemoteException {
- Objects.requireNonNull(fd);
- Objects.requireNonNull(signature);
+ public int updateFont(int baseVersion, @NonNull FontUpdateRequest request) {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(request.getFd());
+ Objects.requireNonNull(request.getSignature());
Preconditions.checkArgumentNonnegative(baseVersion);
getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
"UPDATE_FONTS permission required.");
try {
- installFontFile(fd.getFileDescriptor(), signature, baseVersion);
+ update(baseVersion, Collections.singletonList(request));
return FontManager.RESULT_SUCCESS;
} catch (SystemFontException e) {
Slog.e(TAG, "Failed to update font file", e);
@@ -183,14 +183,21 @@
@NonNull
private final Context mContext;
- @GuardedBy("FontManagerService.this")
+ private final Object mUpdatableFontDirLock = new Object();
+
+ @GuardedBy("mUpdatableFontDirLock")
@NonNull
private final FontCrashDetector mFontCrashDetector;
+ @GuardedBy("mUpdatableFontDirLock")
@Nullable
private final UpdatableFontDir mUpdatableFontDir;
- @GuardedBy("FontManagerService.this")
+ // mSerializedFontMapLock can be acquired while holding mUpdatableFontDirLock.
+ // mUpdatableFontDirLock should not be newly acquired while holding mSerializedFontMapLock.
+ private final Object mSerializedFontMapLock = new Object();
+
+ @GuardedBy("mSerializedFontMapLock")
@Nullable
private SharedMemory mSerializedFontMap = null;
@@ -212,9 +219,15 @@
}
private void initialize() {
- synchronized (FontManagerService.this) {
+ synchronized (mUpdatableFontDirLock) {
if (mUpdatableFontDir == null) {
- mSerializedFontMap = buildNewSerializedFontMap();
+ synchronized (mSerializedFontMapLock) {
+ try {
+ mSerializedFontMap = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+ } catch (IOException | ErrnoException e) {
+ mSerializedFontMap = null;
+ }
+ }
return;
}
if (mFontCrashDetector.hasCrashed()) {
@@ -228,7 +241,7 @@
}
try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
mUpdatableFontDir.loadFontFileMap();
- mSerializedFontMap = buildNewSerializedFontMap();
+ updateSerializedFontMap();
}
}
}
@@ -239,19 +252,19 @@
}
@Nullable /* package */ SharedMemory getCurrentFontMap() {
- synchronized (FontManagerService.this) {
+ synchronized (mSerializedFontMapLock) {
return mSerializedFontMap;
}
}
- /* package */ void installFontFile(FileDescriptor fd, byte[] pkcs7Signature, int baseVersion)
+ /* package */ void update(int baseVersion, List<FontUpdateRequest> requests)
throws SystemFontException {
if (mUpdatableFontDir == null) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
"The font updater is disabled.");
}
- synchronized (FontManagerService.this) {
+ synchronized (mUpdatableFontDirLock) {
// baseVersion == -1 only happens from shell command. This is filtered and treated as
// error from SystemApi call.
if (baseVersion != -1 && mUpdatableFontDir.getConfigVersion() != baseVersion) {
@@ -260,8 +273,8 @@
"The base config version is older than current.");
}
try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
- mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
- mSerializedFontMap = buildNewSerializedFontMap();
+ mUpdatableFontDir.update(requests);
+ updateSerializedFontMap();
}
}
}
@@ -272,10 +285,10 @@
FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
"The font updater is disabled.");
}
- synchronized (FontManagerService.this) {
+ synchronized (mUpdatableFontDirLock) {
try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
mUpdatableFontDir.clearUpdates();
- mSerializedFontMap = buildNewSerializedFontMap();
+ updateSerializedFontMap();
}
}
}
@@ -283,7 +296,8 @@
/* package */ Map<String, File> getFontFileMap() {
if (mUpdatableFontDir == null) {
return Collections.emptyMap();
- } else {
+ }
+ synchronized (mUpdatableFontDirLock) {
return mUpdatableFontDir.getFontFileMap();
}
}
@@ -301,7 +315,7 @@
@Nullable FileDescriptor err,
@NonNull String[] args,
@Nullable ShellCallback callback,
- @NonNull ResultReceiver result) throws RemoteException {
+ @NonNull ResultReceiver result) {
new FontManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
}
@@ -309,24 +323,29 @@
* Returns an active system font configuration.
*/
public @NonNull FontConfig getSystemFontConfig() {
- if (mUpdatableFontDir != null) {
- return mUpdatableFontDir.getSystemFontConfig();
- } else {
+ if (mUpdatableFontDir == null) {
return SystemFonts.getSystemPreinstalledFontConfig();
}
+ synchronized (mUpdatableFontDirLock) {
+ return mUpdatableFontDir.getSystemFontConfig();
+ }
}
/**
- * Make new serialized font map data.
+ * Makes new serialized font map data and updates mSerializedFontMap.
*/
- public @Nullable SharedMemory buildNewSerializedFontMap() {
+ public void updateSerializedFontMap() {
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);
+ SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
+ synchronized (mSerializedFontMapLock) {
+ mSerializedFontMap = serializeFontMap;
+ }
+ return;
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to serialize updatable font map. "
+ "Retrying with system image fonts.", e);
@@ -338,11 +357,13 @@
final Map<String, Typeface> typefaceMap =
SystemFonts.buildSystemTypefaces(fontConfig, fallback);
- return Typeface.serializeFontMap(typefaceMap);
+ SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
+ synchronized (mSerializedFontMapLock) {
+ mSerializedFontMap = serializeFontMap;
+ }
} 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 2029f39..cf9a79f 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -24,6 +24,7 @@
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontUpdateRequest;
import android.graphics.fonts.FontVariationAxis;
import android.graphics.fonts.SystemFonts;
import android.os.Binder;
@@ -44,6 +45,7 @@
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -315,6 +317,7 @@
"Signature file argument is required.");
}
+ // TODO: close fontFd and sigFd.
ParcelFileDescriptor fontFd = shell.openFileForSystem(fontPath, "r");
if (fontFd == null) {
throw new SystemFontException(
@@ -330,29 +333,24 @@
}
try (FileInputStream sigFis = new FileInputStream(sigFd.getFileDescriptor())) {
- try (FileInputStream fontFis = new FileInputStream(fontFd.getFileDescriptor())) {
- int len = sigFis.available();
- if (len > MAX_SIGNATURE_FILE_SIZE_BYTES) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_SIGNATURE_TOO_LARGE,
- "Signature file is too large");
- }
- byte[] signature = new byte[len];
- if (sigFis.read(signature, 0, len) != len) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_INVALID_SIGNATURE_FILE,
- "Invalid read length");
- }
- mService.installFontFile(fontFis.getFD(), signature, -1);
- } catch (IOException e) {
+ int len = sigFis.available();
+ if (len > MAX_SIGNATURE_FILE_SIZE_BYTES) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_SIGNATURE_TOO_LARGE,
+ "Signature file is too large");
+ }
+ byte[] signature = new byte[len];
+ if (sigFis.read(signature, 0, len) != len) {
throw new SystemFontException(
FontManager.RESULT_ERROR_INVALID_SIGNATURE_FILE,
- "Failed to read signature file.", e);
+ "Invalid read length");
}
+ mService.update(
+ -1, Collections.singletonList(new FontUpdateRequest(fontFd, signature)));
} catch (IOException e) {
throw new SystemFontException(
- FontManager.RESULT_ERROR_INVALID_FONT_FILE,
- "Failed to read font files.", e);
+ FontManager.RESULT_ERROR_INVALID_SIGNATURE_FILE,
+ "Failed to read signature file.", e);
}
shell.getOutPrintWriter().println("Success"); // TODO: Output more details.
diff --git a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
index f0d14ba..017f11c 100644
--- a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
+++ b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -29,24 +30,19 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Set;
/* package */ class PersistentSystemFontConfig {
private static final String TAG = "PersistentSystemFontConfig";
private static final String TAG_ROOT = "fontConfig";
private static final String TAG_LAST_MODIFIED_DATE = "lastModifiedDate";
- private static final String TAG_VALUE = "value";
+ private static final String TAG_UPDATED_FONT_DIR = "updatedFontDir";
+ private static final String ATTR_VALUE = "value";
/* package */ static class Config {
public long lastModifiedDate;
-
- public void reset() {
- lastModifiedDate = 0;
- }
-
- public void copyTo(@NonNull Config out) {
- out.lastModifiedDate = lastModifiedDate;
- }
+ public final Set<String> updatedFontDirs = new ArraySet<>();
}
/**
@@ -54,7 +50,6 @@
*/
public static void loadFromXml(@NonNull InputStream is, @NonNull Config out)
throws XmlPullParserException, IOException {
- out.reset();
TypedXmlPullParser parser = Xml.resolvePullParser(is);
int type;
@@ -72,7 +67,10 @@
} else if (depth == 2) {
switch (tag) {
case TAG_LAST_MODIFIED_DATE:
- out.lastModifiedDate = parseLongAttribute(parser, TAG_VALUE, 0);
+ out.lastModifiedDate = parseLongAttribute(parser, ATTR_VALUE, 0);
+ break;
+ case TAG_UPDATED_FONT_DIR:
+ out.updatedFontDirs.add(getAttribute(parser, ATTR_VALUE));
break;
default:
Slog.w(TAG, "Skipping unknown tag: " + tag);
@@ -92,8 +90,13 @@
out.startTag(null, TAG_ROOT);
out.startTag(null, TAG_LAST_MODIFIED_DATE);
- out.attribute(null, TAG_VALUE, Long.toString(config.lastModifiedDate));
+ out.attribute(null, ATTR_VALUE, Long.toString(config.lastModifiedDate));
out.endTag(null, TAG_LAST_MODIFIED_DATE);
+ for (String dir : config.updatedFontDirs) {
+ out.startTag(null, TAG_UPDATED_FONT_DIR);
+ out.attribute(null, ATTR_VALUE, dir);
+ out.endTag(null, TAG_UPDATED_FONT_DIR);
+ }
out.endTag(null, TAG_ROOT);
out.endDocument();
@@ -111,4 +114,9 @@
}
}
+ @NonNull
+ private static String getAttribute(TypedXmlPullParser parser, String attr) {
+ final String value = parser.getAttributeValue(null /* namespace */, attr);
+ return value == null ? "" : value;
+ }
}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 0cb7045..dac94f6 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -21,16 +21,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontUpdateRequest;
import android.graphics.fonts.SystemFonts;
import android.os.FileUtils;
import android.system.ErrnoException;
import android.system.Os;
import android.text.FontConfig;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
-
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -40,10 +40,14 @@
import java.io.IOException;
import java.security.SecureRandom;
import java.time.Instant;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+/**
+ * Manages set of updatable font files.
+ *
+ * <p>This class is not thread safe.
+ */
final class UpdatableFontDir {
private static final String TAG = "UpdatableFontDir";
@@ -69,15 +73,6 @@
boolean rename(File src, File dest);
}
- /** Interface to mock persistent configuration */
- interface PersistentConfig {
- void loadFromXml(PersistentSystemFontConfig.Config out)
- throws XmlPullParserException, IOException;
- void writeToXml(PersistentSystemFontConfig.Config config)
- throws IOException;
- boolean rename(File src, File dest);
- }
-
/** Data class to hold font file path and revision. */
private static final class FontFileInfo {
private final File mFile;
@@ -113,11 +108,7 @@
private final File mConfigFile;
private final File mTmpConfigFile;
- @GuardedBy("UpdatableFontDir.this")
- private final PersistentSystemFontConfig.Config mConfig =
- new PersistentSystemFontConfig.Config();
-
- @GuardedBy("UpdatableFontDir.this")
+ private long mLastModifiedDate;
private int mConfigVersion = 1;
/**
@@ -125,8 +116,7 @@
* FontFileInfo}. All files in this map are validated, and have higher revision numbers than
* corresponding font files in {@link #mPreinstalledFontDirs}.
*/
- @GuardedBy("UpdatableFontDir.this")
- private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
+ private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>();
UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
FsverityUtil fsverityUtil) {
@@ -145,56 +135,107 @@
}
/* package */ void loadFontFileMap() {
- synchronized (UpdatableFontDir.this) {
- boolean success = false;
-
+ mFontFileInfoMap.clear();
+ mLastModifiedDate = 0;
+ boolean success = false;
+ try {
+ PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
try (FileInputStream fis = new FileInputStream(mConfigFile)) {
- PersistentSystemFontConfig.loadFromXml(fis, mConfig);
+ PersistentSystemFontConfig.loadFromXml(fis, config);
} catch (IOException | XmlPullParserException e) {
- mConfig.reset();
+ Slog.e(TAG, "Failed to load config xml file", e);
+ return;
}
+ mLastModifiedDate = config.lastModifiedDate;
- mFontFileInfoMap.clear();
- try {
- File[] dirs = mFilesDir.listFiles();
- if (dirs == null) return;
- for (File dir : dirs) {
- if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
- File[] files = dir.listFiles();
- if (files == null || files.length != 1) return;
- FontFileInfo fontFileInfo = validateFontFile(files[0]);
- addFileToMapIfNewerLocked(fontFileInfo, true /* deleteOldFile */);
+ File[] dirs = mFilesDir.listFiles();
+ if (dirs == null) return;
+ for (File dir : dirs) {
+ if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) {
+ Slog.e(TAG, "Unexpected dir found: " + dir);
+ return;
}
- success = true;
- } catch (Throwable t) {
- // If something happened during loading system fonts, clear all contents in finally
- // block. Here, just dumping errors.
- Slog.e(TAG, "Failed to load font mappings.", t);
- } finally {
- // Delete all files just in case if we find a problematic file.
- if (!success) {
- mFontFileInfoMap.clear();
- FileUtils.deleteContents(mFilesDir);
+ if (!config.updatedFontDirs.contains(dir.getName())) {
+ Slog.i(TAG, "Deleting obsolete dir: " + dir);
+ FileUtils.deleteContentsAndDir(dir);
+ continue;
}
+ File[] files = dir.listFiles();
+ if (files == null || files.length != 1) {
+ Slog.e(TAG, "Unexpected files in dir: " + dir);
+ return;
+ }
+ FontFileInfo fontFileInfo = validateFontFile(files[0]);
+ addFileToMapIfNewer(fontFileInfo, true /* deleteOldFile */);
+ }
+ success = true;
+ } catch (Throwable t) {
+ // If something happened during loading system fonts, clear all contents in finally
+ // block. Here, just dumping errors.
+ Slog.e(TAG, "Failed to load font mappings.", t);
+ } finally {
+ // Delete all files just in case if we find a problematic file.
+ if (!success) {
+ mFontFileInfoMap.clear();
+ mLastModifiedDate = 0;
+ FileUtils.deleteContents(mFilesDir);
}
}
}
/* package */ void clearUpdates() throws SystemFontException {
- synchronized (UpdatableFontDir.this) {
- mFontFileInfoMap.clear();
- FileUtils.deleteContents(mFilesDir);
+ mFontFileInfoMap.clear();
+ FileUtils.deleteContents(mFilesDir);
- mConfig.reset();
- mConfig.lastModifiedDate = Instant.now().getEpochSecond();
- try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
- PersistentSystemFontConfig.writeToXml(fos, mConfig);
+ mLastModifiedDate = Instant.now().getEpochSecond();
+ try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
+ PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig());
+ } catch (Exception e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+ "Failed to write config XML.", e);
+ }
+ mConfigVersion++;
+ }
+
+ /**
+ * Applies multiple {@link FontUpdateRequest}s in transaction.
+ * If one of the request fails, the fonts and config are rolled back to the previous state
+ * before this method is called.
+ */
+ public void update(List<FontUpdateRequest> requests) throws SystemFontException {
+ // Backup the mapping for rollback.
+ ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap);
+ long backupLastModifiedDate = mLastModifiedDate;
+ boolean success = false;
+ try {
+ for (FontUpdateRequest request : requests) {
+ installFontFile(request.getFd().getFileDescriptor(), request.getSignature());
+ }
+
+ // Write config file.
+ mLastModifiedDate = Instant.now().getEpochSecond();
+ try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
+ PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig());
} catch (Exception e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
"Failed to write config XML.", e);
}
+
+ if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+ "Failed to stage the config file.");
+ }
mConfigVersion++;
+ success = true;
+ } finally {
+ if (!success) {
+ mFontFileInfoMap.clear();
+ mFontFileInfoMap.putAll(backupMap);
+ mLastModifiedDate = backupLastModifiedDate;
+ }
}
}
@@ -209,111 +250,79 @@
* @param pkcs7Signature A PKCS#7 detached signature to enable fs-verity for the font file.
* @throws SystemFontException if error occurs.
*/
- void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws SystemFontException {
- synchronized (UpdatableFontDir.this) {
- File newDir = getRandomDir(mFilesDir);
- if (!newDir.mkdir()) {
+ private void installFontFile(FileDescriptor fd, byte[] pkcs7Signature)
+ throws SystemFontException {
+ File newDir = getRandomDir(mFilesDir);
+ if (!newDir.mkdir()) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to create font directory.");
+ }
+ try {
+ // Make newDir executable so that apps can access font file inside newDir.
+ Os.chmod(newDir.getAbsolutePath(), 0711);
+ } catch (ErrnoException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to change mode to 711", e);
+ }
+ boolean success = false;
+ try {
+ File tempNewFontFile = new File(newDir, "font.ttf");
+ try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
+ FileUtils.copy(fd, out.getFD());
+ } catch (IOException e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
- "Failed to create font directory.");
+ "Failed to write font file to storage.", e);
}
try {
- // Make newDir executable so that apps can access font file inside newDir.
- Os.chmod(newDir.getAbsolutePath(), 0711);
+ // Do not parse font file before setting up fs-verity.
+ // setUpFsverity throws IOException if failed.
+ mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
+ pkcs7Signature);
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
+ "Failed to setup fs-verity.", e);
+ }
+ String postScriptName;
+ try {
+ postScriptName = mParser.getPostScriptName(tempNewFontFile);
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_INVALID_FONT_FILE,
+ "Failed to read PostScript name from font file", e);
+ }
+ if (postScriptName == null) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_INVALID_FONT_NAME,
+ "Failed to read PostScript name from font file");
+ }
+ File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
+ if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to move verified font file.");
+ }
+ try {
+ // Make the font file readable by apps.
+ Os.chmod(newFontFile.getAbsolutePath(), 0644);
} catch (ErrnoException e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
"Failed to change mode to 711", e);
}
- boolean success = false;
- try {
- File tempNewFontFile = new File(newDir, "font.ttf");
- try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
- FileUtils.copy(fd, out.getFD());
- } catch (IOException e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
- "Failed to write font file to storage.", e);
- }
- try {
- // Do not parse font file before setting up fs-verity.
- // setUpFsverity throws IOException if failed.
- mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
- pkcs7Signature);
- } catch (IOException e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
- "Failed to setup fs-verity.", e);
- }
- String postScriptName;
- try {
- postScriptName = mParser.getPostScriptName(tempNewFontFile);
- } catch (IOException e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_INVALID_FONT_FILE,
- "Failed to read PostScript name from font file", e);
- }
- if (postScriptName == null) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_INVALID_FONT_NAME,
- "Failed to read PostScript name from font file");
- }
- File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
- if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
- "Failed to move verified font file.");
- }
- try {
- // Make the font file readable by apps.
- Os.chmod(newFontFile.getAbsolutePath(), 0644);
- } catch (ErrnoException e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
- "Failed to change mode to 711", e);
- }
- FontFileInfo fontFileInfo = validateFontFile(newFontFile);
-
- // Write config file.
- PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
- mConfig.copyTo(copied);
-
- copied.lastModifiedDate = Instant.now().getEpochSecond();
- try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
- PersistentSystemFontConfig.writeToXml(fos, copied);
- } catch (Exception e) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
- "Failed to write config XML.", e);
- }
-
- // Backup the mapping for rollback.
- HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
- if (!addFileToMapIfNewerLocked(fontFileInfo, false)) {
- throw new SystemFontException(
- FontManager.RESULT_ERROR_DOWNGRADING,
- "Downgrading font file is forbidden.");
- }
-
- if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
- // If we fail to stage the config file, need to rollback the config.
- mFontFileInfoMap.clear();
- mFontFileInfoMap.putAll(backup);
- throw new SystemFontException(
- FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
- "Failed to stage the config file.");
- }
-
-
- // Now font update is succeeded. Update config version.
- copied.copyTo(mConfig);
- mConfigVersion++;
-
- success = true;
- } finally {
- if (!success) {
- FileUtils.deleteContentsAndDir(newDir);
- }
+ FontFileInfo fontFileInfo = validateFontFile(newFontFile);
+ if (!addFileToMapIfNewer(fontFileInfo, false)) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_DOWNGRADING,
+ "Downgrading font file is forbidden.");
+ }
+ success = true;
+ } finally {
+ if (!success) {
+ FileUtils.deleteContentsAndDir(newDir);
}
}
}
@@ -341,7 +350,7 @@
* higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
* #mPreinstalledFontDirs}).
*/
- private boolean addFileToMapIfNewerLocked(FontFileInfo fontFileInfo, boolean deleteOldFile) {
+ private boolean addFileToMapIfNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) {
String name = fontFileInfo.getFile().getName();
FontFileInfo existingInfo = mFontFileInfoMap.get(name);
final boolean shouldAddToMap;
@@ -445,29 +454,28 @@
}
}
+ private PersistentSystemFontConfig.Config getPersistentConfig() {
+ PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+ config.lastModifiedDate = mLastModifiedDate;
+ for (FontFileInfo info : mFontFileInfoMap.values()) {
+ config.updatedFontDirs.add(info.getRandomizedFontDir().getName());
+ }
+ return config;
+ }
+
Map<String, File> getFontFileMap() {
- Map<String, File> map = new HashMap<>();
- synchronized (UpdatableFontDir.this) {
- for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
- map.put(entry.getKey(), entry.getValue().getFile());
- }
+ Map<String, File> map = new ArrayMap<>();
+ for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
+ map.put(entry.getKey(), entry.getValue().getFile());
}
return map;
}
/* package */ FontConfig getSystemFontConfig() {
- synchronized (UpdatableFontDir.this) {
- return SystemFonts.getSystemFontConfig(
- getFontFileMap(),
- mConfig.lastModifiedDate,
- mConfigVersion
- );
- }
+ return SystemFonts.getSystemFontConfig(getFontFileMap(), mLastModifiedDate, mConfigVersion);
}
/* package */ int getConfigVersion() {
- synchronized (UpdatableFontDir.this) {
- return mConfigVersion;
- }
+ return mConfigVersion;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e5b5350..1a4c8b7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -110,7 +110,6 @@
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -300,45 +299,6 @@
private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
"com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
- /**
- * Debug flag for overriding runtime {@link SystemProperties}.
- */
- @AnyThread
- private static final class DebugFlag {
- private static final Object LOCK = new Object();
- private final String mKey;
- private final boolean mDefaultValue;
- @GuardedBy("LOCK")
- private boolean mValue;
-
- public DebugFlag(String key, boolean defaultValue) {
- mKey = key;
- mDefaultValue = defaultValue;
- mValue = SystemProperties.getBoolean(key, defaultValue);
- }
-
- void refresh() {
- synchronized (LOCK) {
- mValue = SystemProperties.getBoolean(mKey, mDefaultValue);
- }
- }
-
- boolean value() {
- synchronized (LOCK) {
- return mValue;
- }
- }
- }
-
- /**
- * Debug flags that can be overridden using "adb shell setprop <key>"
- * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties".
- */
- private static final class DebugFlags {
- static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
- new DebugFlag("debug.optimize_startinput", false);
- }
-
@UserIdInt
private int mLastSwitchUserId;
@@ -2586,7 +2546,7 @@
+ mCurTokenDisplayId);
}
mIWindowManager.addWindowToken(mCurToken, LayoutParams.TYPE_INPUT_METHOD,
- mCurTokenDisplayId);
+ mCurTokenDisplayId, null /* options */);
} catch (RemoteException e) {
}
return new InputBindResult(
@@ -3687,12 +3647,9 @@
}
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
- } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
- || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
+ } else {
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
- } else {
- res = InputBindResult.NO_EDITOR;
}
} else {
res = InputBindResult.NULL_EDITOR_INFO;
@@ -5270,6 +5227,8 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
boolean asProto = false;
for (int argIndex = 0; argIndex < args.length; argIndex++) {
if (args[argIndex].equals(PROTO_ARG)) {
@@ -5292,8 +5251,6 @@
}
private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
if (useProto) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE);
@@ -5467,10 +5424,6 @@
@BinderThread
@ShellCommandResult
private int onCommandWithSystemIdentity(@Nullable String cmd) {
- if ("refresh_debug_properties".equals(cmd)) {
- return refreshDebugProperties();
- }
-
if ("get-last-switch-user-id".equals(cmd)) {
return mService.getLastSwitchUserId(this);
}
@@ -5505,13 +5458,6 @@
}
@BinderThread
- @ShellCommandResult
- private int refreshDebugProperties() {
- DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
- return ShellCommandResult.SUCCESS;
- }
-
- @BinderThread
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 02a36dc..f646d5d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -220,7 +220,8 @@
*/
@VisibleForTesting
public Context getSettingsContext(int displayId) {
- if (mSettingsContext == null) {
+ // TODO(b/178462039): Cover the case when IME is moved to another ImeContainer.
+ if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
final Context systemUiContext = ActivityThread.currentActivityThread()
.createSystemUiContext(displayId);
final Context windowContext = systemUiContext.createWindowContext(
@@ -229,11 +230,6 @@
windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
mSwitchingDialogToken = mSettingsContext.getWindowContextToken();
}
- // TODO(b/159767464): register the listener to another display again if window token is not
- // yet created.
- if (mSettingsContext.getDisplayId() != displayId) {
- mWindowManagerInternal.moveWindowTokenToDisplay(mSwitchingDialogToken, displayId);
- }
return mSettingsContext;
}
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 6fec906..1dd3d41 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1309,7 +1309,8 @@
final Binder token = new Binder();
Binder.withCleanCallingIdentity(
PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken,
- mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId));
+ mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId,
+ null /* options */));
mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId));
return token;
}
diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
similarity index 98%
rename from services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
rename to services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index fe51d74..b5746bb 100644
--- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.utils.eventlog;
+package com.android.server.location.eventlog;
import android.os.SystemClock;
import android.util.TimeUtils;
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index d16267f..9216a6b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -103,7 +103,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -1489,7 +1488,7 @@
}
if (locations.length > 0) {
- reportLocation(LocationResult.create(Arrays.asList(locations)).validate());
+ reportLocation(LocationResult.wrap(locations).validate());
}
for (Runnable listener : listeners) {
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/injector/LocationEventLog.java
index b8b54b3..8d73518 100644
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/injector/LocationEventLog.java
@@ -31,7 +31,7 @@
import android.os.Build;
import android.os.PowerManager.LocationPowerSaveMode;
-import com.android.server.utils.eventlog.LocalEventLog;
+import com.android.server.location.eventlog.LocalEventLog;
/** In memory event log for location events. */
public class LocationEventLog extends LocalEventLog {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 06ca9ec..221d4fb 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -20,8 +20,8 @@
import static android.location.LocationManager.DELIVER_HISTORICAL_LOCATIONS;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.KEY_FLUSH_COMPLETE;
+import static android.location.LocationManager.KEY_LOCATIONS;
import static android.location.LocationManager.KEY_LOCATION_CHANGED;
-import static android.location.LocationManager.KEY_LOCATION_RESULT;
import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
import static android.location.LocationManager.PASSIVE_PROVIDER;
import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE;
@@ -187,7 +187,8 @@
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
@Nullable Runnable onCompleteCallback) throws RemoteException {
- mListener.onLocationChanged(locationResult, SingleUseCallback.wrap(onCompleteCallback));
+ mListener.onLocationChanged(locationResult.asList(),
+ SingleUseCallback.wrap(onCompleteCallback));
}
@Override
@@ -222,12 +223,16 @@
// allows apps to start a fg service in response to a location PI
options.setTemporaryAppWhitelistDuration(TEMPORARY_APP_ALLOWLIST_DURATION_MS);
+ Intent intent = new Intent().putExtra(KEY_LOCATION_CHANGED,
+ locationResult.getLastLocation());
+ if (locationResult.size() > 1) {
+ intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
+ }
+
mPendingIntent.send(
mContext,
0,
- new Intent()
- .putExtra(KEY_LOCATION_CHANGED, locationResult.getLastLocation())
- .putExtra(KEY_LOCATION_RESULT, locationResult),
+ intent,
onCompleteCallback != null ? (pI, i, rC, rD, rE) -> onCompleteCallback.run()
: null,
null,
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index dce7b08..0d8f643 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -53,7 +53,7 @@
Location location = new Location(l);
location.setIsFromMockProvider(true);
mLocation = location;
- reportLocation(LocationResult.wrap(location));
+ reportLocation(LocationResult.wrap(location).validate());
}
@Override
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index c274c28..32d637f 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.location.Location;
import android.location.LocationResult;
import android.location.provider.ILocationProvider;
import android.location.provider.ILocationProviderManager;
@@ -40,6 +41,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -260,13 +262,25 @@
// executed on binder thread
@Override
- public void onReportLocation(LocationResult locationResult) {
+ public void onReportLocation(Location location) {
synchronized (mLock) {
if (mProxy != this) {
return;
}
- reportLocation(locationResult.validate());
+ reportLocation(LocationResult.wrap(location).validate());
+ }
+ }
+
+ // executed on binder thread
+ @Override
+ public void onReportLocations(List<Location> locations) {
+ synchronized (mLock) {
+ if (mProxy != this) {
+ return;
+ }
+
+ reportLocation(LocationResult.wrap(locations).validate());
}
}
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index ea2788c..6d1c680 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -155,7 +155,7 @@
try {
// Use the privileged method because Lockdown VPN is initiated by the system, so
// no additional permission checks are necessary.
- mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, egressProp);
+ mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, null, egressProp);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
Log.e(TAG, "Failed to start VPN", e);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index f92f3dc..39ed7e8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,8 +16,6 @@
package com.android.server.net;
-import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
-
import android.annotation.NonNull;
import android.net.Network;
import android.net.NetworkTemplate;
@@ -39,28 +37,6 @@
public abstract void resetUserState(int userId);
/**
- * Figure out if networking is blocked for a given set of conditions.
- *
- * This is used by ConnectivityService via passing stale copies of conditions, so it must not
- * take any locks.
- *
- * @param uid The target uid.
- * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
- * @param isNetworkMetered True if the network is metered.
- * @param isBackgroundRestricted True if data saver is enabled.
- *
- * @return true if networking is blocked for the UID under the specified conditions.
- */
- public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
- boolean isBackgroundRestricted) {
- // Log of invoking internal function is disabled because it will be called very
- // frequently. And metrics are unlikely needed on this method because the callers are
- // external and this method doesn't take any locks or perform expensive operations.
- return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
- isBackgroundRestricted, null);
- }
-
- /**
* Informs that an appId has been added or removed from the temp-powersave-allowlist so that
* that network rules for that appId can be updated.
*
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 29eaf4f..b99a552 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -243,6 +243,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -1253,7 +1254,7 @@
// identified carrier, which may want to manage their own notifications. This method
// should be called every time the carrier config changes anyways, and there's no
// reason to alert if there isn't a carrier.
- return;
+ continue;
}
final boolean notifyWarning = getBooleanDefeatingNullable(config,
@@ -2163,13 +2164,14 @@
if (template.matches(probeIdent)) {
if (LOGD) {
Slog.d(TAG, "Found template " + template + " which matches subscriber "
- + NetworkIdentity.scrubSubscriberId(subscriberId));
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
}
return false;
}
}
- Slog.i(TAG, "No policy for subscriber " + NetworkIdentity.scrubSubscriberId(subscriberId)
+ Slog.i(TAG, "No policy for subscriber "
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId)
+ "; generating default policy");
final NetworkPolicy policy = buildDefaultMobilePolicy(subId, subscriberId);
addNetworkPolicyAL(policy);
@@ -3614,14 +3616,15 @@
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
- fout.println(subId + "=" + NetworkIdentity.scrubSubscriberId(subscriberId));
+ fout.println(subId + "="
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
}
fout.decreaseIndent();
fout.println();
for (String[] mergedSubscribers : mMergedSubscriberIds) {
fout.println("Merged subscriptions: " + Arrays.toString(
- NetworkIdentity.scrubSubscriberId(mergedSubscribers)));
+ NetworkIdentityUtils.scrubSubscriberIds(mergedSubscribers)));
}
fout.println();
@@ -5399,6 +5402,17 @@
}
@Override
+ public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+ boolean isNetworkMetered, boolean isBackgroundRestricted) {
+ mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+ // Log of invoking this function is disabled because it will be called very frequently. And
+ // metrics are unlikely needed on this method because the callers are external and this
+ // method doesn't take any locks or perform expensive operations.
+ return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, null);
+ }
+
+ @Override
public boolean isUidRestrictedOnMeteredNetworks(int uid) {
mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
final int uidRules;
@@ -5407,9 +5421,9 @@
uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
isBackgroundRestricted = mRestrictBackground;
}
- //TODO(b/177490332): The logic here might not be correct because it doesn't consider
- // RULE_REJECT_METERED condition. And it could be replaced by
- // isUidNetworkingBlockedInternal().
+ // TODO(b/177490332): The logic here might not be correct because it doesn't consider
+ // RULE_REJECT_METERED condition. And it could be replaced by
+ // isUidNetworkingBlockedInternal().
return isBackgroundRestricted
&& !hasRule(uidRules, RULE_ALLOW_METERED)
&& !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6843733..571b693 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3041,7 +3041,8 @@
}
Binder windowToken = new Binder();
- mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId);
+ mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId,
+ null /* options */);
record = getToastRecord(callingUid, callingPid, pkg, isSystemToast, token,
text, callback, duration, windowToken, displayId, textCallback);
mToastQueue.add(record);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 461d519..619fc4e 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -23,8 +23,8 @@
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index de77372..18c689f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,8 +21,8 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.service.notification.DNDModeProto.ROOT_CONFIG;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import android.app.AppOpsManager;
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
new file mode 100644
index 0000000..a83edb7
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.os;
+
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import android.annotation.AppIdInt;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.FileObserver;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.BootReceiver;
+import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Tombstone;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Optional;
+
+/**
+ * A class to manage native tombstones.
+ */
+public final class NativeTombstoneManager {
+ private static final String TAG = NativeTombstoneManager.class.getSimpleName();
+
+ private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final TombstoneWatcher mWatcher;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SparseArray<TombstoneFile> mTombstones;
+
+ NativeTombstoneManager(Context context) {
+ mTombstones = new SparseArray<TombstoneFile>();
+ mContext = context;
+
+ final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
+ THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+ thread.start();
+ mHandler = thread.getThreadHandler();
+
+ mWatcher = new TombstoneWatcher();
+ mWatcher.startWatching();
+ }
+
+ void onSystemReady() {
+ // Scan existing tombstones.
+ mHandler.post(() -> {
+ final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
+ for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
+ if (tombstoneFiles[i].isFile()) {
+ handleTombstone(tombstoneFiles[i]);
+ }
+ }
+ });
+ }
+
+ private void handleTombstone(File path) {
+ final String filename = path.getName();
+ if (!filename.startsWith("tombstone_")) {
+ return;
+ }
+
+ if (filename.endsWith(".pb")) {
+ handleProtoTombstone(path);
+ } else {
+ BootReceiver.addTombstoneToDropBox(mContext, path);
+ }
+ }
+
+ private void handleProtoTombstone(File path) {
+ final String filename = path.getName();
+ if (!filename.endsWith(".pb")) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ final String suffix = filename.substring("tombstone_".length());
+ final String numberStr = suffix.substring(0, suffix.length() - 3);
+
+ int number;
+ try {
+ number = Integer.parseInt(numberStr);
+ if (number < 0 || number > 99) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+ } catch (NumberFormatException ex) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ ParcelFileDescriptor pfd;
+ try {
+ pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE);
+ } catch (FileNotFoundException ex) {
+ Slog.w(TAG, "failed to open " + path, ex);
+ return;
+ }
+
+ final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
+ if (!parsedTombstone.isPresent()) {
+ IoUtils.closeQuietly(pfd);
+ return;
+ }
+
+ synchronized (mLock) {
+ TombstoneFile previous = mTombstones.get(number);
+ if (previous != null) {
+ previous.dispose();
+ }
+
+ mTombstones.put(number, parsedTombstone.get());
+ }
+ }
+
+ static class TombstoneFile {
+ final ParcelFileDescriptor mPfd;
+
+ final @UserIdInt int mUserId;
+ final @AppIdInt int mAppId;
+
+ boolean mPurged = false;
+
+ TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+ mPfd = pfd;
+ mUserId = userId;
+ mAppId = appId;
+ }
+
+ public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
+ if (mPurged) {
+ return false;
+ }
+
+ if (userId.isPresent() && userId.get() != mUserId) {
+ return false;
+ }
+
+ if (appId.isPresent() && appId.get() != mAppId) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void dispose() {
+ IoUtils.closeQuietly(mPfd);
+ }
+
+ static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+ final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+ final ProtoInputStream stream = new ProtoInputStream(is);
+
+ int uid = 0;
+ String selinuxLabel = "";
+
+ try {
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Tombstone.UID:
+ uid = stream.readInt(Tombstone.UID);
+ break;
+
+ case (int) Tombstone.SELINUX_LABEL:
+ selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } catch (IOException ex) {
+ Slog.e(TAG, "Failed to parse tombstone", ex);
+ return Optional.empty();
+ }
+
+ if (!UserHandle.isApp(uid)) {
+ Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
+ return Optional.empty();
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+ final int appId = UserHandle.getAppId(uid);
+
+ if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
+ Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
+ return Optional.empty();
+ }
+
+ return Optional.of(new TombstoneFile(pfd, userId, appId));
+ }
+ }
+
+ class TombstoneWatcher extends FileObserver {
+ TombstoneWatcher() {
+ // Tombstones can be created either by linking an O_TMPFILE temporary file (CREATE),
+ // or by moving a named temporary file in the same directory on kernels where O_TMPFILE
+ // isn't supported (MOVED_TO).
+ super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);
+ }
+
+ @Override
+ public void onEvent(int event, @Nullable String path) {
+ mHandler.post(() -> {
+ handleTombstone(new File(TOMBSTONE_DIR, path));
+ });
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManagerService.java b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
new file mode 100644
index 0000000..cb3c7ff0
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.os;
+
+import android.content.Context;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * Service that tracks and manages native tombstones.
+ *
+ * @hide
+ */
+public class NativeTombstoneManagerService extends SystemService {
+ private static final String TAG = "NativeTombstoneManagerService";
+
+ private NativeTombstoneManager mManager;
+
+ public NativeTombstoneManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mManager = new NativeTombstoneManager(getContext());
+ LocalServices.addService(NativeTombstoneManager.class, mManager);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mManager.onSystemReady();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 5373f99..66ea554 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -23,7 +23,6 @@
import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
@@ -32,15 +31,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
@@ -294,21 +293,21 @@
/**
* Fetch or calculate checksums for the collection of files.
*
- * @param filesToChecksum split name, null for base and File to fetch checksums for
- * @param optional mask to fetch readily available checksums
- * @param required mask to forcefully calculate if not available
- * @param installerPackageName package name of the installer of the packages
- * @param trustedInstallers array of certificate to trust, two specific cases:
- * null - trust anybody,
- * [] - trust nobody.
- * @param statusReceiver to receive the resulting checksums
+ * @param filesToChecksum split name, null for base and File to fetch checksums for
+ * @param optional mask to fetch readily available checksums
+ * @param required mask to forcefully calculate if not available
+ * @param installerPackageName package name of the installer of the packages
+ * @param trustedInstallers array of certificate to trust, two specific cases:
+ * null - trust anybody,
+ * [] - trust nobody.
+ * @param onChecksumsReadyListener to receive the resulting checksums
*/
public static void getChecksums(List<Pair<String, File>> filesToChecksum,
@Checksum.Type int optional,
@Checksum.Type int required,
@Nullable String installerPackageName,
@Nullable Certificate[] trustedInstallers,
- @NonNull IntentSender statusReceiver,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector) {
List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
@@ -326,14 +325,14 @@
}
long startTime = SystemClock.uptimeMillis();
- processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
- startTime);
+ processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener,
+ injector, startTime);
}
private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
List<Map<Integer, ApkChecksum>> result,
@Checksum.Type int required,
- @NonNull IntentSender statusReceiver,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector,
long startTime) {
final boolean timeout =
@@ -350,7 +349,7 @@
// Not ready, come back later.
injector.getHandler().postDelayed(() -> {
processRequiredChecksums(filesToChecksum, result, required,
- statusReceiver, injector, startTime);
+ onChecksumsReadyListener, injector, startTime);
}, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
return;
}
@@ -363,13 +362,9 @@
}
}
- final Intent intent = new Intent();
- intent.putExtra(EXTRA_CHECKSUMS,
- allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
-
try {
- statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
- } catch (IntentSender.SendIntentException e) {
+ onChecksumsReadyListener.onChecksumsReady(allChecksums);
+ } catch (RemoteException e) {
Slog.w(TAG, e);
}
}
diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java
index a17967f..c18d0e9 100644
--- a/services/core/java/com/android/server/pm/DefaultAppProvider.java
+++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java
@@ -41,14 +41,18 @@
public class DefaultAppProvider {
@NonNull
private final Supplier<RoleManager> mRoleManagerSupplier;
+ @NonNull
+ private final Supplier<UserManagerInternal> mUserManagerInternalSupplier;
/**
* Create a new instance of this class
*
* @param roleManagerSupplier the supplier for {@link RoleManager}
*/
- public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier) {
+ public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier,
+ @NonNull Supplier<UserManagerInternal> userManagerInternalSupplier) {
mRoleManagerSupplier = roleManagerSupplier;
+ mUserManagerInternalSupplier = userManagerInternalSupplier;
}
/**
@@ -132,7 +136,8 @@
*/
@Nullable
public String getDefaultHome(@NonNull int userId) {
- return getRoleHolder(RoleManager.ROLE_HOME, userId);
+ return getRoleHolder(RoleManager.ROLE_HOME,
+ mUserManagerInternalSupplier.get().getProfileParentId(userId));
}
/**
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
new file mode 100644
index 0000000..a32e107
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.content.IntentFilter;
+
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of an immutable default cross-profile intent filter.
+ */
+@Immutable
+final class DefaultCrossProfileIntentFilter {
+
+ @IntDef({
+ Direction.TO_PARENT,
+ Direction.TO_PROFILE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Direction {
+ int TO_PARENT = 0;
+ int TO_PROFILE = 1;
+ }
+
+ /** The intent filter that's used */
+ public final IntentFilter filter;
+
+ /**
+ * The flags related to the forwarding, e.g.
+ * {@link android.content.pm.PackageManager#SKIP_CURRENT_PROFILE} or
+ * {@link android.content.pm.PackageManager#ONLY_IF_NO_MATCH_FOUND}.
+ */
+ public final int flags;
+
+ /**
+ * The direction of forwarding, can be either {@link Direction#TO_PARENT} or
+ * {@link Direction#TO_PROFILE}.
+ */
+ public final @Direction int direction;
+
+ /**
+ * Whether this cross profile intent filter would allow personal data to be shared into
+ * the work profile. If this is {@code true}, this intent filter should be only added to
+ * the profile if the admin does not enable
+ * {@link android.os.UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}.
+ */
+ public final boolean letsPersonalDataIntoProfile;
+
+ private DefaultCrossProfileIntentFilter(IntentFilter filter, int flags,
+ @Direction int direction, boolean letsPersonalDataIntoProfile) {
+ this.filter = requireNonNull(filter);
+ this.flags = flags;
+ this.direction = direction;
+ this.letsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+ }
+
+ static final class Builder {
+ private IntentFilter mFilter = new IntentFilter();
+ private int mFlags;
+ private @Direction int mDirection;
+ private boolean mLetsPersonalDataIntoProfile;
+
+ Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) {
+ mDirection = direction;
+ mFlags = flags;
+ mLetsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+ }
+
+ Builder addAction(String action) {
+ mFilter.addAction(action);
+ return this;
+ }
+
+ Builder addCategory(String category) {
+ mFilter.addCategory(category);
+ return this;
+ }
+
+ Builder addDataType(String type) {
+ try {
+ mFilter.addDataType(type);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ // ignore
+ }
+ return this;
+ }
+
+ Builder addDataScheme(String scheme) {
+ mFilter.addDataScheme(scheme);
+ return this;
+ }
+
+ DefaultCrossProfileIntentFilter build() {
+ return new DefaultCrossProfileIntentFilter(mFilter, mFlags, mDirection,
+ mLetsPersonalDataIntoProfile);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
new file mode 100644
index 0000000..3019439
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND;
+import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE;
+import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH;
+
+import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.provider.AlarmClock;
+import android.provider.MediaStore;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility Class for {@link DefaultCrossProfileIntentFilter}.
+ */
+public class DefaultCrossProfileIntentFiltersUtils {
+
+ private DefaultCrossProfileIntentFiltersUtils() {
+ }
+
+ // Intents from profile to parent user
+ /** Emergency call intent with mime type is always resolved by primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ EMERGENCY_CALL_MIME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_EMERGENCY)
+ .addAction(Intent.ACTION_CALL_PRIVILEGED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Emergency call intent with data schemes is always resolved by primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ EMERGENCY_CALL_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_EMERGENCY)
+ .addAction(Intent.ACTION_CALL_PRIVILEGED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /** Dial intent with mime type can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_MIME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Dial intent with data scheme can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /**
+ * Dial intent with no data scheme or type can be handled by either managed profile or its
+ * parent user.
+ */
+ private static final DefaultCrossProfileIntentFilter DIAL_RAW =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .build();
+
+ /** Pressing the call button can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter CALL_BUTTON =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_BUTTON)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** SMS and MMS are exclusively handled by the primary user. */
+ private static final DefaultCrossProfileIntentFilter SMS_MMS =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_VIEW)
+ .addAction(Intent.ACTION_SENDTO)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("sms")
+ .addDataScheme("smsto")
+ .addDataScheme("mms")
+ .addDataScheme("mmsto")
+ .build();
+
+ /** Mobile network settings is always shown in the primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ MOBILE_NETWORK_SETTINGS =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
+ .addAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** HOME intent is always resolved by the primary user. */
+ static final DefaultCrossProfileIntentFilter HOME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_HOME)
+ .build();
+
+ /** Get content can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter GET_CONTENT =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_GET_CONTENT)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .addDataType("*/*")
+ .build();
+
+ /** Open document intent can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_OPEN_DOCUMENT)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .addDataType("*/*")
+ .build();
+
+ /** Pick for any data type can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_PICK)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("*/*")
+ .build();
+
+ /** Pick without data type can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_RAW =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_PICK)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Speech recognition can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(ACTION_RECOGNIZE_SPEECH)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Media capture can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter MEDIA_CAPTURE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(MediaStore.ACTION_IMAGE_CAPTURE)
+ .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_VIDEO_CAPTURE)
+ .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
+ .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
+ .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+ .addAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Alarm setting can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter SET_ALARM =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(AlarmClock.ACTION_SET_ALARM)
+ .addAction(AlarmClock.ACTION_SHOW_ALARMS)
+ .addAction(AlarmClock.ACTION_SET_TIMER)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ // Intents from parent to profile user
+
+ /** ACTION_SEND can be forwarded to the managed profile on user's choice. */
+ private static final DefaultCrossProfileIntentFilter ACTION_SEND =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_SEND)
+ .addAction(Intent.ACTION_SEND_MULTIPLE)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("*/*")
+ .build();
+
+ /** USB devices attached can get forwarded to the profile. */
+ private static final DefaultCrossProfileIntentFilter
+ USB_DEVICE_ATTACHED =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
+ .addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() {
+ return Arrays.asList(
+ EMERGENCY_CALL_MIME,
+ EMERGENCY_CALL_DATA,
+ DIAL_MIME,
+ DIAL_DATA,
+ DIAL_RAW,
+ CALL_BUTTON,
+ SMS_MMS,
+ SET_ALARM,
+ MEDIA_CAPTURE,
+ RECOGNIZE_SPEECH,
+ ACTION_PICK_RAW,
+ ACTION_PICK_DATA,
+ OPEN_DOCUMENT,
+ GET_CONTENT,
+ USB_DEVICE_ATTACHED,
+ ACTION_SEND,
+ HOME,
+ MOBILE_NETWORK_SETTINGS);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 4f986bd..2a1fc87 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -33,7 +33,7 @@
public static final int DUMP_KEYSETS = 1 << 14;
public static final int DUMP_VERSION = 1 << 15;
public static final int DUMP_INSTALLS = 1 << 16;
- public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17;
+ public static final int DUMP_DOMAIN_VERIFIER = 1 << 17;
public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
public static final int DUMP_FROZEN = 1 << 19;
public static final int DUMP_DEXOPT = 1 << 20;
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index f5ec595..ecafdfd 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -249,24 +249,6 @@
}
/**
- * @return the current startable state.
- */
- public boolean isStartable() {
- synchronized (mLock) {
- return mStartableState.isStartable();
- }
- }
-
- /**
- * @return Whether the package is still being loaded or has been fully loaded.
- */
- public boolean isLoading() {
- synchronized (mLock) {
- return mLoadingState.isLoading();
- }
- }
-
- /**
* @return all current states in a Parcelable.
*/
public IncrementalStatesInfo getIncrementalStatesInfo() {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c4a23f9..f240d85 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1316,10 +1316,6 @@
} finally {
mListeners.finishBroadcast();
}
- PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- pmi.registerInstalledLoadingProgressCallback(packageName,
- new PackageLoadingProgressCallback(packageName, user),
- user.getIdentifier());
super.onPackageAdded(packageName, uid);
}
@@ -1542,38 +1538,5 @@
checkCallbackCount();
}
}
-
- class PackageLoadingProgressCallback extends
- PackageManagerInternal.InstalledLoadingProgressCallback {
- private String mPackageName;
- private UserHandle mUser;
-
- PackageLoadingProgressCallback(String packageName, UserHandle user) {
- super(mCallbackHandler);
- mPackageName = packageName;
- mUser = user;
- }
-
- @Override
- public void onLoadingProgressChanged(float progress) {
- final int n = mListeners.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
- continue;
- }
- try {
- listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
- }
- }
- } finally {
- mListeners.finishBroadcast();
- }
- }
- }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 13fe8a0..6485c0c 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -407,8 +408,14 @@
boolean needsRenderScriptOverride = false;
if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
&& NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
- abiList = Build.SUPPORTED_32_BIT_ABIS;
- needsRenderScriptOverride = true;
+ if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ needsRenderScriptOverride = true;
+ } else {
+ throw new PackageManagerException(
+ INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
+ "Apks with renderscript are not supported on 64-bit only devices");
+ }
}
final int copyRet;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6e5bd94..81b65b2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,7 +24,6 @@
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.Intent.ACTION_MAIN;
-import static android.content.Intent.CATEGORY_BROWSABLE;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
@@ -68,11 +67,7 @@
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_APEX;
@@ -177,6 +172,7 @@
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageChangeObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
@@ -390,6 +386,11 @@
import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationService;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -1063,6 +1064,9 @@
private final ServiceProducer mGetLocalServiceProducer;
private final ServiceProducer mGetSystemServiceProducer;
private final Singleton<ModuleInfoProvider> mModuleInfoProviderProducer;
+ private final Singleton<DomainVerificationManagerInternal>
+ mDomainVerificationManagerInternalProducer;
+ private final Singleton<Handler> mHandlerProducer;
Injector(Context context, Object lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
@@ -1091,6 +1095,9 @@
instantAppResolverConnectionProducer,
Producer<ModuleInfoProvider> moduleInfoProviderProducer,
Producer<LegacyPermissionManagerInternal> legacyPermissionManagerInternalProducer,
+ Producer<DomainVerificationManagerInternal>
+ domainVerificationManagerInternalProducer,
+ Producer<Handler> handlerProducer,
SystemWrapper systemWrapper,
ServiceProducer getLocalServiceProducer,
ServiceProducer getSystemServiceProducer) {
@@ -1128,6 +1135,9 @@
mSystemWrapper = systemWrapper;
mGetLocalServiceProducer = getLocalServiceProducer;
mGetSystemServiceProducer = getSystemServiceProducer;
+ mDomainVerificationManagerInternalProducer =
+ new Singleton<>(domainVerificationManagerInternalProducer);
+ mHandlerProducer = new Singleton<>(handlerProducer);
}
/**
@@ -1273,6 +1283,14 @@
public LegacyPermissionManagerInternal getLegacyPermissionManagerInternal() {
return mLegacyPermissionManagerInternalProducer.get(this, mPackageManager);
}
+
+ public DomainVerificationManagerInternal getDomainVerificationManagerInternal() {
+ return mDomainVerificationManagerInternalProducer.get(this, mPackageManager);
+ }
+
+ public Handler getHandler() {
+ return mHandlerProducer.get(this, mPackageManager);
+ }
}
/** Provides an abstraction to static access to system state. */
@@ -1327,8 +1345,6 @@
public InstantAppRegistry instantAppRegistry;
public InstantAppResolverConnection instantAppResolverConnection;
public ComponentName instantAppResolverSettingsComponent;
- public @Nullable IntentFilterVerifier<ParsedIntentInfo> intentFilterVerifier;
- public @Nullable ComponentName intentFilterVerifierComponent;
public boolean isPreNmr1Upgrade;
public boolean isPreNupgrade;
public boolean isPreQupgrade;
@@ -1451,10 +1467,8 @@
boolean mResolverReplaced = false;
- private final @Nullable ComponentName mIntentFilterVerifierComponent;
- private final @Nullable IntentFilterVerifier<ParsedIntentInfo> mIntentFilterVerifier;
-
- private int mIntentFilterVerificationToken = 0;
+ @NonNull
+ private final DomainVerificationManagerInternal mDomainVerificationManager;
/** The service connection to the ephemeral resolver */
final InstantAppResolverConnection mInstantAppResolverConnection;
@@ -1470,9 +1484,6 @@
private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
- final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
- = new SparseArray<>();
-
// Internal interface for permission manager
private final PermissionManagerServiceInternal mPermissionManager;
@@ -1492,262 +1503,6 @@
private final PackageProperty mPackageProperty = new PackageProperty();
- private static class IFVerificationParams {
- String packageName;
- boolean hasDomainUrls;
- List<ParsedActivity> activities;
- boolean replacing;
- int userId;
- int verifierUid;
-
- public IFVerificationParams(String packageName, boolean hasDomainUrls,
- List<ParsedActivity> activities, boolean _replacing,
- int _userId, int _verifierUid) {
- this.packageName = packageName;
- this.hasDomainUrls = hasDomainUrls;
- this.activities = activities;
- replacing = _replacing;
- userId = _userId;
- verifierUid = _verifierUid;
- }
- }
-
- private interface IntentFilterVerifier<T extends IntentFilter> {
- boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
- T filter, String packageName);
- void startVerifications(int userId);
- void receiveVerificationResponse(int verificationId);
- }
-
- private class IntentVerifierProxy implements IntentFilterVerifier<ParsedIntentInfo> {
- private Context mContext;
- private ComponentName mIntentFilterVerifierComponent;
- private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<>();
-
- public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
- mContext = context;
- mIntentFilterVerifierComponent = verifierComponent;
- }
-
- private String getDefaultScheme() {
- return IntentFilter.SCHEME_HTTPS;
- }
-
- @Override
- public void startVerifications(int userId) {
- // Launch verifications requests
- int count = mCurrentIntentFilterVerifications.size();
- for (int n=0; n<count; n++) {
- int verificationId = mCurrentIntentFilterVerifications.get(n);
- final IntentFilterVerificationState ivs =
- mIntentFilterVerificationStates.get(verificationId);
-
- String packageName = ivs.getPackageName();
-
- ArrayList<ParsedIntentInfo> filters = ivs.getFilters();
- final int filterCount = filters.size();
- ArraySet<String> domainsSet = new ArraySet<>();
- for (int m=0; m<filterCount; m++) {
- ParsedIntentInfo filter = filters.get(m);
- domainsSet.addAll(filter.getHostsList());
- }
- synchronized (mLock) {
- if (mSettings.createIntentFilterVerificationIfNeededLPw(
- packageName, domainsSet) != null) {
- scheduleWriteSettingsLocked();
- }
- }
- sendVerificationRequest(verificationId, ivs);
- }
- mCurrentIntentFilterVerifications.clear();
- }
-
- private void sendVerificationRequest(int verificationId, IntentFilterVerificationState ivs) {
- Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
- verificationIntent.putExtra(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
- verificationId);
- verificationIntent.putExtra(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
- getDefaultScheme());
- verificationIntent.putExtra(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
- ivs.getHostsString());
- verificationIntent.putExtra(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
- ivs.getPackageName());
- verificationIntent.setComponent(mIntentFilterVerifierComponent);
- verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-
- final long whitelistTimeout = getVerificationTimeout();
- final BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setTemporaryAppWhitelistDuration(whitelistTimeout);
-
- DeviceIdleInternal idleController =
- mInjector.getLocalService(DeviceIdleInternal.class);
- idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
- mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout,
- UserHandle.USER_SYSTEM, true, "intent filter verifier");
-
- mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM,
- null, options.toBundle());
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "Sending IntentFilter verification broadcast");
- }
-
- public void receiveVerificationResponse(int verificationId) {
- IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
-
- final boolean verified = ivs.isVerified();
-
- ArrayList<ParsedIntentInfo> filters = ivs.getFilters();
- final int count = filters.size();
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, "Received verification response " + verificationId
- + " for " + count + " filters, verified=" + verified);
- }
- for (int n=0; n<count; n++) {
- ParsedIntentInfo filter = filters.get(n);
- filter.setVerified(verified);
-
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "IntentFilter " + filter.toString()
- + " verified with result:" + verified + " and hosts:"
- + ivs.getHostsString());
- }
-
- mIntentFilterVerificationStates.remove(verificationId);
-
- final String packageName = ivs.getPackageName();
- IntentFilterVerificationInfo ivi;
-
- synchronized (mLock) {
- ivi = mSettings.getIntentFilterVerificationLPr(packageName);
- }
- if (ivi == null) {
- Slog.w(TAG, "IntentFilterVerificationInfo not found for verificationId:"
- + verificationId + " packageName:" + packageName);
- return;
- }
-
- synchronized (mLock) {
- if (verified) {
- ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
- } else {
- ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK);
- }
- scheduleWriteSettingsLocked();
-
- final int userId = ivs.getUserId();
- if (userId != UserHandle.USER_ALL) {
- final int userStatus =
- mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
-
- int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
- boolean needUpdate = false;
-
- // In a success case, we promote from undefined or ASK to ALWAYS. This
- // supports a flow where the app fails validation but then ships an updated
- // APK that passes, and therefore deserves to be in ALWAYS.
- //
- // If validation failed, the undefined state winds up in the basic ASK behavior,
- // but apps that previously passed and became ALWAYS are *demoted* out of
- // that state, since they would not deserve the ALWAYS behavior in case of a
- // clean install.
- switch (userStatus) {
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
- if (!verified) {
- // Don't demote if sysconfig says 'always'
- SystemConfig systemConfig = mInjector.getSystemConfig();
- ArraySet<String> packages = systemConfig.getLinkedApps();
- if (!packages.contains(packageName)) {
- // updatedStatus is already UNDEFINED
- needUpdate = true;
-
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Formerly validated but now failing; demoting");
- }
- } else {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Updating bundled package " + packageName
- + " failed autoVerify, but sysconfig supersedes");
- }
- // leave needUpdate == false here intentionally
- }
- }
- break;
-
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
- // Stay in 'undefined' on verification failure
- if (verified) {
- updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
- }
- needUpdate = true;
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Applying update; old=" + userStatus
- + " new=" + updatedStatus);
- }
- break;
-
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
- // Keep in 'ask' on failure
- if (verified) {
- updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
- needUpdate = true;
- }
- break;
-
- default:
- // Nothing to do
- }
-
- if (needUpdate) {
- mSettings.updateIntentFilterVerificationStatusLPw(
- packageName, updatedStatus, userId);
- scheduleWritePackageRestrictionsLocked(userId);
- }
- } else {
- Slog.i(TAG, "autoVerify ignored when installing for all users");
- }
- }
- }
-
- @Override
- public boolean addOneIntentFilterVerification(int verifierUid, int userId, int verificationId,
- ParsedIntentInfo filter, String packageName) {
- if (!hasValidDomains(filter)) {
- return false;
- }
- IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
- if (ivs == null) {
- ivs = createDomainVerificationState(verifierUid, userId, verificationId,
- packageName);
- }
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Adding verification filter for " + packageName + ": " + filter);
- }
- ivs.addFilter(filter);
- return true;
- }
-
- private IntentFilterVerificationState createDomainVerificationState(int verifierUid,
- int userId, int verificationId, String packageName) {
- IntentFilterVerificationState ivs = new IntentFilterVerificationState(
- verifierUid, userId, packageName);
- ivs.setPendingState();
- synchronized (mLock) {
- mIntentFilterVerificationStates.append(verificationId, ivs);
- mCurrentIntentFilterVerifications.add(verificationId);
- }
- return ivs;
- }
- }
-
- private static boolean hasValidDomains(ParsedIntentInfo filter) {
- return filter.hasCategory(Intent.CATEGORY_BROWSABLE)
- && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
- filter.hasDataScheme(IntentFilter.SCHEME_HTTPS));
- }
-
// Set of pending broadcasts for aggregating enable/disable of components.
@VisibleForTesting(visibility = Visibility.PACKAGE)
public static class PendingPackageBroadcasts {
@@ -1822,8 +1577,8 @@
static final int WRITE_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15;
static final int CHECK_PENDING_VERIFICATION = 16;
- static final int START_INTENT_FILTER_VERIFICATIONS = 17;
- static final int INTENT_FILTER_VERIFIED = 18;
+ // public static final int UNUSED = 17;
+ // public static final int UNUSED = 18;
static final int WRITE_PACKAGE_LIST = 19;
static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
static final int ENABLE_ROLLBACK_STATUS = 21;
@@ -1832,6 +1587,7 @@
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
+ static final int DOMAIN_VERIFICATION = 27;
static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
@@ -1925,6 +1681,74 @@
private final PackageUsage mPackageUsage = new PackageUsage();
private final CompilerStats mCompilerStats = new CompilerStats();
+ private final DomainVerificationConnection mDomainVerificationConnection =
+ new DomainVerificationConnection();
+
+ private class DomainVerificationConnection implements
+ DomainVerificationService.Connection, DomainVerificationProxyV1.Connection,
+ DomainVerificationProxyV2.Connection {
+
+ @Override
+ public void scheduleWriteSettings() {
+ synchronized (mLock) {
+ PackageManagerService.this.scheduleWriteSettingsLocked();
+ }
+ }
+
+ @Override
+ public int getCallingUid() {
+ return Binder.getCallingUid();
+ }
+
+ @UserIdInt
+ @Override
+ public int getCallingUserId() {
+ return UserHandle.getCallingUserId();
+ }
+
+ @Override
+ public void schedule(int code, @Nullable Object object) {
+ Message message = mHandler.obtainMessage(DOMAIN_VERIFICATION);
+ message.arg1 = code;
+ message.obj = object;
+ mHandler.sendMessage(message);
+ }
+
+ @Override
+ public long getPowerSaveTempWhitelistAppDuration() {
+ return PackageManagerService.this.getVerificationTimeout();
+ }
+
+ @Override
+ public DeviceIdleInternal getDeviceIdleInternal() {
+ return mInjector.getLocalService(DeviceIdleInternal.class);
+ }
+
+ @Override
+ public boolean isCallerPackage(int callingUid, @NonNull String packageName) {
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ return callingUid == getPackageUid(packageName, 0, callingUserId);
+ }
+
+ @Nullable
+ @Override
+ public PackageSetting getPackageSettingLocked(@NonNull String pkgName) {
+ return PackageManagerService.this.getPackageSetting(pkgName);
+ }
+
+ @Nullable
+ @Override
+ public AndroidPackage getPackageLocked(@NonNull String pkgName) {
+ return PackageManagerService.this.getPackage(pkgName);
+ }
+
+ @Nullable
+ @Override
+ public AndroidPackage getPackage(@NonNull String packageName) {
+ return getPackageLocked(packageName);
+ }
+ }
+
/**
* Invalidate the package info cache, which includes updating the cached computer.
* @hide
@@ -2145,7 +1969,6 @@
boolean isImplicitImageCaptureIntentAndNotSetByDpc);
int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc);
- long getDomainVerificationStatusLPr(PackageSetting ps, int userId);
void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message);
void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
@@ -2199,6 +2022,7 @@
private final ComponentResolver mComponentResolver;
private final InstantAppResolverConnection mInstantAppResolverConnection;
private final DefaultAppProvider mDefaultAppProvider;
+ private final DomainVerificationManagerInternal mDomainVerificationManager;
// PackageManagerService attributes that are primitives are referenced through the
// pms object directly. Primitives are the only attributes so referenced.
@@ -2244,6 +2068,7 @@
mComponentResolver = args.service.mComponentResolver;
mInstantAppResolverConnection = args.service.mInstantAppResolverConnection;
mDefaultAppProvider = args.service.mDefaultAppProvider;
+ mDomainVerificationManager = args.service.mDomainVerificationManager;
// Used to reference PMS attributes that are primitives and which are not
// updated under control of the PMS lock.
@@ -2755,8 +2580,6 @@
final ArrayList<ResolveInfo> result = new ArrayList<>();
final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
- final ArrayList<ResolveInfo> alwaysAskList = new ArrayList<>();
- final ArrayList<ResolveInfo> neverList = new ArrayList<>();
final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
final int count = candidates.size();
// First, try to use linked apps. Partition the candidates into four lists:
@@ -2772,43 +2595,15 @@
matchAllList.add(info);
continue;
}
- // Try to get the status from User settings first
- long packedStatus = getDomainVerificationStatusLPr(ps, userId);
- int status = (int)(packedStatus >> 32);
- int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
- if (DEBUG_DOMAIN_VERIFICATION || debug) {
- Slog.i(TAG, " + always: " + info.activityInfo.packageName
- + " : linkgen=" + linkGeneration);
- }
- if (!intent.hasCategory(CATEGORY_BROWSABLE)
- || !intent.hasCategory(CATEGORY_DEFAULT)) {
- undefinedList.add(info);
- continue;
- }
-
- // Use link-enabled generation as preferredOrder, i.e.
- // prefer newly-enabled over earlier-enabled.
- info.preferredOrder = linkGeneration;
+ boolean isAlways = mDomainVerificationManager
+ .isApprovedForDomain(ps, intent, userId);
+ if (isAlways) {
alwaysList.add(info);
- } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
- if (DEBUG_DOMAIN_VERIFICATION || debug) {
- Slog.i(TAG, " + never: " + info.activityInfo.packageName);
- }
- neverList.add(info);
- } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
- if (DEBUG_DOMAIN_VERIFICATION || debug) {
- Slog.i(TAG, " + always-ask: " + info.activityInfo.packageName);
- }
- alwaysAskList.add(info);
- } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED ||
- status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) {
- if (DEBUG_DOMAIN_VERIFICATION || debug) {
- Slog.i(TAG, " + ask: " + info.activityInfo.packageName);
- }
+ } else {
undefinedList.add(info);
}
+ continue;
}
}
@@ -2822,25 +2617,12 @@
// Add all undefined apps as we want them to appear in the disambiguation dialog.
result.addAll(undefinedList);
// Maybe add one for the other profile.
- if (xpDomainInfo != null && (
- xpDomainInfo.bestDomainVerificationStatus
- != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) {
+ if (xpDomainInfo != null && xpDomainInfo.wereAnyDomainsVerificationApproved) {
result.add(xpDomainInfo.resolveInfo);
}
includeBrowser = true;
}
- // The presence of any 'always ask' alternatives means we'll also offer browsers.
- // If there were 'always' entries their preferred order has been set, so we also
- // back that off to make the alternatives equivalent
- if (alwaysAskList.size() > 0) {
- for (ResolveInfo i : result) {
- i.preferredOrder = 0;
- }
- result.addAll(alwaysAskList);
- includeBrowser = true;
- }
-
if (includeBrowser) {
// Also add browsers (all of them or only the default one)
if (DEBUG_DOMAIN_VERIFICATION) {
@@ -2891,7 +2673,6 @@
// has explicitly put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state
if (result.size() == 0) {
result.addAll(candidates);
- result.removeAll(neverList);
}
}
return result;
@@ -2985,21 +2766,16 @@
if (ps == null) {
continue;
}
- long verificationState = getDomainVerificationStatusLPr(ps, parentUserId);
- int status = (int)(verificationState >> 32);
if (result == null) {
result = new CrossProfileDomainInfo();
result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
sourceUserId, parentUserId);
- result.bestDomainVerificationStatus = status;
- } else {
- result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
- result.bestDomainVerificationStatus);
}
+
+ result.wereAnyDomainsVerificationApproved |= mDomainVerificationManager
+ .isApprovedForDomain(ps, intent, riTargetUser.targetUserId);
}
- // Don't consider matches with status NEVER across profiles.
- if (result != null && result.bestDomainVerificationStatus
- == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+ if (result != null && !result.wereAnyDomainsVerificationApproved) {
return null;
}
return result;
@@ -3242,26 +3018,20 @@
final String packageName = info.activityInfo.packageName;
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps.getInstantApp(userId)) {
- final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
- final int status = (int)(packedStatus >> 32);
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
- // there's a local instant application installed, but, the user has
- // chosen to never use it; skip resolution and don't acknowledge
- // an instant application is even available
+ if (mDomainVerificationManager.isApprovedForDomain(ps, intent, userId)) {
if (DEBUG_INSTANT) {
- Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName);
- }
- blockResolution = true;
- break;
- } else {
- // we have a locally installed instant application; skip resolution
- // but acknowledge there's an instant application available
- if (DEBUG_INSTANT) {
- Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
+ Slog.v(TAG, "Instant app approvd for intent; pkg: "
+ + packageName);
}
localInstantApp = info;
- break;
+ } else {
+ if (DEBUG_INSTANT) {
+ Slog.v(TAG, "Instant app not approved for intent; pkg: "
+ + packageName);
+ }
+ blockResolution = true;
}
+ break;
}
}
}
@@ -4175,14 +3945,10 @@
if (ps != null) {
// only check domain verification status if the app is not a browser
if (!info.handleAllWebDataURI) {
- // Try to get the status from User settings first
- final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
- final int status = (int) (packedStatus >> 32);
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
- || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
+ if (mDomainVerificationManager.isApprovedForDomain(ps, intent, userId)) {
if (DEBUG_INSTANT) {
- Slog.v(TAG, "DENY instant app;"
- + " pkg: " + packageName + ", status: " + status);
+ Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
+ + ", approved");
}
return false;
}
@@ -4475,21 +4241,6 @@
return updateFlagsForComponent(flags, userId);
}
- // Returns a packed value as a long:
- //
- // high 'int'-sized word: link status: undefined/ask/never/always.
- // low 'int'-sized word: relative priority among 'always' results.
- public long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
- long result = ps.getDomainVerificationStatusForUser(userId);
- // if none available, get the status
- if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
- if (ps.getIntentFilterVerificationInfo() != null) {
- result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32;
- }
- }
- return result;
- }
-
/**
* Checks if the request is from the system or an app that has the appropriate cross-user
* permissions defined as follows:
@@ -5339,55 +5090,6 @@
params.handleIntegrityVerificationFinished();
break;
}
- case START_INTENT_FILTER_VERIFICATIONS: {
- IFVerificationParams params = (IFVerificationParams) msg.obj;
- verifyIntentFiltersIfNeeded(params.userId, params.verifierUid, params.replacing,
- params.packageName, params.hasDomainUrls, params.activities);
- break;
- }
- case INTENT_FILTER_VERIFIED: {
- final int verificationId = msg.arg1;
-
- final IntentFilterVerificationState state = mIntentFilterVerificationStates.get(
- verificationId);
- if (state == null) {
- Slog.w(TAG, "Invalid IntentFilter verification token "
- + verificationId + " received");
- break;
- }
-
- final int userId = state.getUserId();
-
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "Processing IntentFilter verification with token:"
- + verificationId + " and userId:" + userId);
-
- final IntentFilterVerificationResponse response =
- (IntentFilterVerificationResponse) msg.obj;
-
- state.setVerifierResponse(response.callerUid, response.code);
-
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "IntentFilter verification with token:" + verificationId
- + " and userId:" + userId
- + " is settings verifier response with response code:"
- + response.code);
-
- if (response.code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Domains failing verification: "
- + response.getFailedDomainsString());
- }
-
- if (state.isVerificationComplete()) {
- mIntentFilterVerifier.receiveVerificationResponse(verificationId);
- } else {
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "IntentFilter verification with token:" + verificationId
- + " was not said to be complete");
- }
-
- break;
- }
case INSTANT_APP_RESOLUTION_PHASE_TWO: {
InstantAppResolver.doInstantAppResolutionPhaseTwo(mContext,
mInstantAppResolverConnection,
@@ -5448,6 +5150,12 @@
}
break;
}
+ case DOMAIN_VERIFICATION: {
+ int messageCode = msg.arg1;
+ Object object = msg.obj;
+ mDomainVerificationManager.runMessage(messageCode, object);
+ break;
+ }
}
}
}
@@ -5807,19 +5515,19 @@
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional,
@Checksum.Type int required, @Nullable List trustedInstallers,
- @NonNull IntentSender statusReceiver, int userId) {
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
- statusReceiver, userId, mInjector.getBackgroundExecutor(),
+ onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
mInjector.getBackgroundHandler());
}
private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
- @Checksum.Type int optional,
- @Checksum.Type int required, @Nullable List trustedInstallers,
- @NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor,
- @NonNull Handler handler) {
+ @Checksum.Type int optional, @Checksum.Type int required,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
+ @NonNull Executor executor, @NonNull Handler handler) {
Objects.requireNonNull(packageName);
- Objects.requireNonNull(statusReceiver);
+ Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(executor);
Objects.requireNonNull(handler);
@@ -5855,7 +5563,7 @@
() -> mInjector.getIncrementalManager(),
() -> mPmInternal);
ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
- trustedCerts, statusReceiver, injector);
+ trustedCerts, onChecksumsReadyListener, injector);
});
}
@@ -6014,7 +5722,8 @@
}
public static PackageManagerService main(Context context, Installer installer,
- boolean factoryTest, boolean onlyCore) {
+ @NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
+ boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
@@ -6037,7 +5746,8 @@
lock),
(i, pm) -> new Settings(Environment.getDataDirectory(),
RuntimePermissionsPersistence.createInstance(),
- i.getPermissionManagerServiceInternal(), lock),
+ i.getPermissionManagerServiceInternal(),
+ domainVerificationService, lock),
(i, pm) -> AppsFilter.create(pm.mPmInternal, i),
(i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
(i, pm) -> SystemConfig.getInstance(),
@@ -6051,8 +5761,8 @@
(i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
(i, pm) -> (IncrementalManager)
i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
- (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(
- RoleManager.class)),
+ (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
+ () -> LocalServices.getService(UserManagerInternal.class)),
(i, pm) -> new DisplayMetrics(),
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
i.getDisplayMetrics(), pm.mCacheDir,
@@ -6070,6 +5780,13 @@
i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
(i, pm) -> new ModuleInfoProvider(i.getContext(), pm),
(i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
+ (i, pm) -> domainVerificationService,
+ (i, pm) -> {
+ HandlerThread thread = new ServiceThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
+ thread.start();
+ return pm.new PackageHandler(thread.getLooper());
+ },
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService);
@@ -6241,6 +5958,9 @@
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mUserManager = injector.getUserManagerService();
+ mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
+ mHandler = injector.getHandler();
+
mApexManager = testParams.apexManager;
mArtManagerService = testParams.artManagerService;
mAvailableFeatures = testParams.availableFeatures;
@@ -6250,14 +5970,11 @@
mDexManager = testParams.dexManager;
mDirsToScanAsSystem = testParams.dirsToScanAsSystem;
mFactoryTest = testParams.factoryTest;
- mHandler = testParams.handler;
mIncrementalManager = testParams.incrementalManager;
mInstallerService = testParams.installerService;
mInstantAppRegistry = testParams.instantAppRegistry;
mInstantAppResolverConnection = testParams.instantAppResolverConnection;
mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent;
- mIntentFilterVerifier = testParams.intentFilterVerifier;
- mIntentFilterVerifierComponent = testParams.intentFilterVerifierComponent;
mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade;
mIsPreNUpgrade = testParams.isPreNupgrade;
mIsPreQUpgrade = testParams.isPreQupgrade;
@@ -6463,6 +6180,9 @@
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
mAppLib32InstallDir = getAppLib32InstallDir();
+ mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
+ mDomainVerificationManager.setConnection(mDomainVerificationConnection);
+
// Link up the watchers
mPackages.registerObserver(mWatcher);
mSharedLibraries.registerObserver(mWatcher);
@@ -6485,10 +6205,7 @@
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
- HandlerThread handlerThread = new ServiceThread(TAG,
- Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
- handlerThread.start();
- mHandler = new PackageHandler(handlerThread.getLooper());
+ mHandler = injector.getHandler();
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
@@ -6973,7 +6690,6 @@
if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(user.id);
- primeDomainVerificationsLPw(user.id);
}
}
@@ -7080,13 +6796,18 @@
mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr();
mRequiredInstallerPackage = getRequiredInstallerLPr();
mRequiredUninstallerPackage = getRequiredUninstallerLPr();
- mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
- if (mIntentFilterVerifierComponent != null) {
- mIntentFilterVerifier = new IntentVerifierProxy(mContext,
- mIntentFilterVerifierComponent);
- } else {
- mIntentFilterVerifier = null;
- }
+ ComponentName intentFilterVerifierComponent =
+ getIntentFilterVerifierComponentNameLPr();
+ ComponentName domainVerificationAgent =
+ getDomainVerificationAgentComponentNameLPr();
+
+ DomainVerificationProxy domainVerificationProxy = DomainVerificationProxy.makeProxy(
+ intentFilterVerifierComponent, domainVerificationAgent, mContext,
+ mDomainVerificationManager, mDomainVerificationManager.getCollector(),
+ mDomainVerificationConnection);
+
+ mDomainVerificationManager.setProxy(domainVerificationProxy);
+
mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr();
mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
@@ -7095,8 +6816,6 @@
mRequiredVerifierPackage = null;
mRequiredInstallerPackage = null;
mRequiredUninstallerPackage = null;
- mIntentFilterVerifierComponent = null;
- mIntentFilterVerifier = null;
mServicesExtensionPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
}
@@ -7631,6 +7350,40 @@
return null;
}
+ @Nullable
+ private ComponentName getDomainVerificationAgentComponentNameLPr() {
+ Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION);
+ List<ResolveInfo> matches = queryIntentReceiversInternal(intent, null,
+ MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/);
+ ResolveInfo best = null;
+ final int N = matches.size();
+ for (int i = 0; i < N; i++) {
+ final ResolveInfo cur = matches.get(i);
+ final String packageName = cur.getComponentInfo().packageName;
+ if (checkPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
+ packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Domain verification agent found but does not hold permission: "
+ + packageName);
+ continue;
+ }
+
+ if (best == null || cur.priority > best.priority) {
+ if (cur.getComponentInfo().enabled) {
+ best = cur;
+ } else {
+ Slog.w(TAG, "Domain verification agent found but not enabled");
+ }
+ }
+ }
+
+ if (best != null) {
+ return best.getComponentInfo().getComponentName();
+ }
+ Slog.w(TAG, "Domain verification agent not found");
+ return null;
+ }
+
@Override
public @Nullable ComponentName getInstantAppResolverComponent() {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
@@ -7764,60 +7517,6 @@
return matches.get(0).getComponentInfo().getComponentName();
}
- @GuardedBy("mLock")
- private void primeDomainVerificationsLPw(int userId) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Priming domain verifications in user " + userId);
- }
-
- SystemConfig systemConfig = mInjector.getSystemConfig();
- ArraySet<String> packages = systemConfig.getLinkedApps();
-
- for (String packageName : packages) {
- AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null) {
- if (!pkg.isSystem()) {
- Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig <app-link>");
- continue;
- }
-
- ArraySet<String> domains = null;
- for (ParsedActivity a : pkg.getActivities()) {
- for (ParsedIntentInfo filter : a.getIntents()) {
- if (hasValidDomains(filter)) {
- if (domains == null) {
- domains = new ArraySet<>();
- }
- domains.addAll(filter.getHostsList());
- }
- }
- }
-
- if (domains != null && domains.size() > 0) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.v(TAG, " + " + packageName);
- }
- // 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual
- // state w.r.t. the formal app-linkage "no verification attempted" state;
- // and then 'always' in the per-user state actually used for intent resolution.
- final IntentFilterVerificationInfo ivi;
- ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains);
- ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
- mSettings.updateIntentFilterVerificationStatusLPw(packageName,
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId);
- } else {
- Slog.w(TAG, "Sysconfig <app-link> package '" + packageName
- + "' does not handle web links");
- }
- } else {
- Slog.w(TAG, "Unknown package " + packageName + " in sysconfig <app-link>");
- }
- }
-
- scheduleWritePackageRestrictionsLocked(userId);
- scheduleWriteSettingsLocked();
- }
-
private boolean packageIsBrowser(String packageName, int userId) {
List<ResolveInfo> list = queryIntentActivitiesInternal(sBrowserIntent, null,
PackageManager.MATCH_ALL, userId);
@@ -9241,6 +8940,7 @@
@Override
public List<String> getAllPackages() {
+ enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers");
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mLock) {
@@ -9663,9 +9363,8 @@
if (ri.activityInfo.applicationInfo.isInstantApp()) {
final String packageName = ri.activityInfo.packageName;
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
- final int status = (int)(packedStatus >> 32);
- if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
+ if (ps != null && mDomainVerificationManager
+ .isApprovedForDomain(ps, intent, userId)) {
return ri;
}
}
@@ -10157,8 +9856,7 @@
private static class CrossProfileDomainInfo {
/* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
ResolveInfo resolveInfo;
- /* Best domain verification status of the activities found in the other profile */
- int bestDomainVerificationStatus;
+ boolean wereAnyDomainsVerificationApproved;
}
private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
@@ -10244,13 +9942,6 @@
xpDomainInfo, userId, debug);
}
- // Returns a packed value as a long:
- //
- // high 'int'-sized word: link status: undefined/ask/never/always.
- // low 'int'-sized word: relative priority among 'always' results.
- private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
- return liveComputer().getDomainVerificationStatusLPr(ps, userId);
- }
private ResolveInfo querySkipCurrentProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
@@ -13500,7 +13191,7 @@
final int userId = user == null ? 0 : user.getIdentifier();
// Modify state for the given package setting
- commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
+ commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
(parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
if (pkgSetting.getInstantApp(userId)) {
mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
@@ -13757,6 +13448,9 @@
usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
}
+
+ final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
+
// TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
// to avoid adding something that's unsupported due to lack of state, since it's called
// with null.
@@ -13780,7 +13474,8 @@
parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user,
true /*allowInstall*/, instantApp, virtualPreload,
UserManagerService.getInstance(), usesStaticLibraries,
- parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups());
+ parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
+ newDomainSetId);
} else {
// make a deep copy to avoid modifying any existing system state.
pkgSetting = new PackageSetting(pkgSetting);
@@ -13799,7 +13494,7 @@
PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
UserManagerService.getInstance(),
usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
- parsedPackage.getMimeGroups());
+ parsedPackage.getMimeGroups(), newDomainSetId);
}
if (createNewPackage && originalPkgSetting != null) {
// This is the initial transition from the original package, so,
@@ -14644,8 +14339,8 @@
* Adds a scanned package to the system. When this method is finished, the package will
* be available for query, resolution, etc...
*/
- private void commitPackageSettings(AndroidPackage pkg,
- @Nullable AndroidPackage oldPkg, PackageSetting pkgSetting,
+ private void commitPackageSettings(@NonNull AndroidPackage pkg, @Nullable AndroidPackage oldPkg,
+ @NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting,
final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
final String pkgName = pkg.getPackageName();
if (mCustomResolverComponentName != null &&
@@ -14768,6 +14463,12 @@
mAppsFilter.addPackage(pkgSetting, isReplace);
mPackageProperty.addAllProperties(pkg);
+ if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) {
+ mDomainVerificationManager.addPackage(pkgSetting);
+ } else {
+ mDomainVerificationManager.migrateState(oldPkgSetting, pkgSetting);
+ }
+
int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
StringBuilder r = null;
int i;
@@ -16440,77 +16141,31 @@
return DEFAULT_INTEGRITY_VERIFY_ENABLE;
}
+ @Deprecated
@Override
- public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains)
- throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
- "Only intentfilter verification agents can verify applications");
-
- final Message msg = mHandler.obtainMessage(INTENT_FILTER_VERIFIED);
- final IntentFilterVerificationResponse response = new IntentFilterVerificationResponse(
- Binder.getCallingUid(), verificationCode, failedDomains);
- msg.arg1 = id;
- msg.obj = response;
- mHandler.sendMessage(msg);
+ public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {
+ DomainVerificationProxyV1.queueLegacyVerifyResult(mContext, mDomainVerificationConnection,
+ id, verificationCode, failedDomains, Binder.getCallingUid());
}
+ @Deprecated
@Override
public int getIntentVerificationStatus(String packageName, int userId) {
- final int callingUid = Binder.getCallingUid();
- if (UserHandle.getUserId(callingUid) != userId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "getIntentVerificationStatus" + userId);
- }
- if (getInstantAppPackageName(callingUid) != null) {
- return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
- }
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps == null
- || shouldFilterApplicationLocked(
- ps, callingUid, UserHandle.getUserId(callingUid))) {
- return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
- }
- return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
- }
+ return mDomainVerificationManager.getLegacyState(packageName, userId);
}
+ @Deprecated
@Override
public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
-
- boolean result = false;
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (shouldFilterApplicationLocked(
- ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
- return false;
- }
- result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
- }
- if (result) {
- scheduleWritePackageRestrictionsLocked(userId);
- }
- return result;
+ mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
+ return true;
}
+ @Deprecated
@Override
public @NonNull ParceledListSlice<IntentFilterVerificationInfo> getIntentFilterVerifications(
String packageName) {
- final int callingUid = Binder.getCallingUid();
- if (getInstantAppPackageName(callingUid) != null) {
- return ParceledListSlice.emptyList();
- }
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) {
- return ParceledListSlice.emptyList();
- }
- return new ParceledListSlice<>(mSettings.getIntentFilterVerificationsLPr(packageName));
- }
+ return ParceledListSlice.emptyList();
}
@Override
@@ -19506,6 +19161,8 @@
extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList, null);
+ // Unregister progress listener
+ mIncrementalManager.unregisterLoadingProgressCallbacks(codePath);
// Unregister health listener as it will always be healthy from now
mIncrementalManager.unregisterHealthListener(codePath);
}
@@ -20193,13 +19850,6 @@
"Failed to set up verity: " + e);
}
- if (!instantApp) {
- startIntentFilterVerifications(args.user.getIdentifier(), replace, parsedPackage);
- } else {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
- }
- }
final PackageFreezer freezer =
freezePackageForInstall(pkgName, installFlags, "installPackageLI");
boolean shouldCloseFreezerBeforeReturn = true;
@@ -20533,190 +20183,6 @@
}
}
- private void startIntentFilterVerifications(int userId, boolean replacing, AndroidPackage pkg) {
- if (mIntentFilterVerifierComponent == null) {
- Slog.w(TAG, "No IntentFilter verification will not be done as "
- + "there is no IntentFilterVerifier available!");
- return;
- }
-
- final int verifierUid = getPackageUid(
- mIntentFilterVerifierComponent.getPackageName(),
- MATCH_DEBUG_TRIAGED_MISSING,
- (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
-
- Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
- msg.obj = new IFVerificationParams(
- pkg.getPackageName(),
- pkg.isHasDomainUrls(),
- pkg.getActivities(),
- replacing,
- userId,
- verifierUid
- );
- mHandler.sendMessage(msg);
- }
-
- private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
- String packageName,
- boolean hasDomainUrls,
- List<ParsedActivity> activities) {
- int size = activities.size();
- if (size == 0) {
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "No activity, so no need to verify any IntentFilter!");
- return;
- }
-
- if (!hasDomainUrls) {
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "No domain URLs, so no need to verify any IntentFilter!");
- return;
- }
-
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Checking for userId:" + userId
- + " if any IntentFilter from the " + size
- + " Activities needs verification ...");
-
- int count = 0;
- boolean handlesWebUris = false;
- ArraySet<String> domains = new ArraySet<>();
- final boolean previouslyVerified;
- boolean hostSetExpanded = false;
- boolean needToRunVerify = false;
- synchronized (mLock) {
- // If this is a new install and we see that we've already run verification for this
- // package, we have nothing to do: it means the state was restored from backup.
- IntentFilterVerificationInfo ivi =
- mSettings.getIntentFilterVerificationLPr(packageName);
- previouslyVerified = (ivi != null);
- if (!replacing && previouslyVerified) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, "Package " + packageName + " already verified: status="
- + ivi.getStatusString());
- }
- return;
- }
-
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, " Previous verified hosts: "
- + (ivi == null ? "[none]" : ivi.getDomainsString()));
- }
-
- // If any filters need to be verified, then all need to be. In addition, we need to
- // know whether an updating app has any web navigation intent filters, to re-
- // examine handling policy even if not re-verifying.
- final boolean needsVerification = needsNetworkVerificationLPr(packageName);
- for (ParsedActivity a : activities) {
- for (ParsedIntentInfo filter : a.getIntents()) {
- if (filter.handlesWebUris(true)) {
- handlesWebUris = true;
- }
- if (needsVerification && filter.needsVerification()) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "autoVerify requested, processing all filters");
- }
- needToRunVerify = true;
- // It's safe to break out here because filter.needsVerification()
- // can only be true if filter.handlesWebUris(true) returned true, so
- // we've already noted that.
- break;
- }
- }
- }
-
- // Compare the new set of recognized hosts if the app is either requesting
- // autoVerify or has previously used autoVerify but no longer does.
- if (needToRunVerify || previouslyVerified) {
- final int verificationId = mIntentFilterVerificationToken++;
- for (ParsedActivity a : activities) {
- for (ParsedIntentInfo filter : a.getIntents()) {
- // Run verification against hosts mentioned in any web-nav intent filter,
- // even if the filter matches non-web schemes as well
- if (filter.handlesWebUris(false /*onlyWebSchemes*/)) {
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "Verification needed for IntentFilter:" + filter.toString());
- mIntentFilterVerifier.addOneIntentFilterVerification(
- verifierUid, userId, verificationId, filter, packageName);
- domains.addAll(filter.getHostsList());
- count++;
- }
- }
- }
- }
-
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, " Update published hosts: " + domains.toString());
- }
-
- // If we've previously verified this same host set (or a subset), we can trust that
- // a current ALWAYS policy is still applicable. If this is the case, we're done.
- // (If we aren't in ALWAYS, we want to reverify to allow for apps that had failing
- // hosts in their intent filters, then pushed a new apk that removed them and now
- // passes.)
- //
- // Cases:
- // + still autoVerify (needToRunVerify):
- // - preserve current state if all of: unexpanded, in always
- // - otherwise rerun as usual (fall through)
- // + no longer autoVerify (alreadyVerified && !needToRunVerify)
- // - wipe verification history always
- // - preserve current state if all of: unexpanded, in always
- hostSetExpanded = !previouslyVerified
- || (ivi != null && !ivi.getDomains().containsAll(domains));
- final int currentPolicy =
- mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
- final boolean keepCurState = !hostSetExpanded
- && currentPolicy == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-
- if (needToRunVerify && keepCurState) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, "Host set not expanding + ALWAYS -> no need to reverify");
- }
- ivi.setDomains(domains);
- scheduleWriteSettingsLocked();
- return;
- } else if (previouslyVerified && !needToRunVerify) {
- // Prior autoVerify state but not requesting it now. Clear autoVerify history,
- // and preserve the always policy iff the host set is not expanding.
- clearIntentFilterVerificationsLPw(packageName, userId, !keepCurState);
- return;
- }
- }
-
- if (needToRunVerify && count > 0) {
- // app requested autoVerify and has at least one matching intent filter
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
- + " IntentFilter verification" + (count > 1 ? "s" : "")
- + " for userId:" + userId);
- mIntentFilterVerifier.startVerifications(userId);
- } else {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "No web filters or no new host policy for " + packageName);
- }
- }
- }
-
- @GuardedBy("mLock")
- private boolean needsNetworkVerificationLPr(String packageName) {
- IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
- packageName);
- if (ivi == null) {
- return true;
- }
- int status = ivi.getStatus();
- switch (status) {
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
- return true;
-
- default:
- // Nothing to do
- return false;
- }
- }
-
private static boolean isExternal(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
@@ -21386,7 +20852,7 @@
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mLock) {
- clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true);
+ mDomainVerificationManager.clearPackage(deletedPs.name);
clearDefaultBrowserIfNeeded(packageName);
mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName);
mAppsFilter.removePackage(getPackageSetting(packageName));
@@ -21913,8 +21379,6 @@
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
- ps.readUserState(nextUserId).domainVerificationStatus,
- 0 /*linkGeneration*/,
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/);
@@ -22464,40 +21928,6 @@
mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId);
}
- /** This method takes a specific user id as well as UserHandle.USER_ALL. */
- @GuardedBy("mLock")
- private void clearIntentFilterVerificationsLPw(int userId) {
- final int packageCount = mPackages.size();
- for (int i = 0; i < packageCount; i++) {
- AndroidPackage pkg = mPackages.valueAt(i);
- clearIntentFilterVerificationsLPw(pkg.getPackageName(), userId, true);
- }
- }
-
- /** This method takes a specific user id as well as UserHandle.USER_ALL. */
- @GuardedBy("mLock")
- void clearIntentFilterVerificationsLPw(String packageName, int userId,
- boolean alsoResetStatus) {
- if (SystemConfig.getInstance().getLinkedApps().contains(packageName)) {
- // Nope, need to preserve the system configuration approval for this app
- return;
- }
-
- if (userId == UserHandle.USER_ALL) {
- if (mSettings.removeIntentFilterVerificationLPw(packageName,
- mUserManager.getUserIds())) {
- for (int oneUserId : mUserManager.getUserIds()) {
- scheduleWritePackageRestrictionsLocked(oneUserId);
- }
- }
- } else {
- if (mSettings.removeIntentFilterVerificationLPw(packageName, userId,
- alsoResetStatus)) {
- scheduleWritePackageRestrictionsLocked(userId);
- }
- }
- }
-
/** Clears state for all users, and touches intent filter verification policy */
void clearDefaultBrowserIfNeeded(String packageName) {
for (int oneUserId : mUserManager.getUserIds()) {
@@ -22553,8 +21983,7 @@
}
synchronized (mLock) {
mSettings.applyDefaultPreferredAppsLPw(userId);
- clearIntentFilterVerificationsLPw(userId);
- primeDomainVerificationsLPw(userId);
+ mDomainVerificationManager.clearUser(userId);
final int numPackages = mPackages.size();
for (int i = 0; i < numPackages; i++) {
final AndroidPackage pkg = mPackages.valueAt(i);
@@ -22816,28 +22245,8 @@
throw new SecurityException("Only the system may call getIntentFilterVerificationBackup()");
}
- ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
- try {
- final TypedXmlSerializer serializer = Xml.newFastSerializer();
- serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
- serializer.startDocument(null, true);
- serializer.startTag(null, TAG_INTENT_FILTER_VERIFICATION);
-
- synchronized (mLock) {
- mSettings.writeAllDomainVerificationsLPr(serializer, userId);
- }
-
- serializer.endTag(null, TAG_INTENT_FILTER_VERIFICATION);
- serializer.endDocument();
- serializer.flush();
- } catch (Exception e) {
- if (DEBUG_BACKUP) {
- Slog.e(TAG, "Unable to write default apps for backup", e);
- }
- return null;
- }
-
- return dataStream.toByteArray();
+ // TODO(b/170746586)
+ return null;
}
@Override
@@ -22845,22 +22254,7 @@
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the system may call restorePreferredActivities()");
}
-
- try {
- final TypedXmlPullParser parser = Xml.newFastPullParser();
- parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
- restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION,
- (parser1, userId1) -> {
- synchronized (mLock) {
- mSettings.readAllDomainVerificationsLPr(parser1, userId1);
- writeSettingsLPrTEMP();
- }
- });
- } catch (Exception e) {
- if (DEBUG_BACKUP) {
- Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
- }
- }
+ // TODO(b/170746586)
}
@Override
@@ -24113,8 +23507,8 @@
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
- (new PackageManagerShellCommand(this, mContext)).exec(
- this, in, out, err, args, callback, resultReceiver);
+ (new PackageManagerShellCommand(this, mContext,mDomainVerificationManager.getShell()))
+ .exec(this, in, out, err, args, callback, resultReceiver);
}
@SuppressWarnings("resource")
@@ -24296,9 +23690,8 @@
dumpState.setDump(DumpState.DUMP_MESSAGES);
} else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERIFIERS);
- } else if ("i".equals(cmd) || "ifv".equals(cmd)
- || "intent-filter-verifiers".equals(cmd)) {
- dumpState.setDump(DumpState.DUMP_INTENT_FILTER_VERIFIERS);
+ } else if ("dv".equals(cmd) || "domain-verifier".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_DOMAIN_VERIFIER);
} else if ("version".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERSION);
} else if ("k".equals(cmd) || "keysets".equals(cmd)) {
@@ -24392,14 +23785,16 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_INTENT_FILTER_VERIFIERS) &&
+ if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) &&
packageName == null) {
- if (mIntentFilterVerifierComponent != null) {
- String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
+ DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
+ ComponentName verifierComponent = proxy.getComponentName();
+ if (verifierComponent != null) {
+ String verifierPackageName = verifierComponent.getPackageName();
if (!checkin) {
if (dumpState.onTitlePrinted())
pw.println();
- pw.println("Intent Filter Verifier:");
+ pw.println("Domain Verifier:");
pw.print(" Using: ");
pw.print(verifierPackageName);
pw.print(" (uid=");
@@ -24407,14 +23802,14 @@
UserHandle.USER_SYSTEM));
pw.println(")");
} else if (verifierPackageName != null) {
- pw.print("ifv,"); pw.print(verifierPackageName);
+ pw.print("dv,"); pw.print(verifierPackageName);
pw.print(",");
pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
}
} else {
pw.println();
- pw.println("No Intent Filter Verifier available!");
+ pw.println("No Domain Verifier available!");
}
}
@@ -24531,63 +23926,20 @@
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)
- && packageName == null) {
- pw.println();
- int count = mSettings.getPackagesLocked().size();
- if (count == 0) {
- pw.println("No applications!");
- pw.println();
- } else {
- final String prefix = " ";
- Collection<PackageSetting> allPackageSettings =
- mSettings.getPackagesLocked().values();
- if (allPackageSettings.size() == 0) {
- pw.println("No domain preferred apps!");
- pw.println();
- } else {
- pw.println("App verification status:");
- pw.println();
- count = 0;
- for (PackageSetting ps : allPackageSettings) {
- IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
- if (ivi == null || ivi.getPackageName() == null) continue;
- pw.println(prefix + "Package: " + ivi.getPackageName());
- pw.println(prefix + "Domains: " + ivi.getDomainsString());
- pw.println(prefix + "Status: " + ivi.getStatusString());
- pw.println();
- count++;
- }
- if (count == 0) {
- pw.println(prefix + "No app verification established.");
- pw.println();
- }
- for (int userId : mUserManager.getUserIds()) {
- pw.println("App linkages for user " + userId + ":");
- pw.println();
- count = 0;
- for (PackageSetting ps : allPackageSettings) {
- final long status = ps.getDomainVerificationStatusForUser(userId);
- if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
- && !DEBUG_DOMAIN_VERIFICATION) {
- continue;
- }
- pw.println(prefix + "Package: " + ps.name);
- pw.println(prefix + "Domains: " + dumpDomainString(ps.name));
- String statusStr = IntentFilterVerificationInfo.
- getStatusStringFromValue(status);
- pw.println(prefix + "Status: " + statusStr);
- pw.println();
- count++;
- }
- if (count == 0) {
- pw.println(prefix + "No configured app linkages.");
- pw.println();
- }
- }
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+ android.util.IndentingPrintWriter writer =
+ new android.util.IndentingPrintWriter(pw);
+ if (dumpState.onTitlePrinted()) pw.println();
+
+ writer.println("Domain verification status:");
+ writer.increaseIndent();
+ try {
+ mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL);
+ } catch (PackageManager.NameNotFoundException e) {
+ pw.println("Failure printing domain verification information");
+ Slog.e(TAG, "Failure printing domain verification information", e);
}
+ writer.decreaseIndent();
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
@@ -24776,8 +24128,10 @@
UserHandle.USER_SYSTEM));
proto.end(requiredVerifierPackageToken);
- if (mIntentFilterVerifierComponent != null) {
- String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
+ DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
+ ComponentName verifierComponent = proxy.getComponentName();
+ if (verifierComponent != null) {
+ String verifierPackageName = verifierComponent.getPackageName();
final long verifierPackageToken =
proto.start(PackageServiceDumpProto.VERIFIER_PACKAGE);
proto.write(PackageServiceDumpProto.PackageShortProto.NAME, verifierPackageName);
@@ -24902,35 +24256,6 @@
}
}
- private String dumpDomainString(String packageName) {
- List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName)
- .getList();
- List<IntentFilter> filters = getAllIntentFilters(packageName).getList();
-
- ArraySet<String> result = new ArraySet<>();
- if (iviList.size() > 0) {
- for (IntentFilterVerificationInfo ivi : iviList) {
- result.addAll(ivi.getDomains());
- }
- }
- if (filters != null && filters.size() > 0) {
- for (IntentFilter filter : filters) {
- if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
- && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
- filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
- result.addAll(filter.getHostsList());
- }
- }
- }
-
- StringBuilder sb = new StringBuilder(result.size() * 16);
- for (String domain : result) {
- if (sb.length() > 0) sb.append(" ");
- sb.append(domain);
- }
- return sb.toString();
- }
-
// ------- apps on sdcard specific code -------
static final boolean DEBUG_SD_INSTALL = false;
@@ -26126,7 +25451,6 @@
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
scheduleWritePackageListLocked(userId);
- primeDomainVerificationsLPw(userId);
mAppsFilter.onUsersChanged();
}
}
@@ -27716,47 +27040,6 @@
}
@Override
- public boolean registerInstalledLoadingProgressCallback(String packageName,
- PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) {
- final PackageSetting ps = getPackageSettingForUser(packageName, Binder.getCallingUid(),
- userId);
- if (ps == null) {
- return false;
- }
- if (!ps.isPackageLoading()) {
- Slog.w(TAG,
- "Failed registering loading progress callback. Package is fully loaded.");
- return false;
- }
- if (mIncrementalManager == null) {
- Slog.w(TAG,
- "Failed registering loading progress callback. Incremental is not enabled");
- return false;
- }
- return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(),
- (IPackageLoadingProgressCallback) callback.getBinder());
- }
-
- @Override
- public boolean unregisterInstalledLoadingProgressCallback(String packageName,
- PackageManagerInternal.InstalledLoadingProgressCallback callback) {
- final PackageSetting ps;
- synchronized (mLock) {
- ps = mSettings.getPackageLPr(packageName);
- if (ps == null) {
- Slog.w(TAG, "Failed unregistering loading progress callback. Package "
- + packageName + " is not installed");
- return false;
- }
- }
- if (mIncrementalManager == null) {
- return false;
- }
- return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(),
- (IPackageLoadingProgressCallback) callback.getBinder());
- }
-
- @Override
public IncrementalStatesInfo getIncrementalStatesInfo(
@NonNull String packageName, int filterCallingUid, int userId) {
final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid,
@@ -27781,12 +27064,14 @@
ps.setStatesOnCrashOrAnr();
}
+ @Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
- @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler) {
requestChecksumsInternal(packageName, includeSplits, optional, required,
- trustedInstallers, statusReceiver, userId, executor, handler);
+ trustedInstallers, onChecksumsReadyListener, userId, executor, handler);
}
@Override
@@ -28426,6 +27711,11 @@
duration);
return bOptions;
}
+
+ @NonNull
+ public DomainVerificationService.Connection getDomainVerificationConnection() {
+ return mDomainVerificationConnection;
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 212edf6..b5765b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -17,13 +17,9 @@
package com.android.server.pm;
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import android.accounts.IAccountManager;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -108,6 +104,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
+import com.android.server.pm.verify.domain.DomainVerificationShell;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import dalvik.system.DexFile;
@@ -149,6 +146,7 @@
final LegacyPermissionManagerInternal mLegacyPermissionManager;
final PermissionManager mPermissionManager;
final Context mContext;
+ final DomainVerificationShell mDomainVerificationShell;
final private WeakHashMap<String, Resources> mResourceCache =
new WeakHashMap<String, Resources>();
int mTargetUser;
@@ -158,11 +156,13 @@
private static final SecureRandom RANDOM = new SecureRandom();
- PackageManagerShellCommand(PackageManagerService service, Context context) {
+ PackageManagerShellCommand(@NonNull PackageManagerService service,
+ @NonNull Context context, @NonNull DomainVerificationShell domainVerificationShell) {
mInterface = service;
mLegacyPermissionManager = LocalServices.getService(LegacyPermissionManagerInternal.class);
mPermissionManager = context.getSystemService(PermissionManager.class);
mContext = context;
+ mDomainVerificationShell = domainVerificationShell;
}
@Override
@@ -267,10 +267,6 @@
return runGetPrivappDenyPermissions();
case "get-oem-permissions":
return runGetOemPermissions();
- case "set-app-link":
- return runSetAppLink();
- case "get-app-link":
- return runGetAppLink();
case "trim-caches":
return runTrimCaches();
case "create-user":
@@ -309,6 +305,12 @@
case "bypass-staged-installer-check":
return runBypassStagedInstallerCheck();
default: {
+ Boolean domainVerificationResult =
+ mDomainVerificationShell.runCommand(this, cmd);
+ if (domainVerificationResult != null) {
+ return domainVerificationResult ? 0 : 1;
+ }
+
String nextArg = getNextArg();
if (nextArg == null) {
if (cmd.equalsIgnoreCase("-l")) {
@@ -2427,134 +2429,6 @@
return 0;
}
- private String linkStateToString(int state) {
- switch (state) {
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED: return "undefined";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return "ask";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: return "always";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: return "never";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK : return "always ask";
- }
- return "Unknown link state: " + state;
- }
-
- // pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined}
- private int runSetAppLink() throws RemoteException {
- int userId = UserHandle.USER_SYSTEM;
-
- String opt;
- while ((opt = getNextOption()) != null) {
- if (opt.equals("--user")) {
- userId = UserHandle.parseUserArg(getNextArgRequired());
- } else {
- getErrPrintWriter().println("Error: unknown option: " + opt);
- return 1;
- }
- }
-
- // Package name to act on; required
- final String pkg = getNextArg();
- if (pkg == null) {
- getErrPrintWriter().println("Error: no package specified.");
- return 1;
- }
-
- // State to apply; {always|ask|never|undefined}, required
- final String modeString = getNextArg();
- if (modeString == null) {
- getErrPrintWriter().println("Error: no app link state specified.");
- return 1;
- }
-
- final int newMode;
- switch (modeString.toLowerCase()) {
- case "undefined":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
- break;
-
- case "always":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
- break;
-
- case "ask":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
- break;
-
- case "always-ask":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
- break;
-
- case "never":
- newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
- break;
-
- default:
- getErrPrintWriter().println("Error: unknown app link state '" + modeString + "'");
- return 1;
- }
-
- final int translatedUserId =
- translateUserId(userId, UserHandle.USER_NULL, "runSetAppLink");
- final PackageInfo info = mInterface.getPackageInfo(pkg, 0, translatedUserId);
- if (info == null) {
- getErrPrintWriter().println("Error: package " + pkg + " not found.");
- return 1;
- }
-
- if ((info.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
- getErrPrintWriter().println("Error: package " + pkg + " does not handle web links.");
- return 1;
- }
-
- if (!mInterface.updateIntentVerificationStatus(pkg, newMode, translatedUserId)) {
- getErrPrintWriter().println("Error: unable to update app link status for " + pkg);
- return 1;
- }
-
- return 0;
- }
-
- // pm get-app-link [--user USER_ID] PACKAGE
- private int runGetAppLink() throws RemoteException {
- int userId = UserHandle.USER_SYSTEM;
-
- String opt;
- while ((opt = getNextOption()) != null) {
- if (opt.equals("--user")) {
- userId = UserHandle.parseUserArg(getNextArgRequired());
- } else {
- getErrPrintWriter().println("Error: unknown option: " + opt);
- return 1;
- }
- }
-
- // Package name to act on; required
- final String pkg = getNextArg();
- if (pkg == null) {
- getErrPrintWriter().println("Error: no package specified.");
- return 1;
- }
-
- final int translatedUserId =
- translateUserId(userId, UserHandle.USER_NULL, "runGetAppLink");
- final PackageInfo info = mInterface.getPackageInfo(pkg, 0, translatedUserId);
- if (info == null) {
- getErrPrintWriter().println("Error: package " + pkg + " not found.");
- return 1;
- }
-
- if ((info.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
- getErrPrintWriter().println("Error: package " + pkg + " does not handle web links.");
- return 1;
- }
-
- getOutPrintWriter().println(linkStateToString(
- mInterface.getIntentVerificationStatus(pkg, translatedUserId)));
-
- return 0;
- }
-
private int runTrimCaches() throws RemoteException {
String size = getNextArg();
if (size == null) {
@@ -3915,6 +3789,8 @@
pw.println(" --enable: turn on debug logging (default)");
pw.println(" --disable: turn off debug logging");
pw.println("");
+ mDomainVerificationShell.printHelp(pw);
+ pw.println("");
Intent.printIntentArgsHelp(pw , "");
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index ade087b..ca5d2b4 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -39,6 +39,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
/**
* Settings data for a particular package we know about.
@@ -99,18 +100,23 @@
@NonNull
private PackageStateUnserialized pkgState = new PackageStateUnserialized();
+ @NonNull
+ private UUID mDomainSetId;
+
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public PackageSetting(String name, String realName, @NonNull File codePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
long pVersionCode, int pkgFlags, int privateFlags,
int sharedUserId, String[] usesStaticLibraries,
- long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups) {
+ long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups,
+ @NonNull UUID domainSetId) {
super(name, realName, codePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
pVersionCode, pkgFlags, privateFlags,
usesStaticLibraries, usesStaticLibrariesVersions);
this.sharedUserId = sharedUserId;
+ mDomainSetId = domainSetId;
copyMimeGroups(mimeGroups);
}
@@ -168,6 +174,7 @@
sharedUser = orig.sharedUser;
sharedUserId = orig.sharedUserId;
copyMimeGroups(orig.mimeGroups);
+ mDomainSetId = orig.getDomainSetId();
}
private void copyMimeGroups(@Nullable Map<String, ArraySet<String>> newMimeGroups) {
@@ -337,8 +344,8 @@
installSource.originatingPackageName);
proto.end(sourceToken);
}
- proto.write(PackageProto.StatesProto.IS_STARTABLE, incrementalStates.isStartable());
- proto.write(PackageProto.StatesProto.IS_LOADING, incrementalStates.isLoading());
+ proto.write(PackageProto.StatesProto.IS_STARTABLE, isPackageStartable());
+ proto.write(PackageProto.StatesProto.IS_LOADING, isPackageLoading());
writeUsersInfoToProto(proto, PackageProto.USERS);
writePackageUserPermissionsProto(proto, PackageProto.USER_PERMISSIONS, users, dataProvider);
proto.end(packageToken);
@@ -374,6 +381,7 @@
pkg = other.pkg;
sharedUserId = other.sharedUserId;
sharedUser = other.sharedUser;
+ mDomainSetId = other.mDomainSetId;
Set<String> mimeGroupNames = other.mimeGroups != null ? other.mimeGroups.keySet() : null;
updateMimeGroups(mimeGroupNames);
@@ -385,4 +393,14 @@
public PackageStateUnserialized getPkgState() {
return pkgState;
}
+
+ @NonNull
+ public UUID getDomainSetId() {
+ return mDomainSetId;
+ }
+
+ public PackageSetting setDomainSetId(@NonNull UUID domainSetId) {
+ mDomainSetId = domainSetId;
+ return this;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 67bd82b4..3a14283 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -26,8 +26,6 @@
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.IncrementalStatesInfo;
-import android.content.pm.IntentFilterVerificationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.UninstallReason;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
@@ -131,8 +129,6 @@
/** Whether or not an update is available. Ostensibly only for instant apps. */
boolean updateAvailable;
- IntentFilterVerificationInfo verificationInfo;
-
boolean forceQueryableOverride;
@NonNull
@@ -258,7 +254,6 @@
for (int i = 0; i < orig.mUserState.size(); i++) {
mUserState.put(orig.mUserState.keyAt(i), orig.mUserState.valueAt(i));
}
- verificationInfo = orig.verificationInfo;
versionCode = orig.versionCode;
volumeUuid = orig.volumeUuid;
categoryHint = orig.categoryHint;
@@ -350,9 +345,12 @@
return readUserState(userId).getSharedLibraryOverlayPaths();
}
- /** Only use for testing. Do NOT use in production code. */
+ /**
+ * Only use for testing. Do NOT use in production code.
+ */
@VisibleForTesting
- SparseArray<PackageUserState> getUserState() {
+ @Deprecated
+ public SparseArray<PackageUserState> getUserState() {
return mUserState;
}
@@ -496,8 +494,7 @@
ArrayMap<String, PackageUserState.SuspendParams> suspendParams, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
- int domainVerifState, int linkGeneration, int installReason, int uninstallReason,
- String harmfulAppWarning) {
+ int installReason, int uninstallReason, String harmfulAppWarning) {
PackageUserState state = modifyUserState(userId);
state.ceDataInode = ceDataInode;
state.enabled = enabled;
@@ -511,8 +508,6 @@
state.lastDisableAppCaller = lastDisableAppCaller;
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
- state.domainVerificationStatus = domainVerifState;
- state.appLinkGeneration = linkGeneration;
state.installReason = installReason;
state.uninstallReason = uninstallReason;
state.instantApp = instantApp;
@@ -528,7 +523,6 @@
otherState.instantApp,
otherState.virtualPreload, otherState.lastDisableAppCaller,
otherState.enabledComponents, otherState.disabledComponents,
- otherState.domainVerificationStatus, otherState.appLinkGeneration,
otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning);
}
@@ -644,40 +638,6 @@
return excludedUserIds;
}
- IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
- return verificationInfo;
- }
-
- void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
- verificationInfo = info;
- onChanged();
- }
-
- // Returns a packed value as a long:
- //
- // high 'int'-sized word: link status: undefined/ask/never/always.
- // low 'int'-sized word: relative priority among 'always' results.
- long getDomainVerificationStatusForUser(int userId) {
- PackageUserState state = readUserState(userId);
- long result = (long) state.appLinkGeneration;
- result |= ((long) state.domainVerificationStatus) << 32;
- return result;
- }
-
- void setDomainVerificationStatusForUser(final int status, int generation, int userId) {
- PackageUserState state = modifyUserState(userId);
- state.domainVerificationStatus = status;
- if (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
- state.appLinkGeneration = generation;
- onChanged();
- }
- }
-
- void clearDomainVerificationStatusForUser(int userId) {
- modifyUserState(userId).domainVerificationStatus =
- PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
- }
-
protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
int count = mUserState.size();
for (int i = 0; i < count; i++) {
@@ -767,14 +727,14 @@
* @return True if package is startable, false otherwise.
*/
public boolean isPackageStartable() {
- return incrementalStates.isStartable();
+ return getIncrementalStates().isStartable();
}
/**
* @return True if package is still being loaded, false if the package is fully loaded.
*/
public boolean isPackageLoading() {
- return incrementalStates.isLoading();
+ return getIncrementalStates().isLoading();
}
/**
@@ -845,7 +805,6 @@
this.volumeUuid = other.volumeUuid;
this.categoryHint = other.categoryHint;
this.updateAvailable = other.updateAvailable;
- this.verificationInfo = other.verificationInfo;
this.forceQueryableOverride = other.forceQueryableOverride;
this.incrementalStates = other.incrementalStates;
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
index ce77c91..8c5084a 100644
--- a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -16,22 +16,16 @@
package com.android.server.pm;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-
import android.app.admin.SecurityLog;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
-import android.os.IBinder;
-import android.os.Parcelable;
+import android.os.RemoteException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -39,7 +33,6 @@
import com.android.internal.os.BackgroundThread;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@@ -109,26 +102,23 @@
// Capturing local loggingInfo to still log even if hash was invalidated.
try {
pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
- new IntentSender((IIntentSender) new IIntentSender.Stub() {
+ new IOnChecksumsReadyListener.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType,
- IBinder allowlistToken, IIntentReceiver finishedReceiver,
- String requiredPermission, Bundle options) {
- processChecksums(loggingInfo, intent);
+ public void onChecksumsReady(List<ApkChecksum> checksums)
+ throws RemoteException {
+ processChecksums(loggingInfo, checksums);
}
- }), context.getUserId(), mExecutor, this);
+ }, context.getUserId(),
+ mExecutor, this);
} catch (Throwable t) {
Slog.e(TAG, "requestChecksums() failed", t);
enqueueProcessChecksum(loggingInfo, null);
}
}
- void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
- Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
- ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
- ApkChecksum[].class);
-
- for (ApkChecksum checksum : checksums) {
+ void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) {
+ for (int i = 0, size = checksums.size(); i < size; ++i) {
+ ApkChecksum checksum = checksums.get(i);
if (checksum.getType() == CHECKSUM_TYPE) {
processChecksum(loggingInfo, checksum.getValue());
return;
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index ea61ca4..e5a70c3 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -26,6 +26,7 @@
import android.util.TypedXmlSerializer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.BundleUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -78,7 +79,7 @@
if (!changed) {
return false;
}
- if (!UserRestrictionsUtils.isEmpty(restrictions)) {
+ if (!BundleUtils.isEmpty(restrictions)) {
mUserRestrictions.put(userId, restrictions);
} else {
mUserRestrictions.delete(userId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 349d556..fb033e6 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -21,15 +21,12 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
-import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.NonNull;
@@ -108,6 +105,9 @@
import com.android.server.LocalServices;
import com.android.server.backup.PreferredActivityBackupHelper;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationPersistence;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -131,6 +131,7 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedWriter;
import java.io.File;
@@ -147,7 +148,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@@ -155,6 +155,7 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import java.util.UUID;
/**
* Holds information about dynamic settings.
@@ -283,9 +284,9 @@
"persistent-preferred-activities";
static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
"crossProfile-intent-filters";
- private static final String TAG_DOMAIN_VERIFICATION = "domain-verification";
+ public static final String TAG_DOMAIN_VERIFICATION = "domain-verification";
private static final String TAG_DEFAULT_APPS = "default-apps";
- private static final String TAG_ALL_INTENT_FILTER_VERIFICATION =
+ public static final String TAG_ALL_INTENT_FILTER_VERIFICATION =
"all-intent-filter-verifications";
private static final String TAG_DEFAULT_BROWSER = "default-browser";
private static final String TAG_DEFAULT_DIALER = "default-dialer";
@@ -390,12 +391,6 @@
private final WatchedSparseArray<ArraySet<String>> mBlockUninstallPackages =
new WatchedSparseArray<>();
- // Set of restored intent-filter verification states
- @Watched
- private final WatchedArrayMap<String, IntentFilterVerificationInfo>
- mRestoredIntentFilterVerifications =
- new WatchedArrayMap<String, IntentFilterVerificationInfo>();
-
private static final class KernelPackageState {
int appId;
int[] excludedUserIds;
@@ -487,7 +482,10 @@
@Watched
final WatchedSparseArray<String> mDefaultBrowserApp = new WatchedSparseArray<String>();
+ // TODO(b/161161364): This seems unused, and is probably not relevant in the new API, but should
+ // verify.
// App-link priority tracking, per-user
+ @NonNull
@Watched
final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray();
@@ -512,6 +510,8 @@
private final LegacyPermissionDataProvider mPermissionDataProvider;
+ private final DomainVerificationManagerInternal mDomainVerificationManager;
+
/**
* The observer that watches for changes from array members
*/
@@ -538,13 +538,12 @@
mStoppedPackagesFilename = null;
mBackupStoppedPackagesFilename = null;
mKernelMappingFilename = null;
-
+ mDomainVerificationManager = null;
mPackages.registerObserver(mObserver);
mInstallerPackages.registerObserver(mObserver);
mKernelMapping.registerObserver(mObserver);
mDisabledSysPackages.registerObserver(mObserver);
mBlockUninstallPackages.registerObserver(mObserver);
- mRestoredIntentFilterVerifications.registerObserver(mObserver);
mVersion.registerObserver(mObserver);
mPreferredActivities.registerObserver(mObserver);
mPersistentPreferredActivities.registerObserver(mObserver);
@@ -554,13 +553,14 @@
mOtherAppIds.registerObserver(mObserver);
mRenamedPackages.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
- mNextAppLinkGeneration.registerObserver(mObserver);
Watchable.verifyWatchedAttributes(this, mObserver);
}
Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
- LegacyPermissionDataProvider permissionDataProvider, Object lock) {
+ LegacyPermissionDataProvider permissionDataProvider,
+ @NonNull DomainVerificationManagerInternal domainVerificationManager,
+ @NonNull Object lock) {
mLock = lock;
mAppIds = new WatchedArrayList<>();
mOtherAppIds = new WatchedSparseArray<>();
@@ -587,12 +587,13 @@
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
+ mDomainVerificationManager = domainVerificationManager;
+
mPackages.registerObserver(mObserver);
mInstallerPackages.registerObserver(mObserver);
mKernelMapping.registerObserver(mObserver);
mDisabledSysPackages.registerObserver(mObserver);
mBlockUninstallPackages.registerObserver(mObserver);
- mRestoredIntentFilterVerifications.registerObserver(mObserver);
mVersion.registerObserver(mObserver);
mPreferredActivities.registerObserver(mObserver);
mPersistentPreferredActivities.registerObserver(mObserver);
@@ -602,7 +603,6 @@
mOtherAppIds.registerObserver(mObserver);
mRenamedPackages.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
- mNextAppLinkGeneration.registerObserver(mObserver);
Watchable.verifyWatchedAttributes(this, mObserver);
}
@@ -629,11 +629,12 @@
mBackupStoppedPackagesFilename = null;
mKernelMappingFilename = null;
+ mDomainVerificationManager = r.mDomainVerificationManager;
+
mInstallerPackages.addAll(r.mInstallerPackages);
mKernelMapping.putAll(r.mKernelMapping);
mDisabledSysPackages.putAll(r.mDisabledSysPackages);
mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages);
- mRestoredIntentFilterVerifications.putAll(r.mRestoredIntentFilterVerifications);
mVersion.putAll(r.mVersion);
mVerifierDeviceIdentity = r.mVerifierDeviceIdentity;
WatchedSparseArray.snapshot(
@@ -649,7 +650,6 @@
mKeySetRefs.putAll(r.mKeySetRefs);
mRenamedPackages.snapshot(r.mRenamedPackages);
mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp);
- mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration);
// mReadMessages
mPendingPackages.addAll(r.mPendingPackages);
mSystemDir = null;
@@ -766,7 +766,8 @@
p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
p.secondaryCpuAbiString, p.cpuAbiOverrideString,
p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
- p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups);
+ p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups,
+ mDomainVerificationManager.generateNewId());
if (ret != null) {
ret.getPkgState().setUpdatedSystemApp(false);
}
@@ -786,7 +787,8 @@
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
- long[] usesStaticLibraryNames, Map<String, ArraySet<String>> mimeGroups) {
+ long[] usesStaticLibraryNames, Map<String, ArraySet<String>> mimeGroups,
+ @NonNull UUID domainSetId) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (p.appId == uid) {
@@ -799,7 +801,7 @@
p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags,
pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
- mimeGroups);
+ mimeGroups, domainSetId);
p.appId = uid;
if (registerExistingAppIdLPw(uid, p, name)) {
mPackages.put(name, p);
@@ -863,7 +865,7 @@
UserHandle installUser, boolean allowInstall, boolean instantApp,
boolean virtualPreload, UserManagerService userManager,
String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
- Set<String> mimeGroupNames) {
+ Set<String> mimeGroupNames, @NonNull UUID domainSetId) {
final PackageSetting pkgSetting;
if (originalPkg != null) {
if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
@@ -883,12 +885,13 @@
pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
// Update new package state.
pkgSetting.setTimeStamp(codePath.lastModified());
+ pkgSetting.setDomainSetId(domainSetId);
} else {
pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
0 /*sharedUserId*/, usesStaticLibraries,
- usesStaticLibrariesVersions, createMimeGroups(mimeGroupNames));
+ usesStaticLibrariesVersions, createMimeGroups(mimeGroupNames), domainSetId);
pkgSetting.setTimeStamp(codePath.lastModified());
pkgSetting.sharedUser = sharedUser;
// If this is not a system app, it starts out stopped.
@@ -925,8 +928,6 @@
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
- 0 /*linkGeneration*/,
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/);
@@ -983,7 +984,7 @@
@Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
int pkgPrivateFlags, @NonNull UserManagerService userManager,
@Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
- @Nullable Set<String> mimeGroupNames)
+ @Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
throws PackageManagerException {
final String pkgName = pkgSetting.name;
if (pkgSetting.sharedUser != sharedUser) {
@@ -1059,6 +1060,7 @@
pkgSetting.usesStaticLibrariesVersions = null;
}
pkgSetting.updateMimeGroups(mimeGroupNames);
+ pkgSetting.setDomainSetId(domainSetId);
}
/**
@@ -1168,15 +1170,6 @@
replaceAppIdLPw(p.appId, sharedUser);
}
}
-
- IntentFilterVerificationInfo ivi = mRestoredIntentFilterVerifications.get(p.name);
- if (ivi != null) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, "Applying restored IVI for " + p.name + " : " + ivi.getStatusString());
- }
- mRestoredIntentFilterVerifications.remove(p.name);
- p.setIntentFilterVerificationInfo(ivi);
- }
}
int removePackageLPw(String name) {
@@ -1307,129 +1300,6 @@
return cpir;
}
- /**
- * The following functions suppose that you have a lock for managing access to the
- * mIntentFiltersVerifications map.
- */
-
- /* package protected */
- IntentFilterVerificationInfo getIntentFilterVerificationLPr(String packageName) {
- PackageSetting ps = mPackages.get(packageName);
- if (ps == null) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
- }
- return null;
- }
- return ps.getIntentFilterVerificationInfo();
- }
-
- /* package protected */
- IntentFilterVerificationInfo createIntentFilterVerificationIfNeededLPw(String packageName,
- ArraySet<String> domains) {
- PackageSetting ps = mPackages.get(packageName);
- if (ps == null) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
- }
- return null;
- }
- IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
- if (ivi == null) {
- ivi = new IntentFilterVerificationInfo(packageName, domains);
- ps.setIntentFilterVerificationInfo(ivi);
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(PackageManagerService.TAG,
- "Creating new IntentFilterVerificationInfo for pkg: " + packageName);
- }
- } else {
- ivi.setDomains(domains);
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(PackageManagerService.TAG,
- "Setting domains to existing IntentFilterVerificationInfo for pkg: " +
- packageName + " and with domains: " + ivi.getDomainsString());
- }
- }
- return ivi;
- }
-
- int getIntentFilterVerificationStatusLPr(String packageName, int userId) {
- PackageSetting ps = mPackages.get(packageName);
- if (ps == null) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
- }
- return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
- }
- return (int)(ps.getDomainVerificationStatusForUser(userId) >> 32);
- }
-
- boolean updateIntentFilterVerificationStatusLPw(String packageName, final int status, int userId) {
- // Update the status for the current package
- PackageSetting current = mPackages.get(packageName);
- if (current == null) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
- }
- return false;
- }
-
- final int alwaysGeneration;
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
- alwaysGeneration = mNextAppLinkGeneration.get(userId) + 1;
- mNextAppLinkGeneration.put(userId, alwaysGeneration);
- } else {
- alwaysGeneration = 0;
- }
-
- current.setDomainVerificationStatusForUser(status, alwaysGeneration, userId);
- return true;
- }
-
- /**
- * Used for Settings App and PackageManagerService dump. Should be read only.
- */
- List<IntentFilterVerificationInfo> getIntentFilterVerificationsLPr(
- String packageName) {
- if (packageName == null) {
- return Collections.<IntentFilterVerificationInfo>emptyList();
- }
- ArrayList<IntentFilterVerificationInfo> result = new ArrayList<>();
- for (PackageSetting ps : mPackages.values()) {
- IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
- if (ivi == null || TextUtils.isEmpty(ivi.getPackageName()) ||
- !ivi.getPackageName().equalsIgnoreCase(packageName)) {
- continue;
- }
- result.add(ivi);
- }
- return result;
- }
-
- boolean removeIntentFilterVerificationLPw(String packageName, int userId,
- boolean alsoResetStatus) {
- PackageSetting ps = mPackages.get(packageName);
- if (ps == null) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.w(PackageManagerService.TAG, "No package known: " + packageName);
- }
- return false;
- }
- if (alsoResetStatus) {
- ps.clearDomainVerificationStatusForUser(userId);
- }
- ps.setIntentFilterVerificationInfo(null);
- return true;
- }
-
- boolean removeIntentFilterVerificationLPw(String packageName, int[] userIds) {
- boolean result = false;
- for (int userId : userIds) {
- result |= removeIntentFilterVerificationLPw(packageName, userId, true);
- }
- return result;
- }
-
String removeDefaultBrowserPackageNameLPw(int userId) {
return (userId == UserHandle.USER_ALL) ? null : mDefaultBrowserApp.removeReturnOld(userId);
}
@@ -1591,40 +1461,7 @@
}
}
- private void readDomainVerificationLPw(TypedXmlPullParser parser,
- PackageSettingBase packageSetting) throws XmlPullParserException, IOException {
- IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
- packageSetting.setIntentFilterVerificationInfo(ivi);
- if (DEBUG_PARSER) {
- Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName());
- }
- }
-
- private void readRestoredIntentFilterVerifications(TypedXmlPullParser parser)
- throws XmlPullParserException, IOException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- final String tagName = parser.getName();
- if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
- IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, "Restored IVI for " + ivi.getPackageName()
- + " status=" + ivi.getStatusString());
- }
- mRestoredIntentFilterVerifications.put(ivi.getPackageName(), ivi);
- } else {
- Slog.w(TAG, "Unknown element: " + tagName);
- XmlUtils.skipCurrentTag(parser);
- }
- }
- }
-
- void readDefaultAppsLPw(TypedXmlPullParser parser, int userId)
+ void readDefaultAppsLPw(XmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -1727,8 +1564,6 @@
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
- 0 /*linkGeneration*/,
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/);
@@ -1753,8 +1588,6 @@
return;
}
- int maxAppLinkGeneration = 0;
-
int outerDepth = parser.getDepth();
PackageSetting ps = null;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1817,11 +1650,6 @@
final int verifState = parser.getAttributeInt(null,
ATTR_DOMAIN_VERIFICATON_STATE,
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
- final int linkGeneration =
- parser.getAttributeInt(null, ATTR_APP_LINK_GENERATION, 0);
- if (linkGeneration > maxAppLinkGeneration) {
- maxAppLinkGeneration = linkGeneration;
- }
final int installReason = parser.getAttributeInt(null, ATTR_INSTALL_REASON,
PackageManager.INSTALL_REASON_UNKNOWN);
final int uninstallReason = parser.getAttributeInt(null, ATTR_UNINSTALL_REASON,
@@ -1897,9 +1725,10 @@
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, distractionFlags, suspended, suspendParamsMap,
- instantApp, virtualPreload,
- enabledCaller, enabledComponents, disabledComponents, verifState,
- linkGeneration, installReason, uninstallReason, harmfulAppWarning);
+ instantApp, virtualPreload, enabledCaller, enabledComponents,
+ disabledComponents, installReason, uninstallReason, harmfulAppWarning);
+
+ mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1918,9 +1747,6 @@
}
str.close();
-
- mNextAppLinkGeneration.put(userId, maxAppLinkGeneration + 1);
-
} catch (XmlPullParserException e) {
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR,
@@ -2038,77 +1864,7 @@
serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
}
- void writeDomainVerificationsLPr(TypedXmlSerializer serializer,
- IntentFilterVerificationInfo verificationInfo)
- throws IllegalArgumentException, IllegalStateException, IOException {
- if (verificationInfo != null && verificationInfo.getPackageName() != null) {
- serializer.startTag(null, TAG_DOMAIN_VERIFICATION);
- verificationInfo.writeToXml(serializer);
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Wrote domain verification for package: "
- + verificationInfo.getPackageName());
- }
- serializer.endTag(null, TAG_DOMAIN_VERIFICATION);
- }
- }
-
- // Specifically for backup/restore
- void writeAllDomainVerificationsLPr(TypedXmlSerializer serializer, int userId)
- throws IllegalArgumentException, IllegalStateException, IOException {
- serializer.startTag(null, TAG_ALL_INTENT_FILTER_VERIFICATION);
- final int N = mPackages.size();
- for (int i = 0; i < N; i++) {
- PackageSetting ps = mPackages.valueAt(i);
- IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
- if (ivi != null) {
- writeDomainVerificationsLPr(serializer, ivi);
- }
- }
- serializer.endTag(null, TAG_ALL_INTENT_FILTER_VERIFICATION);
- }
-
- // Specifically for backup/restore
- void readAllDomainVerificationsLPr(TypedXmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- mRestoredIntentFilterVerifications.clear();
-
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
- IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
- final String pkgName = ivi.getPackageName();
- final PackageSetting ps = mPackages.get(pkgName);
- if (ps != null) {
- // known/existing package; update in place
- ps.setIntentFilterVerificationInfo(ivi);
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Restored IVI for existing app " + pkgName
- + " status=" + ivi.getStatusString());
- }
- } else {
- mRestoredIntentFilterVerifications.put(pkgName, ivi);
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "Restored IVI for pending app " + pkgName
- + " status=" + ivi.getStatusString());
- }
- }
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Unknown element under <all-intent-filter-verification>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
- }
-
- void writeDefaultAppsLPr(TypedXmlSerializer serializer, int userId)
+ void writeDefaultAppsLPr(XmlSerializer serializer, int userId)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(null, TAG_DEFAULT_APPS);
String defaultBrowser = mDefaultBrowserApp.get(userId);
@@ -2217,15 +1973,6 @@
ustate.lastDisableAppCaller);
}
}
- if (ustate.domainVerificationStatus !=
- PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
- serializer.attributeInt(null, ATTR_DOMAIN_VERIFICATON_STATE,
- ustate.domainVerificationStatus);
- }
- if (ustate.appLinkGeneration != 0) {
- serializer.attributeInt(null, ATTR_APP_LINK_GENERATION,
- ustate.appLinkGeneration);
- }
if (ustate.installReason != PackageManager.INSTALL_REASON_UNKNOWN) {
serializer.attributeInt(null, ATTR_INSTALL_REASON, ustate.installReason);
}
@@ -2569,22 +2316,7 @@
}
}
- final int numIVIs = mRestoredIntentFilterVerifications.size();
- if (numIVIs > 0) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, "Writing restored-ivi entries to packages.xml");
- }
- serializer.startTag(null, "restored-ivi");
- for (int i = 0; i < numIVIs; i++) {
- IntentFilterVerificationInfo ivi = mRestoredIntentFilterVerifications.valueAt(i);
- writeDomainVerificationsLPr(serializer, ivi);
- }
- serializer.endTag(null, "restored-ivi");
- } else {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, " no restored IVI entries to write");
- }
- }
+ mDomainVerificationManager.writeSettings(serializer);
mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
@@ -2961,6 +2693,8 @@
serializer.attributeFloat(null, "loadingProgress",
pkg.getIncrementalStates().getProgress());
+ serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
+
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
@@ -2973,7 +2707,7 @@
writeSigningKeySetLPr(serializer, pkg.keySetData);
writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
writeKeySetAliasesLPr(serializer, pkg.keySetData);
- writeDomainVerificationsLPr(serializer, pkg.verificationInfo);
+ mDomainVerificationManager.writeLegacySettings(serializer, pkg.name);
writeMimeGroupLPr(serializer, pkg.mimeGroups);
serializer.endTag(null, "package");
@@ -3104,8 +2838,6 @@
if (nname != null && oname != null) {
mRenamedPackages.put(nname, oname);
}
- } else if (tagName.equals("restored-ivi")) {
- readRestoredIntentFilterVerifications(parser);
} else if (tagName.equals("last-platform-version")) {
// Upgrade from older XML schema
final VersionInfo internal = findOrCreateVersion(
@@ -3147,6 +2879,11 @@
ver.sdkVersion = parser.getAttributeInt(null, ATTR_SDK_VERSION);
ver.databaseVersion = parser.getAttributeInt(null, ATTR_DATABASE_VERSION);
ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);
+ } else if (tagName.equals(DomainVerificationPersistence.TAG_DOMAIN_VERIFICATIONS)) {
+ mDomainVerificationManager.readSettings(parser);
+ } else if (tagName.equals(
+ DomainVerificationLegacySettings.TAG_DOMAIN_VERIFICATIONS_LEGACY)) {
+ mDomainVerificationManager.readLegacySettings(parser);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
+ parser.getName());
@@ -3628,9 +3365,15 @@
if (codePathStr.contains("/priv-app/")) {
pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
+
+ // When reading a disabled setting, use a disabled domainSetId, which makes it easier to
+ // debug invalid entries. The actual logic for migrating to a new ID is done in other
+ // methods that use DomainVerificationManagerInternal#generateNewId
+ UUID domainSetId = DomainVerificationManagerInternal.DISABLED_ID;
PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr,
- versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null);
+ versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null,
+ domainSetId);
long timeStamp = parser.getAttributeLongHex(null, "ft", 0);
if (timeStamp == 0) {
timeStamp = parser.getAttributeLong(null, "ts", 0);
@@ -3703,6 +3446,7 @@
boolean isStartable = false;
boolean isLoading = false;
float loadingProgress = 0;
+ UUID domainSetId;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
@@ -3739,6 +3483,15 @@
categoryHint = parser.getAttributeInt(null, "categoryHint",
ApplicationInfo.CATEGORY_UNDEFINED);
+ String domainSetIdString = parser.getAttributeValue(null, "domainSetId");
+
+ if (TextUtils.isEmpty(domainSetIdString)) {
+ // If empty, assume restoring from previous platform version and generate an ID
+ domainSetId = mDomainVerificationManager.generateNewId();
+ } else {
+ domainSetId = UUID.fromString(domainSetIdString);
+ }
+
systemStr = parser.getAttributeValue(null, "publicFlags");
if (systemStr != null) {
try {
@@ -3810,7 +3563,7 @@
legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags,
null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/, domainSetId);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
+ userId + " pkg=" + packageSetting);
@@ -3831,7 +3584,7 @@
versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
null /*usesStaticLibraries*/,
null /*usesStaticLibraryVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/, domainSetId);
packageSetting.setTimeStamp(timeStamp);
packageSetting.firstInstallTime = firstInstallTime;
packageSetting.lastUpdateTime = lastUpdateTime;
@@ -3946,7 +3699,11 @@
packageSetting.installSource =
packageSetting.installSource.setInitiatingPackageSignatures(signatures);
} else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
- readDomainVerificationLPw(parser, packageSetting);
+ IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
+ mDomainVerificationManager.addLegacySetting(packageSetting.name, ivi);
+ if (DEBUG_PARSER) {
+ Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName());
+ }
} else if (tagName.equals(TAG_MIME_GROUP)) {
packageSetting.mimeGroups = readMimeGroupLPw(parser, packageSetting.mimeGroups);
} else if (tagName.equals(TAG_USES_STATIC_LIB)) {
@@ -4212,6 +3969,7 @@
removeCrossProfileIntentFiltersLPw(userId);
mRuntimePermissionsPersistence.onUserRemovedLPw(userId);
+ mDomainVerificationManager.clearUser(userId);
writePackageListLPr();
diff --git a/services/core/java/com/android/server/pm/SettingsXml.java b/services/core/java/com/android/server/pm/SettingsXml.java
new file mode 100644
index 0000000..9588a27
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SettingsXml.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Stack;
+
+/**
+ * A very specialized serialization/parsing wrapper around {@link TypedXmlSerializer} and {@link
+ * TypedXmlPullParser} intended for use with PackageManager related settings files.
+ * Assumptions/chosen behaviors:
+ * <ul>
+ * <li>No namespace support</li>
+ * <li>Data for a parent object is stored as attributes</li>
+ * <li>All attribute read methods return a default false, -1, or null</li>
+ * <li>Default values will not be written</li>
+ * <li>Children are sub-elements</li>
+ * <li>Collections are repeated sub-elements, no attribute support for collections</li>
+ * </ul>
+ */
+public class SettingsXml {
+
+ private static final String TAG = "SettingsXml";
+
+ private static final boolean DEBUG_THROW_EXCEPTIONS = false;
+
+ private static final String FEATURE_INDENT =
+ "http://xmlpull.org/v1/doc/features.html#indent-output";
+
+ private static final int DEFAULT_NUMBER = -1;
+
+ public static Serializer serializer(TypedXmlSerializer serializer) {
+ return new Serializer(serializer);
+ }
+
+ public static ReadSection parser(TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ return new ReadSectionImpl(parser);
+ }
+
+ public static class Serializer implements AutoCloseable {
+
+ @NonNull
+ private final TypedXmlSerializer mXmlSerializer;
+
+ private final WriteSectionImpl mWriteSection;
+
+ private Serializer(TypedXmlSerializer serializer) {
+ mXmlSerializer = serializer;
+ mWriteSection = new WriteSectionImpl(mXmlSerializer);
+ }
+
+ public WriteSection startSection(@NonNull String sectionName) throws IOException {
+ return mWriteSection.startSection(sectionName);
+ }
+
+ @Override
+ public void close() throws IOException {
+ mWriteSection.closeCompletely();
+ mXmlSerializer.endDocument();
+ }
+ }
+
+ public interface ReadSection extends AutoCloseable {
+
+ @NonNull
+ String getName();
+
+ @NonNull
+ String getDescription();
+
+ boolean has(String attrName);
+
+ @Nullable
+ String getString(String attrName);
+
+ /**
+ * @return value as String or {@param defaultValue} if doesn't exist
+ */
+ @NonNull
+ String getString(String attrName, @NonNull String defaultValue);
+
+ /**
+ * @return value as boolean or false if doesn't exist
+ */
+ boolean getBoolean(String attrName);
+
+ /**
+ * @return value as boolean or {@param defaultValue} if doesn't exist
+ */
+ boolean getBoolean(String attrName, boolean defaultValue);
+
+ /**
+ * @return value as int or {@link #DEFAULT_NUMBER} if doesn't exist
+ */
+ int getInt(String attrName);
+
+ /**
+ * @return value as int or {@param defaultValue} if doesn't exist
+ */
+ int getInt(String attrName, int defaultValue);
+
+ /**
+ * @return value as long or {@link #DEFAULT_NUMBER} if doesn't exist
+ */
+ long getLong(String attrName);
+
+ /**
+ * @return value as long or {@param defaultValue} if doesn't exist
+ */
+ long getLong(String attrName, int defaultValue);
+
+ ChildSection children();
+ }
+
+ /**
+ * <pre><code>
+ * ChildSection child = parentSection.children();
+ * while (child.moveToNext(TAG_CHILD)) {
+ * String readValue = child.getString(...);
+ * ...
+ * }
+ * </code></pre>
+ */
+ public interface ChildSection extends ReadSection {
+ boolean moveToNext();
+
+ boolean moveToNext(@NonNull String expectedChildTagName);
+ }
+
+ public static class ReadSectionImpl implements ChildSection {
+
+ @Nullable
+ private final InputStream mInput;
+
+ @NonNull
+ private final TypedXmlPullParser mParser;
+
+ @NonNull
+ private final Stack<Integer> mDepthStack = new Stack<>();
+
+ public ReadSectionImpl(@NonNull InputStream input)
+ throws IOException, XmlPullParserException {
+ mInput = input;
+ mParser = Xml.newFastPullParser();
+ mParser.setInput(mInput, StandardCharsets.UTF_8.name());
+ moveToFirstTag();
+ }
+
+ public ReadSectionImpl(@NonNull TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ mInput = null;
+ mParser = parser;
+ moveToFirstTag();
+ }
+
+ private void moveToFirstTag() throws IOException, XmlPullParserException {
+ // Move to first tag
+ int type;
+ //noinspection StatementWithEmptyBody
+ while ((type = mParser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ }
+ }
+
+ @NonNull
+ @Override
+ public String getName() {
+ return mParser.getName();
+ }
+
+ @NonNull
+ @Override
+ public String getDescription() {
+ return mParser.getPositionDescription();
+ }
+
+ @Override
+ public boolean has(String attrName) {
+ return mParser.getAttributeValue(null, attrName) != null;
+ }
+
+ @Nullable
+ @Override
+ public String getString(String attrName) {
+ return mParser.getAttributeValue(null, attrName);
+ }
+
+ @NonNull
+ @Override
+ public String getString(String attrName, @NonNull String defaultValue) {
+ String value = mParser.getAttributeValue(null, attrName);
+ if (value == null) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ @Override
+ public boolean getBoolean(String attrName) {
+ return getBoolean(attrName, false);
+ }
+
+ @Override
+ public boolean getBoolean(String attrName, boolean defaultValue) {
+ return mParser.getAttributeBoolean(null, attrName, defaultValue);
+ }
+
+ @Override
+ public int getInt(String attrName) {
+ return getInt(attrName, DEFAULT_NUMBER);
+ }
+
+ @Override
+ public int getInt(String attrName, int defaultValue) {
+ return mParser.getAttributeInt(null, attrName, defaultValue);
+ }
+
+ @Override
+ public long getLong(String attrName) {
+ return getLong(attrName, DEFAULT_NUMBER);
+ }
+
+ @Override
+ public long getLong(String attrName, int defaultValue) {
+ return mParser.getAttributeLong(null, attrName, defaultValue);
+ }
+
+ @Override
+ public ChildSection children() {
+ mDepthStack.push(mParser.getDepth());
+ return this;
+ }
+
+ @Override
+ public boolean moveToNext() {
+ return moveToNextInternal(null);
+ }
+
+ @Override
+ public boolean moveToNext(@NonNull String expectedChildTagName) {
+ return moveToNextInternal(expectedChildTagName);
+ }
+
+ private boolean moveToNextInternal(@Nullable String expectedChildTagName) {
+ try {
+ int depth = mDepthStack.peek();
+ boolean hasTag = false;
+ int type;
+ while (!hasTag
+ && (type = mParser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || mParser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (expectedChildTagName != null
+ && !expectedChildTagName.equals(mParser.getName())) {
+ continue;
+ }
+
+ hasTag = true;
+ }
+
+ if (!hasTag) {
+ mDepthStack.pop();
+ }
+
+ return hasTag;
+ } catch (Exception ignored) {
+ return false;
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (mDepthStack.isEmpty()) {
+ Slog.wtf(TAG, "Children depth stack was not empty, data may have been lost",
+ new Exception());
+ }
+ if (mInput != null) {
+ mInput.close();
+ }
+ }
+ }
+
+ public interface WriteSection extends AutoCloseable {
+
+ WriteSection startSection(@NonNull String sectionName) throws IOException;
+
+ WriteSection attribute(String attrName, @Nullable String value) throws IOException;
+
+ WriteSection attribute(String attrName, int value) throws IOException;
+
+ WriteSection attribute(String attrName, long value) throws IOException;
+
+ WriteSection attribute(String attrName, boolean value) throws IOException;
+
+ @Override
+ void close() throws IOException;
+
+ void finish() throws IOException;
+ }
+
+ private static class WriteSectionImpl implements WriteSection {
+
+ @NonNull
+ private final TypedXmlSerializer mXmlSerializer;
+
+ @NonNull
+ private final Stack<String> mTagStack = new Stack<>();
+
+ private WriteSectionImpl(@NonNull TypedXmlSerializer xmlSerializer) {
+ mXmlSerializer = xmlSerializer;
+ }
+
+ @Override
+ public WriteSection startSection(@NonNull String sectionName) throws IOException {
+ // Try to start the tag first before we push it to the stack
+ mXmlSerializer.startTag(null, sectionName);
+ mTagStack.push(sectionName);
+ return this;
+ }
+
+ @Override
+ public WriteSection attribute(String attrName, String value) throws IOException {
+ if (value != null) {
+ mXmlSerializer.attribute(null, attrName, value);
+ }
+ return this;
+ }
+
+ @Override
+ public WriteSection attribute(String attrName, int value) throws IOException {
+ if (value != DEFAULT_NUMBER) {
+ mXmlSerializer.attributeInt(null, attrName, value);
+ }
+ return this;
+ }
+
+ @Override
+ public WriteSection attribute(String attrName, long value) throws IOException {
+ if (value != DEFAULT_NUMBER) {
+ mXmlSerializer.attributeLong(null, attrName, value);
+ }
+ return this;
+ }
+
+ @Override
+ public WriteSection attribute(String attrName, boolean value) throws IOException {
+ if (value) {
+ mXmlSerializer.attributeBoolean(null, attrName, value);
+ }
+ return this;
+ }
+
+ @Override
+ public void finish() throws IOException {
+ close();
+ }
+
+ @Override
+ public void close() throws IOException {
+ mXmlSerializer.endTag(null, mTagStack.pop());
+ }
+
+ private void closeCompletely() throws IOException {
+ if (DEBUG_THROW_EXCEPTIONS && mTagStack != null && !mTagStack.isEmpty()) {
+ throw new IllegalStateException(
+ "tag stack is not empty when closing, contains " + mTagStack);
+ } else if (mTagStack != null) {
+ while (!mTagStack.isEmpty()) {
+ close();
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 89729b5..9b092c0 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1535,6 +1535,27 @@
pw.println(")");
}
+ public void dumpShortcuts(@NonNull PrintWriter pw, int matchFlags) {
+ final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0;
+ final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0;
+ final boolean matchManifest = (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0;
+ final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0;
+
+ final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
+ | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
+ | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
+ | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
+
+ final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
+ final int size = shortcuts.size();
+ for (int i = 0; i < size; i++) {
+ final ShortcutInfo si = shortcuts.valueAt(i);
+ if ((si.getFlags() & shortcutFlags) != 0) {
+ pw.println(si.toDumpString(""));
+ }
+ }
+ }
+
@Override
public JSONObject dumpCheckin(boolean clear) throws JSONException {
final JSONObject result = super.dumpCheckin(clear);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 3c4457d..863e3fe5 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -40,6 +40,7 @@
import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IShortcutService;
import android.content.pm.LauncherApps;
@@ -4556,6 +4557,10 @@
private int mUserId = UserHandle.USER_SYSTEM;
+ private int mShortcutMatchFlags = ShortcutManager.FLAG_MATCH_CACHED
+ | ShortcutManager.FLAG_MATCH_DYNAMIC | ShortcutManager.FLAG_MATCH_MANIFEST
+ | ShortcutManager.FLAG_MATCH_PINNED;
+
private void parseOptionsLocked(boolean takeUser)
throws CommandException {
String opt;
@@ -4571,6 +4576,9 @@
break;
}
// fallthrough
+ case "--flags":
+ mShortcutMatchFlags = Integer.parseInt(getNextArgRequired());
+ break;
default:
throw new CommandException("Unknown option: " + opt);
}
@@ -4606,9 +4614,15 @@
case "clear-shortcuts":
handleClearShortcuts();
break;
+ case "get-shortcuts":
+ handleGetShortcuts();
+ break;
case "verify-states": // hidden command to verify various internal states.
handleVerifyStates();
break;
+ case "has-shortcut-access":
+ handleHasShortcutAccess();
+ break;
default:
return handleDefaultCommands(cmd);
}
@@ -4640,7 +4654,7 @@
pw.println("[Deprecated] cmd shortcut get-default-launcher [--user USER_ID]");
pw.println(" Show the default launcher");
pw.println(" Note: This command is deprecated. Callers should query the default"
- + " launcher directly from RoleManager instead.");
+ + " launcher from RoleManager instead.");
pw.println();
pw.println("cmd shortcut unload-user [--user USER_ID]");
pw.println(" Unload a user from the memory");
@@ -4649,6 +4663,13 @@
pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE");
pw.println(" Remove all shortcuts from a package, including pinned shortcuts");
pw.println();
+ pw.println("cmd shortcut get-shortcuts [--user USER_ID] [--flags FLAGS] PACKAGE");
+ pw.println(" Show the shortcuts for a package that match the given flags");
+ pw.println();
+ pw.println("cmd shortcut has-shortcut-access [--user USER_ID] PACKAGE");
+ pw.println(" Prints \"true\" if the package can access shortcuts,"
+ + " \"false\" otherwise");
+ pw.println();
}
private void handleResetThrottling() throws CommandException {
@@ -4693,11 +4714,24 @@
private void handleGetDefaultLauncher() throws CommandException {
synchronized (mLock) {
parseOptionsLocked(/* takeUser =*/ true);
+
+ final String defaultLauncher = getDefaultLauncher(mUserId);
+ if (defaultLauncher == null) {
+ throw new CommandException(
+ "Failed to get the default launcher for user " + mUserId);
+ }
+
+ // Get the class name of the component from PM to keep the old behaviour.
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
- // Default launcher from package manager.
- final ComponentName defaultLauncher = mPackageManagerInternal
- .getHomeActivitiesAsUser(allHomeCandidates, getParentOrSelfUserId(mUserId));
- getOutPrintWriter().println("Launcher: " + defaultLauncher);
+ mPackageManagerInternal.getHomeActivitiesAsUser(allHomeCandidates,
+ getParentOrSelfUserId(mUserId));
+ for (ResolveInfo ri : allHomeCandidates) {
+ final ComponentInfo ci = ri.getComponentInfo();
+ if (ci.packageName.equals(defaultLauncher)) {
+ getOutPrintWriter().println("Launcher: " + ci.getComponentName());
+ break;
+ }
+ }
}
}
@@ -4723,6 +4757,24 @@
}
}
+ private void handleGetShortcuts() throws CommandException {
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
+ final String packageName = getNextArgRequired();
+
+ Slog.i(TAG, "cmd: handleGetShortcuts: user=" + mUserId + ", flags="
+ + mShortcutMatchFlags + ", package=" + packageName);
+
+ final ShortcutUser user = ShortcutService.this.getUserShortcutsLocked(mUserId);
+ final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ return;
+ }
+
+ p.dumpShortcuts(getOutPrintWriter(), mShortcutMatchFlags);
+ }
+ }
+
private void handleVerifyStates() throws CommandException {
try {
verifyStatesForce(); // This will throw when there's an issue.
@@ -4730,6 +4782,16 @@
throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th));
}
}
+
+ private void handleHasShortcutAccess() throws CommandException {
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
+ final String packageName = getNextArgRequired();
+
+ boolean shortcutAccess = hasShortcutHostPermissionInner(packageName, mUserId);
+ getOutPrintWriter().println(Boolean.toString(shortcutAccess));
+ }
+ }
}
// === Unit test support ===
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index c182d68..c17d2e4 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -298,4 +298,11 @@
* Gets all {@link UserInfo UserInfos}.
*/
public abstract @NonNull UserInfo[] getUserInfos();
+
+ /**
+ * Sets all default cross profile intent filters between {@code parentUserId} and
+ * {@code profileUserId}.
+ */
+ public abstract void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int parentUserId, @UserIdInt int profileUserId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f43240b..753f22d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -80,6 +80,7 @@
import android.os.UserManager.EnforcingUser;
import android.os.UserManager.QuietModeFlag;
import android.os.storage.StorageManager;
+import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.stats.devicepolicy.DevicePolicyEnums;
@@ -108,6 +109,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.BundleUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemService;
@@ -1960,11 +1962,11 @@
final Bundle global = mDevicePolicyGlobalUserRestrictions.mergeAll();
final RestrictionsSet local = getDevicePolicyLocalRestrictionsForTargetUserLR(userId);
- if (UserRestrictionsUtils.isEmpty(global) && local.isEmpty()) {
+ if (BundleUtils.isEmpty(global) && local.isEmpty()) {
// Common case first.
return baseRestrictions;
}
- final Bundle effective = UserRestrictionsUtils.clone(baseRestrictions);
+ final Bundle effective = BundleUtils.clone(baseRestrictions);
UserRestrictionsUtils.merge(effective, global);
UserRestrictionsUtils.merge(effective, local.mergeAll());
@@ -2105,7 +2107,7 @@
@Override
public Bundle getUserRestrictions(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
- return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
+ return BundleUtils.clone(getEffectiveUserRestrictions(userId));
}
@Override
@@ -2129,7 +2131,7 @@
synchronized (mRestrictionsLock) {
// Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
// a copy.
- final Bundle newRestrictions = UserRestrictionsUtils.clone(
+ final Bundle newRestrictions = BundleUtils.clone(
mBaseUserRestrictions.getRestrictions(userId));
newRestrictions.putBoolean(key, value);
@@ -2727,7 +2729,7 @@
if (userVersion < 7) {
// Previously only one user could enforce global restrictions, now it is per-user.
synchronized (mRestrictionsLock) {
- if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions)
+ if (!BundleUtils.isEmpty(oldGlobalUserRestrictions)
&& mDeviceOwnerUserId != UserHandle.USER_NULL) {
mDevicePolicyGlobalUserRestrictions.updateRestrictions(
mDeviceOwnerUserId, oldGlobalUserRestrictions);
@@ -3562,6 +3564,9 @@
t.traceBegin("PM.onNewUserCreated-" + userId);
mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
t.traceEnd();
+ applyDefaultUserSettings(userTypeDetails, userId);
+ setDefaultCrossProfileIntentFilters(userId, userTypeDetails, restrictions, parentId);
+
if (preCreate) {
// Must start user (which will be stopped right away, through
// UserController.finishUserUnlockedCompleted) so services can properly
@@ -3597,6 +3602,78 @@
return userInfo;
}
+ private void applyDefaultUserSettings(UserTypeDetails userTypeDetails, @UserIdInt int userId) {
+ final Bundle systemSettings = userTypeDetails.getDefaultSystemSettings();
+ final Bundle secureSettings = userTypeDetails.getDefaultSecureSettings();
+ if (systemSettings.isEmpty() && secureSettings.isEmpty()) {
+ return;
+ }
+
+ final int systemSettingsSize = systemSettings.size();
+ final String[] systemSettingsArray = systemSettings.keySet().toArray(
+ new String[systemSettingsSize]);
+ for (int i = 0; i < systemSettingsSize; i++) {
+ final String setting = systemSettingsArray[i];
+ if (!Settings.System.putStringForUser(
+ mContext.getContentResolver(), setting, systemSettings.getString(setting),
+ userId)) {
+ Slog.e(LOG_TAG, "Failed to insert default system setting: " + setting);
+ }
+ }
+
+ final int secureSettingsSize = secureSettings.size();
+ final String[] secureSettingsArray = secureSettings.keySet().toArray(
+ new String[secureSettingsSize]);
+ for (int i = 0; i < secureSettingsSize; i++) {
+ final String setting = secureSettingsArray[i];
+ if (!Settings.Secure.putStringForUser(
+ mContext.getContentResolver(), setting, secureSettings.getString(setting),
+ userId)) {
+ Slog.e(LOG_TAG, "Failed to insert default secure setting: " + setting);
+ }
+ }
+ }
+
+ /**
+ * Sets all default cross profile intent filters between {@code parentUserId} and
+ * {@code profileUserId}, does nothing if {@code userType} is not a profile.
+ */
+ private void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int profileUserId, UserTypeDetails profileDetails,
+ Bundle profileRestrictions, @UserIdInt int parentUserId) {
+ if (profileDetails == null || !profileDetails.isProfile()) {
+ return;
+ }
+ final List<DefaultCrossProfileIntentFilter> filters =
+ profileDetails.getDefaultCrossProfileIntentFilters();
+ if (filters.isEmpty()) {
+ return;
+ }
+
+ // Skip filters that allow data to be shared into the profile, if admin has disabled it.
+ final boolean disallowSharingIntoProfile =
+ profileRestrictions.getBoolean(
+ UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+ /* defaultValue = */ false);
+ final int size = profileDetails.getDefaultCrossProfileIntentFilters().size();
+ for (int i = 0; i < size; i++) {
+ final DefaultCrossProfileIntentFilter filter =
+ profileDetails.getDefaultCrossProfileIntentFilters().get(i);
+ if (disallowSharingIntoProfile && filter.letsPersonalDataIntoProfile) {
+ continue;
+ }
+ if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PARENT) {
+ mPm.addCrossProfileIntentFilter(
+ filter.filter, mContext.getOpPackageName(), profileUserId, parentUserId,
+ filter.flags);
+ } else {
+ mPm.addCrossProfileIntentFilter(
+ filter.filter, mContext.getOpPackageName(), parentUserId, profileUserId,
+ filter.flags);
+ }
+ }
+ }
+
/**
* Finds and converts a previously pre-created user into a regular user, if possible.
*
@@ -3880,6 +3957,7 @@
*/
@Override
public boolean removeUser(@UserIdInt int userId) {
+ Slog.i(LOG_TAG, "removeUser u" + userId);
checkManageOrCreateUsersPermission("Only the system can remove users");
final String restriction = getUserRemovalRestriction(userId);
@@ -5461,6 +5539,15 @@
return allInfos;
}
}
+
+ @Override
+ public void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int parentUserId, @UserIdInt int profileUserId) {
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(profileUserId);
+ final Bundle restrictions = getEffectiveUserRestrictions(profileUserId);
+ UserManagerService.this.setDefaultCrossProfileIntentFilters(
+ profileUserId, userTypeDetails, restrictions, parentUserId);
+ }
}
/**
@@ -5725,8 +5812,8 @@
// merge existing base restrictions with the new type's default restrictions
synchronized (mRestrictionsLock) {
- if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) {
- final Bundle newRestrictions = UserRestrictionsUtils.clone(
+ if (!BundleUtils.isEmpty(newUserType.getDefaultRestrictions())) {
+ final Bundle newRestrictions = BundleUtils.clone(
mBaseUserRestrictions.getRestrictions(userInfo.id));
UserRestrictionsUtils.merge(newRestrictions,
newUserType.getDefaultRestrictions());
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 51dff40..ff90043 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -44,6 +44,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.BundleUtils;
import com.android.server.LocalServices;
import com.google.android.collect.Sets;
@@ -384,10 +385,6 @@
return in != null ? in : new Bundle();
}
- public static boolean isEmpty(@Nullable Bundle in) {
- return (in == null) || (in.size() == 0);
- }
-
/**
* Returns {@code true} if given bundle is not null and contains {@code true} for a given
* restriction.
@@ -396,17 +393,6 @@
return in != null && in.getBoolean(restriction);
}
- /**
- * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
- * bundle.
- *
- * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
- * {@link Bundle#EMPTY})
- */
- public static @NonNull Bundle clone(@Nullable Bundle in) {
- return (in != null) ? new Bundle(in) : new Bundle();
- }
-
public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
Objects.requireNonNull(dest);
Preconditions.checkArgument(dest != in);
@@ -483,10 +469,10 @@
if (a == b) {
return true;
}
- if (isEmpty(a)) {
- return isEmpty(b);
+ if (BundleUtils.isEmpty(a)) {
+ return BundleUtils.isEmpty(b);
}
- if (isEmpty(b)) {
+ if (BundleUtils.isEmpty(b)) {
return false;
}
for (String key : a.keySet()) {
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 5fa46b9..17ce386 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -28,8 +28,12 @@
import android.os.UserManager;
import com.android.internal.util.Preconditions;
+import com.android.server.BundleUtils;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Contains the details about a multiuser "user type", such as a
@@ -82,6 +86,24 @@
*/
private final @Nullable Bundle mDefaultRestrictions;
+ /**
+ * List of {@link android.provider.Settings.System} to apply by default to newly created users
+ * of this type.
+ */
+ private final @Nullable Bundle mDefaultSystemSettings;
+
+ /**
+ * List of {@link android.provider.Settings.Secure} to apply by default to newly created users
+ * of this type.
+ */
+ private final @Nullable Bundle mDefaultSecureSettings;
+
+ /**
+ * List of {@link DefaultCrossProfileIntentFilter} to allow by default for newly created
+ * profiles.
+ */
+ private final @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters;
+
// Fields for profiles only, controlling the nature of their badges.
// All badge information should be set if {@link #hasBadge()} is true.
@@ -133,7 +155,10 @@
int iconBadge, int badgePlain, int badgeNoBackground,
@Nullable int[] badgeLabels, @Nullable int[] badgeColors,
@Nullable int[] darkThemeBadgeColors,
- @Nullable Bundle defaultRestrictions) {
+ @Nullable Bundle defaultRestrictions,
+ @Nullable Bundle defaultSystemSettings,
+ @Nullable Bundle defaultSecureSettings,
+ @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters) {
this.mName = name;
this.mEnabled = enabled;
this.mMaxAllowed = maxAllowed;
@@ -141,6 +166,9 @@
this.mBaseType = baseType;
this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
this.mDefaultRestrictions = defaultRestrictions;
+ this.mDefaultSystemSettings = defaultSystemSettings;
+ this.mDefaultSecureSettings = defaultSecureSettings;
+ this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters;
this.mIconBadge = iconBadge;
this.mBadgePlain = badgePlain;
@@ -263,9 +291,9 @@
return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
}
- /** Returns a Bundle representing the default user restrictions. */
+ /** Returns a {@link Bundle} representing the default user restrictions. */
@NonNull Bundle getDefaultRestrictions() {
- return UserRestrictionsUtils.clone(mDefaultRestrictions);
+ return BundleUtils.clone(mDefaultRestrictions);
}
/** Adds the default user restrictions to the given bundle of restrictions. */
@@ -273,6 +301,24 @@
UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions);
}
+ /** Returns a {@link Bundle} representing the default system settings. */
+ @NonNull Bundle getDefaultSystemSettings() {
+ return BundleUtils.clone(mDefaultSystemSettings);
+ }
+
+ /** Returns a {@link Bundle} representing the default secure settings. */
+ @NonNull Bundle getDefaultSecureSettings() {
+ return BundleUtils.clone(mDefaultSecureSettings);
+ }
+
+ /** Returns a list of default cross profile intent filters. */
+ @NonNull List<DefaultCrossProfileIntentFilter> getDefaultCrossProfileIntentFilters() {
+ return mDefaultCrossProfileIntentFilters != null
+ ? new ArrayList<>(mDefaultCrossProfileIntentFilters)
+ : Collections.emptyList();
+ }
+
+
/** Dumps details of the UserTypeDetails. Do not parse this. */
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mName: "); pw.println(mName);
@@ -325,6 +371,10 @@
private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
private int mDefaultUserInfoPropertyFlags = 0;
private @Nullable Bundle mDefaultRestrictions = null;
+ private @Nullable Bundle mDefaultSystemSettings = null;
+ private @Nullable Bundle mDefaultSecureSettings = null;
+ private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
+ null;
private boolean mEnabled = true;
private int mLabel = Resources.ID_NULL;
private @Nullable int[] mBadgeLabels = null;
@@ -407,6 +457,22 @@
return this;
}
+ public Builder setDefaultSystemSettings(@Nullable Bundle settings) {
+ mDefaultSystemSettings = settings;
+ return this;
+ }
+
+ public Builder setDefaultSecureSettings(@Nullable Bundle settings) {
+ mDefaultSecureSettings = settings;
+ return this;
+ }
+
+ public Builder setDefaultCrossProfileIntentFilters(
+ @Nullable List<DefaultCrossProfileIntentFilter> intentFilters) {
+ mDefaultCrossProfileIntentFilters = intentFilters;
+ return this;
+ }
+
@UserInfoFlag int getBaseType() {
return mBaseType;
}
@@ -425,17 +491,28 @@
Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
"UserTypeDetails " + mName + " has badge but no badgeColors.");
}
+ if (!isProfile()) {
+ Preconditions.checkArgument(mDefaultCrossProfileIntentFilters == null
+ || mDefaultCrossProfileIntentFilters.isEmpty(),
+ "UserTypeDetails %s has a non empty "
+ + "defaultCrossProfileIntentFilters", mName);
+ }
return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
- mDefaultRestrictions);
+ mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
+ mDefaultCrossProfileIntentFilters);
}
private boolean hasBadge() {
return mIconBadge != Resources.ID_NULL;
}
+ private boolean isProfile() {
+ return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
+ }
+
// TODO(b/143784345): Refactor this when we clean up UserInfo.
private boolean hasValidBaseType() {
return mBaseType == UserInfo.FLAG_FULL
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1ffbf60..6aac0b2 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -133,7 +133,9 @@
com.android.internal.R.color.profile_badge_1_dark,
com.android.internal.R.color.profile_badge_2_dark,
com.android.internal.R.color.profile_badge_3_dark)
- .setDefaultRestrictions(null);
+ .setDefaultRestrictions(getDefaultManagedProfileRestrictions())
+ .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
+ .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter());
}
/**
@@ -256,12 +258,35 @@
return restrictions;
}
+ private static Bundle getDefaultManagedProfileRestrictions() {
+ final Bundle restrictions = new Bundle();
+ restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true);
+ return restrictions;
+ }
+
+ private static Bundle getDefaultManagedProfileSecureSettings() {
+ // Only add String values to the bundle, settings are written as Strings eventually
+ final Bundle settings = new Bundle();
+ settings.putString(
+ android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1");
+ settings.putString(
+ android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1");
+ return settings;
+ }
+
+ private static List<DefaultCrossProfileIntentFilter>
+ getDefaultManagedCrossProfileIntentFilter() {
+ return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters();
+ }
+
/**
* Reads the given xml parser to obtain device user-type customization, and updates the given
* map of {@link UserTypeDetails.Builder}s accordingly.
* <p>
* The xml file can specify the attributes according to the set... methods below.
*/
+ // TODO(b/176973369): Add parsing logic to support custom settings/filters
+ // in config_user_types.xml
@VisibleForTesting
static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders,
XmlResourceParser parser) {
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 9dde7df..629f120 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -406,7 +406,8 @@
ParsedProcess proc = procs.get(key);
retProcs.put(proc.getName(),
new ProcessInfo(proc.getName(), new ArraySet<>(proc.getDeniedPermissions()),
- proc.getGwpAsanMode()));
+ proc.getGwpAsanMode(), proc.getMemtagMode(),
+ proc.getNativeHeapZeroInit()));
}
return retProcs;
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
new file mode 100644
index 0000000..36efb39
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.os.Binder;
+import android.os.Build;
+import android.util.ArraySet;
+import android.util.Patterns;
+
+import com.android.server.SystemConfig;
+import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.List;
+import java.util.Set;
+
+public class DomainVerificationCollector {
+
+ @NonNull
+ private final PlatformCompat mPlatformCompat;
+
+ @NonNull
+ private final SystemConfig mSystemConfig;
+
+ public DomainVerificationCollector(@NonNull PlatformCompat platformCompat,
+ @NonNull SystemConfig systemConfig) {
+ mPlatformCompat = platformCompat;
+ mSystemConfig = systemConfig;
+ }
+
+ /**
+ * With the updated form of the app links verification APIs, an app will be required to declare
+ * domains inside an intent filter which includes all of the following:
+ * <ul>
+ * <li>- android:autoVerify="true"</li>
+ * <li>- Intent.ACTION_VIEW</li>
+ * <li>- Intent.CATEGORY_BROWSABLE</li>
+ * <li>- Intent.CATEGORY_DEFAULT</li>
+ * <li>- Only IntentFilter.SCHEME_HTTP and/or IntentFilter.SCHEME_HTTPS,
+ * with no other schemes</li>
+ * </ul>
+ *
+ * On prior versions of Android, Intent.CATEGORY_BROWSABLE was not a requirement, other
+ * schemes were allowed, and setting autoVerify to true in any intent filter would implicitly
+ * pretend that all intent filters were set to autoVerify="true".
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ public static final long RESTRICT_DOMAINS = 175408749L;
+
+ @NonNull
+ public ArraySet<String> collectAllWebDomains(@NonNull AndroidPackage pkg) {
+ return collectDomains(pkg, false);
+ }
+
+ /**
+ * Effectively {@link #collectAllWebDomains(AndroidPackage)}, but requires
+ * {@link IntentFilter#getAutoVerify()} == true.
+ */
+ @NonNull
+ public ArraySet<String> collectAutoVerifyDomains(@NonNull AndroidPackage pkg) {
+ return collectDomains(pkg, true);
+ }
+
+ @NonNull
+ private ArraySet<String> collectDomains(@NonNull AndroidPackage pkg,
+ boolean checkAutoVerify) {
+ boolean restrictDomains =
+ DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS);
+
+ ArraySet<String> domains = new ArraySet<>();
+
+ if (restrictDomains) {
+ collectDomains(domains, pkg, checkAutoVerify);
+ } else {
+ collectDomainsLegacy(domains, pkg, checkAutoVerify);
+ }
+
+ return domains;
+ }
+
+ /** @see #RESTRICT_DOMAINS */
+ private void collectDomainsLegacy(@NonNull Set<String> domains,
+ @NonNull AndroidPackage pkg, boolean checkAutoVerify) {
+ if (!checkAutoVerify) {
+ // Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2
+ collectDomains(domains, pkg, false);
+ return;
+ }
+
+ List<ParsedActivity> activities = pkg.getActivities();
+ int activitiesSize = activities.size();
+
+ // Due to a bug in the platform, for backwards compatibility, assume that all linked apps
+ // require auto verification, even if they forget to mark their manifest as such.
+ boolean needsAutoVerify = mSystemConfig.getLinkedApps().contains(pkg.getPackageName());
+ if (!needsAutoVerify) {
+ for (int activityIndex = 0; activityIndex < activitiesSize && !needsAutoVerify;
+ activityIndex++) {
+ ParsedActivity activity = activities.get(activityIndex);
+ List<ParsedIntentInfo> intents = activity.getIntents();
+ int intentsSize = intents.size();
+ for (int intentIndex = 0; intentIndex < intentsSize && !needsAutoVerify;
+ intentIndex++) {
+ ParsedIntentInfo intent = intents.get(intentIndex);
+ needsAutoVerify = intent.needsVerification();
+ }
+ }
+
+ if (!needsAutoVerify) {
+ return;
+ }
+ }
+
+ for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
+ ParsedActivity activity = activities.get(activityIndex);
+ List<ParsedIntentInfo> intents = activity.getIntents();
+ int intentsSize = intents.size();
+ for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) {
+ ParsedIntentInfo intent = intents.get(intentIndex);
+ if (intent.handlesWebUris(false)) {
+ int authorityCount = intent.countDataAuthorities();
+ for (int index = 0; index < authorityCount; index++) {
+ domains.add(intent.getDataAuthority(index).getHost());
+ }
+ }
+ }
+ }
+ }
+
+ /** @see #RESTRICT_DOMAINS */
+ private void collectDomains(@NonNull Set<String> domains,
+ @NonNull AndroidPackage pkg, boolean checkAutoVerify) {
+ List<ParsedActivity> activities = pkg.getActivities();
+ int activitiesSize = activities.size();
+ for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
+ ParsedActivity activity = activities.get(activityIndex);
+ List<ParsedIntentInfo> intents = activity.getIntents();
+ int intentsSize = intents.size();
+ for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) {
+ ParsedIntentInfo intent = intents.get(intentIndex);
+ if (checkAutoVerify && !intent.getAutoVerify()) {
+ continue;
+ }
+
+ if (!intent.hasCategory(Intent.CATEGORY_DEFAULT)
+ || !intent.handlesWebUris(checkAutoVerify)) {
+ continue;
+ }
+
+ // TODO(b/159952358): There seems to be no way to associate the exact host
+ // with its scheme, meaning all hosts have to be verified as if they were
+ // web schemes. This means that given the following:
+ // <intent-filter android:autoVerify="true">
+ // ...
+ // <data android:scheme="https" android:host="one.example.com"/>
+ // <data android:scheme="https" android:host="two.example.com"/>
+ // <data android:host="three.example.com"/>
+ // <data android:scheme="nonWeb" android:host="four.example.com"/>
+ // </intent-filter>
+ // The verification agent will be asked to verify four.example.com, which the
+ // app will probably fail. This can be re-configured to work properly by the
+ // app developer by declaring a separate intent-filter. This may not be worth
+ // fixing.
+ int authorityCount = intent.countDataAuthorities();
+ for (int index = 0; index < authorityCount; index++) {
+ String host = intent.getDataAuthority(index).getHost();
+ // It's easy to misconfigure autoVerify intent filters, so to avoid
+ // adding unintended hosts, check if the host is an HTTP domain.
+ if (Patterns.DOMAIN_NAME.matcher(host).matches()) {
+ domains.add(host);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
new file mode 100644
index 0000000..af9978b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.PackageUtils;
+import android.util.SparseArray;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.Arrays;
+
+public class DomainVerificationDebug {
+
+ @NonNull
+ private final DomainVerificationCollector mCollector;
+
+ DomainVerificationDebug(DomainVerificationCollector collector) {
+ mCollector = collector;
+ }
+
+ public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+ @Nullable @UserIdInt Integer userId,
+ @NonNull DomainVerificationService.Connection connection,
+ @NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap)
+ throws NameNotFoundException {
+ ArrayMap<String, Integer> reusedMap = new ArrayMap<>();
+ ArraySet<String> reusedSet = new ArraySet<>();
+
+ if (packageName == null) {
+ int size = stateMap.size();
+ for (int index = 0; index < size; index++) {
+ DomainVerificationPkgState pkgState = stateMap.valueAt(index);
+ String pkgName = pkgState.getPackageName();
+ PackageSetting pkgSetting = connection.getPackageSettingLocked(pkgName);
+ if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ continue;
+ }
+
+ boolean wasHeaderPrinted = printState(writer, pkgState, pkgSetting.getPkg(),
+ reusedMap, false);
+ printState(writer, pkgState, pkgSetting.getPkg(), userId, reusedSet,
+ wasHeaderPrinted);
+ }
+ } else {
+ DomainVerificationPkgState pkgState = stateMap.get(packageName);
+ if (pkgState == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ PackageSetting pkgSetting = connection.getPackageSettingLocked(packageName);
+ if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ AndroidPackage pkg = pkgSetting.getPkg();
+ printState(writer, pkgState, pkg, reusedMap, false);
+ printState(writer, pkgState, pkg, userId, reusedSet, true);
+ }
+ }
+
+ boolean printState(@NonNull IndentingPrintWriter writer,
+ @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg,
+ @NonNull ArrayMap<String, Integer> reusedMap, boolean wasHeaderPrinted) {
+ reusedMap.clear();
+ reusedMap.putAll(pkgState.getStateMap());
+
+ ArraySet<String> declaredDomains = mCollector.collectAutoVerifyDomains(pkg);
+ int declaredSize = declaredDomains.size();
+ for (int declaredIndex = 0; declaredIndex < declaredSize; declaredIndex++) {
+ String domain = declaredDomains.valueAt(declaredIndex);
+ reusedMap.putIfAbsent(domain, DomainVerificationState.STATE_NO_RESPONSE);
+ }
+
+ boolean printedHeader = false;
+
+ if (!reusedMap.isEmpty()) {
+ if (!wasHeaderPrinted) {
+ Signature[] signatures = pkg.getSigningDetails().signatures;
+ String signaturesDigest = signatures == null ? null : Arrays.toString(
+ PackageUtils.computeSignaturesSha256Digests(
+ pkg.getSigningDetails().signatures));
+
+ writer.println(pkgState.getPackageName() + ":");
+ writer.increaseIndent();
+ writer.println("ID: " + pkgState.getId());
+ writer.println("Signatures: " + signaturesDigest);
+ writer.decreaseIndent();
+ printedHeader = true;
+ }
+
+ writer.increaseIndent();
+ writer.println("Domain verification state:");
+ writer.increaseIndent();
+ int stateSize = reusedMap.size();
+ for (int stateIndex = 0; stateIndex < stateSize; stateIndex++) {
+ String domain = reusedMap.keyAt(stateIndex);
+ Integer state = reusedMap.valueAt(stateIndex);
+ writer.print(domain);
+ writer.print(": ");
+ writer.println(DomainVerificationManager.stateToDebugString(state));
+ }
+ writer.decreaseIndent();
+ writer.decreaseIndent();
+ }
+
+ return printedHeader;
+ }
+
+ void printState(@NonNull IndentingPrintWriter writer,
+ @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg,
+ @Nullable @UserIdInt Integer userId, @NonNull ArraySet<String> reusedSet,
+ boolean wasHeaderPrinted) {
+ if (userId == null) {
+ return;
+ }
+
+ ArraySet<String> allWebDomains = mCollector.collectAllWebDomains(pkg);
+ SparseArray<DomainVerificationUserState> userStates =
+ pkgState.getUserSelectionStates();
+ if (userId == UserHandle.USER_ALL) {
+ int size = userStates.size();
+ if (size == 0) {
+ printState(writer, pkgState, userId, null, reusedSet, allWebDomains,
+ wasHeaderPrinted);
+ } else {
+ for (int index = 0; index < size; index++) {
+ DomainVerificationUserState userState = userStates.valueAt(index);
+ printState(writer, pkgState, userState.getUserId(), userState, reusedSet,
+ allWebDomains, wasHeaderPrinted);
+ }
+ }
+ } else {
+ DomainVerificationUserState userState = userStates.get(userId);
+ printState(writer, pkgState, userId, userState, reusedSet, allWebDomains,
+ wasHeaderPrinted);
+ }
+ }
+
+ boolean printState(@NonNull IndentingPrintWriter writer,
+ @NonNull DomainVerificationPkgState pkgState, @UserIdInt int userId,
+ @Nullable DomainVerificationUserState userState, @NonNull ArraySet<String> reusedSet,
+ @NonNull ArraySet<String> allWebDomains, boolean wasHeaderPrinted) {
+ reusedSet.clear();
+ reusedSet.addAll(allWebDomains);
+ if (userState != null) {
+ reusedSet.removeAll(userState.getEnabledHosts());
+ }
+
+ boolean printedHeader = false;
+
+ ArraySet<String> enabledHosts = userState == null ? null : userState.getEnabledHosts();
+ int enabledSize = CollectionUtils.size(enabledHosts);
+ int disabledSize = reusedSet.size();
+ if (enabledSize > 0 || disabledSize > 0) {
+ if (!wasHeaderPrinted) {
+ writer.println(pkgState.getPackageName() + " " + pkgState.getId() + ":");
+ printedHeader = true;
+ }
+
+ boolean isLinkHandlingAllowed = userState == null
+ || !userState.isDisallowLinkHandling();
+
+ writer.increaseIndent();
+ writer.print("User ");
+ writer.print(userId == UserHandle.USER_ALL ? "all" : userId);
+ writer.println(":");
+ writer.increaseIndent();
+ writer.print("Verification link handling allowed: ");
+ writer.println(isLinkHandlingAllowed);
+ writer.println("Selection state:");
+ writer.increaseIndent();
+
+ if (enabledSize > 0) {
+ writer.println("Enabled:");
+ writer.increaseIndent();
+ for (int enabledIndex = 0; enabledIndex < enabledSize; enabledIndex++) {
+ //noinspection ConstantConditions
+ writer.println(enabledHosts.valueAt(enabledIndex));
+ }
+ writer.decreaseIndent();
+ }
+
+ if (disabledSize > 0) {
+ writer.println("Disabled:");
+ writer.increaseIndent();
+ for (int disabledIndex = 0; disabledIndex < disabledSize; disabledIndex++) {
+ writer.println(reusedSet.valueAt(disabledIndex));
+ }
+ writer.decreaseIndent();
+ }
+
+ writer.decreaseIndent();
+ writer.decreaseIndent();
+ writer.decreaseIndent();
+ }
+
+ return printedHeader;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
new file mode 100644
index 0000000..c521f82
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Process;
+
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+
+public class DomainVerificationEnforcer {
+
+ @NonNull
+ private final Context mContext;
+
+ public DomainVerificationEnforcer(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Enforced when mutating any state from shell or internally in the system process.
+ */
+ public void assertInternal(int callingUid) {
+ switch (callingUid) {
+ case Process.ROOT_UID:
+ case Process.SHELL_UID:
+ case Process.SYSTEM_UID:
+ break;
+ default:
+ throw new SecurityException(
+ "Caller " + callingUid + " is not allowed to change internal state");
+ }
+ }
+
+ /**
+ * Enforced when retrieving state for a package. The system, the verifier, and anyone approved
+ * to mutate user selections are allowed through.
+ */
+ public void assertApprovedQuerent(int callingUid, @NonNull DomainVerificationProxy proxy) {
+ switch (callingUid) {
+ case Process.ROOT_UID:
+ case Process.SHELL_UID:
+ case Process.SYSTEM_UID:
+ break;
+ default:
+ if (!proxy.isCallerVerifier(callingUid)) {
+ mContext.enforcePermission(
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
+ Binder.getCallingPid(), callingUid,
+ "Caller " + callingUid
+ + " is not allowed to query domain verification state");
+ }
+ break;
+ }
+ }
+
+ /**
+ * Enforced when mutating domain verification state inside an exposed API method.
+ */
+ public void assertApprovedVerifier(int callingUid, @NonNull DomainVerificationProxy proxy)
+ throws SecurityException {
+ boolean isAllowed;
+ switch (callingUid) {
+ case Process.ROOT_UID:
+ case Process.SHELL_UID:
+ case Process.SYSTEM_UID:
+ isAllowed = true;
+ break;
+ default:
+ // TODO(b/159952358): Remove permission check? The component package should
+ // have been checked when the verifier component was first scanned in PMS.
+ mContext.enforcePermission(
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
+ Binder.getCallingPid(), callingUid,
+ "Caller " + callingUid + " does not hold DOMAIN_VERIFICATION_AGENT");
+ isAllowed = proxy.isCallerVerifier(callingUid);
+ break;
+ }
+
+ if (!isAllowed) {
+ throw new SecurityException("Caller " + callingUid
+ + " is not the approved domain verification agent, isVerifier = "
+ + proxy.isCallerVerifier(callingUid));
+ }
+ }
+
+ /**
+ * Enforced when mutating user selection state inside an exposed API method.
+ */
+ public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
+ @UserIdInt int targetUserId) throws SecurityException {
+ if (callingUserId != targetUserId) {
+ mContext.enforcePermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ Binder.getCallingPid(), callingUid,
+ "Caller is not allowed to edit other users");
+ }
+
+ mContext.enforcePermission(
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
+ Binder.getCallingPid(), callingUid,
+ "Caller is not allowed to edit user selections");
+ }
+
+ public void callerIsLegacyUserSelector(int callingUid) {
+ mContext.enforcePermission(
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS,
+ Binder.getCallingPid(), callingUid,
+ "Caller is not allowed to edit user state");
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
new file mode 100644
index 0000000..c787356
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import android.util.SparseIntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.SettingsXml;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Reads and writes the old {@link android.content.pm.IntentFilterVerificationInfo} so that it can
+ * be migrated in to the new API. Will throw away the state once it's successfully applied so that
+ * eventually there will be no legacy state on the device.
+ *
+ * This attempt is best effort, and if the legacy state is lost that's acceptable. The user setting
+ * in the legacy API may have been set incorrectly because it was never made obvious to the user
+ * what it actually toggled, so there's a strong argument to prevent migration anyways. The user
+ * can just set their preferences again, this time with finer grained control, if the legacy state
+ * gets dropped.
+ */
+public class DomainVerificationLegacySettings {
+
+ public static final String TAG_DOMAIN_VERIFICATIONS_LEGACY = "domain-verifications-legacy";
+ public static final String TAG_USER_STATES = "user-states";
+ public static final String ATTR_PACKAGE_NAME = "packageName";
+ public static final String TAG_USER_STATE = "user-state";
+ public static final String ATTR_USER_ID = "userId";
+ public static final String ATTR_STATE = "state";
+
+ @NonNull
+ private final Object mLock = new Object();
+
+ @NonNull
+ private final ArrayMap<String, LegacyState> mStates = new ArrayMap<>();
+
+ public void add(@NonNull String packageName, @NonNull IntentFilterVerificationInfo info) {
+ synchronized (mLock) {
+ getOrCreateStateLocked(packageName).setInfo(info);
+ }
+ }
+
+ public void add(@NonNull String packageName, @UserIdInt int userId, int state) {
+ synchronized (mLock) {
+ getOrCreateStateLocked(packageName).addUserState(userId, state);
+ }
+ }
+
+ public int getUserState(@NonNull String packageName, @UserIdInt int userId) {
+ synchronized (mLock) {
+ LegacyState state = mStates.get(packageName);
+ if (state != null) {
+ return state.getUserState(userId);
+ }
+ }
+ return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
+
+ @Nullable
+ public SparseIntArray getUserStates(@NonNull String packageName) {
+ synchronized (mLock) {
+ LegacyState state = mStates.get(packageName);
+ if (state != null) {
+ // Yes, this returns outside of the lock, but we assume that retrieval generally
+ // only happens after all adding has concluded from reading settings.
+ return state.getUserStates();
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public IntentFilterVerificationInfo remove(@NonNull String packageName) {
+ synchronized (mLock) {
+ LegacyState state = mStates.get(packageName);
+ if (state != null && !state.isAttached()) {
+ state.markAttached();
+ return state.getInfo();
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ private LegacyState getOrCreateStateLocked(@NonNull String packageName) {
+ LegacyState state = mStates.get(packageName);
+ if (state == null) {
+ state = new LegacyState();
+ mStates.put(packageName, state);
+ }
+
+ return state;
+ }
+
+ public void writeSettings(TypedXmlSerializer xmlSerializer) throws IOException {
+ try (SettingsXml.Serializer serializer = SettingsXml.serializer(xmlSerializer)) {
+ try (SettingsXml.WriteSection ignored =
+ serializer.startSection(TAG_DOMAIN_VERIFICATIONS_LEGACY)) {
+ synchronized (mLock) {
+ final int statesSize = mStates.size();
+ for (int stateIndex = 0; stateIndex < statesSize; stateIndex++) {
+ final LegacyState state = mStates.valueAt(stateIndex);
+ final SparseIntArray userStates = state.getUserStates();
+ if (userStates == null) {
+ continue;
+ }
+
+ final String packageName = mStates.keyAt(stateIndex);
+ try (SettingsXml.WriteSection userStatesSection =
+ serializer.startSection(TAG_USER_STATES)
+ .attribute(ATTR_PACKAGE_NAME, packageName)) {
+ final int userStatesSize = userStates.size();
+ for (int userStateIndex = 0; userStateIndex < userStatesSize;
+ userStateIndex++) {
+ final int userId = userStates.keyAt(userStateIndex);
+ final int userState = userStates.valueAt(userStateIndex);
+ userStatesSection.startSection(TAG_USER_STATE)
+ .attribute(ATTR_USER_ID, userId)
+ .attribute(ATTR_STATE, userState)
+ .finish();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void readSettings(TypedXmlPullParser xmlParser)
+ throws IOException, XmlPullParserException {
+ final SettingsXml.ChildSection child = SettingsXml.parser(xmlParser).children();
+ while (child.moveToNext()) {
+ if (TAG_USER_STATES.equals(child.getName())) {
+ readUserStates(child);
+ }
+ }
+ }
+
+ private void readUserStates(SettingsXml.ReadSection section) {
+ String packageName = section.getString(ATTR_PACKAGE_NAME);
+ synchronized (mLock) {
+ final LegacyState legacyState = getOrCreateStateLocked(packageName);
+ final SettingsXml.ChildSection child = section.children();
+ while (child.moveToNext()) {
+ if (TAG_USER_STATE.equals(child.getName())) {
+ readUserState(child, legacyState);
+ }
+ }
+ }
+ }
+
+ private void readUserState(SettingsXml.ReadSection section, LegacyState legacyState) {
+ int userId = section.getInt(ATTR_USER_ID);
+ int state = section.getInt(ATTR_STATE);
+ legacyState.addUserState(userId, state);
+ }
+
+ static class LegacyState {
+ @Nullable
+ private IntentFilterVerificationInfo mInfo;
+
+ @Nullable
+ private SparseIntArray mUserStates;
+
+ private boolean attached;
+
+ @Nullable
+ public IntentFilterVerificationInfo getInfo() {
+ return mInfo;
+ }
+
+ public int getUserState(int userId) {
+ return mUserStates.get(userId,
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
+ }
+
+ @Nullable
+ public SparseIntArray getUserStates() {
+ return mUserStates;
+ }
+
+ public void setInfo(@NonNull IntentFilterVerificationInfo info) {
+ mInfo = info;
+ }
+
+ public void addUserState(@UserIdInt int userId, int state) {
+ if (mUserStates == null) {
+ mUserStates = new SparseIntArray(1);
+ }
+ mUserStates.put(userId, state);
+ }
+
+ public boolean isAttached() {
+ return attached;
+ }
+
+ public void markAttached() {
+ attached = true;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
new file mode 100644
index 0000000..7ad275a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.content.Intent;
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.UUID;
+
+public interface DomainVerificationManagerInternal extends DomainVerificationManager {
+
+ UUID DISABLED_ID = new UUID(0, 0);
+
+ /**
+ * Generate a new domain set ID to be used for attaching new packages.
+ */
+ @NonNull
+ UUID generateNewId();
+
+ void setConnection(@NonNull Connection connection);
+
+ @NonNull
+ DomainVerificationProxy getProxy();
+
+ /**
+ * Update the proxy implementation that talks to the domain verification agent on device. The
+ * default proxy is a stub that does nothing, and broadcast functionality will only work once a
+ * real implementation is attached.
+ */
+ void setProxy(@NonNull DomainVerificationProxy proxy);
+
+ /**
+ * @see DomainVerificationProxy.BaseConnection#runMessage(int, Object)
+ */
+ boolean runMessage(int messageCode, Object object);
+
+ /**
+ * Restores or creates internal state for the new package. This can either be from scanning a
+ * package at boot, or a truly new installation on the device. It is expected that the {@link
+ * PackageSetting#getDomainSetId()} already be set to the correct value.
+ * <p>
+ * If this is from scan, there should be a pending state that was previous read using {@link
+ * #readSettings(TypedXmlPullParser)}, which will be attached as-is to the package. In this
+ * case, a broadcast will not be sent to the domain verification agent on device, as it is
+ * assumed nothing has changed since the device rebooted.
+ * <p>
+ * If this is a new install, state will be restored from a previous call to {@link
+ * #restoreSettings(TypedXmlPullParser)}, or a new one will be generated. In either case, a
+ * broadcast will be sent to the domain verification agent so it may re-run any verification
+ * logic for the newly associated domains.
+ * <p>
+ * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
+ * lock. This should never be called from within the domain verification classes themselves.
+ * <p>
+ * This will NOT call {@link #writeSettings(TypedXmlSerializer)}. That must be handled by the
+ * caller.
+ */
+ void addPackage(@NonNull PackageSetting newPkgSetting);
+
+ /**
+ * Migrates verification state from a previous install to a new one. It is expected that the
+ * {@link PackageSetting#getDomainSetId()} already be set to the correct value, usually from
+ * {@link #generateNewId()}. This will preserve {@link #STATE_SUCCESS} domains under the
+ * assumption that the new package will pass the same server side config as the previous
+ * package, as they have matching signatures.
+ * <p>
+ * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
+ * lock. This should never be called from within the domain verification classes themselves.
+ * <p>
+ * This will NOT call {@link #writeSettings(TypedXmlSerializer)}. That must be handled by the
+ * caller.
+ */
+ void migrateState(@NonNull PackageSetting oldPkgSetting, @NonNull PackageSetting newPkgSetting);
+
+ /**
+ * Serializes the entire internal state. This is equivalent to a full backup of the existing
+ * verification state. This write includes legacy state, as a sibling tag the modern state.
+ */
+ void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException;
+
+ /**
+ * Read back a list of {@link DomainVerificationPkgState}s previously written by {@link
+ * #writeSettings(TypedXmlSerializer)}. Assumes that the
+ * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS} tag has already been entered.
+ * <p>
+ * This is expected to only be used to re-attach states for packages already known to be on the
+ * device. If restoring from a backup, use {@link #restoreSettings(TypedXmlPullParser)}.
+ */
+ void readSettings(@NonNull TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException;
+
+ /**
+ * Read back data from
+ * {@link DomainVerificationLegacySettings#writeSettings(TypedXmlSerializer)}. Assumes that the
+ * {@link DomainVerificationLegacySettings#TAG_DOMAIN_VERIFICATIONS_LEGACY} tag has already
+ * been entered.
+ */
+ void readLegacySettings(@NonNull TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException;
+
+ /**
+ * Remove all state for the given package.
+ */
+ void clearPackage(@NonNull String packageName);
+
+ /**
+ * Delete all the state for a user. This can be because the user has been removed from the
+ * device, or simply that the state for a user should be deleted.
+ */
+ void clearUser(@UserIdInt int userId);
+
+ /**
+ * Restore a list of {@link DomainVerificationPkgState}s previously written by {@link
+ * #writeSettings(TypedXmlSerializer)}. Assumes that the
+ * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS}
+ * tag has already been entered.
+ * <p>
+ * This is <b>only</b> for restore, and will override package states, ignoring if their {@link
+ * DomainVerificationInfo#getIdentifier()}s match. It's expected that any restored domains marked
+ * as success verify against the server correctly, although the verification agent may decide to
+ * re-verify them when it gets the chance.
+ */
+ /*
+ * TODO(b/170746586): Figure out how to verify that package signatures match at snapshot time
+ * and restore time.
+ */
+ void restoreSettings(@NonNull TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException;
+
+ /**
+ * Set aside a legacy {@link IntentFilterVerificationInfo} that will be restored to a pending
+ * {@link DomainVerificationPkgState} once it's added through
+ * {@link #addPackage(PackageSetting)}.
+ */
+ void addLegacySetting(@NonNull String packageName, @NonNull IntentFilterVerificationInfo info);
+
+ /**
+ * Set aside a legacy user selection that will be restored to a pending
+ * {@link DomainVerificationPkgState} once it's added through
+ * {@link #addPackage(PackageSetting)}.
+ */
+ void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
+
+ /**
+ * Until the legacy APIs are entirely removed, returns the legacy state from the previously
+ * written info stored in {@link com.android.server.pm.Settings}.
+ */
+ int getLegacyState(@NonNull String packageName, @UserIdInt int userId);
+
+ /**
+ * Serialize a legacy setting that wasn't attached yet.
+ * TODO: Does this even matter? Should consider for removal.
+ */
+ void writeLegacySettings(TypedXmlSerializer serializer, String name);
+
+ /**
+ * Print the verification state and user selection state of a package.
+ *
+ * @param packageName the package whose state to change, or all packages if none is specified
+ * @param userId the specific user to print, or null to skip printing user selection
+ * states, supports {@link android.os.UserHandle#USER_ALL}
+ */
+ void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+ @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
+
+ @NonNull
+ DomainVerificationShell getShell();
+
+ @NonNull
+ DomainVerificationCollector getCollector();
+
+ /**
+ * Check if a resolving URI is approved to takeover the domain as the sole resolved target.
+ * This can be because the domain was auto-verified for the package, or if the user manually
+ * chose to enable the domain for the package.
+ */
+ boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
+ @UserIdInt int userId);
+
+ /**
+ * @return the domain verification set ID for the given package, or null if the ID is
+ * unavailable
+ */
+ @Nullable
+ UUID getDomainVerificationInfoId(@NonNull String packageName);
+
+ @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
+ void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+ @NonNull Set<String> domains, int state)
+ throws IllegalArgumentException, NameNotFoundException;
+
+
+ interface Connection {
+
+ /**
+ * Notify that a settings change has been made and that eventually
+ * {@link #writeSettings(TypedXmlSerializer)} should be invoked by the parent.
+ */
+ void scheduleWriteSettings();
+
+ /**
+ * Delegate to {@link Binder#getCallingUid()} to allow mocking in tests.
+ */
+ int getCallingUid();
+
+ /**
+ * Delegate to {@link UserHandle#getCallingUserId()} to allow mocking in tests.
+ */
+ @UserIdInt
+ int getCallingUserId();
+
+ /**
+ * @see DomainVerificationProxy.BaseConnection#schedule(int, java.lang.Object)
+ */
+ void schedule(int code, @Nullable Object object);
+
+ @Nullable
+ PackageSetting getPackageSettingLocked(@NonNull String pkgName);
+
+ @Nullable
+ AndroidPackage getPackageLocked(@NonNull String pkgName);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
new file mode 100644
index 0000000..8aa6337
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
+import android.content.pm.verify.domain.DomainVerificationManagerImpl;
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.IDomainVerificationManager;
+import android.os.ServiceSpecificException;
+import android.util.ArraySet;
+
+import java.util.List;
+import java.util.UUID;
+
+class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
+
+ @NonNull
+ private DomainVerificationService mService;
+
+ DomainVerificationManagerStub(DomainVerificationService service) {
+ mService = service;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getValidVerificationPackageNames() {
+ try {
+ return mService.getValidVerificationPackageNames();
+ } catch (Exception e) {
+ throw rethrow(e);
+ }
+ }
+
+ @Nullable
+ @Override
+ public DomainVerificationInfo getDomainVerificationInfo(String packageName) {
+ try {
+ return mService.getDomainVerificationInfo(packageName);
+ } catch (Exception e) {
+ throw rethrow(e);
+ }
+ }
+
+ @Override
+ public void setDomainVerificationStatus(String domainSetId, List<String> domains,
+ int state) {
+ try {
+ mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
+ new ArraySet<>(domains), state);
+ } catch (Exception e) {
+ throw rethrow(e);
+ }
+ }
+
+ @Override
+ public void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed,
+ @UserIdInt int userId) {
+ try {
+ mService.setDomainVerificationLinkHandlingAllowed(packageName, allowed, userId);
+ } catch (Exception e) {
+ throw rethrow(e);
+ }
+ }
+
+ @Override
+ public void setDomainVerificationUserSelection(String domainSetId, List<String> domains,
+ boolean enabled, @UserIdInt int userId) {
+ try {
+ mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
+ new ArraySet<>(domains), enabled, userId);
+ } catch (Exception e) {
+ throw rethrow(e);
+ }
+ }
+
+ @Nullable
+ @Override
+ public DomainVerificationUserSelection getDomainVerificationUserSelection(
+ String packageName, @UserIdInt int userId) {
+ try {
+ return mService.getDomainVerificationUserSelection(packageName, userId);
+ } catch (Exception e) {
+ throw rethrow(e);
+ }
+ }
+
+ private RuntimeException rethrow(Exception exception) throws RuntimeException {
+ if (exception instanceof InvalidDomainSetException) {
+ int packedErrorCode = DomainVerificationManagerImpl.ERROR_INVALID_DOMAIN_SET;
+ packedErrorCode |= ((InvalidDomainSetException) exception).getReason() << 16;
+ return new ServiceSpecificException(packedErrorCode,
+ ((InvalidDomainSetException) exception).getPackageName());
+ } else if (exception instanceof NameNotFoundException) {
+ return new ServiceSpecificException(
+ DomainVerificationManagerImpl.ERROR_NAME_NOT_FOUND);
+ } else if (exception instanceof RuntimeException) {
+ return (RuntimeException) exception;
+ } else {
+ return new RuntimeException(exception);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationMessageCodes.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationMessageCodes.java
new file mode 100644
index 0000000..7af78c6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationMessageCodes.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.os.Handler;
+
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+
+/**
+ * Codes that are sent through the {@link PackageManagerService} {@link Handler} and eventually
+ * delegated to {@link DomainVerificationService} and {@link DomainVerificationProxy}.
+ *
+ * These codes are wrapped and thus exclusive to the domain verification APIs. They do not have be
+ * distinct from any of the codes inside {@link PackageManagerService}.
+ */
+public final class DomainVerificationMessageCodes {
+
+ public static final int SEND_REQUEST = 1;
+ public static final int LEGACY_SEND_REQUEST = 2;
+ public static final int LEGACY_ON_INTENT_FILTER_VERIFIED = 3;
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
new file mode 100644
index 0000000..679f948
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.server.pm.SettingsXml;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.UUID;
+
+public class DomainVerificationPersistence {
+
+ private static final String TAG = "DomainVerificationPersistence";
+
+ public static final String TAG_DOMAIN_VERIFICATIONS = "domain-verifications";
+ public static final String TAG_ACTIVE = "active";
+ public static final String TAG_RESTORED = "restored";
+
+ public static final String TAG_PACKAGE_STATE = "package-state";
+ private static final String ATTR_PACKAGE_NAME = "packageName";
+ private static final String ATTR_ID = "id";
+ private static final String ATTR_HAS_AUTO_VERIFY_DOMAINS = "hasAutoVerifyDomains";
+ private static final String TAG_USER_STATES = "user-states";
+
+ public static final String TAG_USER_STATE = "user-state";
+ public static final String ATTR_USER_ID = "userId";
+ public static final String ATTR_DISALLOW_LINK_HANDLING = "disallowLinkHandling";
+ public static final String TAG_ENABLED_HOSTS = "enabled-hosts";
+ public static final String TAG_HOST = "host";
+
+ private static final String TAG_STATE = "state";
+ public static final String TAG_DOMAIN = "domain";
+ public static final String ATTR_NAME = "name";
+ public static final String ATTR_STATE = "state";
+
+ public static void writeToXml(@NonNull TypedXmlSerializer xmlSerializer,
+ @NonNull DomainVerificationStateMap<DomainVerificationPkgState> attached,
+ @NonNull ArrayMap<String, DomainVerificationPkgState> pending,
+ @NonNull ArrayMap<String, DomainVerificationPkgState> restored) throws IOException {
+ try (SettingsXml.Serializer serializer = SettingsXml.serializer(xmlSerializer)) {
+ try (SettingsXml.WriteSection ignored = serializer.startSection(
+ TAG_DOMAIN_VERIFICATIONS)) {
+ // Both attached and pending states are written to the active set, since both
+ // should be restored when the device reboots or runs a backup. They're merged into
+ // the same list because at read time the distinction isn't relevant. The pending
+ // list should generally be empty at this point anyways.
+ ArraySet<DomainVerificationPkgState> active = new ArraySet<>();
+
+ int attachedSize = attached.size();
+ for (int attachedIndex = 0; attachedIndex < attachedSize; attachedIndex++) {
+ active.add(attached.valueAt(attachedIndex));
+ }
+
+ int pendingSize = pending.size();
+ for (int pendingIndex = 0; pendingIndex < pendingSize; pendingIndex++) {
+ active.add(pending.valueAt(pendingIndex));
+ }
+
+ try (SettingsXml.WriteSection activeSection = serializer.startSection(TAG_ACTIVE)) {
+ writePackageStates(activeSection, active);
+ }
+
+ try (SettingsXml.WriteSection restoredSection = serializer.startSection(
+ TAG_RESTORED)) {
+ writePackageStates(restoredSection, restored.values());
+ }
+ }
+ }
+ }
+
+ private static void writePackageStates(@NonNull SettingsXml.WriteSection section,
+ @NonNull Collection<DomainVerificationPkgState> states) throws IOException {
+ if (states.isEmpty()) {
+ return;
+ }
+
+ for (DomainVerificationPkgState state : states) {
+ writePkgStateToXml(section, state);
+ }
+ }
+
+ @NonNull
+ public static ReadResult readFromXml(@NonNull TypedXmlPullParser parentParser)
+ throws IOException, XmlPullParserException {
+ ArrayMap<String, DomainVerificationPkgState> active = new ArrayMap<>();
+ ArrayMap<String, DomainVerificationPkgState> restored = new ArrayMap<>();
+
+ SettingsXml.ChildSection child = SettingsXml.parser(parentParser).children();
+ while (child.moveToNext()) {
+ switch (child.getName()) {
+ case TAG_ACTIVE:
+ readPackageStates(child, active);
+ break;
+ case TAG_RESTORED:
+ readPackageStates(child, restored);
+ break;
+ }
+ }
+
+ return new ReadResult(active, restored);
+ }
+
+ private static void readPackageStates(@NonNull SettingsXml.ReadSection section,
+ @NonNull ArrayMap<String, DomainVerificationPkgState> map) {
+ SettingsXml.ChildSection child = section.children();
+ while (child.moveToNext(TAG_PACKAGE_STATE)) {
+ DomainVerificationPkgState pkgState = createPkgStateFromXml(child);
+ if (pkgState != null) {
+ // State is unique by package name
+ map.put(pkgState.getPackageName(), pkgState);
+ }
+ }
+ }
+
+ /**
+ * Reads a package state from XML. Assumes the starting {@link #TAG_PACKAGE_STATE} has already
+ * been entered.
+ */
+ @Nullable
+ public static DomainVerificationPkgState createPkgStateFromXml(
+ @NonNull SettingsXml.ReadSection section) {
+ String packageName = section.getString(ATTR_PACKAGE_NAME);
+ String idString = section.getString(ATTR_ID);
+ boolean hasAutoVerifyDomains = section.getBoolean(ATTR_HAS_AUTO_VERIFY_DOMAINS);
+ if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(idString)) {
+ return null;
+ }
+ UUID id = UUID.fromString(idString);
+
+ final ArrayMap<String, Integer> stateMap = new ArrayMap<>();
+ final SparseArray<DomainVerificationUserState> userStates = new SparseArray<>();
+
+ SettingsXml.ChildSection child = section.children();
+ while (child.moveToNext()) {
+ switch (child.getName()) {
+ case TAG_STATE:
+ readDomainStates(child, stateMap);
+ break;
+ case TAG_USER_STATES:
+ readUserStates(child, userStates);
+ break;
+ }
+ }
+
+ return new DomainVerificationPkgState(packageName, id, hasAutoVerifyDomains, stateMap,
+ userStates);
+ }
+
+ private static void readUserStates(@NonNull SettingsXml.ReadSection section,
+ @NonNull SparseArray<DomainVerificationUserState> userStates) {
+ SettingsXml.ChildSection child = section.children();
+ while (child.moveToNext(TAG_USER_STATE)) {
+ DomainVerificationUserState userState = createUserStateFromXml(child);
+ if (userState != null) {
+ userStates.put(userState.getUserId(), userState);
+ }
+ }
+ }
+
+ private static void readDomainStates(@NonNull SettingsXml.ReadSection stateSection,
+ @NonNull ArrayMap<String, Integer> stateMap) {
+ SettingsXml.ChildSection child = stateSection.children();
+ while (child.moveToNext(TAG_DOMAIN)) {
+ String name = child.getString(ATTR_NAME);
+ int state = child.getInt(ATTR_STATE, DomainVerificationState.STATE_NO_RESPONSE);
+ stateMap.put(name, state);
+ }
+ }
+
+ public static void writePkgStateToXml(@NonNull SettingsXml.WriteSection parentSection,
+ @NonNull DomainVerificationPkgState pkgState) throws IOException {
+ try (SettingsXml.WriteSection ignored =
+ parentSection.startSection(TAG_PACKAGE_STATE)
+ .attribute(ATTR_PACKAGE_NAME, pkgState.getPackageName())
+ .attribute(ATTR_ID, pkgState.getId().toString())
+ .attribute(ATTR_HAS_AUTO_VERIFY_DOMAINS,
+ pkgState.isHasAutoVerifyDomains())) {
+ writeStateMap(parentSection, pkgState.getStateMap());
+ writeUserStates(parentSection, pkgState.getUserSelectionStates());
+ }
+ }
+
+ private static void writeUserStates(@NonNull SettingsXml.WriteSection parentSection,
+ @NonNull SparseArray<DomainVerificationUserState> states) throws IOException {
+ int size = states.size();
+ if (size == 0) {
+ return;
+ }
+
+ try (SettingsXml.WriteSection section = parentSection.startSection(TAG_USER_STATES)) {
+ for (int index = 0; index < size; index++) {
+ writeUserStateToXml(section, states.valueAt(index));
+ }
+ }
+ }
+
+ private static void writeStateMap(@NonNull SettingsXml.WriteSection parentSection,
+ @NonNull ArrayMap<String, Integer> stateMap) throws IOException {
+ if (stateMap.isEmpty()) {
+ return;
+ }
+
+ try (SettingsXml.WriteSection stateSection = parentSection.startSection(TAG_STATE)) {
+ int size = stateMap.size();
+ for (int index = 0; index < size; index++) {
+ stateSection.startSection(TAG_DOMAIN)
+ .attribute(ATTR_NAME, stateMap.keyAt(index))
+ .attribute(ATTR_STATE, stateMap.valueAt(index))
+ .finish();
+ }
+ }
+ }
+
+ /**
+ * Reads a user state from XML. Assumes the starting {@link #TAG_USER_STATE} has already been
+ * entered.
+ */
+ @Nullable
+ public static DomainVerificationUserState createUserStateFromXml(
+ @NonNull SettingsXml.ReadSection section) {
+ int userId = section.getInt(ATTR_USER_ID);
+ if (userId == -1) {
+ return null;
+ }
+
+ boolean disallowLinkHandling = section.getBoolean(ATTR_DISALLOW_LINK_HANDLING);
+ ArraySet<String> enabledHosts = new ArraySet<>();
+
+ SettingsXml.ChildSection child = section.children();
+ while (child.moveToNext(TAG_ENABLED_HOSTS)) {
+ readEnabledHosts(child, enabledHosts);
+ }
+
+ return new DomainVerificationUserState(userId, enabledHosts, disallowLinkHandling);
+ }
+
+ private static void readEnabledHosts(@NonNull SettingsXml.ReadSection section,
+ @NonNull ArraySet<String> enabledHosts) {
+ SettingsXml.ChildSection child = section.children();
+ while (child.moveToNext(TAG_HOST)) {
+ String hostName = child.getString(ATTR_NAME);
+ if (!TextUtils.isEmpty(hostName)) {
+ enabledHosts.add(hostName);
+ }
+ }
+ }
+
+ public static void writeUserStateToXml(@NonNull SettingsXml.WriteSection parentSection,
+ @NonNull DomainVerificationUserState userState) throws IOException {
+ try (SettingsXml.WriteSection section =
+ parentSection.startSection(TAG_USER_STATE)
+ .attribute(ATTR_USER_ID, userState.getUserId())
+ .attribute(ATTR_DISALLOW_LINK_HANDLING,
+ userState.isDisallowLinkHandling())) {
+ ArraySet<String> enabledHosts = userState.getEnabledHosts();
+ if (!enabledHosts.isEmpty()) {
+ try (SettingsXml.WriteSection enabledHostsSection =
+ section.startSection(TAG_ENABLED_HOSTS)) {
+ int size = enabledHosts.size();
+ for (int index = 0; index < size; index++) {
+ enabledHostsSection.startSection(TAG_HOST)
+ .attribute(ATTR_NAME, enabledHosts.valueAt(index))
+ .finish();
+ }
+ }
+ }
+ }
+ }
+
+ public static class ReadResult {
+
+ @NonNull
+ public final ArrayMap<String, DomainVerificationPkgState> active;
+
+ @NonNull
+ public final ArrayMap<String, DomainVerificationPkgState> restored;
+
+ public ReadResult(@NonNull ArrayMap<String, DomainVerificationPkgState> active,
+ @NonNull ArrayMap<String, DomainVerificationPkgState> restored) {
+ this.active = active;
+ this.restored = restored;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
new file mode 100644
index 0000000..53540c8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -0,0 +1,1241 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.content.pm.verify.domain.IDomainVerificationManager;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.SystemConfig;
+import com.android.server.SystemService;
+import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+public class DomainVerificationService extends SystemService
+ implements DomainVerificationManagerInternal, DomainVerificationShell.Callback {
+
+ private static final String TAG = "DomainVerificationService";
+
+ public static final boolean DEBUG_APPROVAL = true;
+
+ /**
+ * The new user preference API for verifying domains marked autoVerify=true in
+ * AndroidManifest.xml intent filters is not yet implemented in the current platform preview.
+ * This is anticipated to ship before S releases.
+ *
+ * For now, it is possible to preview the new user preference changes by enabling this
+ * ChangeId and using the <code>adb shell pm set-app-links-user-selection</code> and similar
+ * commands.
+ */
+ @ChangeId
+ @Disabled
+ private static final long SETTINGS_API_V2 = 178111421;
+
+ /**
+ * States that are currently alive and attached to a package. Entries are exclusive with the
+ * state stored in {@link DomainVerificationSettings}, as any pending/restored state should be
+ * immediately attached once its available.
+ * <p>
+ * Generally this should be not accessed directly. Prefer calling {@link
+ * #getAndValidateAttachedLocked(UUID, Set, boolean)}.
+ *
+ * @see #getAndValidateAttachedLocked(UUID, Set, boolean)
+ **/
+ @GuardedBy("mLock")
+ @NonNull
+ private final DomainVerificationStateMap<DomainVerificationPkgState> mAttachedPkgStates =
+ new DomainVerificationStateMap<>();
+
+ /**
+ * Lock for all state reads/writes.
+ */
+ private final Object mLock = new Object();
+
+ @NonNull
+ private Connection mConnection;
+
+ @NonNull
+ private final SystemConfig mSystemConfig;
+
+ @NonNull
+ private final PlatformCompat mPlatformCompat;
+
+ @NonNull
+ private final DomainVerificationSettings mSettings;
+
+ @NonNull
+ private final DomainVerificationCollector mCollector;
+
+ @NonNull
+ private final DomainVerificationEnforcer mEnforcer;
+
+ @NonNull
+ private final DomainVerificationDebug mDebug;
+
+ @NonNull
+ private final DomainVerificationShell mShell;
+
+ @NonNull
+ private final DomainVerificationLegacySettings mLegacySettings;
+
+ @NonNull
+ private final IDomainVerificationManager.Stub mStub = new DomainVerificationManagerStub(this);
+
+ @NonNull
+ private DomainVerificationProxy mProxy = new DomainVerificationProxyUnavailable();
+
+ public DomainVerificationService(@NonNull Context context, @NonNull SystemConfig systemConfig,
+ @NonNull PlatformCompat platformCompat) {
+ super(context);
+ mSystemConfig = systemConfig;
+ mPlatformCompat = platformCompat;
+ mSettings = new DomainVerificationSettings();
+ mCollector = new DomainVerificationCollector(platformCompat, systemConfig);
+ mEnforcer = new DomainVerificationEnforcer(context);
+ mDebug = new DomainVerificationDebug(mCollector);
+ mShell = new DomainVerificationShell(this);
+ mLegacySettings = new DomainVerificationLegacySettings();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.DOMAIN_VERIFICATION_SERVICE, mStub);
+ }
+
+ @Override
+ public void setConnection(@NonNull Connection connection) {
+ mConnection = connection;
+ }
+
+ @NonNull
+ @Override
+ public DomainVerificationProxy getProxy() {
+ return mProxy;
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ super.onBootPhase(phase);
+ if (phase != SystemService.PHASE_BOOT_COMPLETED || !hasRealVerifier()) {
+ return;
+ }
+
+ verifyPackages(null, false);
+ }
+
+ @Override
+ public void onUserUnlocked(@NonNull TargetUser user) {
+ super.onUserUnlocked(user);
+
+ // Package verification is sent at both boot and user unlock. The latter will allow v1
+ // verification agents to respond to the request, since they will not be directBootAware.
+ // However, ideally v2 implementations are boot aware and can handle the initial boot
+ // broadcast, to start verifying packages as soon as possible. It's possible this causes
+ // unnecessary duplication at device start up, but the implementation is responsible for
+ // de-duplicating.
+ // TODO: This can be improved by checking if the broadcast was received by the
+ // verification agent in the initial boot broadcast
+ verifyPackages(null, false);
+ }
+
+ @Override
+ public void setProxy(@NonNull DomainVerificationProxy proxy) {
+ mProxy = proxy;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getValidVerificationPackageNames() {
+ mEnforcer.assertApprovedVerifier(mConnection.getCallingUid(), mProxy);
+ List<String> packageNames = new ArrayList<>();
+ synchronized (mLock) {
+ int size = mAttachedPkgStates.size();
+ for (int index = 0; index < size; index++) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+ if (pkgState.isHasAutoVerifyDomains()) {
+ packageNames.add(pkgState.getPackageName());
+ }
+ }
+ }
+ return packageNames;
+ }
+
+ @Nullable
+ @Override
+ public UUID getDomainVerificationInfoId(@NonNull String packageName) {
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState != null) {
+ return pkgState.getId();
+ } else {
+ return null;
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
+ throws NameNotFoundException {
+ mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ return null;
+ }
+
+ AndroidPackage pkg = mConnection.getPackageLocked(packageName);
+ if (pkg == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ Map<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
+
+ // TODO(b/159952358): Should the domain list be cached?
+ ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
+ if (domains.isEmpty()) {
+ return null;
+ }
+
+ int size = domains.size();
+ for (int index = 0; index < size; index++) {
+ hostToStateMap.putIfAbsent(domains.valueAt(index),
+ DomainVerificationState.STATE_NO_RESPONSE);
+ }
+
+ // TODO(b/159952358): Do not return if no values are editable (all ignored states)?
+ return new DomainVerificationInfo(pkgState.getId(), packageName, hostToStateMap);
+ }
+ }
+
+ @Override
+ public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
+ int state) throws InvalidDomainSetException, NameNotFoundException {
+ if (state < DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED) {
+ if (state != DomainVerificationState.STATE_SUCCESS) {
+ throw new IllegalArgumentException(
+ "Verifier can only set STATE_SUCCESS or codes greater than or equal to "
+ + "STATE_FIRST_VERIFIER_DEFINED");
+ }
+ }
+
+ setDomainVerificationStatusInternal(mConnection.getCallingUid(), domainSetId, domains,
+ state);
+ }
+
+ @Override
+ public void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
+ @NonNull Set<String> domains, int state)
+ throws InvalidDomainSetException, NameNotFoundException {
+ mEnforcer.assertApprovedVerifier(callingUid, mProxy);
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+ true /* forAutoVerify */);
+ ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+ for (String domain : domains) {
+ Integer previousState = stateMap.get(domain);
+ if (previousState != null
+ && !DomainVerificationManager.isStateModifiable(previousState)) {
+ continue;
+ }
+
+ stateMap.put(domain, state);
+ }
+ }
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ @Override
+ public void setDomainVerificationStatusInternal(@Nullable String packageName, int state,
+ @Nullable ArraySet<String> domains) throws NameNotFoundException {
+ mEnforcer.assertInternal(mConnection.getCallingUid());
+
+ switch (state) {
+ case DomainVerificationState.STATE_NO_RESPONSE:
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_APPROVED:
+ case DomainVerificationState.STATE_DENIED:
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "State must be one of NO_RESPONSE, SUCCESS, APPROVED, or DENIED");
+ }
+
+ if (packageName == null) {
+ synchronized (mLock) {
+ ArraySet<String> validDomains = new ArraySet<>();
+
+ int size = mAttachedPkgStates.size();
+ for (int index = 0; index < size; index++) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+ String pkgName = pkgState.getPackageName();
+ PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+ if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ continue;
+ }
+
+ AndroidPackage pkg = pkgSetting.getPkg();
+
+ validDomains.clear();
+
+ ArraySet<String> autoVerifyDomains = mCollector.collectAutoVerifyDomains(pkg);
+ if (domains == null) {
+ validDomains.addAll(autoVerifyDomains);
+ } else {
+ validDomains.addAll(domains);
+ validDomains.retainAll(autoVerifyDomains);
+ }
+
+ setDomainVerificationStatusInternal(pkgState, state, validDomains);
+ }
+ }
+ } else {
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName);
+ if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ AndroidPackage pkg = pkgSetting.getPkg();
+ if (domains == null) {
+ domains = mCollector.collectAutoVerifyDomains(pkg);
+ } else {
+ domains.retainAll(mCollector.collectAutoVerifyDomains(pkg));
+ }
+
+ setDomainVerificationStatusInternal(pkgState, state, domains);
+ }
+ }
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ private void setDomainVerificationStatusInternal(@NonNull DomainVerificationPkgState pkgState,
+ int state, @NonNull ArraySet<String> validDomains) {
+ ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+ int size = validDomains.size();
+ for (int index = 0; index < size; index++) {
+ stateMap.put(validDomains.valueAt(index), state);
+ }
+ }
+
+ @Override
+ public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
+ boolean allowed) throws NameNotFoundException {
+ setDomainVerificationLinkHandlingAllowed(packageName, allowed,
+ mConnection.getCallingUserId());
+ }
+
+ public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
+ boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
+ mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), userId);
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ pkgState.getOrCreateUserSelectionState(userId)
+ .setDisallowLinkHandling(!allowed);
+ }
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ @Override
+ public void setDomainVerificationLinkHandlingAllowedInternal(@Nullable String packageName,
+ boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
+ mEnforcer.assertInternal(mConnection.getCallingUid());
+ if (packageName == null) {
+ synchronized (mLock) {
+ int pkgStateSize = mAttachedPkgStates.size();
+ for (int pkgStateIndex = 0; pkgStateIndex < pkgStateSize; pkgStateIndex++) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex);
+ if (userId == UserHandle.USER_ALL) {
+ SparseArray<DomainVerificationUserState> userStates =
+ pkgState.getUserSelectionStates();
+ int userStatesSize = userStates.size();
+ for (int userStateIndex = 0; userStateIndex < userStatesSize;
+ userStateIndex++) {
+ userStates.valueAt(userStateIndex)
+ .setDisallowLinkHandling(!allowed);
+ }
+ } else {
+ pkgState.getOrCreateUserSelectionState(userId)
+ .setDisallowLinkHandling(!allowed);
+ }
+ }
+
+ }
+ } else {
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ pkgState.getOrCreateUserSelectionState(userId)
+ .setDisallowLinkHandling(!allowed);
+ }
+ }
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ @Override
+ public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+ @NonNull Set<String> domains, boolean enabled)
+ throws InvalidDomainSetException, NameNotFoundException {
+ setDomainVerificationUserSelection(domainSetId, domains, enabled,
+ mConnection.getCallingUserId());
+ }
+
+ public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
+ @NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
+ throws InvalidDomainSetException, NameNotFoundException {
+ mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), userId);
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+ false /* forAutoVerify */);
+ DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
+ if (enabled) {
+ userState.addHosts(domains);
+ } else {
+ userState.removeHosts(domains);
+ }
+ }
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ @Override
+ public void setDomainVerificationUserSelectionInternal(@UserIdInt int userId,
+ @Nullable String packageName, boolean enabled, @NonNull ArraySet<String> domains)
+ throws NameNotFoundException {
+ mEnforcer.assertInternal(mConnection.getCallingUid());
+
+ if (packageName == null) {
+ synchronized (mLock) {
+ Set<String> validDomains = new ArraySet<>();
+
+ int size = mAttachedPkgStates.size();
+ for (int index = 0; index < size; index++) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+ String pkgName = pkgState.getPackageName();
+ PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+ if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ continue;
+ }
+
+ validDomains.clear();
+ validDomains.addAll(domains);
+
+ setDomainVerificationUserSelectionInternal(userId, pkgState,
+ pkgSetting.getPkg(), enabled, validDomains);
+ }
+ }
+ } else {
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName);
+ if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ setDomainVerificationUserSelectionInternal(userId, pkgState, pkgSetting.getPkg(),
+ enabled, domains);
+ }
+ }
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ private void setDomainVerificationUserSelectionInternal(int userId,
+ @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg,
+ boolean enabled, Set<String> domains) {
+ domains.retainAll(mCollector.collectAllWebDomains(pkg));
+
+ SparseArray<DomainVerificationUserState> userStates =
+ pkgState.getUserSelectionStates();
+ if (userId == UserHandle.USER_ALL) {
+ int size = userStates.size();
+ for (int index = 0; index < size; index++) {
+ DomainVerificationUserState userState = userStates.valueAt(index);
+ if (enabled) {
+ userState.addHosts(domains);
+ } else {
+ userState.removeHosts(domains);
+ }
+ }
+ } else {
+ DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
+ if (enabled) {
+ userState.addHosts(domains);
+ } else {
+ userState.removeHosts(domains);
+ }
+ }
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ @Nullable
+ @Override
+ public DomainVerificationUserSelection getDomainVerificationUserSelection(
+ @NonNull String packageName) throws NameNotFoundException {
+ return getDomainVerificationUserSelection(packageName,
+ mConnection.getCallingUserId());
+ }
+
+ @Nullable
+ @Override
+ public DomainVerificationUserSelection getDomainVerificationUserSelection(
+ @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
+ mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), userId);
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ return null;
+ }
+
+ AndroidPackage pkg = mConnection.getPackageLocked(packageName);
+ if (pkg == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
+
+ ArrayMap<String, Boolean> hostToUserSelectionMap = new ArrayMap<>();
+
+ ArraySet<String> domains = mCollector.collectAllWebDomains(pkg);
+ int domainsSize = domains.size();
+ for (int index = 0; index < domainsSize; index++) {
+ hostToUserSelectionMap.put(domains.valueAt(index), false);
+ }
+
+ boolean openVerifiedLinks = false;
+ DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
+ if (userState != null) {
+ openVerifiedLinks = !userState.isDisallowLinkHandling();
+ ArraySet<String> enabledHosts = userState.getEnabledHosts();
+ int hostsSize = enabledHosts.size();
+ for (int index = 0; index < hostsSize; index++) {
+ hostToUserSelectionMap.put(enabledHosts.valueAt(index), true);
+ }
+ }
+
+ return new DomainVerificationUserSelection(pkgState.getId(), packageName,
+ UserHandle.of(userId), openVerifiedLinks, hostToUserSelectionMap);
+ }
+ }
+
+ @NonNull
+ @Override
+ public UUID generateNewId() {
+ // TODO(b/159952358): Domain set ID collisions
+ return UUID.randomUUID();
+ }
+
+ @Override
+ public void migrateState(@NonNull PackageSetting oldPkgSetting,
+ @NonNull PackageSetting newPkgSetting) {
+ String pkgName = newPkgSetting.name;
+ boolean sendBroadcast;
+
+ synchronized (mLock) {
+ UUID oldDomainSetId = oldPkgSetting.getDomainSetId();
+ UUID newDomainSetId = newPkgSetting.getDomainSetId();
+ DomainVerificationPkgState oldPkgState = mAttachedPkgStates.remove(oldDomainSetId);
+
+ AndroidPackage oldPkg = oldPkgSetting.getPkg();
+ AndroidPackage newPkg = newPkgSetting.getPkg();
+
+ ArrayMap<String, Integer> newStateMap = new ArrayMap<>();
+ SparseArray<DomainVerificationUserState> newUserStates = new SparseArray<>();
+
+ if (oldPkgState == null || oldPkg == null || newPkg == null) {
+ // Should be impossible, but to be safe, continue with a new blank state instead
+ Slog.wtf(TAG, "Invalid state nullability old state = " + oldPkgState
+ + ", old pkgSetting = " + oldPkgSetting
+ + ", new pkgSetting = " + newPkgSetting
+ + ", old pkg = " + oldPkg
+ + ", new pkg = " + newPkg, new Exception());
+
+ DomainVerificationPkgState newPkgState = new DomainVerificationPkgState(
+ pkgName, newDomainSetId, true, newStateMap, newUserStates);
+ mAttachedPkgStates.put(pkgName, newDomainSetId, newPkgState);
+ return;
+ }
+
+ ArrayMap<String, Integer> oldStateMap = oldPkgState.getStateMap();
+ ArraySet<String> newAutoVerifyDomains = mCollector.collectAutoVerifyDomains(newPkg);
+ int newDomainsSize = newAutoVerifyDomains.size();
+
+ for (int newDomainsIndex = 0; newDomainsIndex < newDomainsSize; newDomainsIndex++) {
+ String domain = newAutoVerifyDomains.valueAt(newDomainsIndex);
+ Integer oldStateInteger = oldStateMap.get(domain);
+ if (oldStateInteger != null) {
+ int oldState = oldStateInteger;
+ switch (oldState) {
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_RESTORED:
+ case DomainVerificationState.STATE_MIGRATED:
+ newStateMap.put(domain, oldState);
+ break;
+ default:
+ // In all other cases, the state code is left unset
+ // (STATE_NO_RESPONSE) to signal to the verification agent that any
+ // existing error has been cleared and the domain should be
+ // re-attempted. This makes update of a package a signal to
+ // re-verify.
+ break;
+ }
+ }
+ }
+
+ SparseArray<DomainVerificationUserState> oldUserStates =
+ oldPkgState.getUserSelectionStates();
+ int oldUserStatesSize = oldUserStates.size();
+ if (oldUserStatesSize > 0) {
+ ArraySet<String> newWebDomains = mCollector.collectAutoVerifyDomains(newPkg);
+ for (int oldUserStatesIndex = 0; oldUserStatesIndex < oldUserStatesSize;
+ oldUserStatesIndex++) {
+ int userId = oldUserStates.keyAt(oldUserStatesIndex);
+ DomainVerificationUserState oldUserState = oldUserStates.valueAt(
+ oldUserStatesIndex);
+ ArraySet<String> oldEnabledHosts = oldUserState.getEnabledHosts();
+ ArraySet<String> newEnabledHosts = new ArraySet<>(oldEnabledHosts);
+ newEnabledHosts.retainAll(newWebDomains);
+ DomainVerificationUserState newUserState = new DomainVerificationUserState(
+ userId, newEnabledHosts, oldUserState.isDisallowLinkHandling());
+ newUserStates.put(userId, newUserState);
+ }
+ }
+
+ boolean hasAutoVerifyDomains = newDomainsSize > 0;
+ boolean needsBroadcast =
+ applyImmutableState(pkgName, newStateMap, newAutoVerifyDomains);
+
+ sendBroadcast = hasAutoVerifyDomains && needsBroadcast;
+
+ mAttachedPkgStates.put(pkgName, newDomainSetId, new DomainVerificationPkgState(
+ pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates));
+ }
+
+ if (sendBroadcast) {
+ sendBroadcastForPackage(pkgName);
+ }
+ }
+
+ // TODO(b/159952358): Handle valid domainSetIds for PackageSettings with no AndroidPackage
+ @Override
+ public void addPackage(@NonNull PackageSetting newPkgSetting) {
+ // TODO(b/159952358): Optimize packages without any domains. Those wouldn't have to be in
+ // the state map, but it would require handling the "migration" case where an app either
+ // gains or loses all domains.
+
+ UUID domainSetId = newPkgSetting.getDomainSetId();
+ String pkgName = newPkgSetting.name;
+
+ boolean sendBroadcast = true;
+
+ DomainVerificationPkgState pkgState;
+ pkgState = mSettings.getPendingState(pkgName);
+ if (pkgState != null) {
+ // Don't send when attaching from pending read, which is usually boot scan. Re-send on
+ // boot is handled in a separate method once all packages are added.
+ sendBroadcast = false;
+ } else {
+ pkgState = mSettings.getRestoredState(pkgName);
+ }
+
+ AndroidPackage pkg = newPkgSetting.getPkg();
+ ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
+ boolean hasAutoVerifyDomains = !domains.isEmpty();
+ boolean isPendingOrRestored = pkgState != null;
+ if (isPendingOrRestored) {
+ pkgState.setId(domainSetId);
+ } else {
+ pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains);
+ }
+
+ boolean needsBroadcast = applyImmutableState(pkgState, domains);
+ if (needsBroadcast && !isPendingOrRestored) {
+ // TODO(b/159952358): Test this behavior
+ // Attempt to preserve user experience by automatically verifying all domains from
+ // legacy state if they were previously approved, or by automatically enabling all
+ // hosts through user selection if legacy state indicates a user previously made the
+ // choice in settings to allow supported links. The domain verification agent should
+ // re-verify these links (set to STATE_MIGRATED) at the next possible opportunity,
+ // and disable them if appropriate.
+ ArraySet<String> webDomains = null;
+
+ SparseIntArray legacyUserStates = mLegacySettings.getUserStates(pkgName);
+ int userStateSize = legacyUserStates == null ? 0 : legacyUserStates.size();
+ for (int index = 0; index < userStateSize; index++) {
+ int userId = legacyUserStates.keyAt(index);
+ int legacyStatus = legacyUserStates.valueAt(index);
+ if (legacyStatus
+ == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+ if (webDomains == null) {
+ webDomains = mCollector.collectAllWebDomains(pkg);
+ }
+
+ pkgState.getOrCreateUserSelectionState(userId).addHosts(webDomains);
+ }
+ }
+
+ IntentFilterVerificationInfo legacyInfo = mLegacySettings.remove(pkgName);
+ if (legacyInfo != null
+ && legacyInfo.getStatus()
+ == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+ ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+ int domainsSize = domains.size();
+ for (int index = 0; index < domainsSize; index++) {
+ stateMap.put(domains.valueAt(index), DomainVerificationState.STATE_MIGRATED);
+ }
+ }
+ }
+
+ synchronized (mLock) {
+ mAttachedPkgStates.put(pkgName, domainSetId, pkgState);
+ }
+
+ if (sendBroadcast && hasAutoVerifyDomains) {
+ sendBroadcastForPackage(pkgName);
+ }
+ }
+
+ private boolean applyImmutableState(@NonNull DomainVerificationPkgState pkgState,
+ @NonNull ArraySet<String> autoVerifyDomains) {
+ return applyImmutableState(pkgState.getPackageName(), pkgState.getStateMap(),
+ autoVerifyDomains);
+ }
+
+ /**
+ * Applies any immutable state as the final step when adding or migrating state. Currently only
+ * applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a package.
+ *
+ * @return whether or not a broadcast is necessary for this package
+ */
+ private boolean applyImmutableState(@NonNull String packageName,
+ @NonNull ArrayMap<String, Integer> stateMap,
+ @NonNull ArraySet<String> autoVerifyDomains) {
+ if (mSystemConfig.getLinkedApps().contains(packageName)) {
+ int domainsSize = autoVerifyDomains.size();
+ for (int index = 0; index < domainsSize; index++) {
+ stateMap.put(autoVerifyDomains.valueAt(index),
+ DomainVerificationState.STATE_SYS_CONFIG);
+ }
+ return false;
+ } else {
+ int size = stateMap.size();
+ for (int index = size - 1; index >= 0; index--) {
+ Integer state = stateMap.valueAt(index);
+ // If no longer marked in SysConfig, demote any previous SysConfig state
+ if (state == DomainVerificationState.STATE_SYS_CONFIG) {
+ stateMap.removeAt(index);
+ }
+ }
+
+ return true;
+ }
+ }
+
+ @Override
+ public void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException {
+ synchronized (mLock) {
+ mSettings.writeSettings(serializer, mAttachedPkgStates);
+ }
+
+ mLegacySettings.writeSettings(serializer);
+ }
+
+ @Override
+ public void readSettings(@NonNull TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ synchronized (mLock) {
+ mSettings.readSettings(parser, mAttachedPkgStates);
+ }
+ }
+
+ @Override
+ public void readLegacySettings(@NonNull TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ mLegacySettings.readSettings(parser);
+ }
+
+ @Override
+ public void restoreSettings(@NonNull TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ synchronized (mLock) {
+ mSettings.restoreSettings(parser, mAttachedPkgStates);
+ }
+ }
+
+ @Override
+ public void addLegacySetting(@NonNull String packageName,
+ @NonNull IntentFilterVerificationInfo info) {
+ mLegacySettings.add(packageName, info);
+ }
+
+ @Override
+ public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) {
+ mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid());
+ mLegacySettings.add(packageName, userId, state);
+ }
+
+ @Override
+ public int getLegacyState(@NonNull String packageName, @UserIdInt int userId) {
+ return mLegacySettings.getUserState(packageName, userId);
+ }
+
+ @Override
+ public void writeLegacySettings(TypedXmlSerializer serializer, String name) {
+
+ }
+
+ @Override
+ public void clearPackage(@NonNull String packageName) {
+ synchronized (mLock) {
+ mAttachedPkgStates.remove(packageName);
+ }
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ @Override
+ public void clearUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ int attachedSize = mAttachedPkgStates.size();
+ for (int index = 0; index < attachedSize; index++) {
+ mAttachedPkgStates.valueAt(index).removeUser(userId);
+ }
+
+ mSettings.removeUser(userId);
+ }
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ @Override
+ public boolean runMessage(int messageCode, Object object) {
+ return mProxy.runMessage(messageCode, object);
+ }
+
+ @Override
+ public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+ @Nullable @UserIdInt Integer userId) throws NameNotFoundException {
+ synchronized (mLock) {
+ mDebug.printState(writer, packageName, userId, mConnection, mAttachedPkgStates);
+ }
+ }
+
+ @NonNull
+ @Override
+ public DomainVerificationShell getShell() {
+ return mShell;
+ }
+
+ @NonNull
+ @Override
+ public DomainVerificationCollector getCollector() {
+ return mCollector;
+ }
+
+ private void sendBroadcastForPackage(@NonNull String packageName) {
+ mProxy.sendBroadcastForPackages(Collections.singleton(packageName));
+ }
+
+ private boolean hasRealVerifier() {
+ return !(mProxy instanceof DomainVerificationProxyUnavailable);
+ }
+
+ /**
+ * Validates parameters provided by an external caller. Checks that an ID is still live and that
+ * any provided domains are valid. Should be called at the beginning of each API that takes in a
+ * {@link UUID} domain set ID.
+ */
+ @GuardedBy("mLock")
+ private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
+ @NonNull Set<String> domains, boolean forAutoVerify)
+ throws InvalidDomainSetException, NameNotFoundException {
+ if (domainSetId == null) {
+ throw new InvalidDomainSetException(null, null,
+ InvalidDomainSetException.REASON_ID_NULL);
+ }
+
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(domainSetId);
+ if (pkgState == null) {
+ throw new InvalidDomainSetException(domainSetId, null,
+ InvalidDomainSetException.REASON_ID_INVALID);
+ }
+
+ String pkgName = pkgState.getPackageName();
+ PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+ if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ throw DomainVerificationUtils.throwPackageUnavailable(pkgName);
+ }
+
+ if (CollectionUtils.isEmpty(domains)) {
+ throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
+ InvalidDomainSetException.REASON_SET_NULL_OR_EMPTY);
+ }
+ AndroidPackage pkg = pkgSetting.getPkg();
+ ArraySet<String> declaredDomains = forAutoVerify
+ ? mCollector.collectAutoVerifyDomains(pkg)
+ : mCollector.collectAllWebDomains(pkg);
+
+ if (domains.retainAll(declaredDomains)) {
+ throw new InvalidDomainSetException(domainSetId, pkgState.getPackageName(),
+ InvalidDomainSetException.REASON_UNKNOWN_DOMAIN);
+ }
+
+ return pkgState;
+ }
+
+ @Override
+ public void verifyPackages(@Nullable List<String> packageNames, boolean reVerify) {
+ mEnforcer.assertInternal(mConnection.getCallingUid());
+ Set<String> packagesToBroadcast = new ArraySet<>();
+
+ if (packageNames == null) {
+ synchronized (mLock) {
+ int pkgStatesSize = mAttachedPkgStates.size();
+ for (int pkgStateIndex = 0; pkgStateIndex < pkgStatesSize; pkgStateIndex++) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex);
+ addIfShouldBroadcastLocked(packagesToBroadcast, pkgState, reVerify);
+ }
+ }
+ } else {
+ synchronized (mLock) {
+ int size = packageNames.size();
+ for (int index = 0; index < size; index++) {
+ String packageName = packageNames.get(index);
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState != null) {
+ addIfShouldBroadcastLocked(packagesToBroadcast, pkgState, reVerify);
+ }
+ }
+ }
+ }
+
+ if (!packagesToBroadcast.isEmpty()) {
+ mProxy.sendBroadcastForPackages(packagesToBroadcast);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void addIfShouldBroadcastLocked(@NonNull Collection<String> packageNames,
+ @NonNull DomainVerificationPkgState pkgState, boolean reVerify) {
+ if ((reVerify && pkgState.isHasAutoVerifyDomains()) || shouldReBroadcastPackage(pkgState)) {
+ packageNames.add(pkgState.getPackageName());
+ }
+ }
+
+ /**
+ * Determine whether or not a broadcast should be sent at boot for the given {@param pkgState}.
+ * Sends only if the only states recorded are default as decided by {@link
+ * DomainVerificationManager#isStateDefault(int)}.
+ *
+ * If any other state is set, it's assumed that the domain verification agent is aware of the
+ * package and has already scheduled future verification requests.
+ */
+ private boolean shouldReBroadcastPackage(DomainVerificationPkgState pkgState) {
+ if (!pkgState.isHasAutoVerifyDomains()) {
+ return false;
+ }
+
+ ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+ int statesSize = stateMap.size();
+ for (int stateIndex = 0; stateIndex < statesSize; stateIndex++) {
+ Integer state = stateMap.valueAt(stateIndex);
+ if (!DomainVerificationManager.isStateDefault(state)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void clearDomainVerificationState(@Nullable List<String> packageNames) {
+ mEnforcer.assertInternal(mConnection.getCallingUid());
+ synchronized (mLock) {
+ if (packageNames == null) {
+ int size = mAttachedPkgStates.size();
+ for (int index = 0; index < size; index++) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+ String pkgName = pkgState.getPackageName();
+ PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+ if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ continue;
+ }
+ resetDomainState(pkgState, pkgSetting.getPkg());
+ }
+ } else {
+ int size = packageNames.size();
+ for (int index = 0; index < size; index++) {
+ String pkgName = packageNames.get(index);
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName);
+ PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
+ if (pkgSetting == null || pkgSetting.getPkg() == null) {
+ continue;
+ }
+ resetDomainState(pkgState, pkgSetting.getPkg());
+ }
+ }
+ }
+ }
+
+ /**
+ * Reset states that are mutable by the domain verification agent.
+ */
+ private void resetDomainState(@NonNull DomainVerificationPkgState pkgState,
+ @NonNull AndroidPackage pkg) {
+ ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+ int size = stateMap.size();
+ for (int index = size - 1; index >= 0; index--) {
+ Integer state = stateMap.valueAt(index);
+ boolean reset;
+ switch (state) {
+ case DomainVerificationState.STATE_SUCCESS:
+ case DomainVerificationState.STATE_RESTORED:
+ reset = true;
+ break;
+ default:
+ reset = state >= DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED;
+ break;
+ }
+
+ if (reset) {
+ stateMap.removeAt(index);
+ }
+ }
+
+ applyImmutableState(pkgState, mCollector.collectAutoVerifyDomains(pkg));
+ }
+
+ @Override
+ public void clearUserSelections(@Nullable List<String> packageNames, @UserIdInt int userId) {
+ mEnforcer.assertInternal(mConnection.getCallingUid());
+ synchronized (mLock) {
+ if (packageNames == null) {
+ int size = mAttachedPkgStates.size();
+ for (int index = 0; index < size; index++) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+ if (userId == UserHandle.USER_ALL) {
+ pkgState.removeAllUsers();
+ } else {
+ pkgState.removeUser(userId);
+ }
+ }
+ } else {
+ int size = packageNames.size();
+ for (int index = 0; index < size; index++) {
+ String pkgName = packageNames.get(index);
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName);
+ if (userId == UserHandle.USER_ALL) {
+ pkgState.removeAllUsers();
+ } else {
+ pkgState.removeUser(userId);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
+ @UserIdInt int userId) {
+ String packageName = pkgSetting.name;
+ if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, intent, userId, false, "not valid intent");
+ }
+ return false;
+ }
+
+ String host = intent.getData().getHost();
+ final AndroidPackage pkg = pkgSetting.getPkg();
+
+ // Should never be null, but if it is, skip this and assume that v2 is enabled
+ if (pkg != null) {
+ // To allow an instant app to immediately open domains after being installed by the
+ // user, auto approve them for any declared autoVerify domains.
+ if (pkgSetting.getInstantApp(userId)
+ && mCollector.collectAutoVerifyDomains(pkg).contains(host)) {
+ return true;
+ }
+
+ if (!DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, SETTINGS_API_V2)) {
+ int legacyState = mLegacySettings.getUserState(packageName, userId);
+ switch (legacyState) {
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+ // If nothing specifically set, assume v2 rules
+ break;
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK:
+ // With v2 split into 2 lists, always and undefined, the concept of whether
+ // or not to ask is irrelevant. Assume the user wants this application to
+ // open the domain.
+ return true;
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
+ // Never has the same semantics are before
+ return false;
+ }
+ }
+ }
+
+ synchronized (mLock) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+ if (pkgState == null) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, intent, userId, false, "pkgState unavailable");
+ }
+ return false;
+ }
+
+ ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+ DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
+
+ // Only allow autoVerify approval if the user hasn't disabled it
+ if (userState == null || !userState.isDisallowLinkHandling()) {
+ // Check if the exact host matches
+ Integer state = stateMap.get(host);
+ if (state != null && DomainVerificationManager.isStateVerified(state)) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, intent, userId, true, "host verified exactly");
+ }
+ return true;
+ }
+
+ // Otherwise see if the host matches a verified domain by wildcard
+ int stateMapSize = stateMap.size();
+ for (int index = 0; index < stateMapSize; index++) {
+ if (!DomainVerificationManager.isStateVerified(stateMap.valueAt(index))) {
+ continue;
+ }
+
+ String domain = stateMap.keyAt(index);
+ if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, intent, userId, true,
+ "host verified by wildcard");
+ }
+ return true;
+ }
+ }
+ }
+
+ // Check user state if available
+ if (userState == null) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, intent, userId, false, "userState unavailable");
+ }
+ return false;
+ }
+
+ // See if the user has approved the exact host
+ ArraySet<String> enabledHosts = userState.getEnabledHosts();
+ if (enabledHosts.contains(host)) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, intent, userId, true,
+ "host enabled by user exactly");
+ }
+ return true;
+ }
+
+ // See if the host matches a user selection by wildcard
+ int enabledHostsSize = enabledHosts.size();
+ for (int index = 0; index < enabledHostsSize; index++) {
+ String domain = enabledHosts.valueAt(index);
+ if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, intent, userId, true,
+ "host enabled by user through wildcard");
+ }
+ return true;
+ }
+ }
+
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, intent, userId, false, "not approved");
+ }
+ return false;
+ }
+ }
+
+ private void debugApproval(@NonNull String packageName, @NonNull Intent intent,
+ @UserIdInt int userId, boolean approved, @NonNull String reason) {
+ String approvalString = approved ? "approved" : "denied";
+ Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for " + intent
+ + " for user " + userId + ": " + reason);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
new file mode 100644
index 0000000..073967e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class DomainVerificationSettings {
+
+ /**
+ * States read from disk that have yet to attach to a package, but are expected to, generally in
+ * the context of scanning packages already on disk. This is expected to be empty once the boot
+ * package scan completes.
+ **/
+ @GuardedBy("mLock")
+ @NonNull
+ private final ArrayMap<String, DomainVerificationPkgState> mPendingPkgStates = new ArrayMap<>();
+
+ /**
+ * States from restore that have yet to attach to a package. These are special in that their IDs
+ * are dropped when the package is installed/otherwise becomes available, because the ID will
+ * not match if the data is restored from a different device install.
+ * <p>
+ * If multiple restore calls come in and they overlap, the latest entry added for a package name
+ * will be taken, dropping any previous versions.
+ **/
+ @GuardedBy("mLock")
+ @NonNull
+ private final ArrayMap<String, DomainVerificationPkgState> mRestoredPkgStates =
+ new ArrayMap<>();
+
+ /**
+ * Lock for all state reads/writes.
+ */
+ private final Object mLock = new Object();
+
+
+ public void writeSettings(@NonNull TypedXmlSerializer xmlSerializer,
+ @NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
+ throws IOException {
+ synchronized (mLock) {
+ DomainVerificationPersistence.writeToXml(xmlSerializer, liveState,
+ mPendingPkgStates, mRestoredPkgStates);
+ }
+ }
+
+ /**
+ * Parses a previously stored set of states and merges them with {@param liveState}, directly
+ * mutating the values. This is intended for reading settings written by {@link
+ * #writeSettings(TypedXmlSerializer, DomainVerificationStateMap)} on the same device setup.
+ */
+ public void readSettings(@NonNull TypedXmlPullParser parser,
+ @NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
+ throws IOException, XmlPullParserException {
+ DomainVerificationPersistence.ReadResult result =
+ DomainVerificationPersistence.readFromXml(parser);
+ ArrayMap<String, DomainVerificationPkgState> active = result.active;
+ ArrayMap<String, DomainVerificationPkgState> restored = result.restored;
+
+ synchronized (mLock) {
+ int activeSize = active.size();
+ for (int activeIndex = 0; activeIndex < activeSize; activeIndex++) {
+ DomainVerificationPkgState pkgState = active.valueAt(activeIndex);
+ String pkgName = pkgState.getPackageName();
+ DomainVerificationPkgState existingState = liveState.get(pkgName);
+ if (existingState != null) {
+ // This branch should never be possible. Settings should be read from disk
+ // before any states are attached. But just in case, handle it.
+ if (!existingState.getId().equals(pkgState.getId())) {
+ mergePkgState(existingState, pkgState);
+ }
+ } else {
+ mPendingPkgStates.put(pkgName, pkgState);
+ }
+ }
+
+ int restoredSize = restored.size();
+ for (int restoredIndex = 0; restoredIndex < restoredSize; restoredIndex++) {
+ DomainVerificationPkgState pkgState = restored.valueAt(restoredIndex);
+ mRestoredPkgStates.put(pkgState.getPackageName(), pkgState);
+ }
+ }
+ }
+
+ /**
+ * Parses a previously stored set of states and merges them with {@param liveState}, directly
+ * mutating the values. This is intended for restoration across device setups.
+ */
+ public void restoreSettings(@NonNull TypedXmlPullParser parser,
+ @NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
+ throws IOException, XmlPullParserException {
+ // TODO(b/170746586): Restoration assumes user IDs match, which is probably not the case on
+ // a new device.
+
+ DomainVerificationPersistence.ReadResult result =
+ DomainVerificationPersistence.readFromXml(parser);
+
+ // When restoring settings, both active and previously restored are merged, since they
+ // should both go into the newly restored data. Active is added on top of restored just
+ // in case a duplicate is found. Active should be preferred.
+ ArrayMap<String, DomainVerificationPkgState> stateList = result.restored;
+ stateList.putAll(result.active);
+
+ synchronized (mLock) {
+ for (int stateIndex = 0; stateIndex < stateList.size(); stateIndex++) {
+ DomainVerificationPkgState newState = stateList.valueAt(stateIndex);
+ String pkgName = newState.getPackageName();
+ DomainVerificationPkgState existingState = liveState.get(pkgName);
+ if (existingState == null) {
+ existingState = mPendingPkgStates.get(pkgName);
+ }
+ if (existingState == null) {
+ existingState = mRestoredPkgStates.get(pkgName);
+ }
+
+ if (existingState != null) {
+ mergePkgState(existingState, newState);
+ } else {
+ // If there's no existing state, that means the new state has to be transformed
+ // in preparation for attaching to brand new package that may eventually be
+ // installed. This means coercing STATE_SUCCESS and STATE_RESTORED to
+ // STATE_RESTORED and dropping everything else, the same logic that
+ // mergePkgState runs, without the merge part.
+ ArrayMap<String, Integer> stateMap = newState.getStateMap();
+ int size = stateMap.size();
+ for (int index = 0; index < size; index++) {
+ Integer stateInteger = stateMap.valueAt(index);
+ if (stateInteger != null) {
+ int state = stateInteger;
+ if (state == DomainVerificationState.STATE_SUCCESS
+ || state == DomainVerificationState.STATE_RESTORED) {
+ stateMap.setValueAt(index, state);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Merges a newly restored state with existing state. This should only be called for restore,
+ * when the IDs aren't required to match.
+ * <p>
+ * If the existing state for a domain is
+ * {@link DomainVerificationState#STATE_NO_RESPONSE}, then it will be overridden with
+ * {@link DomainVerificationState#STATE_RESTORED} if the restored state is
+ * {@link DomainVerificationState#STATE_SUCCESS} or
+ * {@link DomainVerificationState#STATE_RESTORED}.
+ * <p>
+ * Otherwise the existing state is preserved, assuming any system rules, success state, or
+ * specific error codes are fresher than the restored state. Essentially state is only restored
+ * to grant additional verifications to an app.
+ * <p>
+ * For user selection state, presence in either state will be considered an enabled host. NOTE:
+ * only {@link UserHandle#USER_SYSTEM} is merged. There is no restore path in place for
+ * multiple users.
+ * <p>
+ * TODO(b/170746586): Figure out the restore path for multiple users
+ * <p>
+ * This will mutate {@param oldState} to contain the merged state.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public static void mergePkgState(@NonNull DomainVerificationPkgState oldState,
+ @NonNull DomainVerificationPkgState newState) {
+ ArrayMap<String, Integer> oldStateMap = oldState.getStateMap();
+ ArrayMap<String, Integer> newStateMap = newState.getStateMap();
+ int size = newStateMap.size();
+ for (int index = 0; index < size; index++) {
+ String domain = newStateMap.keyAt(index);
+ Integer newStateCode = newStateMap.valueAt(index);
+ Integer oldStateCodeInteger = oldStateMap.get(domain);
+ if (oldStateCodeInteger == null) {
+ // Cannot add domains to an app
+ continue;
+ }
+
+ int oldStateCode = oldStateCodeInteger;
+ if (oldStateCode == DomainVerificationState.STATE_NO_RESPONSE) {
+ if (newStateCode == DomainVerificationState.STATE_SUCCESS
+ || newStateCode == DomainVerificationState.STATE_RESTORED) {
+ oldStateMap.put(domain, DomainVerificationState.STATE_RESTORED);
+ }
+ }
+ }
+
+ SparseArray<DomainVerificationUserState> oldSelectionStates =
+ oldState.getUserSelectionStates();
+
+ SparseArray<DomainVerificationUserState> newSelectionStates =
+ newState.getUserSelectionStates();
+
+ DomainVerificationUserState newUserState = newSelectionStates.get(UserHandle.USER_SYSTEM);
+ if (newUserState != null) {
+ ArraySet<String> newEnabledHosts = newUserState.getEnabledHosts();
+ DomainVerificationUserState oldUserState =
+ oldSelectionStates.get(UserHandle.USER_SYSTEM);
+
+ boolean disallowLinkHandling = newUserState.isDisallowLinkHandling();
+ if (oldUserState == null) {
+ oldUserState = new DomainVerificationUserState(UserHandle.USER_SYSTEM,
+ newEnabledHosts, disallowLinkHandling);
+ oldSelectionStates.put(UserHandle.USER_SYSTEM, oldUserState);
+ } else {
+ oldUserState.addHosts(newEnabledHosts)
+ .setDisallowLinkHandling(disallowLinkHandling);
+ }
+ }
+ }
+
+ public void removeUser(@UserIdInt int userId) {
+ int pendingSize = mPendingPkgStates.size();
+ for (int index = 0; index < pendingSize; index++) {
+ mPendingPkgStates.valueAt(index).removeUser(userId);
+ }
+
+ // TODO(b/170746586): Restored assumes user IDs match, which is probably not the case
+ // on a new device
+ int restoredSize = mRestoredPkgStates.size();
+ for (int index = 0; index < restoredSize; index++) {
+ mRestoredPkgStates.valueAt(index).removeUser(userId);
+ }
+ }
+
+ @Nullable
+ public DomainVerificationPkgState getPendingState(@NonNull String pkgName) {
+ synchronized (mLock) {
+ return mPendingPkgStates.get(pkgName);
+ }
+ }
+
+ @Nullable
+ public DomainVerificationPkgState getRestoredState(@NonNull String pkgName) {
+ synchronized (mLock) {
+ return mRestoredPkgStates.get(pkgName);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
new file mode 100644
index 0000000..7f9e75a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.content.pm.verify.domain.DomainVerificationUserSelection;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+
+import com.android.modules.utils.BasicShellCommandHandler;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class DomainVerificationShell {
+
+ @NonNull
+ private final Callback mCallback;
+
+ public DomainVerificationShell(@NonNull Callback callback) {
+ mCallback = callback;
+ }
+
+ public void printHelp(@NonNull PrintWriter pw) {
+ pw.println(" get-app-links [--user <USER_ID>] [<PACKAGE>]");
+ pw.println(" Prints the domain verification state for the given package, or for all");
+ pw.println(" packages if none is specified.");
+ pw.println(" --user <USER_ID>: include user selections (includes all domains, not");
+ pw.println(" just autoVerify ones)");
+ pw.println(" reset-app-links [--user <USER_ID>] [<PACKAGE>]");
+ pw.println(" Resets domain verification state for the given package, or for all");
+ pw.println(" packages if none is specified.");
+ pw.println(" --user <USER_ID>: clear user selection state instead; note this means");
+ pw.println(" domain verification state will NOT be cleared");
+ pw.println(" <PACKAGE>: the package to reset, or \"all\" to reset all packages");
+ pw.println(" verify-app-links [--re-verify] [<PACKAGE>]");
+ pw.println(" Broadcasts a verification request for the given package, or for all");
+ pw.println(" packages if none is specified. Only sends if the package has previously");
+ pw.println(" not recorded a response.");
+ pw.println(" --re-verify: send even if the package has recorded a response");
+ pw.println(" set-app-links [--package <PACKAGE>] <STATE> <DOMAINS>...");
+ pw.println(" Manually set the state of a domain for a package. The domain must be");
+ pw.println(" declared by the package as autoVerify for this to work. This command");
+ pw.println(" will not report a failure for domains that could not be applied.");
+ pw.println(" --package <PACKAGE>: the package to set, or \"all\" to set all packages");
+ pw.println(" <STATE>: the code to set the domains to, valid values are:");
+ pw.println(" STATE_NO_RESPONSE (0): reset as if no response was ever recorded.");
+ pw.println(" STATE_SUCCESS (1): treat domain as successfully verified by domain.");
+ pw.println(" verification agent. Note that the domain verification agent can");
+ pw.println(" override this.");
+ pw.println(" STATE_APPROVED (2): treat domain as always approved, preventing the");
+ pw.println(" domain verification agent from changing it.");
+ pw.println(" STATE_DENIED (3): treat domain as always denied, preveting the domain");
+ pw.println(" verification agent from changing it.");
+ pw.println(" <DOMAINS>: space separated list of domains to change, or \"all\" to");
+ pw.println(" change every domain.");
+ pw.println(" set-app-links-user-selection --user <USER_ID> [--package <PACKAGE>]");
+ pw.println(" <ENABLED> <DOMAINS>...");
+ pw.println(" Manually set the state of a host user selection for a package. The domain");
+ pw.println(" must be declared by the package for this to work. This command will not");
+ pw.println(" report a failure for domains that could not be applied.");
+ pw.println(" --user <USER_ID>: the user to change selections for");
+ pw.println(" --package <PACKAGE>: the package to set, or \"all\" to set all packages");
+ pw.println(" <ENABLED>: whether or not to approve the domain");
+ pw.println(" <DOMAINS>: space separated list of domains to change, or \"all\" to");
+ pw.println(" change every domain.");
+ pw.println(" set-app-links-allowed --user <USER_ID> [--package <PACKAGE>] <ALLOWED>");
+ pw.println(" <ENABLED> <DOMAINS>...");
+ pw.println(" Toggle the auto verified link handling setting for a package.");
+ pw.println(" --user <USER_ID>: the user to change selections for");
+ pw.println(" --package <PACKAGE>: the package to set, or \"all\" to set all packages");
+ pw.println(" packages will be reset if no one package is specified.");
+ pw.println(" <ALLOWED>: true to allow the package to open auto verified links, false");
+ pw.println(" to disable");
+ }
+
+ /**
+ * Run a shell/debugging command.
+ *
+ * @return null if the command is unhandled, true if the command succeeded, false if it failed
+ */
+ public Boolean runCommand(@NonNull BasicShellCommandHandler commandHandler,
+ @NonNull String command) {
+ switch (command) {
+ case "get-app-links":
+ return runGetAppLinks(commandHandler);
+ case "reset-app-links":
+ return runResetAppLinks(commandHandler);
+ case "verify-app-links":
+ return runVerifyAppLinks(commandHandler);
+ case "set-app-links":
+ return runSetAppLinks(commandHandler);
+ case "set-app-links-user-selection":
+ return runSetAppLinksUserSelection(commandHandler);
+ case "set-app-links-allowed":
+ return runSetAppLinksAllowed(commandHandler);
+ }
+
+ return null;
+ }
+
+
+ // pm set-app-links [--package <PACKAGE>] <STATE> <DOMAINS>...
+ private boolean runSetAppLinks(@NonNull BasicShellCommandHandler commandHandler) {
+ String packageName = null;
+
+ String option;
+ while ((option = commandHandler.getNextOption()) != null) {
+ if (option.equals("--package")) {
+ packageName = commandHandler.getNextArgRequired();
+ } else {
+ commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+ return false;
+ }
+ }
+
+ if (TextUtils.isEmpty(packageName)) {
+ commandHandler.getErrPrintWriter().println("Error: no package specified");
+ return false;
+ } else if (packageName.equalsIgnoreCase("all")) {
+ packageName = null;
+ }
+
+ String state = commandHandler.getNextArgRequired();
+ int stateInt;
+ switch (state) {
+ case "STATE_NO_RESPONSE":
+ case "0":
+ stateInt = DomainVerificationState.STATE_NO_RESPONSE;
+ break;
+ case "STATE_SUCCESS":
+ case "1":
+ stateInt = DomainVerificationState.STATE_SUCCESS;
+ break;
+ case "STATE_APPROVED":
+ case "2":
+ stateInt = DomainVerificationState.STATE_APPROVED;
+ break;
+ case "STATE_DENIED":
+ case "3":
+ stateInt = DomainVerificationState.STATE_DENIED;
+ break;
+ default:
+ commandHandler.getErrPrintWriter().println("Invalid state option: " + state);
+ return false;
+ }
+
+ ArraySet<String> domains = new ArraySet<>(getRemainingArgs(commandHandler));
+ if (domains.isEmpty()) {
+ commandHandler.getErrPrintWriter().println("No domains specified");
+ return false;
+ }
+
+ if (domains.size() == 1 && domains.contains("all")) {
+ domains = null;
+ }
+
+ try {
+ mCallback.setDomainVerificationStatusInternal(packageName, stateInt,
+ domains);
+ } catch (NameNotFoundException e) {
+ commandHandler.getErrPrintWriter().println("Package not found: " + packageName);
+ return false;
+ }
+ return true;
+ }
+
+ // pm set-app-links-user-selection --user <USER_ID> [--package <PACKAGE>] <ENABLED> <DOMAINS>...
+ private boolean runSetAppLinksUserSelection(@NonNull BasicShellCommandHandler commandHandler) {
+ Integer userId = null;
+ String packageName = null;
+
+ String option;
+ while ((option = commandHandler.getNextOption()) != null) {
+ switch (option) {
+ case "--user":
+ userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
+ break;
+ case "--package":
+ packageName = commandHandler.getNextArgRequired();
+ break;
+ default:
+ commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+ return false;
+ }
+ }
+
+ if (TextUtils.isEmpty(packageName)) {
+ commandHandler.getErrPrintWriter().println("Error: no package specified");
+ return false;
+ } else if (packageName.equalsIgnoreCase("all")) {
+ packageName = null;
+ }
+
+ if (userId == null) {
+ commandHandler.getErrPrintWriter().println("Error: User ID not specified");
+ return false;
+ }
+
+ userId = translateUserId(userId, "runSetAppLinksUserSelection");
+
+ String enabledString = commandHandler.getNextArgRequired();
+
+ // Manually ensure that "true" and "false" are the only options, to ensure a domain isn't
+ // accidentally parsed as a boolean
+ boolean enabled;
+ switch (enabledString) {
+ case "true":
+ enabled = true;
+ break;
+ case "false":
+ enabled = false;
+ break;
+ default:
+ commandHandler.getErrPrintWriter().println(
+ "Invalid enabled param: " + enabledString);
+ return false;
+ }
+
+ ArraySet<String> domains = new ArraySet<>(getRemainingArgs(commandHandler));
+ if (domains.isEmpty()) {
+ commandHandler.getErrPrintWriter().println("No domains specified");
+ return false;
+ }
+
+ try {
+ mCallback.setDomainVerificationUserSelectionInternal(userId,
+ packageName, enabled, domains);
+ } catch (NameNotFoundException e) {
+ commandHandler.getErrPrintWriter().println("Package not found: " + packageName);
+ return false;
+ }
+ return true;
+ }
+
+ // pm get-app-links [--user <USER_ID>] [<PACKAGE>]
+ private boolean runGetAppLinks(@NonNull BasicShellCommandHandler commandHandler) {
+ Integer userId = null;
+
+ String option;
+ while ((option = commandHandler.getNextOption()) != null) {
+ if (option.equals("--user")) {
+ userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
+ } else {
+ commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+ return false;
+ }
+ }
+
+ userId = userId == null ? null : translateUserId(userId, "runGetAppLinks");
+
+ String packageName = commandHandler.getNextArg();
+
+ try (IndentingPrintWriter writer = new IndentingPrintWriter(
+ commandHandler.getOutPrintWriter(), /* singleIndent */ " ", /* wrapLength */
+ 120)) {
+ writer.increaseIndent();
+ try {
+ mCallback.printState(writer, packageName, userId);
+ } catch (NameNotFoundException e) {
+ commandHandler.getErrPrintWriter().println(
+ "Error: package " + packageName + " unavailable");
+ return false;
+ }
+ writer.decreaseIndent();
+ return true;
+ }
+ }
+
+ // pm reset-app-links [--user USER_ID] [<PACKAGE>]
+ private boolean runResetAppLinks(@NonNull BasicShellCommandHandler commandHandler) {
+ Integer userId = null;
+
+ String option;
+ while ((option = commandHandler.getNextOption()) != null) {
+ if (option.equals("--user")) {
+ userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
+ } else {
+ commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+ return false;
+ }
+ }
+
+ userId = userId == null ? null : translateUserId(userId, "runResetAppLinks");
+
+ List<String> packageNames;
+ String pkgNameArg = commandHandler.peekNextArg();
+ if (TextUtils.isEmpty(pkgNameArg)) {
+ commandHandler.getErrPrintWriter().println("Error: no package specified");
+ return false;
+ } else if (pkgNameArg.equalsIgnoreCase("all")) {
+ packageNames = null;
+ } else {
+ packageNames = Arrays.asList(commandHandler.peekRemainingArgs());
+ }
+
+ if (userId != null) {
+ mCallback.clearUserSelections(packageNames, userId);
+ } else {
+ mCallback.clearDomainVerificationState(packageNames);
+ }
+
+ return true;
+ }
+
+ // pm verify-app-links [--re-verify] [<PACKAGE>]
+ private boolean runVerifyAppLinks(@NonNull BasicShellCommandHandler commandHandler) {
+ boolean reVerify = false;
+ String option;
+ while ((option = commandHandler.getNextOption()) != null) {
+ if (option.equals("--re-verify")) {
+ reVerify = true;
+ } else {
+ commandHandler.getErrPrintWriter().println("Error: unknown option: " + option);
+ return false;
+ }
+ }
+
+ List<String> packageNames = null;
+ String pkgNameArg = commandHandler.getNextArg();
+ if (!TextUtils.isEmpty(pkgNameArg)) {
+ packageNames = Collections.singletonList(pkgNameArg);
+ }
+
+ mCallback.verifyPackages(packageNames, reVerify);
+
+ return true;
+ }
+
+ // pm set-app-links-allowed [--package <PACKAGE>] [--user <USER_ID>] <ALLOWED>
+ private boolean runSetAppLinksAllowed(@NonNull BasicShellCommandHandler commandHandler) {
+ String packageName = null;
+ Integer userId = null;
+ Boolean allowed = null;
+ String option;
+ while ((option = commandHandler.getNextOption()) != null) {
+ if (option.equals("--package")) {
+ packageName = commandHandler.getNextArgRequired();
+ } if (option.equals("--user")) {
+ userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
+ } else if (allowed == null) {
+ allowed = Boolean.valueOf(option);
+ } else {
+ commandHandler.getErrPrintWriter().println("Error: unexpected option: " + option);
+ return false;
+ }
+ }
+
+ if (TextUtils.isEmpty(packageName)) {
+ commandHandler.getErrPrintWriter().println("Error: no package specified");
+ return false;
+ } else if (packageName.equalsIgnoreCase("all")) {
+ packageName = null;
+ }
+
+ if (userId == null) {
+ commandHandler.getErrPrintWriter().println("Error: user ID not specified");
+ return false;
+ }
+
+ if (allowed == null) {
+ commandHandler.getErrPrintWriter().println("Error: allowed setting not specified");
+ return false;
+ }
+
+ userId = translateUserId(userId, "runSetAppLinksAllowed");
+
+ try {
+ mCallback.setDomainVerificationLinkHandlingAllowedInternal(packageName, allowed,
+ userId);
+ } catch (NameNotFoundException e) {
+ commandHandler.getErrPrintWriter().println("Package not found: " + packageName);
+ return false;
+ }
+
+ return true;
+ }
+
+ private ArrayList<String> getRemainingArgs(@NonNull BasicShellCommandHandler commandHandler) {
+ ArrayList<String> args = new ArrayList<>();
+ String arg;
+ while ((arg = commandHandler.getNextArg()) != null) {
+ args.add(arg);
+ }
+ return args;
+ }
+
+ private int translateUserId(@UserIdInt int userId, @NonNull String logContext) {
+ return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, true, true, logContext, "pm command");
+ }
+
+ /**
+ * Separated interface from {@link DomainVerificationManagerInternal} to hide methods that are
+ * even more internal, and so that testing is easier.
+ */
+ public interface Callback {
+
+ /**
+ * Variant for use by PackageManagerShellCommand to allow the system/developer to override
+ * the state for a domain.
+ *
+ * @param packageName the package whose state to change, or all packages if none is
+ * specified
+ * @param state the new state code, valid values are
+ * {@link DomainVerificationState#STATE_NO_RESPONSE},
+ * {@link DomainVerificationState#STATE_SUCCESS}, {@link
+ * DomainVerificationState#STATE_APPROVED}, and {@link
+ * DomainVerificationState#STATE_DENIED}
+ * @param domains the set of domains to change, or null to change all of them
+ */
+ void setDomainVerificationStatusInternal(@Nullable String packageName, int state,
+ @Nullable ArraySet<String> domains) throws PackageManager.NameNotFoundException;
+
+ /**
+ * Variant for use by PackageManagerShellCommand to allow the system/developer to override
+ * the state for a domain.
+ *
+ * @param packageName the package whose state to change, or all packages if non is
+ * specified
+ * @param enabled whether the domain is now approved by the user
+ * @param domains the set of domains to change
+ */
+ void setDomainVerificationUserSelectionInternal(@UserIdInt int userId,
+ @Nullable String packageName, boolean enabled, @NonNull ArraySet<String> domains)
+ throws PackageManager.NameNotFoundException;
+
+ /**
+ * @see DomainVerificationManager#getDomainVerificationUserSelection(String)
+ */
+ @Nullable
+ DomainVerificationUserSelection getDomainVerificationUserSelection(
+ @NonNull String packageName, @UserIdInt int userId)
+ throws PackageManager.NameNotFoundException;
+
+ /**
+ * Variant for use by PackageManagerShellCommand to allow the system/developer to override
+ * the setting for a package.
+ *
+ * @param packageName the package whose state to change, or all packages if non is
+ * specified
+ * @param allowed whether the package is allowed to automatically open links through
+ * domain verification
+ */
+ void setDomainVerificationLinkHandlingAllowedInternal(@Nullable String packageName,
+ boolean allowed, @UserIdInt int userId) throws NameNotFoundException;
+
+ /**
+ * Reset all the domain verification states for all domains for the given package names, or
+ * all package names if null is provided.
+ */
+ void clearDomainVerificationState(@Nullable List<String> packageNames);
+
+ /**
+ * Reset all the user selections for the given package names, or all package names if null
+ * is provided.
+ */
+ void clearUserSelections(@Nullable List<String> packageNames, @UserIdInt int userId);
+
+ /**
+ * Broadcast a verification request for the given package names, or all package names if
+ * null is provided. By default only re-broadcasts if a package has not recorded a
+ * response.
+ *
+ * @param reVerify send even if the package has previously recorded a response
+ */
+ void verifyPackages(@Nullable List<String> packageNames, boolean reVerify);
+
+ /**
+ * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer)
+ */
+ void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+ @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
new file mode 100644
index 0000000..474f822
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain;
+
+import android.annotation.CheckResult;
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+
+import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+final class DomainVerificationUtils {
+
+ /**
+ * Consolidates package exception messages. A generic unavailable message is included since the
+ * caller doesn't bother to check why the package isn't available.
+ */
+ @CheckResult
+ static NameNotFoundException throwPackageUnavailable(@NonNull String packageName)
+ throws NameNotFoundException {
+ throw new NameNotFoundException("Package " + packageName + " unavailable");
+ }
+
+ static boolean isDomainVerificationIntent(Intent intent) {
+ return intent.isWebIntent()
+ && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+ && intent.hasCategory(Intent.CATEGORY_DEFAULT);
+ }
+
+ static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg,
+ long changeId) {
+ //noinspection ConstantConditions
+ return Binder.withCleanCallingIdentity(
+ () -> platformCompat.isChangeEnabled(changeId, buildMockAppInfo(pkg)));
+ }
+
+ /**
+ * Passed to {@link PlatformCompat} because this can be invoked mid-install process or when
+ * {@link PackageManagerService#mLock} is being held, and {@link PlatformCompat} will not be
+ * able to query the pending {@link ApplicationInfo} from {@link PackageManager}.
+ * <p>
+ * TODO(b/177613575): Can a different API be used?
+ */
+ @NonNull
+ private static ApplicationInfo buildMockAppInfo(@NonNull AndroidPackage pkg) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = pkg.getPackageName();
+ appInfo.targetSdkVersion = pkg.getTargetSdkVersion();
+ return appInfo;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING
new file mode 100644
index 0000000..c6c9791
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "PackageManagerServiceUnitTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.test.verify.domain"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
new file mode 100644
index 0000000..48099aa
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.models;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * State for a single package for the domain verification APIs. Stores the state of each individual
+ * domain declared by the package, including its verification state and user selection state.
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public class DomainVerificationPkgState {
+
+ @NonNull
+ private final String mPackageName;
+
+ @NonNull
+ private UUID mId;
+
+ /**
+ * Whether or not the package declares any autoVerify domains. This is separate from an empty
+ * check on the map itself, because an empty map means no response recorded, not necessarily no
+ * domains declared. When this is false, {@link #mStateMap} will be empty, but
+ * {@link #mUserSelectionStates} may contain any domains the user has explicitly chosen to
+ * allow this package to open, which may or may not be marked autoVerify.
+ */
+ private final boolean mHasAutoVerifyDomains;
+
+ /**
+ * Map of domains to state integers. Only domains that are not set to the default value of
+ * {@link DomainVerificationState#STATE_NO_RESPONSE} are included.
+ *
+ * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
+ * such as storing no state when the package is marked as a linked app in SystemConfig.
+ */
+ @NonNull
+ private final ArrayMap<String, Integer> mStateMap;
+
+ @NonNull
+ private final SparseArray<DomainVerificationUserState> mUserSelectionStates;
+
+ public DomainVerificationPkgState(@NonNull String packageName, @NonNull UUID id,
+ boolean hasAutoVerifyDomains) {
+ this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0));
+ }
+
+ @Nullable
+ public DomainVerificationUserState getUserSelectionState(@UserIdInt int userId) {
+ return mUserSelectionStates.get(userId);
+ }
+
+ @Nullable
+ public DomainVerificationUserState getOrCreateUserSelectionState(@UserIdInt int userId) {
+ DomainVerificationUserState userState = mUserSelectionStates.get(userId);
+ if (userState == null) {
+ userState = new DomainVerificationUserState(userId);
+ mUserSelectionStates.put(userId, userState);
+ }
+ return userState;
+ }
+
+ public void setId(@NonNull UUID id) {
+ mId = id;
+ }
+
+ public void removeUser(@UserIdInt int userId) {
+ mUserSelectionStates.remove(userId);
+ }
+
+ public void removeAllUsers() {
+ mUserSelectionStates.clear();
+ }
+
+ private int userSelectionStatesHashCode() {
+ return mUserSelectionStates.contentHashCode();
+ }
+
+ private boolean userSelectionStatesEquals(
+ @NonNull SparseArray<DomainVerificationUserState> other) {
+ return mUserSelectionStates.contentEquals(other);
+ }
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new DomainVerificationPkgState.
+ *
+ * @param stateMap
+ * Map of domains to state integers. Only domains that are not set to the default value of
+ * {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
+ *
+ * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
+ * such as storing no state when the package is marked as a linked app in SystemConfig.
+ */
+ @DataClass.Generated.Member
+ public DomainVerificationPkgState(
+ @NonNull String packageName,
+ @NonNull UUID id,
+ boolean hasAutoVerifyDomains,
+ @NonNull ArrayMap<String,Integer> stateMap,
+ @NonNull SparseArray<DomainVerificationUserState> userSelectionStates) {
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mId = id;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mId);
+ this.mHasAutoVerifyDomains = hasAutoVerifyDomains;
+ this.mStateMap = stateMap;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mStateMap);
+ this.mUserSelectionStates = userSelectionStates;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mUserSelectionStates);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull UUID getId() {
+ return mId;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isHasAutoVerifyDomains() {
+ return mHasAutoVerifyDomains;
+ }
+
+ /**
+ * Map of domains to state integers. Only domains that are not set to the default value of
+ * {@link DomainVerificationManager#STATE_NO_RESPONSE} are included.
+ *
+ * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations,
+ * such as storing no state when the package is marked as a linked app in SystemConfig.
+ */
+ @DataClass.Generated.Member
+ public @NonNull ArrayMap<String,Integer> getStateMap() {
+ return mStateMap;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull SparseArray<DomainVerificationUserState> getUserSelectionStates() {
+ return mUserSelectionStates;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "DomainVerificationPkgState { " +
+ "packageName = " + mPackageName + ", " +
+ "id = " + mId + ", " +
+ "hasAutoVerifyDomains = " + mHasAutoVerifyDomains + ", " +
+ "stateMap = " + mStateMap + ", " +
+ "userSelectionStates = " + mUserSelectionStates +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(DomainVerificationPkgState other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ DomainVerificationPkgState that = (DomainVerificationPkgState) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && Objects.equals(mPackageName, that.mPackageName)
+ && Objects.equals(mId, that.mId)
+ && mHasAutoVerifyDomains == that.mHasAutoVerifyDomains
+ && Objects.equals(mStateMap, that.mStateMap)
+ && userSelectionStatesEquals(that.mUserSelectionStates);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + Objects.hashCode(mId);
+ _hash = 31 * _hash + Boolean.hashCode(mHasAutoVerifyDomains);
+ _hash = 31 * _hash + Objects.hashCode(mStateMap);
+ _hash = 31 * _hash + userSelectionStatesHashCode();
+ return _hash;
+ }
+
+ @DataClass.Generated(
+ time = 1608234185474L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationPkgState.java",
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationUserState> mUserSelectionStates\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getUserSelectionState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationUserState getOrCreateUserSelectionState(int)\npublic void setId(java.util.UUID)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userSelectionStatesHashCode()\nprivate boolean userSelectionStatesEquals(android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationStateMap.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationStateMap.java
new file mode 100644
index 0000000..88ccd83
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationStateMap.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.models;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * A feature specific implementation of a multi-key map, since lookups by both a {@link String}
+ * package name and {@link UUID} domain set ID should be supported.
+ *
+ * @param <ValueType> stored object type
+ */
+public class DomainVerificationStateMap<ValueType> {
+
+ private static final String TAG = "DomainVerificationStateMap";
+
+ @NonNull
+ private final ArrayMap<String, ValueType> mPackageNameMap = new ArrayMap<>();
+
+ @NonNull
+ private final ArrayMap<UUID, ValueType> mDomainSetIdMap = new ArrayMap<>();
+
+ public int size() {
+ return mPackageNameMap.size();
+ }
+
+ @NonNull
+ public ValueType valueAt(@IntRange(from = 0) int index) {
+ return mPackageNameMap.valueAt(index);
+ }
+
+ @Nullable
+ public ValueType get(@NonNull String packageName) {
+ return mPackageNameMap.get(packageName);
+ }
+
+ @Nullable
+ public ValueType get(@NonNull UUID domainSetId) {
+ return mDomainSetIdMap.get(domainSetId);
+ }
+
+ public void put(@NonNull String packageName, @NonNull UUID domainSetId,
+ @NonNull ValueType valueType) {
+ if (mPackageNameMap.containsKey(packageName)) {
+ remove(packageName);
+ }
+
+ mPackageNameMap.put(packageName, valueType);
+ mDomainSetIdMap.put(domainSetId, valueType);
+ }
+
+ @Nullable
+ public ValueType remove(@NonNull String packageName) {
+ ValueType valueRemoved = mPackageNameMap.remove(packageName);
+ if (valueRemoved != null) {
+ int index = mDomainSetIdMap.indexOfValue(valueRemoved);
+ if (index >= 0) {
+ mDomainSetIdMap.removeAt(index);
+ }
+ }
+ return valueRemoved;
+ }
+
+ @Nullable
+ public ValueType remove(@NonNull UUID domainSetId) {
+ ValueType valueRemoved = mDomainSetIdMap.remove(domainSetId);
+ if (valueRemoved != null) {
+ int index = mPackageNameMap.indexOfValue(valueRemoved);
+ if (index >= 0) {
+ mPackageNameMap.removeAt(index);
+ }
+ }
+ return valueRemoved;
+ }
+
+ @NonNull
+ public List<String> getPackageNames() {
+ return new ArrayList<>(mPackageNameMap.keySet());
+ }
+
+ /**
+ * Exposes the backing values collection of the one of the internal maps. Should only be used
+ * for test assertions.
+ */
+ @VisibleForTesting
+ public Collection<ValueType> values() {
+ return new ArrayList<>(mPackageNameMap.values());
+ }
+
+ @Override
+ public String toString() {
+ return "DomainVerificationStateMap{"
+ + "packageNameMap=" + mPackageNameMap
+ + ", domainSetIdMap=" + mDomainSetIdMap
+ + '}';
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
new file mode 100644
index 0000000..8e82608
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.models;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.util.ArraySet;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Set;
+
+/**
+ * Tracks which domains have been explicitly enabled by the user, allowing it to automatically open
+ * that domain when a web URL Intent is sent ft.
+ */
+@DataClass(genSetters = true, genEqualsHashCode = true, genToString = true)
+public class DomainVerificationUserState {
+
+ @UserIdInt
+ private final int mUserId;
+
+ /** List of domains which have been enabled by the user. **/
+ @NonNull
+ private final ArraySet<String> mEnabledHosts;
+
+ /** Whether to disallow this package from automatically opening links by auto verification. */
+ private boolean mDisallowLinkHandling;
+
+ public DomainVerificationUserState(@UserIdInt int userId) {
+ mUserId = userId;
+ mEnabledHosts = new ArraySet<>();
+ }
+
+ public DomainVerificationUserState addHosts(@NonNull ArraySet<String> newHosts) {
+ mEnabledHosts.addAll(newHosts);
+ return this;
+ }
+
+ public DomainVerificationUserState addHosts(@NonNull Set<String> newHosts) {
+ mEnabledHosts.addAll(newHosts);
+ return this;
+ }
+
+ public DomainVerificationUserState removeHosts(@NonNull ArraySet<String> newHosts) {
+ mEnabledHosts.removeAll(newHosts);
+ return this;
+ }
+
+ public DomainVerificationUserState removeHosts(@NonNull Set<String> newHosts) {
+ mEnabledHosts.removeAll(newHosts);
+ return this;
+ }
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new DomainVerificationUserState.
+ *
+ * @param enabledHosts
+ * List of domains which have been enabled by the user. *
+ */
+ @DataClass.Generated.Member
+ public DomainVerificationUserState(
+ @UserIdInt int userId,
+ @NonNull ArraySet<String> enabledHosts,
+ boolean disallowLinkHandling) {
+ this.mUserId = userId;
+ com.android.internal.util.AnnotationValidations.validate(
+ UserIdInt.class, null, mUserId);
+ this.mEnabledHosts = enabledHosts;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mEnabledHosts);
+ this.mDisallowLinkHandling = disallowLinkHandling;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * List of domains which have been enabled by the user. *
+ */
+ @DataClass.Generated.Member
+ public @NonNull ArraySet<String> getEnabledHosts() {
+ return mEnabledHosts;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isDisallowLinkHandling() {
+ return mDisallowLinkHandling;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull DomainVerificationUserState setDisallowLinkHandling( boolean value) {
+ mDisallowLinkHandling = value;
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "DomainVerificationUserState { " +
+ "userId = " + mUserId + ", " +
+ "enabledHosts = " + mEnabledHosts + ", " +
+ "disallowLinkHandling = " + mDisallowLinkHandling +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(DomainVerificationUserState other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ DomainVerificationUserState that = (DomainVerificationUserState) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mUserId == that.mUserId
+ && java.util.Objects.equals(mEnabledHosts, that.mEnabledHosts)
+ && mDisallowLinkHandling == that.mDisallowLinkHandling;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mUserId;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mEnabledHosts);
+ _hash = 31 * _hash + Boolean.hashCode(mDisallowLinkHandling);
+ return _hash;
+ }
+
+ @DataClass.Generated(
+ time = 1608234273324L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java",
+ inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledHosts\nprivate boolean mDisallowLinkHandling\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(java.util.Set<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(java.util.Set<java.lang.String>)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
new file mode 100644
index 0000000..715d8fb
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.proxy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.DeviceIdleInternal;
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
+import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+
+import java.util.Objects;
+import java.util.Set;
+
+// TODO(b/170321181): Combine the proxy versions for supporting v1 and v2 at once
+public interface DomainVerificationProxy {
+
+ String TAG = "DomainVerificationProxy";
+
+ boolean DEBUG_PROXIES = false;
+
+ static <ConnectionType extends DomainVerificationProxyV1.Connection
+ & DomainVerificationProxyV2.Connection> DomainVerificationProxy makeProxy(
+ @Nullable ComponentName componentV1, @Nullable ComponentName componentV2,
+ @NonNull Context context, @NonNull DomainVerificationManagerInternal manager,
+ @NonNull DomainVerificationCollector collector, @NonNull ConnectionType connection) {
+ if (DEBUG_PROXIES) {
+ Slog.d(TAG, "Intent filter verification agent: " + componentV1);
+ Slog.d(TAG, "Domain verification agent: " + componentV2);
+ }
+
+ if (componentV2 != null && componentV1 != null
+ && !Objects.equals(componentV2.getPackageName(), componentV1.getPackageName())) {
+ // Only allow a legacy verifier if it's in the same package as the v2 verifier
+ componentV1 = null;
+ }
+
+ DomainVerificationProxy proxyV1 = null;
+ DomainVerificationProxy proxyV2 = null;
+
+ if (componentV1 != null) {
+ proxyV1 = new DomainVerificationProxyV1(context, manager, collector, connection,
+ componentV1);
+ }
+
+ if (componentV2 != null) {
+ proxyV2 = new DomainVerificationProxyV2(context, connection, componentV2);
+ }
+
+ if (proxyV1 != null && proxyV2 != null) {
+ return new DomainVerificationProxyCombined(proxyV1, proxyV2);
+ }
+
+ if (proxyV1 != null) {
+ return proxyV1;
+ }
+
+ if (proxyV2 != null) {
+ return proxyV2;
+ }
+
+ return new DomainVerificationProxyUnavailable();
+ }
+
+ default void sendBroadcastForPackages(@NonNull Set<String> packageNames) {
+ }
+
+ /**
+ * Runs a message on the caller's Handler as a result of {@link BaseConnection#schedule(int,
+ * Object)}. Abstracts the actual scheduling/running from the manager class. This is also
+ * necessary so that different what codes can be used depending on the verifier proxy on device,
+ * to allow backporting v1. The backport proxy may schedule more or less messages than the v2
+ * proxy.
+ *
+ * @param messageCode One of the values in {@link DomainVerificationMessageCodes}.
+ * @param object Arbitrary object that was originally included.
+ */
+ default boolean runMessage(int messageCode, Object object) {
+ return false;
+ }
+
+ default boolean isCallerVerifier(int callingUid) {
+ return false;
+ }
+
+ @Nullable
+ default ComponentName getComponentName() {
+ return null;
+ }
+
+ interface BaseConnection {
+
+ /**
+ * Schedule something to be run later. The implementation is left up to the caller.
+ *
+ * @param code One of the values in {@link DomainVerificationMessageCodes}.
+ * @param object Arbitrary object to include with the message.
+ */
+ void schedule(int code, @Nullable Object object);
+
+ long getPowerSaveTempWhitelistAppDuration();
+
+ DeviceIdleInternal getDeviceIdleInternal();
+
+ boolean isCallerPackage(int callingUid, @NonNull String packageName);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyCombined.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyCombined.java
new file mode 100644
index 0000000..8571c08
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyCombined.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.proxy;
+
+import android.annotation.NonNull;
+
+import java.util.Set;
+
+class DomainVerificationProxyCombined implements DomainVerificationProxy {
+
+ @NonNull
+ private final DomainVerificationProxy mProxyV1;
+ @NonNull
+ private final DomainVerificationProxy mProxyV2;
+
+ DomainVerificationProxyCombined(@NonNull DomainVerificationProxy proxyV1,
+ @NonNull DomainVerificationProxy proxyV2) {
+ mProxyV1 = proxyV1;
+ mProxyV2 = proxyV2;
+ }
+
+ @Override
+ public void sendBroadcastForPackages(@NonNull Set<String> packageNames) {
+ mProxyV2.sendBroadcastForPackages(packageNames);
+ mProxyV1.sendBroadcastForPackages(packageNames);
+ }
+
+ @Override
+ public boolean runMessage(int messageCode, Object object) {
+ // Both proxies must run, so cannot use a direct ||, which may skip the right hand side
+ boolean resultV2 = mProxyV2.runMessage(messageCode, object);
+ boolean resultV1 = mProxyV1.runMessage(messageCode, object);
+ return resultV2 || resultV1;
+ }
+
+ @Override
+ public boolean isCallerVerifier(int callingUid) {
+ return mProxyV2.isCallerVerifier(callingUid) || mProxyV1.isCallerVerifier(callingUid);
+ }
+}
diff --git a/core/java/android/graphics/fonts/SystemFontState.aidl b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java
similarity index 73%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java
index 19b20f2..bd77983 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package com.android.server.pm.verify.domain.proxy;
-/** @hide */
-parcelable SystemFontState;
\ No newline at end of file
+/** Stub implementation for when the verification agent is unavailable */
+public class DomainVerificationProxyUnavailable implements DomainVerificationProxy {
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
new file mode 100644
index 0000000..eab89e98
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.proxy;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.BroadcastOptions;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.verify.domain.DomainVerificationInfo;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationState;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+public class DomainVerificationProxyV1 implements DomainVerificationProxy {
+
+ private static final String TAG = "DomainVerificationProxyV1";
+
+ private static final boolean DEBUG_BROADCASTS = false;
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final Connection mConnection;
+
+ @NonNull
+ private final ComponentName mVerifierComponent;
+
+ @NonNull
+ private final DomainVerificationManagerInternal mManager;
+
+ @NonNull
+ private final DomainVerificationCollector mCollector;
+
+ @NonNull
+ private final Object mLock = new Object();
+
+ @NonNull
+ @GuardedBy("mLock")
+ private final ArrayMap<Integer, Pair<UUID, String>> mRequests = new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ private int mVerificationToken = 0;
+
+ public DomainVerificationProxyV1(@NonNull Context context,
+ @NonNull DomainVerificationManagerInternal manager,
+ @NonNull DomainVerificationCollector collector, @NonNull Connection connection,
+ @NonNull ComponentName verifierComponent) {
+ mContext = context;
+ mConnection = connection;
+ mVerifierComponent = verifierComponent;
+ mManager = manager;
+ mCollector = collector;
+ }
+
+ public static void queueLegacyVerifyResult(@NonNull Context context,
+ @NonNull DomainVerificationProxyV1.Connection connection, int verificationId,
+ int verificationCode, @Nullable List<String> failedDomains, int callingUid) {
+ context.enforceCallingOrSelfPermission(
+ Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+ "Only the intent filter verification agent can verify applications");
+
+ connection.schedule(DomainVerificationMessageCodes.LEGACY_ON_INTENT_FILTER_VERIFIED,
+ new Response(callingUid, verificationId, verificationCode, failedDomains));
+ }
+
+ @Override
+ public void sendBroadcastForPackages(@NonNull Set<String> packageNames) {
+ synchronized (mLock) {
+ int size = mRequests.size();
+ for (int index = size - 1; index >= 0; index--) {
+ Pair<UUID, String> pair = mRequests.valueAt(index);
+ if (packageNames.contains(pair.second)) {
+ mRequests.removeAt(index);
+ }
+ }
+ }
+ mConnection.schedule(DomainVerificationMessageCodes.LEGACY_SEND_REQUEST, packageNames);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public boolean runMessage(int messageCode, Object object) {
+ switch (messageCode) {
+ case DomainVerificationMessageCodes.LEGACY_SEND_REQUEST:
+ @SuppressWarnings("unchecked") Set<String> packageNames = (Set<String>) object;
+ if (DEBUG_BROADCASTS) {
+ Slog.d(TAG, "Requesting domain verification for " + packageNames);
+ }
+
+ ArrayMap<Integer, Pair<UUID, String>> newRequests = new ArrayMap<>(
+ packageNames.size());
+ synchronized (mLock) {
+ for (String packageName : packageNames) {
+ UUID domainSetId = mManager.getDomainVerificationInfoId(packageName);
+ if (domainSetId == null) {
+ continue;
+ }
+
+ newRequests.put(mVerificationToken++,
+ Pair.create(domainSetId, packageName));
+ }
+ mRequests.putAll(newRequests);
+ }
+
+ sendBroadcasts(newRequests);
+ return true;
+ case DomainVerificationMessageCodes.LEGACY_ON_INTENT_FILTER_VERIFIED:
+ Response response = (Response) object;
+
+ Pair<UUID, String> pair = mRequests.get(response.verificationId);
+ if (pair == null) {
+ return true;
+ }
+
+ UUID domainSetId = pair.first;
+ String packageName = pair.second;
+ DomainVerificationInfo set;
+ try {
+ set = mManager.getDomainVerificationInfo(packageName);
+ } catch (PackageManager.NameNotFoundException ignored) {
+ return true;
+ }
+
+ if (!Objects.equals(domainSetId, set.getIdentifier())) {
+ return true;
+ }
+
+ Set<String> successfulDomains = new ArraySet<>(set.getHostToStateMap().keySet());
+ successfulDomains.removeAll(response.failedDomains);
+
+ int callingUid = response.callingUid;
+ try {
+ mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ successfulDomains, DomainVerificationState.STATE_SUCCESS);
+ } catch (DomainVerificationManager.InvalidDomainSetException
+ | PackageManager.NameNotFoundException ignored) {
+ }
+ try {
+ mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ new ArraySet<>(response.failedDomains),
+ DomainVerificationState.STATE_LEGACY_FAILURE);
+ } catch (DomainVerificationManager.InvalidDomainSetException
+ | PackageManager.NameNotFoundException ignored) {
+ }
+
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isCallerVerifier(int callingUid) {
+ return mConnection.isCallerPackage(callingUid, mVerifierComponent.getPackageName());
+ }
+
+ @SuppressWarnings("deprecation")
+ private void sendBroadcasts(@NonNull ArrayMap<Integer, Pair<UUID, String>> verifications) {
+ final long allowListTimeout = mConnection.getPowerSaveTempWhitelistAppDuration();
+ mConnection.getDeviceIdleInternal().addPowerSaveTempWhitelistApp(Process.myUid(),
+ mVerifierComponent.getPackageName(), allowListTimeout,
+ UserHandle.USER_SYSTEM, true, "domain verification agent");
+
+ int size = verifications.size();
+ for (int index = 0; index < size; index++) {
+ int verificationId = verifications.keyAt(index);
+ String packageName = verifications.valueAt(index).second;
+ AndroidPackage pkg = mConnection.getPackage(packageName);
+
+ String hostsString = buildHostsString(pkg);
+
+ Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION)
+ .setComponent(mVerifierComponent)
+ .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
+ verificationId)
+ .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
+ IntentFilter.SCHEME_HTTPS)
+ .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
+ hostsString)
+ .putExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
+ packageName)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppWhitelistDuration(allowListTimeout);
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null, options.toBundle());
+ }
+ }
+
+ @NonNull
+ private String buildHostsString(@NonNull AndroidPackage pkg) {
+ // The collector itself handles the v1 vs v2 behavior, which is based on targetSdkVersion,
+ // not the version of the verification agent on device.
+ ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
+ return TextUtils.join(" ", domains);
+ }
+
+ private static class Response {
+ public final int callingUid;
+ public final int verificationId;
+ public final int verificationCode;
+ @NonNull
+ public final List<String> failedDomains;
+
+ private Response(int callingUid, int verificationId, int verificationCode,
+ @Nullable List<String> failedDomains) {
+ this.callingUid = callingUid;
+ this.verificationId = verificationId;
+ this.verificationCode = verificationCode;
+ this.failedDomains = failedDomains == null ? Collections.emptyList() : failedDomains;
+ }
+ }
+
+ public interface Connection extends BaseConnection {
+
+ @Nullable
+ AndroidPackage getPackage(@NonNull String packageName);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
new file mode 100644
index 0000000..9fcbce2
--- /dev/null
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.verify.domain.proxy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.BroadcastOptions;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationRequest;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
+
+import java.util.Set;
+
+public class DomainVerificationProxyV2 implements DomainVerificationProxy {
+
+ private static final String TAG = "DomainVerificationProxyV2";
+
+ private static final boolean DEBUG_BROADCASTS = true;
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final Connection mConnection;
+
+ @NonNull
+ private final ComponentName mVerifierComponent;
+
+ public DomainVerificationProxyV2(@NonNull Context context, @NonNull Connection connection,
+ @NonNull ComponentName verifierComponent) {
+ mContext = context;
+ mConnection = connection;
+ mVerifierComponent = verifierComponent;
+ }
+
+ @Override
+ public void sendBroadcastForPackages(@NonNull Set<String> packageNames) {
+ mConnection.schedule(com.android.server.pm.verify.domain.DomainVerificationMessageCodes.SEND_REQUEST, packageNames);
+ }
+
+ @Override
+ public boolean runMessage(int messageCode, Object object) {
+ switch (messageCode) {
+ case DomainVerificationMessageCodes.SEND_REQUEST:
+ @SuppressWarnings("unchecked") Set<String> packageNames = (Set<String>) object;
+ DomainVerificationRequest request = new DomainVerificationRequest(packageNames);
+
+ final long allowListTimeout = mConnection.getPowerSaveTempWhitelistAppDuration();
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppWhitelistDuration(allowListTimeout);
+
+ mConnection.getDeviceIdleInternal().addPowerSaveTempWhitelistApp(Process.myUid(),
+ mVerifierComponent.getPackageName(), allowListTimeout,
+ UserHandle.USER_SYSTEM, true, "domain verification agent");
+
+ Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION)
+ .setComponent(mVerifierComponent)
+ .putExtra(DomainVerificationManager.EXTRA_VERIFICATION_REQUEST, request)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+ if (DEBUG_BROADCASTS) {
+ Slog.d(TAG, "Requesting domain verification for " + packageNames);
+ }
+
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null, options.toBundle());
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isCallerVerifier(int callingUid) {
+ return mConnection.isCallerPackage(callingUid, mVerifierComponent.getPackageName());
+ }
+
+ @Nullable
+ @Override
+ public ComponentName getComponentName() {
+ return mVerifierComponent;
+ }
+
+ public interface Connection extends BaseConnection {
+ }
+}
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index e901f66f..ac358db 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -34,10 +34,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
import com.android.server.policy.devicestate.config.Conditions;
-import com.android.server.policy.devicestate.config.DeviceState;
import com.android.server.policy.devicestate.config.DeviceStateConfig;
import com.android.server.policy.devicestate.config.LidSwitchCondition;
import com.android.server.policy.devicestate.config.NumericRange;
@@ -54,6 +55,7 @@
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.BooleanSupplier;
@@ -82,7 +84,7 @@
private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
@VisibleForTesting
- static final int DEFAULT_DEVICE_STATE = 0;
+ static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
@@ -116,31 +118,38 @@
@VisibleForTesting
static DeviceStateProviderImpl createFromConfig(@NonNull Context context,
@Nullable ReadableConfig readableConfig) {
- SparseArray<Conditions> conditionsForState = new SparseArray<>();
+ List<DeviceState> deviceStateList = new ArrayList<>();
+ List<Conditions> conditionsList = new ArrayList<>();
+
if (readableConfig != null) {
DeviceStateConfig config = parseConfig(readableConfig);
if (config != null) {
- for (DeviceState stateConfig : config.getDeviceState()) {
- int state = stateConfig.getIdentifier().intValue();
- Conditions conditions = stateConfig.getConditions();
- conditionsForState.put(state, conditions);
+ for (com.android.server.policy.devicestate.config.DeviceState stateConfig :
+ config.getDeviceState()) {
+ final int state = stateConfig.getIdentifier().intValue();
+ final String name = stateConfig.getName() == null ? "" : stateConfig.getName();
+ deviceStateList.add(new DeviceState(state, name));
+
+ final Conditions condition = stateConfig.getConditions();
+ conditionsList.add(condition);
}
}
}
- if (conditionsForState.size() == 0) {
- conditionsForState.put(DEFAULT_DEVICE_STATE, null);
+ if (deviceStateList.size() == 0) {
+ deviceStateList.add(DEFAULT_DEVICE_STATE);
+ conditionsList.add(null);
}
- return new DeviceStateProviderImpl(context, conditionsForState);
+ return new DeviceStateProviderImpl(context, deviceStateList, conditionsList);
}
// Lock for internal state.
private final Object mLock = new Object();
private final Context mContext;
- // List of supported states in ascending order.
- private final int[] mOrderedStates;
- // Map of state to a boolean supplier that returns true when all required conditions are met for
- // the device to be in the state.
+ // List of supported states in ascending order based on their identifier.
+ private final DeviceState[] mOrderedStates;
+ // Map of state identifier to a boolean supplier that returns true when all required conditions
+ // are met for the device to be in the state.
private final SparseArray<BooleanSupplier> mStateConditions;
@Nullable
@@ -155,12 +164,16 @@
private final Map<Sensor, SensorEvent> mLatestSensorEvent = new ArrayMap<>();
private DeviceStateProviderImpl(@NonNull Context context,
- @NonNull SparseArray<Conditions> conditionsForState) {
+ @NonNull List<DeviceState> deviceStates,
+ @NonNull List<Conditions> stateConditions) {
+ Preconditions.checkArgument(deviceStates.size() == stateConditions.size(),
+ "Number of device states must be equal to the number of device state conditions.");
+
mContext = context;
- mOrderedStates = new int[conditionsForState.size()];
- for (int i = 0; i < conditionsForState.size(); i++) {
- mOrderedStates[i] = conditionsForState.keyAt(i);
- }
+
+ DeviceState[] orderedStates = deviceStates.toArray(new DeviceState[deviceStates.size()]);
+ Arrays.sort(orderedStates, Comparator.comparingInt(DeviceState::getIdentifier));
+ mOrderedStates = orderedStates;
// Whether or not this instance should register to receive lid switch notifications from
// InputManagerInternal. If there are no device state conditions that are based on the lid
@@ -171,9 +184,9 @@
final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>();
mStateConditions = new SparseArray<>();
- for (int i = 0; i < mOrderedStates.length; i++) {
- int state = mOrderedStates[i];
- Conditions conditions = conditionsForState.get(state);
+ for (int i = 0; i < stateConditions.size(); i++) {
+ final int state = deviceStates.get(i).getIdentifier();
+ final Conditions conditions = stateConditions.get(i);
if (conditions == null) {
mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
continue;
@@ -261,7 +274,7 @@
/** Notifies the listener that the set of supported device states has changed. */
private void notifySupportedStatesChanged() {
- int[] supportedStates;
+ DeviceState[] supportedStates;
synchronized (mLock) {
if (mListener == null) {
return;
@@ -281,9 +294,9 @@
return;
}
- int newState = mOrderedStates[0];
+ int newState = mOrderedStates[0].getIdentifier();
for (int i = 0; i < mOrderedStates.length; i++) {
- int state = mOrderedStates[i];
+ int state = mOrderedStates[i].getIdentifier();
if (mStateConditions.get(state).getAsBoolean()) {
newState = state;
break;
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index ff51237..82fc22c 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -74,8 +74,8 @@
mHandler = handler;
DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class);
- deviceStateManager.registerDeviceStateListener(new DeviceStateListener(context),
- new HandlerExecutor(handler));
+ deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler),
+ new DeviceStateListener(context));
}
void finishedGoingToSleep() {
@@ -209,16 +209,23 @@
* resource.
*/
private class DeviceStateListener implements DeviceStateManager.DeviceStateListener {
- private final int mFoldedDeviceState;
+ private final int[] mFoldedDeviceStates;
DeviceStateListener(Context context) {
- mFoldedDeviceState = context.getResources().getInteger(
- com.android.internal.R.integer.config_foldedDeviceState);
+ mFoldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
}
@Override
public void onDeviceStateChanged(int deviceState) {
- setDeviceFolded(deviceState == mFoldedDeviceState);
+ boolean folded = false;
+ for (int i = 0; i < mFoldedDeviceStates.length; i++) {
+ if (deviceState == mFoldedDeviceStates[i]) {
+ folded = true;
+ break;
+ }
+ }
+ setDeviceFolded(folded);
}
}
}
diff --git a/services/core/java/com/android/server/policy/IconUtilities.java b/services/core/java/com/android/server/policy/IconUtilities.java
deleted file mode 100644
index 884d7d4..0000000
--- a/services/core/java/com/android/server/policy/IconUtilities.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
-import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.TableMaskFilter;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.content.res.Resources;
-import android.content.Context;
-
-/**
- * Various utilities shared amongst the Launcher's classes.
- */
-public final class IconUtilities {
-
- private int mIconWidth = -1;
- private int mIconHeight = -1;
- private int mIconTextureWidth = -1;
- private int mIconTextureHeight = -1;
-
- private final Rect mOldBounds = new Rect();
- private final Canvas mCanvas = new Canvas();
- private final DisplayMetrics mDisplayMetrics;
-
- private ColorFilter mDisabledColorFilter;
-
- public IconUtilities(Context context) {
- final Resources resources = context.getResources();
- DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
- final float density = metrics.density;
- final float blurPx = 5 * density;
-
- mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
- mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
- mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
- Paint.FILTER_BITMAP_FLAG));
- }
-
- /**
- * Returns a bitmap suitable for the all apps view. The bitmap will be a power
- * of two sized ARGB_8888 bitmap that can be used as a gl texture.
- */
- public Bitmap createIconBitmap(Drawable icon) {
- int width = mIconWidth;
- int height = mIconHeight;
-
- if (icon instanceof PaintDrawable) {
- PaintDrawable painter = (PaintDrawable) icon;
- painter.setIntrinsicWidth(width);
- painter.setIntrinsicHeight(height);
- } else if (icon instanceof BitmapDrawable) {
- // Ensure the bitmap has a density.
- BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
- Bitmap bitmap = bitmapDrawable.getBitmap();
- if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
- bitmapDrawable.setTargetDensity(mDisplayMetrics);
- }
- }
- int sourceWidth = icon.getIntrinsicWidth();
- int sourceHeight = icon.getIntrinsicHeight();
-
- if (sourceWidth > 0 && sourceHeight > 0) {
- // There are intrinsic sizes.
- if (width < sourceWidth || height < sourceHeight) {
- // It's too big, scale it down.
- final float ratio = (float) sourceWidth / sourceHeight;
- if (sourceWidth > sourceHeight) {
- height = (int) (width / ratio);
- } else if (sourceHeight > sourceWidth) {
- width = (int) (height * ratio);
- }
- } else if (sourceWidth < width && sourceHeight < height) {
- // It's small, use the size they gave us.
- width = sourceWidth;
- height = sourceHeight;
- }
- }
-
- // no intrinsic size --> use default size
- int textureWidth = mIconTextureWidth;
- int textureHeight = mIconTextureHeight;
-
- final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
- Bitmap.Config.ARGB_8888);
- final Canvas canvas = mCanvas;
- canvas.setBitmap(bitmap);
-
- final int left = (textureWidth-width) / 2;
- final int top = (textureHeight-height) / 2;
-
- mOldBounds.set(icon.getBounds());
- icon.setBounds(left, top, left+width, top+height);
- icon.draw(canvas);
- icon.setBounds(mOldBounds);
-
- return bitmap;
- }
-
- public ColorFilter getDisabledColorFilter() {
- if (mDisabledColorFilter != null) {
- return mDisabledColorFilter;
- }
- ColorMatrix brightnessMatrix = new ColorMatrix();
- float brightnessF = 0.5f;
- int brightnessI = (int) (255 * brightnessF);
- // Brightness: C-new = C-old*(1-amount) + amount
- float scale = 1f - brightnessF;
- float[] mat = brightnessMatrix.getArray();
- mat[0] = scale;
- mat[6] = scale;
- mat[12] = scale;
- mat[4] = brightnessI;
- mat[9] = brightnessI;
- mat[14] = brightnessI;
-
- ColorMatrix filterMatrix = new ColorMatrix();
- filterMatrix.setSaturation(0);
- filterMatrix.preConcat(brightnessMatrix);
-
- mDisabledColorFilter = new ColorMatrixColorFilter(filterMatrix);
- return mDisabledColorFilter;
- }
-}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a407e8e..bc81961 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,6 +222,7 @@
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import com.android.server.wm.WindowManagerService;
import java.io.File;
import java.io.FileNotFoundException;
@@ -1913,6 +1914,7 @@
handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */);
}
});
+
mKeyguardDelegate = new KeyguardServiceDelegate(mContext,
new StateCallback() {
@Override
@@ -3136,7 +3138,7 @@
private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
final int res = applyKeyguardOcclusionChange();
if (res != 0) return res;
- if (keyguardGoingAway) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && keyguardGoingAway) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e9d6440..db33e75 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -417,8 +417,7 @@
WindowManagerFuncs windowManagerFuncs);
/**
- * Check permissions when adding a window or a window token from
- * {@link android.app.WindowContext}.
+ * Check permissions when adding a window.
*
* @param type The window type
* @param isRoundedCornerOverlay {@code true} to indicate the adding window is
@@ -431,7 +430,6 @@
* else an error code, usually
* {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add.
*
- * @see IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, String)
* @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
*/
int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
@@ -1158,7 +1156,7 @@
* @param startTime the start time of the animation in uptime milliseconds
* @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
- public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+ void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
/**
* Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index c2a1c79..a95628f 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@
import com.android.internal.policy.IKeyguardService;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
@@ -398,7 +399,7 @@
}
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
- if (mKeyguardService != null) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index eb9df75..9a91848 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -120,7 +120,7 @@
* @return List of EnergyMeasurement objects containing energy measurements for all
* available energy meters.
*/
- android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds);
+ android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds);
/**
* Returns boolean indicating if connection to power stats HAL was established.
@@ -235,13 +235,13 @@
}
@Override
- public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+ public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
android.hardware.power.stats.EnergyMeasurement[] energyMeasurementHAL = null;
if (sVintfPowerStats != null) {
try {
energyMeasurementHAL =
- sVintfPowerStats.get().readEnergyMeters(channelIds);
+ sVintfPowerStats.get().readEnergyMeter(channelIds);
} catch (RemoteException e) {
if (DEBUG) Slog.d(TAG, "Failed to get energy measurements from PowerStats HAL");
}
@@ -311,7 +311,7 @@
}
@Override
- public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+ public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
return nativeReadEnergyMeters(channelIds);
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 78a227e..37fc5a0 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -53,8 +53,9 @@
public final class PowerStatsLogger extends Handler {
private static final String TAG = PowerStatsLogger.class.getSimpleName();
private static final boolean DEBUG = false;
- protected static final int MSG_LOG_TO_DATA_STORAGE_TIMER = 0;
- protected static final int MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP = 1;
+ protected static final int MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP = 0;
+ protected static final int MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY = 1;
+ protected static final int MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY = 2;
private final PowerStatsDataStorage mPowerStatsMeterStorage;
private final PowerStatsDataStorage mPowerStatsModelStorage;
@@ -64,22 +65,33 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_LOG_TO_DATA_STORAGE_TIMER:
- if (DEBUG) Slog.d(TAG, "Logging to data storage on timer");
+ case MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY:
+ if (DEBUG) Slog.d(TAG, "Logging to data storage on high frequency timer");
// Log power meter data.
EnergyMeasurement[] energyMeasurements =
- mPowerStatsHALWrapper.readEnergyMeters(new int[0]);
+ mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
mPowerStatsMeterStorage.write(
EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
- // Log power model data.
- EnergyConsumerResult[] energyConsumerResults =
+ // Log power model data without attribution data.
+ EnergyConsumerResult[] ecrNoAttribution =
mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
mPowerStatsModelStorage.write(
- EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults));
- if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults);
+ EnergyConsumerResultUtils.getProtoBytes(ecrNoAttribution, false));
+ if (DEBUG) EnergyConsumerResultUtils.print(ecrNoAttribution);
+ break;
+
+ case MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY:
+ if (DEBUG) Slog.d(TAG, "Logging to data storage on low frequency timer");
+
+ // Log power model data with attribution data.
+ EnergyConsumerResult[] ecrAttribution =
+ mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
+ mPowerStatsModelStorage.write(
+ EnergyConsumerResultUtils.getProtoBytes(ecrAttribution, true));
+ if (DEBUG) EnergyConsumerResultUtils.print(ecrAttribution);
break;
case MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP:
@@ -163,7 +175,7 @@
// deserialize, then re-serialize. This is computationally inefficient.
EnergyConsumerResult[] energyConsumerResult =
EnergyConsumerResultUtils.unpackProtoMessage(data);
- EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos);
+ EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos, true);
if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResult);
} catch (IOException e) {
Slog.e(TAG, "Failed to write energy model data to incident report.");
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 5fe5db6..ea41980 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -21,7 +21,9 @@
import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -245,10 +247,50 @@
future, energyConsumerIds));
return future;
}
+
+ @Override
+ public PowerEntity[] getPowerEntityInfo() {
+ return getPowerStatsHal().getPowerEntityInfo();
+ }
+
+ @Override
+ public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+ int[] powerEntityIds) {
+ final CompletableFuture<StateResidencyResult[]> future = new CompletableFuture<>();
+ mHandler.sendMessage(
+ PooledLambda.obtainMessage(PowerStatsService.this::getStateResidencyAsync,
+ future, powerEntityIds));
+ return future;
+ }
+
+ @Override
+ public Channel[] getEnergyMeterInfo() {
+ return getPowerStatsHal().getEnergyMeterInfo();
+ }
+
+ @Override
+ public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+ int[] channelIds) {
+ final CompletableFuture<EnergyMeasurement[]> future = new CompletableFuture<>();
+ mHandler.sendMessage(
+ PooledLambda.obtainMessage(PowerStatsService.this::readEnergyMeterAsync,
+ future, channelIds));
+ return future;
+ }
}
private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future,
int[] energyConsumerIds) {
future.complete(getPowerStatsHal().getEnergyConsumed(energyConsumerIds));
}
+
+ private void getStateResidencyAsync(CompletableFuture<StateResidencyResult[]> future,
+ int[] powerEntityIds) {
+ future.complete(getPowerStatsHal().getStateResidency(powerEntityIds));
+ }
+
+ private void readEnergyMeterAsync(CompletableFuture<EnergyMeasurement[]> future,
+ int[] channelIds) {
+ future.complete(getPowerStatsHal().readEnergyMeter(channelIds));
+ }
}
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index 766cf9c..bd003d3 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -18,6 +18,7 @@
import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.PowerEntity;
@@ -433,23 +434,40 @@
}
static class EnergyConsumerResultUtils {
- public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult) {
+ public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult,
+ boolean includeAttribution) {
ProtoOutputStream pos = new ProtoOutputStream();
- packProtoMessage(energyConsumerResult, pos);
+ packProtoMessage(energyConsumerResult, pos, includeAttribution);
return pos.getBytes();
}
public static void packProtoMessage(EnergyConsumerResult[] energyConsumerResult,
- ProtoOutputStream pos) {
+ ProtoOutputStream pos, boolean includeAttribution) {
if (energyConsumerResult == null) return;
for (int i = 0; i < energyConsumerResult.length; i++) {
- long token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+ long ecrToken = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
pos.write(EnergyConsumerResultProto.ID, energyConsumerResult[i].id);
pos.write(EnergyConsumerResultProto.TIMESTAMP_MS,
energyConsumerResult[i].timestampMs);
pos.write(EnergyConsumerResultProto.ENERGY_UWS, energyConsumerResult[i].energyUWs);
- pos.end(token);
+
+ if (includeAttribution) {
+ final int attributionLength = energyConsumerResult[i].attribution.length;
+
+ for (int j = 0; j < attributionLength; j++) {
+ final EnergyConsumerAttribution energyConsumerAttribution =
+ energyConsumerResult[i].attribution[j];
+ final long ecaToken = pos.start(EnergyConsumerResultProto.ATTRIBUTION);
+ pos.write(EnergyConsumerAttributionProto.UID,
+ energyConsumerAttribution.uid);
+ pos.write(EnergyConsumerAttributionProto.ENERGY_UWS,
+ energyConsumerAttribution.energyUWs);
+ pos.end(ecaToken);
+ }
+ }
+
+ pos.end(ecrToken);
}
}
@@ -480,9 +498,45 @@
}
}
+ private static EnergyConsumerAttribution unpackEnergyConsumerAttributionProto(
+ ProtoInputStream pis) throws IOException {
+ final EnergyConsumerAttribution energyConsumerAttribution =
+ new EnergyConsumerAttribution();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) EnergyConsumerAttributionProto.UID:
+ energyConsumerAttribution.uid =
+ pis.readInt(EnergyConsumerAttributionProto.UID);
+ break;
+
+ case (int) EnergyConsumerAttributionProto.ENERGY_UWS:
+ energyConsumerAttribution.energyUWs =
+ pis.readLong(EnergyConsumerAttributionProto.ENERGY_UWS);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return energyConsumerAttribution;
+
+ default:
+ Slog.e(TAG, "Unhandled field in EnergyConsumerAttributionProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in EnergyConsumerAttributionProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
private static EnergyConsumerResult unpackEnergyConsumerResultProto(ProtoInputStream pis)
throws IOException {
EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+ final List<EnergyConsumerAttribution> energyConsumerAttributionList =
+ new ArrayList<EnergyConsumerAttribution>();
while (true) {
try {
@@ -501,7 +555,18 @@
pis.readLong(EnergyConsumerResultProto.ENERGY_UWS);
break;
+ case (int) EnergyConsumerResultProto.ATTRIBUTION:
+ final long token = pis.start(EnergyConsumerResultProto.ATTRIBUTION);
+ energyConsumerAttributionList.add(
+ unpackEnergyConsumerAttributionProto(pis));
+ pis.end(token);
+ break;
+
case ProtoInputStream.NO_MORE_FIELDS:
+ energyConsumerResult.attribution =
+ energyConsumerAttributionList.toArray(
+ new EnergyConsumerAttribution[
+ energyConsumerAttributionList.size()]);
return energyConsumerResult;
default:
@@ -520,9 +585,16 @@
if (energyConsumerResult == null) return;
for (int i = 0; i < energyConsumerResult.length; i++) {
- Slog.d(TAG, "EnergyConsumerId: " + energyConsumerResult[i].id
- + ", Timestamp (ms): " + energyConsumerResult[i].timestampMs
- + ", Energy (uWs): " + energyConsumerResult[i].energyUWs);
+ final EnergyConsumerResult result = energyConsumerResult[i];
+ Slog.d(TAG, "EnergyConsumerId: " + result.id
+ + ", Timestamp (ms): " + result.timestampMs
+ + ", Energy (uWs): " + result.energyUWs);
+ final int attributionLength = result.attribution.length;
+ for (int j = 0; j < attributionLength; j++) {
+ final EnergyConsumerAttribution attribution = result.attribution[j];
+ Slog.d(TAG, " UID: " + attribution.uid
+ + " Energy (uWs): " + attribution.energyUWs);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
index f8b9601..7c6999a 100644
--- a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
+++ b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
@@ -70,7 +70,7 @@
}
private int pullOnDevicePowerMeasurement(int atomTag, List<StatsEvent> events) {
- EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters(new int[0]);
+ EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
if (energyMeasurements == null) {
return StatsManager.PULL_SKIP;
}
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
index 7cba00f..f8a4135 100644
--- a/services/core/java/com/android/server/powerstats/TimerTrigger.java
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -29,18 +29,30 @@
private static final String TAG = TimerTrigger.class.getSimpleName();
private static final boolean DEBUG = false;
// TODO(b/166689029): Make configurable through global settings.
- private static final long LOG_PERIOD_MS = 120 * 1000;
+ private static final long LOG_PERIOD_MS_LOW_FREQUENCY = 60 * 60 * 1000; // 1 hour
+ private static final long LOG_PERIOD_MS_HIGH_FREQUENCY = 2 * 60 * 1000; // 2 minutes
private final Handler mHandler;
- private Runnable mLogData = new Runnable() {
+ private Runnable mLogDataLowFrequency = new Runnable() {
@Override
public void run() {
// Do not wake the device for these messages. Opportunistically log rail data every
- // LOG_PERIOD_MS.
- mHandler.postDelayed(mLogData, LOG_PERIOD_MS);
- if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data");
- logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER);
+ // LOG_PERIOD_MS_LOW_FREQUENCY.
+ mHandler.postDelayed(mLogDataLowFrequency, LOG_PERIOD_MS_LOW_FREQUENCY);
+ if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data low frequency");
+ logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+ }
+ };
+
+ private Runnable mLogDataHighFrequency = new Runnable() {
+ @Override
+ public void run() {
+ // Do not wake the device for these messages. Opportunistically log rail data every
+ // LOG_PERIOD_MS_HIGH_FREQUENCY.
+ mHandler.postDelayed(mLogDataHighFrequency, LOG_PERIOD_MS_HIGH_FREQUENCY);
+ if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data high frequency");
+ logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
}
};
@@ -50,7 +62,8 @@
mHandler = mContext.getMainThreadHandler();
if (triggerEnabled) {
- mLogData.run();
+ mLogDataLowFrequency.run();
+ mLogDataHighFrequency.run();
}
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index 57e39b6..b995b19 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -45,6 +45,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.ServiceConnector;
+import java.lang.ref.WeakReference;
+
/** Manages the connection to the remote rotation resolver service. */
class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResolverService> {
@@ -128,13 +130,20 @@
mProposedRotation = proposedRotation;
mCurrentRotation = currentRotation;
mPackageName = packageName;
- mIRotationResolverCallback = new RotationResolverCallback();
+ mIRotationResolverCallback = new RotationResolverCallback(this);
mCancellationSignalInternal = cancellationSignal;
mRequestStartTimeMillis = SystemClock.elapsedRealtime();
}
void cancelInternal() {
+ synchronized (mLock) {
+ if (mIsFulfilled) {
+ Slog.v(TAG, "Trying to cancel the request that has been already fulfilled.");
+ return;
+ }
+ mIsFulfilled = true;
+ }
Handler.getMain().post(() -> {
synchronized (mLock) {
try {
@@ -147,9 +156,6 @@
}
}
});
- synchronized (mLock) {
- mIsFulfilled = true;
- }
mCallbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
}
@@ -160,44 +166,53 @@
ipw.decreaseIndent();
}
- private class RotationResolverCallback extends IRotationResolverCallback.Stub {
+ private static class RotationResolverCallback extends IRotationResolverCallback.Stub {
+ private WeakReference<RotationRequest> mRequestWeakReference;
+
+ RotationResolverCallback(RotationRequest request) {
+ this.mRequestWeakReference = new WeakReference<>(request);
+ }
+
@Override
public void onSuccess(int rotation) {
- synchronized (mLock) {
- if (mIsFulfilled) {
+ final RotationRequest request = mRequestWeakReference.get();
+ synchronized (request.mLock) {
+ if (request.mIsFulfilled) {
Slog.w(TAG, "Callback received after the rotation request is fulfilled.");
return;
}
- mIsFulfilled = true;
- mCallbackInternal.onSuccess(rotation);
+ request.mIsFulfilled = true;
+ request.mCallbackInternal.onSuccess(rotation);
final long timeToCalculate =
- SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
- logRotationStats(mProposedRotation, mCurrentRotation, rotation,
+ SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
+ logRotationStats(request.mProposedRotation, request.mCurrentRotation, rotation,
timeToCalculate);
}
}
@Override
public void onFailure(int error) {
- synchronized (mLock) {
- if (mIsFulfilled) {
+ final RotationRequest request = mRequestWeakReference.get();
+ synchronized (request.mLock) {
+ if (request.mIsFulfilled) {
Slog.w(TAG, "Callback received after the rotation request is fulfilled.");
return;
}
- mIsFulfilled = true;
- mCallbackInternal.onFailure(error);
+ request.mIsFulfilled = true;
+ request.mCallbackInternal.onFailure(error);
final long timeToCalculate =
- SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
- logRotationStats(mProposedRotation, mCurrentRotation, RESOLUTION_FAILURE,
- timeToCalculate);
+ SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
+ logRotationStats(request.mProposedRotation, request.mCurrentRotation,
+ RESOLUTION_FAILURE, timeToCalculate);
}
}
@Override
public void onCancellable(@NonNull ICancellationSignal cancellation) {
- synchronized (mLock) {
- mCancellation = cancellation;
- if (mCancellationSignalInternal.isCanceled()) {
+ final RotationRequest request = mRequestWeakReference.get();
+ synchronized (request.mLock) {
+ request.mCancellation = cancellation;
+ if (request.mCancellationSignalInternal.isCanceled()) {
// Dispatch the cancellation signal if the client has cancelled the request.
try {
cancellation.cancel();
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 3dbc32a..6f7c016 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -122,14 +122,9 @@
}
});
- if (mRemoteService != null) {
- mRemoteService.resolveRotationLocked(mCurrentRequest);
- mCurrentRequest.mIsDispatched = true;
- } else {
- Slog.w(TAG, "Remote service is not available at this moment.");
- callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
- cancelLocked();
- }
+
+ mRemoteService.resolveRotationLocked(mCurrentRequest);
+ mCurrentRequest.mIsDispatched = true;
}
@GuardedBy("mLock")
@@ -198,15 +193,6 @@
if (mCurrentRequest == null) {
return;
}
-
- if (mCurrentRequest.mIsFulfilled) {
- if (isVerbose()) {
- Slog.d(TAG, "Trying to cancel the request that has been already fulfilled.");
- }
- mCurrentRequest = null;
- return;
- }
-
mCurrentRequest.cancelInternal();
mCurrentRequest = null;
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 4a37e79..03d7664 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -191,7 +191,7 @@
TAG);
final RotationResolverManagerPerUserService service = getServiceForUserLocked(
UserHandle.getCallingUserId());
- new RotationResolverShellCommend(service).exec(this, in, out, err, args, callback,
+ new RotationResolverShellCommand(service).exec(this, in, out, err, args, callback,
resultReceiver);
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
similarity index 95%
rename from services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java
rename to services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
index 0a873892..54a9edb 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
@@ -26,13 +26,13 @@
import java.io.PrintWriter;
-final class RotationResolverShellCommend extends ShellCommand {
+final class RotationResolverShellCommand extends ShellCommand {
private static final int INITIAL_RESULT_CODE = -1;
@NonNull
private final RotationResolverManagerPerUserService mService;
- RotationResolverShellCommend(@NonNull RotationResolverManagerPerUserService service) {
+ RotationResolverShellCommand(@NonNull RotationResolverManagerPerUserService service) {
mService = service;
}
diff --git a/services/core/java/com/android/server/timedetector/DeviceConfig.java b/services/core/java/com/android/server/timedetector/DeviceConfig.java
new file mode 100644
index 0000000..7b9ad0f
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/DeviceConfig.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timedetector;
+
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+
+import java.time.Duration;
+import java.util.concurrent.Executor;
+
+/**
+ * A helper class for reading / monitoring the {@link
+ * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace for server-configured flags.
+ */
+public final class DeviceConfig {
+
+ /**
+ * An annotation used to indicate when a {@link
+ * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} key is required.
+ *
+ * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider
+ * also shares the {@link android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the
+ * prefix "geotz_" on all of its key strings.
+ */
+ @StringDef(prefix = "KEY_", value = {
+ KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED,
+ KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT,
+ KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+ })
+ @interface DeviceConfigKey {}
+
+ /**
+ * The key to force location time zone detection on for a device. Only intended for use during
+ * release testing with droidfooders. The user can still disable the feature by turning off the
+ * master location switch, or disabling automatic time zone detection.
+ */
+ @DeviceConfigKey
+ public static final String KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED =
+ "force_location_time_zone_detection_enabled";
+
+ /**
+ * The key for the default value used to determine whether location time zone detection is
+ * enabled when the user hasn't explicitly set it yet.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT =
+ "location_time_zone_detection_enabled_default";
+
+ /**
+ * The key for the minimum delay after location time zone detection has been enabled before the
+ * location time zone manager can report it is uncertain about the time zone.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
+ "location_time_zone_detection_uncertainty_delay_millis";
+
+ /**
+ * The key for the timeout passed to a location time zone provider that tells it how long it has
+ * to provide an explicit first suggestion without being declared uncertain.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
+ "ltpz_init_timeout_millis";
+
+ /**
+ * The key for the extra time added to {@link
+ * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
+ * manager before the location time zone provider will actually be declared uncertain.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
+ "ltpz_init_timeout_fuzz_millis";
+
+ /** Creates an instance. */
+ public DeviceConfig() {}
+
+ /** Adds a listener for the system_time namespace. */
+ public void addListener(
+ @NonNull Executor handlerExecutor, @NonNull Runnable listener) {
+ android.provider.DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_SYSTEM_TIME,
+ handlerExecutor,
+ properties -> listener.run());
+ }
+
+ /**
+ * Returns a boolean value from {@link android.provider.DeviceConfig} from the system_time
+ * namespace, or {@code defaultValue} if there is no explicit value set.
+ */
+ public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) {
+ return android.provider.DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
+ }
+
+ /**
+ * Returns a positive duration from {@link android.provider.DeviceConfig} from the system_time
+ * namespace, or {@code defaultValue} if there is no explicit value set.
+ */
+ @Nullable
+ public Duration getDurationFromMillis(
+ @DeviceConfigKey String key, @Nullable Duration defaultValue) {
+ long deviceConfigValue =
+ android.provider.DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1);
+ if (deviceConfigValue < 0) {
+ return defaultValue;
+ }
+ return Duration.ofMillis(deviceConfigValue);
+ }
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
similarity index 91%
rename from services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
rename to services/core/java/com/android/server/timedetector/EnvironmentImpl.java
index 4f3f9dc..5cd1718 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
+++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
@@ -25,7 +25,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.os.Build;
-import android.os.Environment;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -39,11 +38,11 @@
import java.util.Objects;
/**
- * The real implementation of {@link TimeDetectorStrategyImpl.Callback} used on device.
+ * The real implementation of {@link TimeDetectorStrategyImpl.Environment} used on device.
*/
-public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategyImpl.Callback {
+public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment {
- private final static String TAG = "timedetector.TimeDetectorStrategyCallbackImpl";
+ private static final String TAG = TimeDetectorService.TAG;
private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
@@ -52,7 +51,7 @@
* incorrect for sure.
*/
private static final Instant TIME_LOWER_BOUND = Instant.ofEpochMilli(
- Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
+ Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME));
/**
* By default telephony and network only suggestions are accepted and telephony takes
@@ -74,7 +73,7 @@
@NonNull private final AlarmManager mAlarmManager;
@NonNull private final int[] mOriginPriorities;
- public TimeDetectorStrategyCallbackImpl(@NonNull Context context) {
+ public EnvironmentImpl(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
mContentResolver = Objects.requireNonNull(context.getContentResolver());
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 71b1a49..bbbd19f 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -50,7 +50,7 @@
* implementation to deal with the logic around time detection.
*/
public final class TimeDetectorService extends ITimeDetectorService.Stub {
- private static final String TAG = "TimeDetectorService";
+ static final String TAG = "time_detector";
public static class Lifecycle extends SystemService {
@@ -73,8 +73,8 @@
@NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
private static TimeDetectorService create(@NonNull Context context) {
- TimeDetectorStrategyImpl.Callback callback = new TimeDetectorStrategyCallbackImpl(context);
- TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(callback);
+ TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context);
+ TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(environment);
Handler handler = FgThread.getHandler();
TimeDetectorService timeDetectorService =
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 2fbeb75..7cd4184 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -40,6 +40,7 @@
import java.time.Instant;
import java.util.Arrays;
+import java.util.Objects;
/**
* An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to
@@ -51,7 +52,7 @@
public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
private static final boolean DBG = false;
- private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
+ private static final String LOG_TAG = TimeDetectorService.TAG;
/** A score value used to indicate "no score", either due to validation failure or age. */
private static final int TELEPHONY_INVALID_SCORE = -1;
@@ -88,7 +89,7 @@
private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
@NonNull
- private final Callback mCallback;
+ private final Environment mEnvironment;
// Used to store the last time the system clock state was set automatically. It is used to
// detect (and log) issues with the realtime clock or whether the clock is being set without
@@ -127,7 +128,7 @@
* moved to {@link TimeDetectorStrategy}. There are similar issues with
* {@link #systemClockMillis()} while any process can modify the system clock.
*/
- public interface Callback {
+ public interface Environment {
/**
* The absolute threshold below which the system clock need not be updated. i.e. if setting
@@ -170,8 +171,8 @@
void releaseWakeLock();
}
- TimeDetectorStrategyImpl(@NonNull Callback callback) {
- mCallback = callback;
+ TimeDetectorStrategyImpl(@NonNull Environment environment) {
+ mEnvironment = Objects.requireNonNull(environment);
}
@Override
@@ -267,7 +268,7 @@
@Override
public synchronized void handleAutoTimeConfigChanged() {
- boolean enabled = mCallback.isAutoTimeDetectionEnabled();
+ boolean enabled = mEnvironment.isAutoTimeDetectionEnabled();
// When automatic time detection is enabled we update the system clock instantly if we can.
// Conversely, when automatic time detection is disabled we leave the clock as it is.
if (enabled) {
@@ -286,20 +287,20 @@
ipw.increaseIndent(); // level 1
ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
- ipw.println("mCallback.isAutoTimeDetectionEnabled()="
- + mCallback.isAutoTimeDetectionEnabled());
- ipw.println("mCallback.elapsedRealtimeMillis()=" + mCallback.elapsedRealtimeMillis());
- ipw.println("mCallback.systemClockMillis()=" + mCallback.systemClockMillis());
- ipw.println("mCallback.systemClockUpdateThresholdMillis()="
- + mCallback.systemClockUpdateThresholdMillis());
- ipw.printf("mCallback.autoTimeLowerBound()=%s(%s)\n",
- mCallback.autoTimeLowerBound(),
- mCallback.autoTimeLowerBound().toEpochMilli());
+ ipw.println("mEnvironment.isAutoTimeDetectionEnabled()="
+ + mEnvironment.isAutoTimeDetectionEnabled());
+ ipw.println("mEnvironment.elapsedRealtimeMillis()=" + mEnvironment.elapsedRealtimeMillis());
+ ipw.println("mEnvironment.systemClockMillis()=" + mEnvironment.systemClockMillis());
+ ipw.println("mEnvironment.systemClockUpdateThresholdMillis()="
+ + mEnvironment.systemClockUpdateThresholdMillis());
+ ipw.printf("mEnvironment.autoTimeLowerBound()=%s(%s)\n",
+ mEnvironment.autoTimeLowerBound(),
+ mEnvironment.autoTimeLowerBound().toEpochMilli());
String priorities =
- Arrays.stream(mCallback.autoOriginPriorities())
+ Arrays.stream(mEnvironment.autoOriginPriorities())
.mapToObj(TimeDetectorStrategy::originToString)
.collect(joining(",", "[", "]"));
- ipw.println("mCallback.autoOriginPriorities()=" + priorities);
+ ipw.println("mEnvironment.autoOriginPriorities()=" + priorities);
ipw.println("Time change log:");
ipw.increaseIndent(); // level 2
@@ -372,7 +373,7 @@
}
// We can validate the suggestion against the reference time clock.
- long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
if (elapsedRealtimeMillis < newUtcTime.getReferenceTimeMillis()) {
// elapsedRealtime clock went backwards?
Slog.w(LOG_TAG, "New reference time is in the future? Ignoring."
@@ -391,7 +392,7 @@
private boolean validateSuggestionAgainstLowerBound(
@NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) {
- Instant lowerBound = mCallback.autoTimeLowerBound();
+ Instant lowerBound = mEnvironment.autoTimeLowerBound();
// Suggestion is definitely wrong if it comes before lower time bound.
if (lowerBound.isAfter(Instant.ofEpochMilli(newUtcTime.getValue()))) {
@@ -405,13 +406,13 @@
@GuardedBy("this")
private void doAutoTimeDetection(@NonNull String detectionReason) {
- if (!mCallback.isAutoTimeDetectionEnabled()) {
+ if (!mEnvironment.isAutoTimeDetectionEnabled()) {
// Avoid doing unnecessary work with this (race-prone) check.
return;
}
// Try the different origins one at a time.
- int[] originPriorities = mCallback.autoOriginPriorities();
+ int[] originPriorities = mEnvironment.autoOriginPriorities();
for (int origin : originPriorities) {
TimestampedValue<Long> newUtcTime = null;
String cause = null;
@@ -470,7 +471,7 @@
@GuardedBy("this")
@Nullable
private TelephonyTimeSuggestion findBestTelephonySuggestion() {
- long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
// Telephony time suggestions are assumed to be derived from NITZ or NITZ-like signals.
// These have a number of limitations:
@@ -579,7 +580,7 @@
}
TimestampedValue<Long> utcTime = networkSuggestion.getUtcTime();
- long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
@@ -599,7 +600,7 @@
}
TimestampedValue<Long> utcTime = gnssTimeSuggestion.getUtcTime();
- long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
@@ -619,7 +620,7 @@
}
TimestampedValue<Long> utcTime = externalTimeSuggestion.getUtcTime();
- long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis();
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
// The latest suggestion is not valid, usually due to its age.
return null;
@@ -634,7 +635,7 @@
boolean isOriginAutomatic = isOriginAutomatic(origin);
if (isOriginAutomatic) {
- if (!mCallback.isAutoTimeDetectionEnabled()) {
+ if (!mEnvironment.isAutoTimeDetectionEnabled()) {
if (DBG) {
Slog.d(LOG_TAG, "Auto time detection is not enabled."
+ " origin=" + originToString(origin)
@@ -644,7 +645,7 @@
return false;
}
} else {
- if (mCallback.isAutoTimeDetectionEnabled()) {
+ if (mEnvironment.isAutoTimeDetectionEnabled()) {
if (DBG) {
Slog.d(LOG_TAG, "Auto time detection is enabled."
+ " origin=" + originToString(origin)
@@ -655,11 +656,11 @@
}
}
- mCallback.acquireWakeLock();
+ mEnvironment.acquireWakeLock();
try {
return setSystemClockUnderWakeLock(origin, time, cause);
} finally {
- mCallback.releaseWakeLock();
+ mEnvironment.releaseWakeLock();
}
}
@@ -671,9 +672,9 @@
private boolean setSystemClockUnderWakeLock(
@Origin int origin, @NonNull TimestampedValue<Long> newTime, @NonNull String cause) {
- long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
boolean isOriginAutomatic = isOriginAutomatic(origin);
- long actualSystemClockMillis = mCallback.systemClockMillis();
+ long actualSystemClockMillis = mEnvironment.systemClockMillis();
if (isOriginAutomatic) {
// CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
// may be setting the clock.
@@ -701,7 +702,7 @@
// Check if the new signal would make sufficient difference to the system clock. If it's
// below the threshold then ignore it.
long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
- long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
+ long systemClockUpdateThreshold = mEnvironment.systemClockUpdateThresholdMillis();
if (absTimeDifference < systemClockUpdateThreshold) {
if (DBG) {
Slog.d(LOG_TAG, "Not setting system clock. New time and"
@@ -715,7 +716,7 @@
return true;
}
- mCallback.setSystemClock(newSystemClockMillis);
+ mEnvironment.setSystemClock(newSystemClockMillis);
String logMsg = "Set system clock using time=" + newTime
+ " cause=" + cause
+ " elapsedRealtimeMillis=" + elapsedRealtimeMillis
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
similarity index 78%
rename from services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
rename to services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 1357608..f52b9b1 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -33,43 +33,52 @@
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
+import com.android.server.timedetector.DeviceConfig;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
- * The real implementation of {@link TimeZoneDetectorStrategyImpl.Callback}.
+ * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
*/
-public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategyImpl.Callback {
+public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment {
- private static final String LOG_TAG = "TimeZoneDetectorCallbackImpl";
+ private static final String LOG_TAG = TimeZoneDetectorService.TAG;
private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
@NonNull private final ContentResolver mCr;
@NonNull private final UserManager mUserManager;
- @NonNull private final boolean mGeoDetectionFeatureEnabled;
+ @NonNull private final DeviceConfig mDeviceConfig;
+ @NonNull private final boolean mGeoDetectionSupported;
@NonNull private final LocationManager mLocationManager;
+
// @NonNull after setConfigChangeListener() is called.
+ @GuardedBy("this")
private ConfigurationChangeListener mConfigChangeListener;
- TimeZoneDetectorCallbackImpl(@NonNull Context context, @NonNull Handler handler,
- boolean geoDetectionFeatureEnabled) {
+ EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
+ @NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) {
mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler);
+ Executor handlerExecutor = new HandlerExecutor(mHandler);
mCr = context.getContentResolver();
mUserManager = context.getSystemService(UserManager.class);
mLocationManager = context.getSystemService(LocationManager.class);
- mGeoDetectionFeatureEnabled = geoDetectionFeatureEnabled;
+ mDeviceConfig = deviceConfig;
+ mGeoDetectionSupported = geoDetectionSupported;
- // Wire up the change listener. All invocations are performed on the mHandler thread.
+ // Wire up the change listeners. All invocations are performed on the mHandler thread.
// Listen for the user changing / the user's location mode changing.
IntentFilter filter = new IntentFilter();
@@ -103,18 +112,29 @@
handleConfigChangeOnHandlerThread();
}
}, UserHandle.USER_ALL);
+
+ // Add async callbacks for changes to server-side flags: some of the flags affect device /
+ // user config. All changes can be treated like a config change. If flags that affect config
+ // haven't changed then call will be a no-op.
+ mDeviceConfig.addListener(
+ handlerExecutor,
+ this::handleConfigChangeOnHandlerThread);
}
private void handleConfigChangeOnHandlerThread() {
- if (mConfigChangeListener == null) {
- Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
+ synchronized (this) {
+ if (mConfigChangeListener == null) {
+ Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
+ }
+ mConfigChangeListener.onChange();
}
- mConfigChangeListener.onChange();
}
@Override
public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
- mConfigChangeListener = Objects.requireNonNull(listener);
+ synchronized (this) {
+ mConfigChangeListener = Objects.requireNonNull(listener);
+ }
}
@Override
@@ -174,7 +194,10 @@
// time zone detection: if we wrote it down then we'd set the value explicitly, which
// would prevent detecting "default" later. That might influence what happens on later
// releases that support geo detection on the same hardware.
- if (isGeoDetectionSupported()) {
+ // Also avoid writing the geo detection enabled setting for devices that are currently
+ // force-enabled: otherwise we might overwrite a droidfood user's real setting
+ // permanently.
+ if (isGeoDetectionSupported() && !isGeoDetectionForceEnabled()) {
final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
}
@@ -191,7 +214,7 @@
}
private boolean isGeoDetectionSupported() {
- return mGeoDetectionFeatureEnabled;
+ return mGeoDetectionSupported;
}
private boolean isAutoDetectionEnabled() {
@@ -213,12 +236,25 @@
}
private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
- final boolean geoDetectionEnabledByDefault = false;
+ // We may never use this, but it gives us a way to force location-based time zone detection
+ // on for testers (where their other settings allow).
+ boolean forceEnabled = isGeoDetectionForceEnabled();
+ if (forceEnabled) {
+ return true;
+ }
+
+ final boolean geoDetectionEnabledByDefault = mDeviceConfig.getBoolean(
+ DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, false);
return Settings.Secure.getIntForUser(mCr,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
(geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
}
+ private boolean isGeoDetectionForceEnabled() {
+ return mDeviceConfig.getBoolean(
+ DeviceConfig.KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, false);
+ }
+
private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) {
// See comment in setAutoDetectionEnabledIfRequired. http://b/171953500
if (isGeoDetectionEnabled(userId) != enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
index b63df05..4eb1b99 100644
--- a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
+++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
@@ -19,8 +19,11 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
+import java.time.Duration;
import java.util.ArrayDeque;
/**
@@ -49,18 +52,15 @@
*/
public final class ReferenceWithHistory<V> {
- private static final Object NULL_MARKER = "{null marker}";
-
/** The maximum number of references to store. */
private final int mMaxHistorySize;
- /**
- * The history storage. Note that ArrayDeque doesn't support {@code null} so this stores Object
- * and not V. Use {@link #packNullIfRequired(Object)} and {@link #unpackNullIfRequired(Object)}
- * to convert to / from the storage object.
- */
+ /** The number of times {@link #set(Object)} has been called. */
+ private int mSetCount;
+
+ /** The history storage. */
@Nullable
- private ArrayDeque<Object> mValues;
+ private ArrayDeque<TimestampedValue<V>> mValues;
/**
* Creates an instance that records, at most, the specified number of values.
@@ -78,8 +78,8 @@
if (mValues == null || mValues.isEmpty()) {
return null;
}
- Object value = mValues.getFirst();
- return unpackNullIfRequired(value);
+ TimestampedValue<V> valueHolder = mValues.getFirst();
+ return valueHolder.getValue();
}
/**
@@ -98,8 +98,10 @@
V previous = get();
- Object nullSafeValue = packNullIfRequired(newValue);
- mValues.addFirst(nullSafeValue);
+ TimestampedValue<V> valueHolder =
+ new TimestampedValue<>(SystemClock.elapsedRealtime(), newValue);
+ mValues.addFirst(valueHolder);
+ mSetCount++;
return previous;
}
@@ -110,10 +112,13 @@
if (mValues == null) {
ipw.println("{Empty}");
} else {
- int i = 0;
- for (Object value : mValues) {
- ipw.println(i + ": " + unpackNullIfRequired(value));
- i++;
+ int i = mSetCount;
+ for (TimestampedValue<V> valueHolder : mValues) {
+ ipw.print(--i);
+ ipw.print("@");
+ ipw.print(Duration.ofMillis(valueHolder.getReferenceTimeMillis()).toString());
+ ipw.print(": ");
+ ipw.println(valueHolder.getValue());
}
}
ipw.flush();
@@ -130,23 +135,4 @@
public String toString() {
return String.valueOf(get());
}
-
- /**
- * Turns a non-nullable Object into a nullable value. See also
- * {@link #packNullIfRequired(Object)}.
- */
- @SuppressWarnings("unchecked")
- @Nullable
- private V unpackNullIfRequired(@NonNull Object value) {
- return value == NULL_MARKER ? null : (V) value;
- }
-
- /**
- * Turns a nullable value into a non-nullable Object. See also
- * {@link #unpackNullIfRequired(Object)}.
- */
- @NonNull
- private Object packNullIfRequired(@Nullable V value) {
- return value == null ? NULL_MARKER : value;
- }
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 2ead3be..bd71ddf 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -59,7 +59,7 @@
public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
implements IBinder.DeathRecipient {
- private static final String TAG = "TimeZoneDetectorService";
+ static final String TAG = "time_zone_detector";
/**
* A "feature switch" for location-based time zone detection. If this is {@code false}. It is
@@ -67,19 +67,19 @@
* is important.
*/
@Nullable
- private static Boolean sGeoLocationTimeZoneDetectionEnabled;
+ private static Boolean sGeoLocationTimeZoneDetectionSupported;
/** Returns {@code true} if the location-based time zone detection feature is enabled. */
- public static boolean isGeoLocationTimeZoneDetectionEnabled(Context context) {
- if (sGeoLocationTimeZoneDetectionEnabled == null) {
+ public static boolean isGeoLocationTimeZoneDetectionSupported(Context context) {
+ if (sGeoLocationTimeZoneDetectionSupported == null) {
// The config value is expected to be the main switch. Platform developers can also
// enable the feature using a persistent system property.
- sGeoLocationTimeZoneDetectionEnabled = context.getResources().getBoolean(
+ sGeoLocationTimeZoneDetectionSupported = context.getResources().getBoolean(
com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
|| SystemProperties.getBoolean(
"persist.sys.location_time_zone_detection_feature_enabled", false);
}
- return sGeoLocationTimeZoneDetectionEnabled;
+ return sGeoLocationTimeZoneDetectionSupported;
}
/**
@@ -98,11 +98,11 @@
Context context = getContext();
Handler handler = FgThread.getHandler();
- boolean geolocationTimeZoneDetectionEnabled =
- isGeoLocationTimeZoneDetectionEnabled(context);
+ boolean geolocationTimeZoneDetectionSupported =
+ isGeoLocationTimeZoneDetectionSupported(context);
TimeZoneDetectorStrategy timeZoneDetectorStrategy =
TimeZoneDetectorStrategyImpl.create(
- context, handler, geolocationTimeZoneDetectionEnabled);
+ context, handler, geolocationTimeZoneDetectionSupported);
// Create and publish the local service for use by internal callers.
TimeZoneDetectorInternal internal =
@@ -330,7 +330,7 @@
boolean isGeoTimeZoneDetectionSupported() {
enforceManageTimeZoneDetectorPermission();
- return isGeoLocationTimeZoneDetectionEnabled(mContext);
+ return isGeoLocationTimeZoneDetectionSupported(mContext);
}
@Override
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 781668b..c464b74 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -38,6 +38,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.timedetector.DeviceConfig;
import java.util.ArrayList;
import java.util.List;
@@ -59,7 +60,7 @@
* conditions.
*/
@VisibleForTesting
- public interface Callback {
+ public interface Environment {
/**
* Sets a {@link ConfigurationChangeListener} that will be invoked when there are any
@@ -97,7 +98,7 @@
void storeConfiguration(@UserIdInt int userId, TimeZoneConfiguration newConfiguration);
}
- private static final String LOG_TAG = "TimeZoneDetectorStrategy";
+ private static final String LOG_TAG = TimeZoneDetectorService.TAG;
private static final boolean DBG = false;
/**
@@ -164,7 +165,7 @@
private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10;
@NonNull
- private final Callback mCallback;
+ private final Environment mEnvironment;
@GuardedBy("this")
@NonNull
@@ -203,17 +204,18 @@
*/
public static TimeZoneDetectorStrategyImpl create(
@NonNull Context context, @NonNull Handler handler,
- boolean geolocationTimeZoneDetectionEnabled) {
+ boolean geoDetectionSupported) {
- TimeZoneDetectorCallbackImpl callback = new TimeZoneDetectorCallbackImpl(
- context, handler, geolocationTimeZoneDetectionEnabled);
- return new TimeZoneDetectorStrategyImpl(callback);
+ DeviceConfig deviceConfig = new DeviceConfig();
+ EnvironmentImpl environment = new EnvironmentImpl(
+ context, handler, deviceConfig, geoDetectionSupported);
+ return new TimeZoneDetectorStrategyImpl(environment);
}
@VisibleForTesting
- public TimeZoneDetectorStrategyImpl(@NonNull Callback callback) {
- mCallback = Objects.requireNonNull(callback);
- mCallback.setConfigChangeListener(this::handleConfigChanged);
+ public TimeZoneDetectorStrategyImpl(@NonNull Environment environment) {
+ mEnvironment = Objects.requireNonNull(environment);
+ mEnvironment.setConfigChangeListener(this::handleConfigChanged);
}
/**
@@ -230,13 +232,13 @@
@Override
@NonNull
public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
- return mCallback.getConfigurationInternal(userId);
+ return mEnvironment.getConfigurationInternal(userId);
}
@Override
@NonNull
public synchronized ConfigurationInternal getCurrentUserConfigurationInternal() {
- int currentUserId = mCallback.getCurrentUserId();
+ int currentUserId = mEnvironment.getCurrentUserId();
return getConfigurationInternal(currentUserId);
}
@@ -257,9 +259,9 @@
return false;
}
- // Store the configuration / notify as needed. This will cause the mCallback to invoke
+ // Store the configuration / notify as needed. This will cause the mEnvironment to invoke
// handleConfigChanged() asynchronously.
- mCallback.storeConfiguration(userId, newConfiguration);
+ mEnvironment.storeConfiguration(userId, newConfiguration);
String logMsg = "Configuration changed:"
+ " oldConfiguration=" + oldConfiguration
@@ -275,8 +277,9 @@
public synchronized void suggestGeolocationTimeZone(
@NonNull GeolocationTimeZoneSuggestion suggestion) {
- int currentUserId = mCallback.getCurrentUserId();
- ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ConfigurationInternal currentUserConfig =
+ mEnvironment.getConfigurationInternal(currentUserId);
if (DBG) {
Slog.d(LOG_TAG, "Geolocation suggestion received."
+ " currentUserConfig=" + currentUserConfig
@@ -299,7 +302,7 @@
public synchronized boolean suggestManualTimeZone(
@UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion) {
- int currentUserId = mCallback.getCurrentUserId();
+ int currentUserId = mEnvironment.getCurrentUserId();
if (userId != currentUserId) {
Slog.w(LOG_TAG, "Manual suggestion received but user != current user, userId=" + userId
+ " suggestion=" + suggestion);
@@ -332,8 +335,9 @@
public synchronized void suggestTelephonyTimeZone(
@NonNull TelephonyTimeZoneSuggestion suggestion) {
- int currentUserId = mCallback.getCurrentUserId();
- ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ConfigurationInternal currentUserConfig =
+ mEnvironment.getConfigurationInternal(currentUserId);
if (DBG) {
Slog.d(LOG_TAG, "Telephony suggestion received. currentUserConfig=" + currentUserConfig
+ " newSuggestion=" + suggestion);
@@ -423,7 +427,7 @@
String zoneId;
// Introduce bias towards the device's current zone when there are multiple zone suggested.
- String deviceTimeZone = mCallback.getDeviceTimeZone();
+ String deviceTimeZone = mEnvironment.getDeviceTimeZone();
if (zoneIds.contains(deviceTimeZone)) {
if (DBG) {
Slog.d(LOG_TAG,
@@ -486,7 +490,7 @@
@GuardedBy("this")
private void setDeviceTimeZoneIfRequired(@NonNull String newZoneId, @NonNull String cause) {
- String currentZoneId = mCallback.getDeviceTimeZone();
+ String currentZoneId = mEnvironment.getDeviceTimeZone();
// Avoid unnecessary changes / intents.
if (newZoneId.equals(currentZoneId)) {
@@ -501,7 +505,7 @@
return;
}
- mCallback.setDeviceTimeZone(newZoneId);
+ mEnvironment.setDeviceTimeZone(newZoneId);
String msg = "Set device time zone."
+ ", currentZoneId=" + currentZoneId
+ ", newZoneId=" + newZoneId
@@ -572,8 +576,9 @@
// This method is called whenever the user changes or the config for any user changes. We
// don't know what happened, so we capture the current user's config, check to see if we
// need to clear state associated with a previous user, and rerun detection.
- int currentUserId = mCallback.getCurrentUserId();
- ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ConfigurationInternal currentUserConfig =
+ mEnvironment.getConfigurationInternal(currentUserId);
GeolocationTimeZoneSuggestion latestGeoLocationSuggestion =
mLatestGeoLocationSuggestion.get();
@@ -603,14 +608,14 @@
ipw.println("TimeZoneDetectorStrategy:");
ipw.increaseIndent(); // level 1
- int currentUserId = mCallback.getCurrentUserId();
- ipw.println("mCallback.getCurrentUserId()=" + currentUserId);
- ConfigurationInternal configuration = mCallback.getConfigurationInternal(currentUserId);
- ipw.println("mCallback.getConfiguration(currentUserId)=" + configuration);
+ int currentUserId = mEnvironment.getCurrentUserId();
+ ipw.println("mEnvironment.getCurrentUserId()=" + currentUserId);
+ ConfigurationInternal configuration = mEnvironment.getConfigurationInternal(currentUserId);
+ ipw.println("mEnvironment.getConfiguration(currentUserId)=" + configuration);
ipw.println("[Capabilities=" + configuration.createCapabilitiesAndConfig() + "]");
- ipw.println("mCallback.isDeviceTimeZoneInitialized()="
- + mCallback.isDeviceTimeZoneInitialized());
- ipw.println("mCallback.getDeviceTimeZone()=" + mCallback.getDeviceTimeZone());
+ ipw.println("mEnvironment.isDeviceTimeZoneInitialized()="
+ + mEnvironment.isDeviceTimeZoneInitialized());
+ ipw.println("mEnvironment.getDeviceTimeZone()=" + mEnvironment.getDeviceTimeZone());
ipw.println("Time zone change log:");
ipw.increaseIndent(); // level 2
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
index 83b33ee..a54288f 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import com.android.server.LocalServices;
+import com.android.server.timedetector.DeviceConfig;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
@@ -32,18 +33,22 @@
*/
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
- private static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
- private static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
- private static final Duration PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
+ private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
+ private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
+ Duration.ofMinutes(1);
+ private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
@NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
@NonNull private final LocationTimeZoneProviderController mController;
+ @NonNull private final DeviceConfig mDeviceConfig;
@NonNull private final ConfigurationChangeListener mConfigurationChangeListener;
ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
+ @NonNull DeviceConfig deviceConfig,
@NonNull LocationTimeZoneProviderController controller) {
super(threadingDomain);
mController = Objects.requireNonNull(controller);
+ mDeviceConfig = Objects.requireNonNull(deviceConfig);
mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
// Listen for configuration changes.
@@ -66,18 +71,24 @@
@Override
@NonNull
Duration getProviderInitializationTimeout() {
- return PROVIDER_INITIALIZATION_TIMEOUT;
+ return mDeviceConfig.getDurationFromMillis(
+ DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+ DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
}
@Override
@NonNull
Duration getProviderInitializationTimeoutFuzz() {
- return PROVIDER_INITIALIZATION_TIMEOUT_FUZZ;
+ return mDeviceConfig.getDurationFromMillis(
+ DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+ DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
}
@Override
@NonNull
Duration getUncertaintyDelay() {
- return PROVIDER_UNCERTAINTY_DELAY;
+ return mDeviceConfig.getDurationFromMillis(
+ DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+ DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 5bee7ee..364eaf8 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -43,6 +43,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.SystemService;
+import com.android.server.timedetector.DeviceConfig;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
import com.android.server.timezonedetector.TimeZoneDetectorService;
@@ -74,11 +75,17 @@
* mode" where the real binder clients are replaced by {@link
* SimulatedLocationTimeZoneProviderProxy}. This means that the real client providers are never
* bound (ensuring no real location events will be received) and simulated events / behaviors
- * can be injected via the command line. To enter simulation mode for a provider, use
- * "{@code adb shell setprop persist.sys.location_tz_simulation_mode.<provider name> 1}" and reboot.
- * e.g. "{@code adb shell setprop persist.sys.location_tz_simulation_mode.primary 1}}"
- * Then use "{@code adb shell cmd location_time_zone_manager help}" for injection. Set the system
- * properties to "0" and reboot to return to exit simulation mode.
+ * can be injected via the command line.
+ *
+ * <p>To enter simulation mode for a provider, use {@code adb shell cmd location_time_zone_manager
+ * set_provider_mode_override <provider name> simulated} and restart the service with {@code
+ * adb shell cmd location_time_zone_manager stop} and {@code adb shell cmd
+ * location_time_zone_manager start}.
+ *
+ * <p>e.g. {@code adb shell cmd location_time_zone_manager set_provider_mode_override primary
+ * simulated}.
+ *
+ * <p>See {@code adb shell cmd location_time_zone_manager help}" for more options.
*/
public class LocationTimeZoneManagerService extends Binder {
@@ -96,7 +103,7 @@
@Override
public void onStart() {
Context context = getContext();
- if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionEnabled(context)) {
+ if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
mService = new LocationTimeZoneManagerService(context);
// The service currently exposes no LocalService or Binder API, but it extends
@@ -110,7 +117,7 @@
@Override
public void onBootPhase(int phase) {
Context context = getContext();
- if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionEnabled(context)) {
+ if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// The location service must be functioning after this boot phase.
mService.onSystemReady();
@@ -221,10 +228,10 @@
LocationTimeZoneProvider secondary = createSecondaryProvider();
mLocationTimeZoneDetectorController =
new ControllerImpl(mThreadingDomain, primary, secondary);
- ControllerCallbackImpl callback = new ControllerCallbackImpl(
- mThreadingDomain);
+ DeviceConfig deviceConfig = new DeviceConfig();
mEnvironment = new ControllerEnvironmentImpl(
- mThreadingDomain, mLocationTimeZoneDetectorController);
+ mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController);
+ ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
mLocationTimeZoneDetectorController.initialize(mEnvironment, callback);
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 38ae51f..83085cc 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -977,7 +977,7 @@
AudioPortConfig sourceConfig = mAudioSource.activeConfig();
List<AudioPortConfig> sinkConfigs = new ArrayList<>();
AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
- boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
+ boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated || mAudioPatch == null;
for (AudioDevicePort audioSink : mAudioSink) {
AudioPortConfig sinkConfig = audioSink.activeConfig();
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index fd12c2d2..b6ddd93 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -28,16 +28,15 @@
import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
import android.os.ParcelUuid;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -55,19 +54,18 @@
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
+ @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
@NonNull private final UnderlyingNetworkTrackerCallback mCb;
@NonNull private final Dependencies mDeps;
@NonNull private final Handler mHandler;
@NonNull private final ConnectivityManager mConnectivityManager;
- @NonNull private final SubscriptionManager mSubscriptionManager;
- @NonNull private final SparseArray<NetworkCallback> mCellBringupCallbacks = new SparseArray<>();
+ @NonNull private final Map<Integer, NetworkCallback> mCellBringupCallbacks = new ArrayMap<>();
@NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback();
@NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
- @NonNull private final Set<Integer> mSubIds = new ArraySet<>();
-
- @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
+ @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+ private boolean mIsRunning = true;
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
@@ -75,11 +73,13 @@
public UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb) {
this(
vcnContext,
subscriptionGroup,
+ snapshot,
requiredUnderlyingNetworkCapabilities,
cb,
new Dependencies());
@@ -88,11 +88,13 @@
private UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb,
@NonNull Dependencies deps) {
mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
mRequiredUnderlyingNetworkCapabilities =
Objects.requireNonNull(
requiredUnderlyingNetworkCapabilities,
@@ -103,7 +105,6 @@
mHandler = new Handler(mVcnContext.getLooper());
mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
- mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class);
registerNetworkRequests();
}
@@ -149,34 +150,47 @@
private void updateSubIdsAndCellularRequests() {
mVcnContext.ensureRunningOnLooperThread();
- Set<Integer> prevSubIds = new ArraySet<>(mSubIds);
- mSubIds.clear();
-
- // Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup
- // STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony
- List<SubscriptionInfo> subInfos =
- mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup);
-
- for (SubscriptionInfo subInfo : subInfos) {
- final int subId = subInfo.getSubscriptionId();
- mSubIds.add(subId);
-
- if (!mCellBringupCallbacks.contains(subId)) {
- final NetworkBringupCallback cb = new NetworkBringupCallback();
- mCellBringupCallbacks.put(subId, cb);
-
- mConnectivityManager.requestBackgroundNetwork(
- getCellNetworkRequestForSubId(subId), mHandler, cb);
- }
+ // Don't bother re-filing NetworkRequests if this Tracker has been torn down.
+ if (!mIsRunning) {
+ return;
}
- // unregister all NetworkCallbacks for outdated subIds
- for (final int subId : prevSubIds) {
- if (!mSubIds.contains(subId)) {
- final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
- mConnectivityManager.unregisterNetworkCallback(cb);
- }
+ final Set<Integer> subIdsInSubGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup);
+
+ // new subIds to track = (updated list of subIds) - (currently tracked subIds)
+ final Set<Integer> subIdsToRegister = new ArraySet<>(subIdsInSubGroup);
+ subIdsToRegister.removeAll(mCellBringupCallbacks.keySet());
+
+ // subIds to stop tracking = (currently tracked subIds) - (updated list of subIds)
+ final Set<Integer> subIdsToUnregister = new ArraySet<>(mCellBringupCallbacks.keySet());
+ subIdsToUnregister.removeAll(subIdsInSubGroup);
+
+ for (final int subId : subIdsToRegister) {
+ final NetworkBringupCallback cb = new NetworkBringupCallback();
+ mCellBringupCallbacks.put(subId, cb);
+
+ mConnectivityManager.requestBackgroundNetwork(
+ getCellNetworkRequestForSubId(subId), mHandler, cb);
}
+
+ for (final int subId : subIdsToUnregister) {
+ final NetworkCallback cb = mCellBringupCallbacks.remove(subId);
+ mConnectivityManager.unregisterNetworkCallback(cb);
+ }
+ }
+
+ /**
+ * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
+ *
+ * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkTracker to
+ * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
+ * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
+ */
+ public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+ Objects.requireNonNull(snapshot, "Missing snapshot");
+
+ mLastSnapshot = snapshot;
+ updateSubIdsAndCellularRequests();
}
/** Tears down this Tracker, and releases all underlying network requests. */
@@ -186,11 +200,12 @@
mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback);
mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback);
- for (final int subId : mSubIds) {
- final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
+ for (final NetworkCallback cb : mCellBringupCallbacks.values()) {
mConnectivityManager.unregisterNetworkCallback(cb);
}
- mSubIds.clear();
+ mCellBringupCallbacks.clear();
+
+ mIsRunning = false;
}
/** Returns whether the currently selected Network matches the given network. */
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 132883e..5ec527a 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -27,9 +27,18 @@
import android.os.ParcelUuid;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Represents an single instance of a VCN.
@@ -63,41 +72,86 @@
*/
private static final int MSG_EVENT_NETWORK_REQUESTED = MSG_EVENT_BASE + 1;
+ /**
+ * The TelephonySubscriptionSnapshot tracked by VcnManagementService has changed.
+ *
+ * <p>This updated snapshot should be cached locally and passed to all VcnGatewayConnections.
+ *
+ * @param obj TelephonySubscriptionSnapshot
+ */
+ private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2;
+
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
+ /**
+ * Causes this VCN to immediately enter Safemode.
+ *
+ * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
+ * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+ */
+ private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
+ @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
@NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
new HashMap<>();
@NonNull private VcnConfig mConfig;
+ @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
- private boolean mIsRunning = true;
+ /**
+ * Whether this Vcn instance is active and running.
+ *
+ * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
+ * shut down or has entered safe mode.
+ *
+ * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
+ * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
+ * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
+ * Looper.
+ */
+ // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
+ private final AtomicBoolean mIsActive = new AtomicBoolean(true);
public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
- @NonNull VcnConfig config) {
- this(vcnContext, subscriptionGroup, config, new Dependencies());
+ @NonNull VcnConfig config,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ config,
+ snapshot,
+ vcnSafemodeCallback,
+ new Dependencies());
}
- private Vcn(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ mVcnSafemodeCallback =
+ Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
mConfig = Objects.requireNonNull(config, "Missing config");
+ mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
// Register to receive cached and future NetworkRequests
mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
@@ -110,11 +164,29 @@
sendMessage(obtainMessage(MSG_EVENT_CONFIG_UPDATED, config));
}
+ /** Asynchronously updates the Subscription snapshot for this VCN. */
+ public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+ Objects.requireNonNull(snapshot, "Missing snapshot");
+
+ sendMessage(obtainMessage(MSG_EVENT_SUBSCRIPTIONS_CHANGED, snapshot));
+ }
+
/** Asynchronously tears down this Vcn instance, including VcnGatewayConnection(s) */
public void teardownAsynchronously() {
sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
}
+ /** Synchronously checks whether this Vcn is active. */
+ public boolean isActive() {
+ return mIsActive.get();
+ }
+
+ /** Get current Gateways for testing purposes */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public Set<VcnGatewayConnection> getVcnGatewayConnections() {
+ return Collections.unmodifiableSet(new HashSet<>(mVcnGatewayConnections.values()));
+ }
+
private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
@Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
@@ -126,7 +198,7 @@
@Override
public void handleMessage(@NonNull Message msg) {
- if (!mIsRunning) {
+ if (!isActive()) {
return;
}
@@ -137,9 +209,15 @@
case MSG_EVENT_NETWORK_REQUESTED:
handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
break;
+ case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
+ handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
+ break;
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
+ case MSG_CMD_ENTER_SAFEMODE:
+ handleEnterSafemode();
+ break;
default:
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
}
@@ -161,7 +239,13 @@
gatewayConnection.teardownAsynchronously();
}
- mIsRunning = false;
+ mIsActive.set(false);
+ }
+
+ private void handleEnterSafemode() {
+ handleTeardown();
+
+ mVcnSafemodeCallback.onEnteredSafemode();
}
private void handleNetworkRequested(
@@ -192,13 +276,27 @@
"Bringing up new VcnGatewayConnection for request " + request.requestId);
final VcnGatewayConnection vcnGatewayConnection =
- new VcnGatewayConnection(
- mVcnContext, mSubscriptionGroup, gatewayConnectionConfig);
+ mDeps.newVcnGatewayConnection(
+ mVcnContext,
+ mSubscriptionGroup,
+ mLastSnapshot,
+ gatewayConnectionConfig,
+ new VcnGatewayStatusCallbackImpl());
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
}
+ private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
+ mLastSnapshot = snapshot;
+
+ if (isActive()) {
+ for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
+ gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
+ }
+ }
+ }
+
private boolean requestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
@@ -214,11 +312,43 @@
}
/** Retrieves the network score for a VCN Network */
- private int getNetworkScore() {
+ // Package visibility for use in VcnGatewayConnection
+ static int getNetworkScore() {
// TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in
// subGrp" value
return 52;
}
- private static class Dependencies {}
+ /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public interface VcnGatewayStatusCallback {
+ /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+ @Override
+ public void onEnteredSafemode() {
+ sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+ }
+ }
+
+ /** External dependencies used by Vcn, for injection in tests */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class Dependencies {
+ /** Builds a new VcnGatewayConnection */
+ public VcnGatewayConnection newVcnGatewayConnection(
+ VcnContext vcnContext,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ VcnGatewayConnectionConfig connectionConfig,
+ VcnGatewayStatusCallback gatewayStatusCallback) {
+ return new VcnGatewayConnection(
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 39c9606..9ecdf1b 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -17,8 +17,11 @@
package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static com.android.server.VcnManagementService.VDBG;
@@ -34,8 +37,10 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
import android.net.annotations.PolicyDirection;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
@@ -47,23 +52,29 @@
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -113,6 +124,9 @@
public class VcnGatewayConnection extends StateMachine {
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
+ private static final int[] MERGED_CAPABILITIES =
+ new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING};
+
private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE;
@@ -359,6 +373,16 @@
*/
private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
+ /**
+ * Sent when this VcnGatewayConnection is notified of a change in TelephonySubscriptions.
+ *
+ * <p>Relevant in all states.
+ *
+ * @param arg1 The "all" token; this signal is always honored.
+ */
+ // TODO(b/178426520): implement handling of this event
+ private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9;
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -379,10 +403,13 @@
@NonNull
final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
+ @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+ @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@@ -457,28 +484,43 @@
public VcnGatewayConnection(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
- @NonNull VcnGatewayConnectionConfig connectionConfig) {
- this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies());
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback,
+ new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnGatewayConnection(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
@NonNull Dependencies deps) {
super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+ mGatewayStatusCallback =
+ Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
+ mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
+
mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
mUnderlyingNetworkTracker =
mDeps.newUnderlyingNetworkTracker(
mVcnContext,
subscriptionGroup,
+ mLastSnapshot,
mConnectionConfig.getAllUnderlyingCapabilities(),
mUnderlyingNetworkTrackerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
@@ -533,10 +575,28 @@
mUnderlyingNetworkTracker.teardown();
}
+ /**
+ * Notify this Gateway that subscriptions have changed.
+ *
+ * <p>This snapshot should be used to update any keepalive requests necessary for potential
+ * underlying Networks in this Gateway's subscription group.
+ */
+ public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+ Objects.requireNonNull(snapshot, "Missing snapshot");
+ mVcnContext.ensureRunningOnLooperThread();
+
+ mLastSnapshot = snapshot;
+ mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
+
+ sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
+ }
+
private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
@Override
public void onSelectedUnderlyingNetworkChanged(
@Nullable UnderlyingNetworkRecord underlying) {
+ // TODO(b/179091925): Move the delayed-message handling to BaseState
+
// If underlying is null, all underlying networks have been lost. Disconnect VCN after a
// timeout.
if (underlying == null) {
@@ -668,7 +728,8 @@
case EVENT_TRANSFORM_CREATED: // Fallthrough
case EVENT_SETUP_COMPLETED: // Fallthrough
case EVENT_DISCONNECT_REQUESTED: // Fallthrough
- case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+ case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
+ case EVENT_SUBSCRIPTIONS_CHANGED:
logUnexpectedEvent(msg.what);
break;
default:
@@ -921,6 +982,8 @@
transitionTo(mDisconnectingState);
break;
case EVENT_SESSION_CLOSED:
+ // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+ // message may not be posted again. Defer to ensure immediate shutdown.
deferMessage(msg);
transitionTo(mDisconnectingState);
@@ -941,7 +1004,108 @@
}
}
- private abstract class ConnectedStateBase extends ActiveBaseState {}
+ private abstract class ConnectedStateBase extends ActiveBaseState {
+ protected void updateNetworkAgent(
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull NetworkAgent agent,
+ @NonNull ChildSessionConfiguration childConfig) {
+ final NetworkCapabilities caps =
+ buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+ final LinkProperties lp =
+ buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+ agent.sendNetworkCapabilities(caps);
+ agent.sendLinkProperties(lp);
+ }
+
+ protected NetworkAgent buildNetworkAgent(
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig) {
+ final NetworkCapabilities caps =
+ buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+ final LinkProperties lp =
+ buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+ final NetworkAgent agent =
+ new NetworkAgent(
+ mVcnContext.getContext(),
+ mVcnContext.getLooper(),
+ TAG,
+ caps,
+ lp,
+ Vcn.getNetworkScore(),
+ new NetworkAgentConfig(),
+ mVcnContext.getVcnNetworkProvider()) {
+ @Override
+ public void unwanted() {
+ teardownAsynchronously();
+ }
+ };
+
+ agent.register();
+ agent.markConnected();
+
+ return agent;
+ }
+
+ protected void applyTransform(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull Network underlyingNetwork,
+ @NonNull IpSecTransform transform,
+ int direction) {
+ try {
+ // TODO: Set underlying network of tunnel interface
+
+ // Transforms do not need to be persisted; the IkeSession will keep them alive
+ mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
+ } catch (IOException e) {
+ Slog.d(TAG, "Transform application failed for network " + token, e);
+ sessionLost(token, e);
+ }
+ }
+
+ protected void setupInterface(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig) {
+ setupInterface(token, tunnelIface, childConfig, null);
+ }
+
+ protected void setupInterface(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig,
+ @Nullable ChildSessionConfiguration oldChildConfig) {
+ try {
+ final Set<LinkAddress> newAddrs =
+ new ArraySet<>(childConfig.getInternalAddresses());
+ final Set<LinkAddress> existingAddrs = new ArraySet<>();
+ if (oldChildConfig != null) {
+ existingAddrs.addAll(oldChildConfig.getInternalAddresses());
+ }
+
+ final Set<LinkAddress> toAdd = new ArraySet<>();
+ toAdd.addAll(newAddrs);
+ toAdd.removeAll(existingAddrs);
+
+ final Set<LinkAddress> toRemove = new ArraySet<>();
+ toRemove.addAll(existingAddrs);
+ toRemove.removeAll(newAddrs);
+
+ for (LinkAddress address : toAdd) {
+ tunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
+ }
+
+ for (LinkAddress address : toRemove) {
+ tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength());
+ }
+ } catch (IOException e) {
+ Slog.d(TAG, "Adding address to tunnel failed for token " + token, e);
+ sessionLost(token, e);
+ }
+ }
+ }
/**
* Stable state representing a VCN that has a functioning connection to the mobility anchor.
@@ -951,7 +1115,89 @@
*/
class ConnectedState extends ConnectedStateBase {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ // Successful connection, clear failed attempt counter
+ mFailedAttempts = 0;
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ handleUnderlyingNetworkChanged(msg);
+ break;
+ case EVENT_SESSION_CLOSED:
+ // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+ // message may not be posted again. Defer to ensure immediate shutdown.
+ deferMessage(msg);
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_SESSION_LOST:
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_TRANSFORM_CREATED:
+ final EventTransformCreatedInfo transformCreatedInfo =
+ (EventTransformCreatedInfo) msg.obj;
+
+ applyTransform(
+ mCurrentToken,
+ mTunnelIface,
+ mUnderlying.network,
+ transformCreatedInfo.transform,
+ transformCreatedInfo.direction);
+ break;
+ case EVENT_SETUP_COMPLETED:
+ mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
+
+ setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
+
+ private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
+ final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ if (mUnderlying == null) {
+ // Ignored for now; a new network may be coming up. If none does, the delayed
+ // NETWORK_LOST disconnect will be fired, and tear down the session + network.
+ return;
+ }
+
+ // mUnderlying assumed non-null, given check above.
+ // If network changed, migrate. Otherwise, update any existing networkAgent.
+ if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
+ mIkeSession.setNetwork(mUnderlying.network);
+ } else {
+ // oldUnderlying is non-null & underlying network itself has not changed
+ // (only network properties were changed).
+
+ // Network not yet set up, or child not yet connected.
+ if (mNetworkAgent != null && mChildConfig != null) {
+ // If only network properties changed and agent is active, update properties
+ updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+ }
+ }
+ }
+
+ protected void setupInterfaceAndNetworkAgent(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig) {
+ setupInterface(token, tunnelIface, childConfig);
+
+ if (mNetworkAgent == null) {
+ mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
+ } else {
+ updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+ }
+ }
}
/**
@@ -966,7 +1212,8 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
static NetworkCapabilities buildNetworkCapabilities(
- @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
+ @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
+ @Nullable UnderlyingNetworkRecord underlying) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
builder.addTransportType(TRANSPORT_CELLULAR);
@@ -978,6 +1225,52 @@
builder.addCapability(cap);
}
+ if (underlying != null) {
+ final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
+
+ // Mirror merged capabilities.
+ for (int cap : MERGED_CAPABILITIES) {
+ if (underlyingCaps.hasCapability(cap)) {
+ builder.addCapability(cap);
+ }
+ }
+
+ // Set admin UIDs for ConnectivityDiagnostics use.
+ final int[] underlyingAdminUids = underlyingCaps.getAdministratorUids();
+ Arrays.sort(underlyingAdminUids); // Sort to allow contains check below.
+
+ final int[] adminUids;
+ if (underlyingCaps.getOwnerUid() > 0 // No owner UID specified
+ && 0 > Arrays.binarySearch(// Owner UID not found in admin UID list.
+ underlyingAdminUids, underlyingCaps.getOwnerUid())) {
+ adminUids = Arrays.copyOf(underlyingAdminUids, underlyingAdminUids.length + 1);
+ adminUids[adminUids.length - 1] = underlyingCaps.getOwnerUid();
+ Arrays.sort(adminUids);
+ } else {
+ adminUids = underlyingAdminUids;
+ }
+ builder.setAdministratorUids(adminUids);
+
+ // Set TransportInfo for SysUI use (never parcelled out of SystemServer).
+ if (underlyingCaps.hasTransport(TRANSPORT_WIFI)
+ && underlyingCaps.getTransportInfo() instanceof WifiInfo) {
+ final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo();
+ builder.setTransportInfo(new VcnTransportInfo(wifiInfo));
+ } else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR)
+ && underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
+ final TelephonyNetworkSpecifier telNetSpecifier =
+ (TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier();
+ builder.setTransportInfo(new VcnTransportInfo(telNetSpecifier.getSubscriptionId()));
+ } else {
+ Slog.wtf(
+ TAG,
+ "Unknown transport type or missing TransportInfo/NetworkSpecifier for"
+ + " non-null underlying network");
+ }
+ }
+
+ // TODO: Make a VcnNetworkSpecifier, and match all underlying subscription IDs.
+
return builder.build();
}
@@ -1138,10 +1431,15 @@
public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
Set<Integer> requiredUnderlyingNetworkCapabilities,
UnderlyingNetworkTrackerCallback callback) {
return new UnderlyingNetworkTracker(
- vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback);
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ requiredUnderlyingNetworkCapabilities,
+ callback);
}
/** Builds a new IkeSession. */
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index b9babae..fe4ea30 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -25,6 +25,9 @@
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
import java.util.Objects;
import java.util.Set;
@@ -52,8 +55,13 @@
super(context, looper, VcnNetworkProvider.class.getSimpleName());
}
- // Package-private
- void registerListener(@NonNull NetworkRequestListener listener) {
+ /**
+ * Registers a NetworkRequestListener with this NetworkProvider.
+ *
+ * <p>Upon registering, the provided listener will receive all cached requests.
+ */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public void registerListener(@NonNull NetworkRequestListener listener) {
mListeners.add(listener);
// Send listener all cached requests
@@ -62,8 +70,9 @@
}
}
- // Package-private
- void unregisterListener(@NonNull NetworkRequestListener listener) {
+ /** Unregisters the specified listener from receiving future NetworkRequests. */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public void unregisterListener(@NonNull NetworkRequestListener listener) {
mListeners.remove(listener);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3198453..5697564 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1094,7 +1094,8 @@
return;
}
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
- mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
+ mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
+ null /* options */);
final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
try {
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 2ecefea..3bbc81a 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,12 +16,27 @@
package com.android.server.wm;
+import static android.os.Build.IS_USER;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_L;
+import static com.android.server.accessibility.AccessibilityTraceProto.ACCESSIBILITY_SERVICE;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALENDAR_TIME;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PARAMS;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS;
+import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME;
+import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME;
+import static com.android.server.accessibility.AccessibilityTraceProto.WHERE;
+import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MANAGER_SERVICE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.utils.RegionUtils.forEachRect;
@@ -29,7 +44,9 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
+import android.app.Application;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
@@ -41,15 +58,20 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.InsetsSource;
import android.view.MagnificationSpec;
@@ -64,13 +86,22 @@
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.TraceBuffer;
+import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
+import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -79,26 +110,37 @@
* This class contains the accessibility related logic of the window manager.
*/
final class AccessibilityController {
+ private static final String TAG = AccessibilityController.class.getSimpleName();
+ private static final Object STATIC_LOCK = new Object();
+ static AccessibilityControllerInternal
+ getAccessibilityControllerInternal(WindowManagerService service) {
+ return AccessibilityControllerInternalImpl.getInstance(service);
+ }
+
+ private final AccessibilityTracing mAccessibilityTracing;
private final WindowManagerService mService;
-
private static final Rect EMPTY_RECT = new Rect();
private static final float[] sTempFloats = new float[9];
- public AccessibilityController(WindowManagerService service) {
+ AccessibilityController(WindowManagerService service) {
mService = service;
+ mAccessibilityTracing = AccessibilityTracing.getInstance(service);
}
private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
-
private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
new SparseArray<>();
// Set to true if initializing window population complete.
private boolean mAllObserversInitialized = true;
- public boolean setMagnificationCallbacksLocked(int displayId,
- MagnificationCallbacks callbacks) {
+ boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".setMagnificationCallbacks",
+ "displayId=" + displayId + "; callbacks={" + callbacks + "}");
+ }
boolean result = false;
if (callbacks != null) {
if (mDisplayMagnifiers.get(displayId) != null) {
@@ -118,7 +160,7 @@
if (displayMagnifier == null) {
throw new IllegalStateException("Magnification callbacks already cleared!");
}
- displayMagnifier.destroyLocked();
+ displayMagnifier.destroy();
mDisplayMagnifiers.remove(displayId);
result = true;
}
@@ -133,8 +175,13 @@
* @param callback The callback.
* @return {@code false} if display id is not valid or an embedded display.
*/
- public boolean setWindowsForAccessibilityCallbackLocked(int displayId,
+ boolean setWindowsForAccessibilityCallback(int displayId,
WindowsForAccessibilityCallback callback) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".setWindowsForAccessibilityCallback",
+ "displayId=" + displayId + "; callback={" + callback + "}");
+ }
final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
if (dc == null) {
return false;
@@ -147,7 +194,7 @@
// empty, that means this mapping didn't be set, and needs to do this again.
// This happened when accessibility window observer is disabled and enabled again.
if (mWindowsForAccessibilityObserver.get(displayId) == null) {
- handleWindowObserverOfEmbeddedDisplayLocked(displayId, dc.getParentWindow());
+ handleWindowObserverOfEmbeddedDisplay(displayId, dc.getParentWindow());
}
return false;
} else if (mWindowsForAccessibilityObserver.get(displayId) != null) {
@@ -181,7 +228,12 @@
return true;
}
- public void performComputeChangedWindowsNotLocked(int displayId, boolean forceSend) {
+ void performComputeChangedWindowsNot(int displayId, boolean forceSend) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".performComputeChangedWindowsNot",
+ "displayId=" + displayId + "; forceSend=" + forceSend);
+ }
WindowsForAccessibilityObserver observer = null;
synchronized (mService.mGlobalLock) {
final WindowsForAccessibilityObserver windowsForA11yObserver =
@@ -191,86 +243,119 @@
}
}
if (observer != null) {
- observer.performComputeChangedWindowsNotLocked(forceSend);
+ observer.performComputeChangedWindows(forceSend);
}
}
- public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
+ void setMagnificationSpec(int displayId, MagnificationSpec spec) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".setMagnificationSpec",
+ "displayId=" + displayId + "; spec={" + spec + "}");
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.setMagnificationSpecLocked(spec);
+ displayMagnifier.setMagnificationSpec(spec);
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
- public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) {
+ void getMagnificationRegion(int displayId, Region outMagnificationRegion) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".getMagnificationRegion",
+ "displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion
+ + "}");
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
+ displayMagnifier.getMagnificationRegion(outMagnificationRegion);
}
}
- public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) {
+ void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".onRectangleOnScreenRequested",
+ "displayId=" + displayId + "; rectangle={" + rectangle + "}");
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
+ displayMagnifier.onRectangleOnScreenRequested(rectangle);
}
// Not relevant for the window observer.
}
- public void onWindowLayersChangedLocked(int displayId) {
+ void onWindowLayersChanged(int displayId) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".onWindowLayersChanged", "displayId=" + displayId);
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onWindowLayersChangedLocked();
+ displayMagnifier.onWindowLayersChanged();
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
- public void onRotationChangedLocked(DisplayContent displayContent) {
+ void onRotationChanged(DisplayContent displayContent) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".onRotationChanged",
+ "displayContent={" + displayContent + "}");
+ }
final int displayId = displayContent.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onRotationChangedLocked(displayContent);
+ displayMagnifier.onRotationChanged(displayContent);
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
- public void onAppWindowTransitionLocked(int displayId, int transition) {
+ void onAppWindowTransition(int displayId, int transition) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".onAppWindowTransition",
+ "displayId=" + displayId + "; transition=" + transition);
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onAppWindowTransitionLocked(displayId, transition);
+ displayMagnifier.onAppWindowTransition(displayId, transition);
}
// Not relevant for the window observer.
}
- public void onWindowTransitionLocked(WindowState windowState, int transition) {
+ void onWindowTransition(WindowState windowState, int transition) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".onWindowTransition",
+ "windowState={" + windowState + "}; transition=" + transition);
+ }
final int displayId = windowState.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onWindowTransitionLocked(windowState, transition);
+ displayMagnifier.onWindowTransition(windowState, transition);
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
- public void onWindowFocusChangedNotLocked(int displayId) {
+ void onWindowFocusChangedNot(int displayId) {
// Not relevant for the display magnifier.
-
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".onWindowFocusChangedNot", "displayId=" + displayId);
+ }
WindowsForAccessibilityObserver observer = null;
synchronized (mService.mGlobalLock) {
final WindowsForAccessibilityObserver windowsForA11yObserver =
@@ -280,7 +365,7 @@
}
}
if (observer != null) {
- observer.performComputeChangedWindowsNotLocked(false);
+ observer.performComputeChangedWindows(false);
}
// Since we abandon initializing observers if no window has focus, make sure all observers
// are initialized.
@@ -311,7 +396,7 @@
boolean areAllObserversInitialized = true;
for (int i = unInitializedObservers.size() - 1; i >= 0; --i) {
final WindowsForAccessibilityObserver observer = unInitializedObservers.get(i);
- observer.performComputeChangedWindowsNotLocked(true);
+ observer.performComputeChangedWindows(true);
areAllObserversInitialized &= observer.mInitialized;
}
synchronized (mService.mGlobalLock) {
@@ -324,50 +409,89 @@
* another display is also taken into consideration.
* @param displayIds the display ids of displays when the situation happens.
*/
- public void onSomeWindowResizedOrMovedLocked(int... displayIds) {
+ void onSomeWindowResizedOrMoved(int... displayIds) {
+ onSomeWindowResizedOrMovedWithCallingUid(Binder.getCallingUid(), displayIds);
+ }
+
+ void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".onSomeWindowResizedOrMoved",
+ "displayIds={" + displayIds.toString() + "}",
+ "".getBytes(),
+ callingUid);
+ }
// Not relevant for the display magnifier.
for (int i = 0; i < displayIds.length; i++) {
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayIds[i]);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
}
- public void drawMagnifiedRegionBorderIfNeededLocked(int displayId,
- SurfaceControl.Transaction t) {
+ void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ "displayId=" + displayId + "; transaction={" + t + "}");
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(t);
+ displayMagnifier.drawMagnifiedRegionBorderIfNeeded(t);
}
// Not relevant for the window observer.
}
- public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+ MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow",
+ "windowState={" + windowState + "}");
+ }
final int displayId = windowState.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- return displayMagnifier.getMagnificationSpecForWindowLocked(windowState);
+ return displayMagnifier.getMagnificationSpecForWindow(windowState);
}
return null;
}
- public boolean hasCallbacksLocked() {
+ boolean hasCallbacks() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".hasCallbacks");
+ }
return (mDisplayMagnifiers.size() > 0
|| mWindowsForAccessibilityObserver.size() > 0);
}
- public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) {
+ void setForceShowMagnifiableBounds(int displayId, boolean show) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds",
+ "displayId=" + displayId + "; show=" + show);
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.setForceShowMagnifiableBoundsLocked(show);
+ displayMagnifier.setForceShowMagnifiableBounds(show);
displayMagnifier.showMagnificationBoundsIfNeeded();
}
}
- public void handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId,
+ void handleWindowObserverOfEmbeddedDisplay(int embeddedDisplayId,
WindowState parentWindow) {
+ handleWindowObserverOfEmbeddedDisplay(
+ embeddedDisplayId, parentWindow, Binder.getCallingUid());
+ }
+
+ void handleWindowObserverOfEmbeddedDisplay(
+ int embeddedDisplayId, WindowState parentWindow, int callingUid) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay",
+ "embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={"
+ + parentWindow + "}",
+ "".getBytes(),
+ callingUid);
+ }
if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) {
return;
}
@@ -390,7 +514,7 @@
}
}
- private static void populateTransformationMatrixLocked(WindowState windowState,
+ private static void populateTransformationMatrix(WindowState windowState,
Matrix outMatrix) {
windowState.getTransformationMatrix(sTempFloats, outMatrix);
}
@@ -451,6 +575,7 @@
private final Handler mHandler;
private final DisplayContent mDisplayContent;
private final Display mDisplay;
+ private final AccessibilityTracing mAccessibilityTracing;
private final MagnificationCallbacks mCallbacks;
@@ -458,7 +583,7 @@
private boolean mForceShowMagnifiableBounds = false;
- public DisplayMagnifier(WindowManagerService windowManagerService,
+ DisplayMagnifier(WindowManagerService windowManagerService,
DisplayContent displayContent,
Display display,
MagnificationCallbacks callbacks) {
@@ -469,36 +594,58 @@
mDisplay = display;
mHandler = new MyHandler(mService.mH.getLooper());
mMagnifedViewport = new MagnifiedViewport();
+ mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
mLongAnimationDuration = mDisplayContext.getResources().getInteger(
com.android.internal.R.integer.config_longAnimTime);
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor",
+ "windowManagerService={" + windowManagerService + "}; displayContent={"
+ + displayContent + "}; display={" + display + "}; callbacks={"
+ + callbacks + "}");
+ }
}
- public void setMagnificationSpecLocked(MagnificationSpec spec) {
- mMagnifedViewport.updateMagnificationSpecLocked(spec);
- mMagnifedViewport.recomputeBoundsLocked();
+ void setMagnificationSpec(MagnificationSpec spec) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}");
+ }
+ mMagnifedViewport.updateMagnificationSpec(spec);
+ mMagnifedViewport.recomputeBounds();
mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec);
mService.scheduleAnimationLocked();
}
- public void setForceShowMagnifiableBoundsLocked(boolean show) {
+ void setForceShowMagnifiableBounds(boolean show) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show);
+ }
mForceShowMagnifiableBounds = show;
- mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
+ mMagnifedViewport.setMagnifiedRegionBorderShown(show, true);
}
- public boolean isForceShowingMagnifiableBoundsLocked() {
+ boolean isForceShowingMagnifiableBounds() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds");
+ }
return mForceShowMagnifiableBounds;
}
- public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
+ void onRectangleOnScreenRequested(Rect rectangle) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}");
+ }
if (DEBUG_RECTANGLE_REQUESTED) {
Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
}
- if (!mMagnifedViewport.isMagnifyingLocked()) {
+ if (!mMagnifedViewport.isMagnifying()) {
return;
}
Rect magnifiedRegionBounds = mTempRect2;
- mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
+ mMagnifedViewport.getMagnifiedFrameInContentCoords(magnifiedRegionBounds);
if (magnifiedRegionBounds.contains(rectangle)) {
return;
}
@@ -511,31 +658,42 @@
args).sendToTarget();
}
- public void onWindowLayersChangedLocked() {
+ void onWindowLayersChanged() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged");
+ }
if (DEBUG_LAYERS) {
Slog.i(LOG_TAG, "Layers changed.");
}
- mMagnifedViewport.recomputeBoundsLocked();
+ mMagnifedViewport.recomputeBounds();
mService.scheduleAnimationLocked();
}
- public void onRotationChangedLocked(DisplayContent displayContent) {
+ void onRotationChanged(DisplayContent displayContent) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}");
+ }
if (DEBUG_ROTATION) {
final int rotation = displayContent.getRotation();
Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
- mMagnifedViewport.onRotationChangedLocked(displayContent.getPendingTransaction());
+ mMagnifedViewport.onRotationChanged(displayContent.getPendingTransaction());
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
}
- public void onAppWindowTransitionLocked(int displayId, int transition) {
+ void onAppWindowTransition(int displayId, int transition) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition",
+ "displayId=" + displayId + "; transition=" + transition);
+ }
if (DEBUG_WINDOW_TRANSITIONS) {
Slog.i(LOG_TAG, "Window transition: "
+ AppTransition.appTransitionOldToString(transition)
+ " displayId: " + displayId);
}
- final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+ final boolean magnifying = mMagnifedViewport.isMagnifying();
if (magnifying) {
switch (transition) {
case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN:
@@ -550,13 +708,17 @@
}
}
- public void onWindowTransitionLocked(WindowState windowState, int transition) {
+ void onWindowTransition(WindowState windowState, int transition) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition",
+ "windowState={" + windowState + "}; transition=" + transition);
+ }
if (DEBUG_WINDOW_TRANSITIONS) {
Slog.i(LOG_TAG, "Window transition: "
+ AppTransition.appTransitionOldToString(transition)
+ " displayId: " + windowState.getDisplayId());
}
- final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+ final boolean magnifying = mMagnifedViewport.isMagnifying();
final int type = windowState.mAttrs.type;
switch (transition) {
case WindowManagerPolicy.TRANSIT_ENTER:
@@ -586,7 +748,7 @@
case WindowManager.LayoutParams.TYPE_QS_DIALOG:
case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Rect magnifiedRegionBounds = mTempRect2;
- mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
+ mMagnifedViewport.getMagnifiedFrameInContentCoords(
magnifiedRegionBounds);
Rect touchableRegionBounds = mTempRect1;
windowState.getTouchableRegion(mTempRegion1);
@@ -604,8 +766,12 @@
}
}
- public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
- MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
+ MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
+ "windowState={" + windowState + "}");
+ }
+ MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
if (spec != null && !spec.isNop()) {
if (!windowState.shouldMagnify()) {
return null;
@@ -614,24 +780,38 @@
return spec;
}
- public void getMagnificationRegionLocked(Region outMagnificationRegion) {
+ void getMagnificationRegion(Region outMagnificationRegion) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion",
+ "outMagnificationRegion={" + outMagnificationRegion + "}");
+ }
// Make sure we're working with the most current bounds
- mMagnifedViewport.recomputeBoundsLocked();
- mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
+ mMagnifedViewport.recomputeBounds();
+ mMagnifedViewport.getMagnificationRegion(outMagnificationRegion);
}
- public void destroyLocked() {
+ void destroy() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".destroy");
+ }
mMagnifedViewport.destroyWindow();
}
// Can be called outside of a surface transaction
- public void showMagnificationBoundsIfNeeded() {
+ void showMagnificationBoundsIfNeeded() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded");
+ }
mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
.sendToTarget();
}
- public void drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t) {
- mMagnifedViewport.drawWindowIfNeededLocked(t);
+ void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ "transition={" + t + "}");
+ }
+ mMagnifedViewport.drawWindowIfNeeded(t);
}
void dump(PrintWriter pw, String prefix) {
@@ -665,7 +845,7 @@
private boolean mFullRedrawNeeded;
private int mTempLayer = 0;
- public MagnifiedViewport() {
+ MagnifiedViewport() {
mBorderWidth = mDisplayContext.getResources().getDimension(
com.android.internal.R.dimen.accessibility_magnification_indicator_width);
mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
@@ -681,14 +861,14 @@
mCircularPath = null;
}
- recomputeBoundsLocked();
+ recomputeBounds();
}
- public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
+ void getMagnificationRegion(@NonNull Region outMagnificationRegion) {
outMagnificationRegion.set(mMagnificationRegion);
}
- public void updateMagnificationSpecLocked(MagnificationSpec spec) {
+ void updateMagnificationSpec(MagnificationSpec spec) {
if (spec != null) {
mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
} else {
@@ -698,12 +878,12 @@
// to show the border. We will do so when the pending message is handled.
if (!mHandler.hasMessages(
MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
- setMagnifiedRegionBorderShownLocked(
- isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
+ setMagnifiedRegionBorderShown(
+ isMagnifying() || isForceShowingMagnifiableBounds(), true);
}
}
- public void recomputeBoundsLocked() {
+ void recomputeBounds() {
mDisplay.getRealSize(mTempPoint);
final int screenWidth = mTempPoint.x;
final int screenHeight = mTempPoint.y;
@@ -721,7 +901,7 @@
SparseArray<WindowState> visibleWindows = mTempWindowStates;
visibleWindows.clear();
- populateWindowsOnScreenLocked(visibleWindows);
+ populateWindowsOnScreen(visibleWindows);
final int visibleWindowCount = visibleWindows.size();
for (int i = visibleWindowCount - 1; i >= 0; i--) {
@@ -736,7 +916,7 @@
// Consider the touchable portion of the window
Matrix matrix = mTempMatrix;
- populateTransformationMatrixLocked(windowState, matrix);
+ populateTransformationMatrix(windowState, matrix);
Region touchableRegion = mTempRegion3;
windowState.getTouchableRegion(touchableRegion);
Rect touchableFrame = mTempRect1;
@@ -848,24 +1028,24 @@
return letterboxBounds;
}
- public void onRotationChangedLocked(SurfaceControl.Transaction t) {
+ void onRotationChanged(SurfaceControl.Transaction t) {
// If we are showing the magnification border, hide it immediately so
// the user does not see strange artifacts during rotation. The screenshot
// used for rotation already has the border. After the rotation is complete
// we will show the border.
- if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
- setMagnifiedRegionBorderShownLocked(false, false);
+ if (isMagnifying() || isForceShowingMagnifiableBounds()) {
+ setMagnifiedRegionBorderShown(false, false);
final long delay = (long) (mLongAnimationDuration
* mService.getWindowAnimationScaleLocked());
Message message = mHandler.obtainMessage(
MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
mHandler.sendMessageDelayed(message, delay);
}
- recomputeBoundsLocked();
+ recomputeBounds();
mWindow.updateSize(t);
}
- public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
+ void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
if (shown) {
mFullRedrawNeeded = true;
mOldMagnificationRegion.set(0, 0, 0, 0);
@@ -873,31 +1053,31 @@
mWindow.setShown(shown, animate);
}
- public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
+ void getMagnifiedFrameInContentCoords(Rect rect) {
MagnificationSpec spec = mMagnificationSpec;
mMagnificationRegion.getBounds(rect);
rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
rect.scale(1.0f / spec.scale);
}
- public boolean isMagnifyingLocked() {
+ boolean isMagnifying() {
return mMagnificationSpec.scale > 1.0f;
}
- public MagnificationSpec getMagnificationSpecLocked() {
+ MagnificationSpec getMagnificationSpec() {
return mMagnificationSpec;
}
- public void drawWindowIfNeededLocked(SurfaceControl.Transaction t) {
- recomputeBoundsLocked();
+ void drawWindowIfNeeded(SurfaceControl.Transaction t) {
+ recomputeBounds();
mWindow.drawIfNeeded(t);
}
- public void destroyWindow() {
+ void destroyWindow() {
mWindow.releaseSurface();
}
- private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+ private void populateWindowsOnScreen(SparseArray<WindowState> outWindows) {
mTempLayer = 0;
mDisplayContent.forAllWindows((w) -> {
if (w.isOnScreen() && w.isVisible()
@@ -929,7 +1109,7 @@
private boolean mInvalidated;
- public ViewportWindow(Context context) {
+ ViewportWindow(Context context) {
SurfaceControl surfaceControl = null;
try {
mDisplay.getRealSize(mTempPoint);
@@ -971,7 +1151,7 @@
mInvalidated = true;
}
- public void setShown(boolean shown, boolean animate) {
+ void setShown(boolean shown, boolean animate) {
synchronized (mService.mGlobalLock) {
if (mShown == shown) {
return;
@@ -986,13 +1166,13 @@
@SuppressWarnings("unused")
// Called reflectively from an animator.
- public int getAlpha() {
+ int getAlpha() {
synchronized (mService.mGlobalLock) {
return mAlpha;
}
}
- public void setAlpha(int alpha) {
+ void setAlpha(int alpha) {
synchronized (mService.mGlobalLock) {
if (mAlpha == alpha) {
return;
@@ -1005,7 +1185,7 @@
}
}
- public void setBounds(Region bounds) {
+ void setBounds(Region bounds) {
synchronized (mService.mGlobalLock) {
if (mBounds.equals(bounds)) {
return;
@@ -1018,7 +1198,7 @@
}
}
- public void updateSize(SurfaceControl.Transaction t) {
+ void updateSize(SurfaceControl.Transaction t) {
synchronized (mService.mGlobalLock) {
mDisplay.getRealSize(mTempPoint);
t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y);
@@ -1026,7 +1206,7 @@
}
}
- public void invalidate(Rect dirtyRect) {
+ void invalidate(Rect dirtyRect) {
if (dirtyRect != null) {
mDirtyRect.set(dirtyRect);
} else {
@@ -1036,7 +1216,7 @@
mService.scheduleAnimationLocked();
}
- public void drawIfNeeded(SurfaceControl.Transaction t) {
+ void drawIfNeeded(SurfaceControl.Transaction t) {
synchronized (mService.mGlobalLock) {
if (!mInvalidated) {
return;
@@ -1078,7 +1258,7 @@
}
}
- public void releaseSurface() {
+ void releaseSurface() {
mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
mSurface.release();
}
@@ -1101,7 +1281,7 @@
private final ValueAnimator mShowHideFrameAnimator;
- public AnimationController(Context context, Looper looper) {
+ AnimationController(Context context, Looper looper) {
super(looper);
mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
@@ -1114,7 +1294,7 @@
mShowHideFrameAnimator.setDuration(longAnimationDuration);
}
- public void onFrameShownStateChanged(boolean shown, boolean animate) {
+ void onFrameShownStateChanged(boolean shown, boolean animate) {
obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
}
@@ -1158,7 +1338,7 @@
public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
- public MyHandler(Looper looper) {
+ MyHandler(Looper looper) {
super(looper);
}
@@ -1193,9 +1373,9 @@
case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
synchronized (mService.mGlobalLock) {
- if (mMagnifedViewport.isMagnifyingLocked()
- || isForceShowingMagnifiableBoundsLocked()) {
- mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
+ if (mMagnifedViewport.isMagnifying()
+ || isForceShowingMagnifiableBounds()) {
+ mMagnifedViewport.setMagnifiedRegionBorderShown(true, true);
mService.scheduleAnimationLocked();
}
}
@@ -1252,6 +1432,8 @@
private final Handler mHandler;
+ private final AccessibilityTracing mAccessibilityTracing;
+
private final WindowsForAccessibilityCallback mCallback;
private final int mDisplayId;
@@ -1263,24 +1445,32 @@
// Set to true if initializing window population complete.
private boolean mInitialized;
- public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
+ WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
int displayId,
WindowsForAccessibilityCallback callback) {
mService = windowManagerService;
mCallback = callback;
mDisplayId = displayId;
mHandler = new MyHandler(mService.mH.getLooper());
+ mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
.getSendRecurringAccessibilityEventsInterval();
computeChangedWindows(true);
}
- public void performComputeChangedWindowsNotLocked(boolean forceSend) {
+ void performComputeChangedWindows(boolean forceSend) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows",
+ "forceSend=" + forceSend);
+ }
mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
computeChangedWindows(forceSend);
}
- public void scheduleComputeChangedWindowsLocked() {
+ void scheduleComputeChangedWindows() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows");
+ }
if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
mRecurringAccessibilityEventsIntervalMillis);
@@ -1307,7 +1497,11 @@
*
* @param forceSend Send the windows the accessibility even if they haven't changed.
*/
- public void computeChangedWindows(boolean forceSend) {
+ void computeChangedWindows(boolean forceSend) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend);
+ }
if (DEBUG) {
Slog.i(LOG_TAG, "computeChangedWindows()");
}
@@ -1343,7 +1537,7 @@
unaccountedSpace.set(0, 0, screenWidth, screenHeight);
final SparseArray<WindowState> visibleWindows = mTempWindowStates;
- populateVisibleWindowsOnScreenLocked(visibleWindows);
+ populateVisibleWindowsOnScreen(visibleWindows);
Set<IBinder> addedWindows = mTempBinderSet;
addedWindows.clear();
@@ -1518,7 +1712,7 @@
// Map the frame to get what appears on the screen.
Matrix matrix = mTempMatrix;
- populateTransformationMatrixLocked(windowState, matrix);
+ populateTransformationMatrix(windowState, matrix);
forEachRect(touchableRegion, rect -> {
// Move to origin as all transforms are captured by the matrix.
@@ -1563,7 +1757,7 @@
&& windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
}
- private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+ private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) {
final List<WindowState> tempWindowStatesList = new ArrayList<>();
final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
if (dc == null) {
@@ -1637,4 +1831,292 @@
}
}
}
+
+ private static final class AccessibilityControllerInternalImpl
+ implements AccessibilityControllerInternal {
+
+ private static AccessibilityControllerInternal sInstance;
+ static AccessibilityControllerInternal getInstance(WindowManagerService service) {
+ synchronized (STATIC_LOCK) {
+ if (sInstance == null) {
+ sInstance = new AccessibilityControllerInternalImpl(service);
+ }
+ return sInstance;
+ }
+ }
+
+ private final AccessibilityTracing mTracing;
+ private AccessibilityControllerInternalImpl(WindowManagerService service) {
+ mTracing = AccessibilityTracing.getInstance(service);
+ }
+
+ @Override
+ public void startTrace() {
+ mTracing.startTrace();
+ }
+
+ @Override
+ public void stopTrace() {
+ mTracing.stopTrace();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mTracing.isEnabled();
+ }
+
+ @Override
+ public void logTrace(
+ String where, String callingParams, byte[] a11yDump, int callingUid,
+ StackTraceElement[] stackTrace) {
+ mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace);
+ }
+ }
+
+ private static final class AccessibilityTracing {
+ private static AccessibilityTracing sInstance;
+ static AccessibilityTracing getInstance(WindowManagerService service) {
+ synchronized (STATIC_LOCK) {
+ if (sInstance == null) {
+ sInstance = new AccessibilityTracing(service);
+ }
+ return sInstance;
+ }
+ }
+
+ private static final int BUFFER_CAPACITY = 4096 * 1024;
+ private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb";
+ private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/";
+ private static final String TAG = "AccessibilityTracing";
+ private static final long MAGIC_NUMBER_VALUE =
+ ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final Object mLock = new Object();
+ private final WindowManagerService mService;
+ private final File mTraceFile;
+ private final TraceBuffer mBuffer;
+ private final LogHandler mHandler;
+ private volatile boolean mEnabled;
+
+ AccessibilityTracing(WindowManagerService service) {
+ mService = service;
+ mTraceFile = new File(TRACE_FILENAME);
+ mBuffer = new TraceBuffer(BUFFER_CAPACITY);
+ HandlerThread workThread = new HandlerThread(TAG);
+ workThread.start();
+ mHandler = new LogHandler(workThread.getLooper());
+ }
+
+ /**
+ * Start the trace.
+ */
+ void startTrace() {
+ if (IS_USER) {
+ Slog.e(TAG, "Error: Tracing is not supported on user builds.");
+ return;
+ }
+ synchronized (mLock) {
+ try {
+ Files.createDirectories(Paths.get(TRACE_DIRECTORY));
+ mTraceFile.createNewFile();
+ } catch (Exception e) {
+ Slog.e(TAG, "Error: Failed to create trace file.");
+ return;
+ }
+ mEnabled = true;
+ mBuffer.resetBuffer();
+ }
+ }
+
+ /**
+ * Stops the trace and write the current buffer to disk
+ */
+ void stopTrace() {
+ if (IS_USER) {
+ Slog.e(TAG, "Error: Tracing is not supported on user builds.");
+ return;
+ }
+ synchronized (mLock) {
+ mEnabled = false;
+ if (mEnabled) {
+ Slog.e(TAG, "Error: tracing enabled while waiting for flush.");
+ return;
+ }
+ writeTraceToFile();
+ }
+ }
+
+ boolean isEnabled() {
+ return mEnabled;
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(String where) {
+ if (!mEnabled) {
+ return;
+ }
+ logState(where, "");
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(String where, String callingParams) {
+ if (!mEnabled) {
+ return;
+ }
+ logState(where, callingParams, "".getBytes());
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(String where, String callingParams, byte[] a11yDump) {
+ if (!mEnabled) {
+ return;
+ }
+ logState(where, callingParams, a11yDump, Binder.getCallingUid());
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(
+ String where, String callingParams, byte[] a11yDump, int callingUid) {
+ if (!mEnabled) {
+ return;
+ }
+ StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
+
+ logState(where, callingParams, a11yDump, callingUid, stackTraceElements);
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
+ StackTraceElement[] stackTrace) {
+ if (!mEnabled) {
+ return;
+ }
+
+ log(where, callingParams, a11yDump, callingUid, stackTrace);
+ }
+
+ private String toStackTraceString(StackTraceElement[] stackTraceElements) {
+ if (stackTraceElements == null) {
+ return "";
+ }
+ StringBuilder stringBuilder = new StringBuilder();
+ boolean skip = true;
+ for (int i = 0; i < stackTraceElements.length; i++) {
+ if (stackTraceElements[i].toString().contains(
+ AccessibilityTracing.class.getSimpleName())) {
+ skip = false;
+ } else if (!skip) {
+ stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+ }
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Write the current state to the buffer
+ */
+ private void log(String where, String callingParams, byte[] a11yDump, int callingUid,
+ StackTraceElement[] stackTrace) {
+ SimpleDateFormat fm = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = SystemClock.elapsedRealtimeNanos();
+ args.arg2 = fm.format(new Date()).toString();
+ args.arg3 = where;
+ args.arg4 = Process.myPid() + ":" + Application.getProcessName();
+ args.arg5 = Thread.currentThread().getId() + ":" + Thread.currentThread().getName();
+ args.arg6 = callingUid;
+ args.arg7 = callingParams;
+ args.arg8 = stackTrace;
+ args.arg9 = a11yDump;
+ mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget();
+ }
+
+ /**
+ * Writes the trace buffer to new file for the bugreport.
+ */
+ void writeTraceToFile() {
+ mHandler.sendEmptyMessage(LogHandler.MESSAGE_WRITE_FILE);
+ }
+
+ private class LogHandler extends Handler {
+ public static final int MESSAGE_LOG_TRACE_ENTRY = 1;
+ public static final int MESSAGE_WRITE_FILE = 2;
+
+ LogHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_LOG_TRACE_ENTRY: {
+ final SomeArgs args = (SomeArgs) message.obj;
+ try {
+ ProtoOutputStream os = new ProtoOutputStream();
+ PackageManagerInternal pmInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+
+ long tokenOuter = os.start(ENTRY);
+ String callingStack =
+ toStackTraceString((StackTraceElement[]) args.arg8);
+
+ os.write(ELAPSED_REALTIME_NANOS, (long) args.arg1);
+ os.write(CALENDAR_TIME, (String) args.arg2);
+ os.write(WHERE, (String) args.arg3);
+ os.write(PROCESS_NAME, (String) args.arg4);
+ os.write(THREAD_ID_NAME, (String) args.arg5);
+ os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg6));
+ os.write(CALLING_PARAMS, (String) args.arg7);
+ os.write(CALLING_STACKS, callingStack);
+ os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9);
+
+ long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+ synchronized (mService.mGlobalLock) {
+ mService.dumpDebugLocked(os, WindowTraceLogLevel.ALL);
+ }
+ os.end(tokenInner);
+
+ os.end(tokenOuter);
+ synchronized (mLock) {
+ mBuffer.add(os);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception while tracing state", e);
+ }
+ break;
+ }
+ case MESSAGE_WRITE_FILE: {
+ synchronized (mLock) {
+ writeTraceToFileInternal();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Writes the trace buffer to disk.
+ */
+ private void writeTraceToFileInternal() {
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ mBuffer.writeTraceToFile(mTraceFile, proto);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to write buffer to file", e);
+ }
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 5fe853a..771b712 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -54,6 +54,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -100,6 +101,17 @@
}
@Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(
+ "ActivityClientController", e);
+ }
+ }
+
+ @Override
public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
try {
@@ -963,7 +975,8 @@
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
r.mDisplayContent.mAppTransition.overridePendingAppTransition(
- packageName, enterAnim, exitAnim, null, null);
+ packageName, enterAnim, exitAnim, null, null,
+ r.mOverrideTaskTransition);
}
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 6bca484..3a0eb39 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -978,6 +978,7 @@
final TransitionInfoSnapshot infoSnapshot =
new TransitionInfoSnapshot(info, r, (int) startupTimeMs);
BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+ mLastTransitionInfo.remove(r);
if (!info.isInterestingToLoggerAndObserver()) {
return infoSnapshot;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ecbc01f..f29b57f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -82,7 +81,6 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -203,10 +201,12 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
@@ -252,6 +252,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -269,7 +270,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.permission.PermissionManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -325,6 +325,7 @@
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.Task.ActivityState;
import com.android.server.wm.WindowManagerService.H;
+import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
import com.android.server.wm.utils.InsetUtils;
import com.google.android.collect.Sets;
@@ -677,6 +678,7 @@
boolean mRequestForceTransition;
boolean mEnteringAnimation;
+ boolean mOverrideTaskTransition;
boolean mAppStopped;
// A hint to override the window specified rotation animation, or -1 to use the window specified
@@ -863,6 +865,9 @@
pw.print(Integer.toHexString(taskDescription.getStatusBarColor()));
pw.print(" navigationBarColor=");
pw.println(Integer.toHexString(taskDescription.getNavigationBarColor()));
+ pw.print(" backgroundColorFloating=");
+ pw.println(Integer.toHexString(
+ taskDescription.getBackgroundColorFloating()));
}
}
if (results != null) {
@@ -1357,7 +1362,6 @@
return;
}
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 && isLetterboxed(w);
updateRoundedCorners(w);
@@ -1365,7 +1369,8 @@
if (mLetterbox == null) {
mLetterbox = new Letterbox(() -> makeChildSurface(null),
mWmService.mTransactionFactory,
- mWmService::isLetterboxActivityCornersRounded);
+ mWmService::isLetterboxActivityCornersRounded,
+ this::getLetterboxBackgroundColor);
mLetterbox.attachInput(w);
}
getPosition(mTmpPoint);
@@ -1385,6 +1390,31 @@
}
}
+ private Color getLetterboxBackgroundColor() {
+ final WindowState w = findMainWindow();
+ if (w == null || w.isLetterboxedForDisplayCutout()) {
+ return Color.valueOf(Color.BLACK);
+ }
+ @LetterboxBackgroundType int letterboxBackgroundType =
+ mWmService.getLetterboxBackgroundType();
+ switch (letterboxBackgroundType) {
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColorFloating());
+ }
+ return mWmService.getLetterboxBackgroundColor();
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColor());
+ }
+ // Falling through
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ return mWmService.getLetterboxBackgroundColor();
+ }
+ throw new AssertionError(
+ "Unexpected letterbox background type: " + letterboxBackgroundType);
+ }
+
/** @return {@code true} when main window is letterboxed and activity isn't transparent. */
private boolean isLetterboxed(WindowState mainWindow) {
return mainWindow.isLetterboxedAppWindow() && fillsParent();
@@ -1593,6 +1623,8 @@
if (rotationAnimation >= 0) {
mRotationAnimationHint = rotationAnimation;
}
+
+ mOverrideTaskTransition = options.getOverrideTaskTransition();
}
ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
@@ -3963,7 +3995,8 @@
pendingOptions.getCustomEnterResId(),
pendingOptions.getCustomExitResId(),
pendingOptions.getAnimationStartedListener(),
- pendingOptions.getAnimationFinishedListener());
+ pendingOptions.getAnimationFinishedListener(),
+ pendingOptions.getOverrideTaskTransition());
break;
case ANIM_CLIP_REVEAL:
displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
@@ -6741,20 +6774,6 @@
// layout traversals.
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
-
- // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
- // size compat mode.
- if (providesMaxBounds()) {
- if (DEBUG_CONFIGURATION) {
- ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
- + "due to letterboxing? %s mismatch with parent bounds? %s size compat "
- + "mode %s", getUid(),
- resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null,
- !matchParentBounds(), inSizeCompatMode());
- }
- resolvedConfig.windowConfiguration
- .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
- }
}
/**
@@ -6938,19 +6957,6 @@
return super.getBounds();
}
- @Override
- public boolean providesMaxBounds() {
- // System and SystemUI should always be able to access the physical display bounds,
- // so do not provide it with the overridden maximum bounds.
- // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
- if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
- getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
- return false;
- }
- // Max bounds should be sandboxed when this is letterboxed or in size compat mode.
- return mLetterbox != null || !matchParentBounds() || inSizeCompatMode();
- }
-
@VisibleForTesting
@Override
Rect getAnimationBounds(int appRootTaskClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c6ed16c..79f8229 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1814,8 +1814,7 @@
}
private Task computeTargetTask() {
- if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
- && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+ if (mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
// A new task should be created instead of using existing one.
return null;
} else if (mSourceRecord != null) {
@@ -2098,13 +2097,6 @@
final ActivityRecord top = targetTask.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
- // The above code can remove {@code reusedActivity} from the task, leading to the
- // {@code ActivityRecord} removing its reference to the {@code Task}. The task
- // reference is needed in the call below to {@link setTargetStackAndMoveToFrontIfNeeded}
- if (targetTaskTop.getTask() == null) {
- targetTask.addChild(targetTaskTop);
- }
-
if (top != null) {
if (top.isRootOfTask()) {
// Activity aliases may mean we use different intents for the top activity,
@@ -2512,7 +2504,9 @@
// If bring to front is requested, and no result is requested and we have not been given
// an explicit task to launch in to, and we can find a task that was started with this
// same component, then instead of launching bring that one to the front.
- putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
+ putIntoExistingTask &= !isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)
+ ? (mInTask == null && mStartActivity.resultTo == null)
+ : (mInTask == null);
ActivityRecord intentActivity = null;
if (putIntoExistingTask) {
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 64b1e042..4bcef40 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -130,6 +130,7 @@
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.ActivityThread;
import android.app.AlertDialog;
+import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.Dialog;
@@ -494,6 +495,7 @@
*/
private volatile long mLastStopAppSwitchesTime;
+ private final List<AnrController> mAnrController = new ArrayList<>();
IActivityController mController = null;
boolean mControllerIsAMonkey = false;
@@ -2058,6 +2060,40 @@
return mAppSwitchesAllowed;
}
+ /** Register an {@link AnrController} to control the ANR dialog behavior */
+ public void registerAnrController(AnrController controller) {
+ synchronized (mGlobalLock) {
+ mAnrController.add(controller);
+ }
+ }
+
+ /** Unregister an {@link AnrController} */
+ public void unregisterAnrController(AnrController controller) {
+ synchronized (mGlobalLock) {
+ mAnrController.remove(controller);
+ }
+ }
+
+ /** @return the max ANR delay from all registered {@link AnrController} instances */
+ public long getMaxAnrDelayMillis(ApplicationInfo info) {
+ if (info == null || info.packageName == null) {
+ return 0;
+ }
+
+ final ArrayList<AnrController> controllers;
+ synchronized (mGlobalLock) {
+ controllers = new ArrayList<>(mAnrController);
+ }
+
+ final String packageName = info.packageName;
+ long maxDelayMs = 0;
+ for (AnrController controller : controllers) {
+ maxDelayMs = Math.max(maxDelayMs, controller.getAnrDelayMillis(packageName, info.uid));
+ }
+ maxDelayMs = Math.max(maxDelayMs, 0);
+ return maxDelayMs;
+ }
+
@Override
public void setActivityController(IActivityController controller, boolean imAMonkey) {
mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
@@ -2457,6 +2493,13 @@
if (referrer != null) {
pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
}
+ if (!pae.activity.isAttached()) {
+ // Skip directly because the caller activity may have been destroyed. If a caller
+ // is waiting for the assist data, it will be notified by timeout
+ // (see PendingAssistExtras#run()) and then pendingAssistExtrasTimedOut will clean
+ // up the request.
+ return;
+ }
if (structure != null) {
// Pre-fill the task/activity component for all assist data receivers
structure.setTaskId(pae.activity.getTask().mTaskId);
@@ -4842,15 +4885,21 @@
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
- if (!(e instanceof SecurityException)) {
- Slog.w(TAG, "Activity Task Manager onTransact aborts "
- + " UID:" + Binder.getCallingUid()
- + " PID:" + Binder.getCallingPid(), e);
- }
- throw e;
+ throw logAndRethrowRuntimeExceptionOnTransact(TAG, e);
}
}
+ /** Provides the full stack traces of non-security exception that occurs in onTransact. */
+ static RuntimeException logAndRethrowRuntimeExceptionOnTransact(String name,
+ RuntimeException e) {
+ if (!(e instanceof SecurityException)) {
+ Slog.w(TAG, name + " onTransact aborts"
+ + " UID:" + Binder.getCallingUid()
+ + " PID:" + Binder.getCallingPid(), e);
+ }
+ throw e;
+ }
+
/**
* Sets the corresponding {@link DisplayArea} information for the process global
* configuration. To be called when we need to show IME on a different {@link DisplayArea}
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index fde0369..c589fea 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -39,7 +39,7 @@
import android.os.Bundle;
import com.android.internal.R;
-import com.android.server.policy.IconUtilities;
+import com.android.internal.util.ImageUtils;
/** Displays an ongoing notification for a process displaying an alert window */
class AlertWindowNotification {
@@ -54,7 +54,6 @@
private final NotificationManager mNotificationManager;
private final String mPackageName;
private boolean mPosted;
- private IconUtilities mIconUtilities;
AlertWindowNotification(WindowManagerService service, String packageName) {
mService = service;
@@ -63,7 +62,6 @@
(NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE);
mNotificationTag = CHANNEL_PREFIX + mPackageName;
mRequestCode = sNextRequestCode++;
- mIconUtilities = new IconUtilities(mService.mContext);
}
void post() {
@@ -126,8 +124,9 @@
if (aInfo != null) {
final Drawable drawable = pm.getApplicationIcon(aInfo);
- if (drawable != null) {
- final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
+ int size = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
+ final Bitmap bitmap = ImageUtils.buildScaledBitmap(drawable, size, size);
+ if (bitmap != null) {
builder.setLargeIcon(bitmap);
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 90070c8..5b685b4 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -90,6 +90,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationStartListener;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
@@ -257,6 +258,7 @@
private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
+ private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
private int mLastClipRevealMaxTranslation;
@@ -266,6 +268,7 @@
private final boolean mLowRamRecentsEnabled;
private final int mDefaultWindowAnimationStyleResId;
+ private boolean mOverrideTaskTransition;
private RemoteAnimationController mRemoteAnimationController;
@@ -445,7 +448,7 @@
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
if (mRemoteAnimationController != null) {
- mRemoteAnimationController.goodToGo();
+ mRemoteAnimationController.goodToGo(transit);
}
return redoLayout;
}
@@ -508,6 +511,11 @@
mListeners.remove(listener);
}
+ void registerKeygaurdExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener) {
+ mKeyguardExitAnimationStartListener = listener;
+ }
+
public void notifyAppTransitionFinishedLocked(IBinder token) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onAppTransitionFinishedLocked(token);
@@ -971,7 +979,8 @@
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
boolean freeform, WindowContainer container) {
- if (mNextAppTransitionOverrideRequested && container.canCustomizeAppTransition()) {
+ if (mNextAppTransitionOverrideRequested
+ && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
}
@@ -1175,7 +1184,8 @@
}
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
- IRemoteCallback startedCallback, IRemoteCallback endedCallback) {
+ IRemoteCallback startedCallback, IRemoteCallback endedCallback,
+ boolean overrideTaskTransaction) {
if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionOverrideRequested = true;
@@ -1185,6 +1195,7 @@
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
mAnimationFinishedCallback = endedCallback;
+ mOverrideTaskTransition = overrideTaskTransaction;
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 582aeb3..9ca7eca 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -102,6 +102,7 @@
private final DisplayContent mDisplayContent;
private final WallpaperController mWallpaperControllerLocked;
private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
+ private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
@@ -437,10 +438,14 @@
return adapter;
}
}
- if (mRemoteAnimationDefinition == null) {
- return null;
+ if (mRemoteAnimationDefinition != null) {
+ final RemoteAnimationAdapter adapter = mRemoteAnimationDefinition.getAdapter(
+ transit, activityTypes);
+ if (adapter != null) {
+ return adapter;
+ }
}
- return mRemoteAnimationDefinition.getAdapter(transit, activityTypes);
+ return null;
}
/**
@@ -709,11 +714,17 @@
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
+ for (int i = 0; i < openingApps.size(); ++i) {
+ openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+ }
+ for (int i = 0; i < closingApps.size(); ++i) {
+ closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+ }
+
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
if (accessibilityController != null) {
- accessibilityController.onAppWindowTransitionLocked(
- mDisplayContent.getDisplayId(), transit);
+ accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 35260d3..ee1e64f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -204,6 +204,7 @@
import android.view.InsetsState.InternalInsetsType;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationDefinition;
+import android.view.RoundedCorners;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -337,6 +338,10 @@
= new RotationCache<>(this::calculateDisplayCutoutForRotationUncached);
boolean mIgnoreDisplayCutout;
+ RoundedCorners mInitialRoundedCorners;
+ private final RotationCache<RoundedCorners, RoundedCorners> mRoundedCornerCache =
+ new RotationCache<>(this::calculateRoundedCornersForRotationUncached);
+
/**
* Overridden display size. Initialized with {@link #mInitialDisplayWidth}
* and {@link #mInitialDisplayHeight}, but can be set via shell command "adb shell wm size".
@@ -680,6 +685,10 @@
*/
private boolean mInEnsureActivitiesVisible = false;
+ // Used to indicate that the movement of child tasks to top will not move the display to top as
+ // well and thus won't change the top resumed / focused record
+ boolean mDontMoveToTop;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -983,7 +992,8 @@
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mInsetsStateController = new InsetsStateController(this);
mDisplayFrames = new DisplayFrames(mDisplayId, mInsetsStateController.getRawInsetsState(),
- mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
+ mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation),
+ calculateRoundedCornersForRotation(mDisplayInfo.rotation));
initializeDisplayBaseInfo();
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
@@ -1220,7 +1230,7 @@
if (mWmService.mAccessibilityController != null) {
final int prevDisplayId = prevDc != null ? prevDc.getDisplayId() : INVALID_DISPLAY;
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(prevDisplayId,
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(prevDisplayId,
getDisplayId());
}
}
@@ -1696,8 +1706,9 @@
mTmpConfiguration.unset();
final DisplayInfo info = computeScreenConfiguration(mTmpConfiguration, rotation);
final WmDisplayCutout cutout = calculateDisplayCutoutForRotation(rotation);
+ final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
final DisplayFrames displayFrames = new DisplayFrames(mDisplayId, new InsetsState(), info,
- cutout);
+ cutout, roundedCorners);
token.applyFixedRotationTransform(info, displayFrames, mTmpConfiguration);
}
@@ -1843,7 +1854,7 @@
}
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onRotationChangedLocked(this);
+ mWmService.mAccessibilityController.onRotationChanged(this);
}
}
@@ -1958,6 +1969,23 @@
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
+ RoundedCorners calculateRoundedCornersForRotation(int rotation) {
+ return mRoundedCornerCache.getOrCompute(mInitialRoundedCorners, rotation);
+ }
+
+ private RoundedCorners calculateRoundedCornersForRotationUncached(
+ RoundedCorners roundedCorners, int rotation) {
+ if (roundedCorners == null || roundedCorners == RoundedCorners.NO_ROUNDED_CORNERS) {
+ return RoundedCorners.NO_ROUNDED_CORNERS;
+ }
+
+ if (rotation == ROTATION_0) {
+ return roundedCorners;
+ }
+
+ return roundedCorners.rotate(rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+ }
+
/**
* Compute display info and configuration according to the given rotation without changing
* current display.
@@ -2485,7 +2513,8 @@
void onDisplayInfoChanged() {
final DisplayInfo info = mDisplayInfo;
- mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation));
+ mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation),
+ calculateRoundedCornersForRotation(info.rotation));
mInputMonitor.layoutInputConsumers(info.logicalWidth, info.logicalHeight);
mDisplayPolicy.onDisplayInfoChanged(info);
}
@@ -2518,6 +2547,7 @@
mInitialDisplayHeight = mDisplayInfo.logicalHeight;
mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
mInitialDisplayCutout = mDisplayInfo.displayCutout;
+ mInitialRoundedCorners = mDisplayInfo.roundedCorners;
}
/**
@@ -2535,11 +2565,13 @@
final DisplayCutout newCutout = mIgnoreDisplayCutout
? DisplayCutout.NO_CUTOUT : mDisplayInfo.displayCutout;
final String newUniqueId = mDisplayInfo.uniqueId;
+ final RoundedCorners newRoundedCorners = mDisplayInfo.roundedCorners;
final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth
|| mInitialDisplayHeight != newHeight
|| mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi
- || !Objects.equals(mInitialDisplayCutout, newCutout);
+ || !Objects.equals(mInitialDisplayCutout, newCutout)
+ || !Objects.equals(mInitialRoundedCorners, newRoundedCorners);
final boolean physicalDisplayChanged = !newUniqueId.equals(mCurrentUniqueDisplayId);
if (displayMetricsChanged || physicalDisplayChanged) {
@@ -2558,6 +2590,7 @@
mInitialDisplayHeight = newHeight;
mInitialDisplayDensity = newDensity;
mInitialDisplayCutout = newCutout;
+ mInitialRoundedCorners = newRoundedCorners;
mCurrentUniqueDisplayId = newUniqueId;
reconfigureDisplayLocked();
}
@@ -3371,7 +3404,7 @@
}
void updateAccessibilityOnWindowFocusChanged(AccessibilityController accessibilityController) {
- accessibilityController.onWindowFocusChangedNotLocked(getDisplayId());
+ accessibilityController.onWindowFocusChangedNot(getDisplayId());
}
private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) {
@@ -4934,7 +4967,7 @@
if (!mLocationInParentWindow.equals(x, y)) {
mLocationInParentWindow.set(x, y);
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(mDisplayId);
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(mDisplayId);
}
notifyLocationInParentDisplayChanged();
}
@@ -5931,36 +5964,6 @@
}
}
- /**
- * Returns the number of window tokens without surface on this display. A {@link WindowToken}
- * won't have its {@link SurfaceControl} until a window is added to a {@link WindowToken}.
- * The purpose of this method is to accumulate non-window containing {@link WindowToken}s and
- * limit the usage if the count exceeds a number.
- *
- * @param callingUid app calling uid
- * @return the number of window tokens without surface on this display
- * @see WindowToken#addWindow(WindowState)
- */
- int getWindowTokensWithoutSurfaceCount(int callingUid) {
- List<WindowToken> tokens = new ArrayList<>(mTokenMap.values());
- int count = 0;
- for (int i = tokens.size() - 1; i >= 0; i--) {
- final WindowToken token = tokens.get(i);
- if (callingUid != token.getOwnerUid()) {
- continue;
- }
- // Skip if token is an Activity
- if (token.asActivityRecord() != null) {
- continue;
- }
- if (token.mSurfaceControl != null) {
- continue;
- }
- count++;
- }
- return count;
- }
-
MagnificationSpec getMagnificationSpec() {
return mMagnificationSpec;
}
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 43c1435..e4230a2 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -21,12 +21,12 @@
import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
-import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.InsetsState;
+import android.view.RoundedCorners;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -47,9 +47,6 @@
*/
public final Rect mUnrestricted = new Rect();
- /** The display cutout used for layout (after rotation) */
- @NonNull public WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
-
/**
* During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
*/
@@ -61,26 +58,37 @@
public int mRotation;
public DisplayFrames(int displayId, InsetsState insetsState, DisplayInfo info,
- WmDisplayCutout displayCutout) {
+ WmDisplayCutout displayCutout, RoundedCorners roundedCorners) {
mDisplayId = displayId;
mInsetsState = insetsState;
- onDisplayInfoUpdated(info, displayCutout);
+ onDisplayInfoUpdated(info, displayCutout, roundedCorners);
}
- public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout) {
+ /**
+ * Update {@link DisplayFrames} when {@link DisplayInfo} is updated.
+ *
+ * @param info the updated {@link DisplayInfo}.
+ * @param displayCutout the updated {@link DisplayCutout}.
+ * @param roundedCorners the updated {@link RoundedCorners}.
+ */
+ public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout,
+ RoundedCorners roundedCorners) {
mDisplayWidth = info.logicalWidth;
mDisplayHeight = info.logicalHeight;
mRotation = info.rotation;
- mDisplayCutout = displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
+ final WmDisplayCutout wmDisplayCutout =
+ displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
final InsetsState state = mInsetsState;
final Rect unrestricted = mUnrestricted;
final Rect safe = mDisplayCutoutSafe;
- final DisplayCutout cutout = mDisplayCutout.getDisplayCutout();
+ final DisplayCutout cutout = wmDisplayCutout.getDisplayCutout();
unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
state.setDisplayFrame(unrestricted);
state.setDisplayCutout(cutout);
+ state.setRoundedCorners(roundedCorners != null ? roundedCorners
+ : RoundedCorners.NO_ROUNDED_CORNERS);
if (!cutout.isEmpty()) {
if (cutout.getSafeInsetLeft() > 0) {
safe.left = unrestricted.left + cutout.getSafeInsetLeft();
@@ -118,12 +126,5 @@
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
+ " r=" + mRotation);
- final String myPrefix = prefix + " ";
- dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
- pw.println(myPrefix + "mDisplayCutout=" + mDisplayCutout);
- }
-
- private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
- pw.print(prefix + name + "="); frame.printShortString(pw); pw.println();
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index ed97848..61fe023 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,7 +25,7 @@
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.Display.TYPE_INTERNAL;
-import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
+import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
@@ -35,8 +35,9 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_GESTURES;
+import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
+import static android.view.ViewRootImpl.computeWindowBounds;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -128,7 +129,6 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.util.ArraySet;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -142,8 +142,6 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewDebug;
-import android.view.WindowInsets.Side;
-import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
@@ -168,7 +166,6 @@
import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
-import com.android.server.policy.WindowOrientationListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
@@ -1077,7 +1074,7 @@
rect.bottom = rect.top + getStatusBarHeight(displayFrames);
};
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
- mDisplayContent.setInsetProvider(ITYPE_TOP_GESTURES, win, frameProvider);
+ mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider);
mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider);
break;
case TYPE_NAVIGATION_BAR:
@@ -1104,7 +1101,7 @@
(displayFrames, windowState, inOutFrame) ->
inOutFrame.set(windowState.getFrame()));
- mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win,
+ mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win,
(displayFrames, windowState, inOutFrame) -> {
inOutFrame.top -= mBottomGestureAdditionalInset;
});
@@ -1396,29 +1393,15 @@
*
* @param attrs The LayoutParams of the window.
* @param windowToken The token of the window.
- * @param outFrame The frame of the window.
* @param outInsetsState The insets state of this display from the client's perspective.
* @param localClient Whether the client is from the our process.
* @return Whether to always consume the system bars.
* See {@link #areSystemBarsForcedShownLw(WindowState)}.
*/
- boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, Rect outFrame,
- InsetsState outInsetsState, boolean localClient) {
- final boolean isFixedRotationTransforming =
- windowToken != null && windowToken.isFixedRotationTransforming();
- final ActivityRecord activity = windowToken != null ? windowToken.asActivityRecord() : null;
- final Task task = activity != null ? activity.getTask() : null;
- final Rect taskBounds = isFixedRotationTransforming
- // Use token (activity) bounds if it is rotated because its task is not rotated.
- ? windowToken.getBounds()
- : (task != null ? task.getBounds() : null);
+ boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, InsetsState outInsetsState,
+ boolean localClient) {
final InsetsState state =
mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
- computeWindowBounds(attrs, state, windowToken, outFrame);
- if (taskBounds != null) {
- outFrame.intersect(taskBounds);
- }
-
final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken);
outInsetsState.set(state, inSizeCompatMode || localClient);
if (inSizeCompatMode) {
@@ -1559,28 +1542,6 @@
return !notFocusableForIm;
}
- private void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
- @Nullable WindowToken windowToken, Rect outBounds) {
- final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
- final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
- final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
- final Rect df = windowToken != null ? windowToken.getBounds() : state.getDisplayFrame();
- Insets insets = Insets.of(0, 0, 0, 0);
- for (int i = types.size() - 1; i >= 0; i--) {
- final InsetsSource source = state.peekSource(types.valueAt(i));
- if (source == null) {
- continue;
- }
- insets = Insets.max(insets, source.calculateInsets(
- df, attrs.isFitInsetsIgnoringVisibility()));
- }
- final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
- final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
- final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
- final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
- outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom);
- }
-
/**
* Called for each window attached to the window manager as layout is proceeding. The
* implementation of this function must take care of setting the window's frame, either here or
@@ -1621,7 +1582,7 @@
final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
final InsetsState state = win.getInsetsState();
- computeWindowBounds(attrs, state, win.mToken, df);
+ computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
if (attached == null) {
pf.set(df);
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6a86aee..48e4df7 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -69,7 +69,6 @@
import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.policy.WindowOrientationListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
@@ -253,7 +252,7 @@
if (isDefaultDisplay) {
final Handler uiHandler = UiThread.getHandler();
- mOrientationListener = new OrientationListener(mContext, uiHandler);
+ mOrientationListener = new OrientationListener(mContext, uiHandler, mService);
mOrientationListener.setCurrentRotation(mRotation);
mSettingsObserver = new SettingsObserver(uiHandler);
mSettingsObserver.observe();
@@ -1474,8 +1473,8 @@
final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
boolean mEnabled;
- OrientationListener(Context context, Handler handler) {
- super(context, handler);
+ OrientationListener(Context context, Handler handler, WindowManagerService service) {
+ super(context, handler, service);
}
private class UpdateRunnable implements Runnable {
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index b82fdd2..6d5abe1 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -16,11 +16,11 @@
package com.android.server.wm;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
@@ -196,6 +196,14 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
+ void setDontMoveToTop(DisplayContent dc, boolean dontMoveToTop) {
+ DisplayInfo displayInfo = dc.getDisplayInfo();
+ SettingsProvider.SettingsEntry overrideSettings =
+ mSettingsProvider.getSettings(displayInfo);
+ overrideSettings.mDontMoveToTop = dontMoveToTop;
+ mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+ }
+
boolean shouldShowSystemDecorsLocked(DisplayContent dc) {
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
// Default display should show system decors.
@@ -274,6 +282,10 @@
final int forcedScalingMode = settings.mForcedScalingMode != null
? settings.mForcedScalingMode : FORCE_SCALING_MODE_AUTO;
dc.mDisplayScalingDisabled = forcedScalingMode == FORCE_SCALING_MODE_DISABLED;
+
+ boolean dontMoveToTop = settings.mDontMoveToTop != null
+ ? settings.mDontMoveToTop : false;
+ dc.mDontMoveToTop = dontMoveToTop;
}
/**
@@ -358,6 +370,8 @@
Boolean mIgnoreOrientationRequest;
@Nullable
Boolean mIgnoreDisplayCutout;
+ @Nullable
+ Boolean mDontMoveToTop;
SettingsEntry() {}
@@ -432,6 +446,10 @@
mIgnoreDisplayCutout = other.mIgnoreDisplayCutout;
changed = true;
}
+ if (other.mDontMoveToTop != mDontMoveToTop) {
+ mDontMoveToTop = other.mDontMoveToTop;
+ changed = true;
+ }
return changed;
}
@@ -515,6 +533,11 @@
mIgnoreDisplayCutout = delta.mIgnoreDisplayCutout;
changed = true;
}
+ if (delta.mDontMoveToTop != null
+ && delta.mDontMoveToTop != mDontMoveToTop) {
+ mDontMoveToTop = delta.mDontMoveToTop;
+ changed = true;
+ }
return changed;
}
@@ -531,7 +554,8 @@
&& mImePolicy == null
&& mFixedToUserRotation == null
&& mIgnoreOrientationRequest == null
- && mIgnoreDisplayCutout == null;
+ && mIgnoreDisplayCutout == null
+ && mDontMoveToTop == null;
}
@Override
@@ -553,7 +577,8 @@
&& Objects.equals(mImePolicy, that.mImePolicy)
&& Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
&& Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
- && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout);
+ && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout)
+ && Objects.equals(mDontMoveToTop, that.mDontMoveToTop);
}
@Override
@@ -561,7 +586,8 @@
return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth,
mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy,
- mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout);
+ mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout,
+ mDontMoveToTop);
}
@Override
@@ -581,6 +607,7 @@
+ ", mFixedToUserRotation=" + mFixedToUserRotation
+ ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
+ ", mIgnoreDisplayCutout=" + mIgnoreDisplayCutout
+ + ", mDontMoveToTop=" + mDontMoveToTop
+ '}';
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 5f3ab43..737f810 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -405,6 +405,9 @@
"ignoreOrientationRequest", null /* defaultValue */);
settingsEntry.mIgnoreDisplayCutout = getBooleanAttribute(parser,
"ignoreDisplayCutout", null /* defaultValue */);
+ settingsEntry.mDontMoveToTop = getBooleanAttribute(parser,
+ "dontMoveToTop", null /* defaultValue */);
+
fileData.mSettings.put(name, settingsEntry);
}
XmlUtils.skipCurrentTag(parser);
@@ -496,6 +499,10 @@
out.attributeBoolean(null, "ignoreDisplayCutout",
settingsEntry.mIgnoreDisplayCutout);
}
+ if (settingsEntry.mDontMoveToTop != null) {
+ out.attributeBoolean(null, "dontMoveToTop",
+ settingsEntry.mDontMoveToTop);
+ }
out.endTag(null, "display");
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 803bec8..de4bdaa 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -47,7 +47,7 @@
mTouchRegion.set(touchRegion);
// We need to report touchable region changes to accessibility.
if (mDisplayContent.mWmService.mAccessibilityController != null) {
- mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(
+ mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
mDisplayContent.getDisplayId());
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 3ba7b7d..580d328 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
@@ -218,6 +219,9 @@
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_NAVIGATION_BAR);
state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ state.removeSource(ITYPE_IME);
+ }
}
return state;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 02a43b7..99c9e79 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -19,6 +19,7 @@
import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.SurfaceControl.HIDDEN;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
@@ -45,6 +46,8 @@
private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
private final Supplier<Boolean> mAreCornersRounded;
+ private final Supplier<Color> mColorSupplier;
+
private final Rect mOuter = new Rect();
private final Rect mInner = new Rect();
private final LetterboxSurface mTop = new LetterboxSurface("top");
@@ -64,10 +67,12 @@
*/
public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- Supplier<Boolean> areCornersRounded) {
+ Supplier<Boolean> areCornersRounded,
+ Supplier<Color> colorSupplier) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
mAreCornersRounded = areCornersRounded;
+ mColorSupplier = colorSupplier;
}
/**
@@ -268,6 +273,7 @@
private final String mType;
private SurfaceControl mSurface;
+ private Color mColor;
private final Rect mSurfaceFrameRelative = new Rect();
private final Rect mLayoutFrameGlobal = new Rect();
@@ -292,9 +298,8 @@
.setColorLayer()
.setCallsite("LetterboxSurface.createSurface")
.build();
- t.setLayer(mSurface, -1)
- .setColor(mSurface, new float[]{0, 0, 0})
- .setColorSpaceAgnostic(mSurface, true);
+
+ t.setLayer(mSurface, -1).setColorSpaceAgnostic(mSurface, true);
}
void attachInput(WindowState win) {
@@ -335,7 +340,7 @@
}
public void applySurfaceChanges(SurfaceControl.Transaction t) {
- if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
+ if (!needsApplySurfaceChanges()) {
// Nothing changed.
return;
}
@@ -344,6 +349,14 @@
if (mSurface == null) {
createSurface(t);
}
+
+ mColor = mColorSupplier.get();
+ final float[] rgbTmpFloat = new float[3];
+ rgbTmpFloat[0] = mColor.red();
+ rgbTmpFloat[1] = mColor.green();
+ rgbTmpFloat[2] = mColor.blue();
+ t.setColor(mSurface, rgbTmpFloat);
+
t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
mSurfaceFrameRelative.height());
@@ -358,7 +371,8 @@
}
public boolean needsApplySurfaceChanges() {
- return !mSurfaceFrameRelative.equals(mLayoutFrameRelative);
+ return !mSurfaceFrameRelative.equals(mLayoutFrameRelative)
+ || mColorSupplier.get() != mColor;
}
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e1fdefd..e02cce4 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -421,7 +421,10 @@
if (skipAnimation(task)) {
continue;
}
- addAnimation(task, !recentTaskIds.get(task.mTaskId));
+ addAnimation(task, !recentTaskIds.get(task.mTaskId), false /* hidden */,
+ (type, anim) -> task.forAllWindows(win -> {
+ win.onAnimationFinished(type, anim);
+ }, true /* traverseTopToBottom */));
}
// Skip the animation if there is nothing to animate
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index a1d2072..392f27e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -36,6 +36,7 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
@@ -98,7 +99,7 @@
/**
* Called when the transition is ready to be started, and all leashes have been set up.
*/
- void goodToGo() {
+ void goodToGo(@WindowManager.TransitionOldType int transit) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
if (mPendingAnimations.isEmpty() || mCanceled) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
@@ -123,11 +124,15 @@
// Create the remote wallpaper animation targets (if any)
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
+
+ // TODO(bc-unlock): Create the remote non app animation targets (if any)
+ final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
linkToDeathOfRunner();
- mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
- mFinishedCallback);
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
+ wallpaperTargets, nonAppTargets, mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
@@ -274,6 +279,7 @@
private void setRunningRemoteAnimation(boolean running) {
final int pid = mRemoteAnimationAdapter.getCallingPid();
final int uid = mRemoteAnimationAdapter.getCallingUid();
+
if (pid == 0) {
throw new RuntimeException("Calling pid of remote animation was null");
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bbf6c76..2c97f78 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -37,6 +37,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -932,7 +933,6 @@
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
- win.mWinAnimator.destroyPreservedSurfaceLocked(win.getSyncTransaction());
} while (i > 0);
mWmService.mDestroySurface.clear();
}
@@ -2180,6 +2180,8 @@
// display area, so reparent.
rootTask.reparent(taskDisplayArea, true /* onTop */);
}
+ mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CHANGE, rootTask);
+
// Defer the windowing mode change until after the transition to prevent the activity
// from doing work and changing the activity visuals while animating
// TODO(task-org): Figure-out more structured way to do this long term.
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 6df4536..6567195 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -253,6 +253,20 @@
throw new SecurityException(msg);
}
+ // Check if the caller is allowed to override any app transition animation.
+ final boolean overrideTaskTransition = options.getOverrideTaskTransition();
+ if (aInfo != null && overrideTaskTransition) {
+ final int startTasksFromRecentsPerm = ActivityTaskManagerService.checkPermission(
+ START_TASKS_FROM_RECENTS, callingPid, callingUid);
+ if (startTasksFromRecentsPerm != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + getIntentString(intent)
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with overrideTaskTransition=true";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+
// Check permission for remote animations
final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
if (adapter != null && supervisor.mService.checkPermission(
diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ScreenshotHashController.java
similarity index 79%
rename from services/core/java/com/android/server/wm/ImpressionAttestationController.java
rename to services/core/java/com/android/server/wm/ScreenshotHashController.java
index bad6c80..03f4e28 100644
--- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java
+++ b/services/core/java/com/android/server/wm/ScreenshotHashController.java
@@ -16,7 +16,9 @@
package com.android.server.wm;
-import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.service.screenshot.ScreenshotHasherService.EXTRA_SCREENSHOT_HASH;
+import static android.service.screenshot.ScreenshotHasherService.EXTRA_VERIFICATION_STATUS;
+import static android.service.screenshot.ScreenshotHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -44,9 +46,9 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.service.attestation.IImpressionAttestationService;
-import android.service.attestation.ImpressionAttestationService;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.IScreenshotHasherService;
+import android.service.screenshot.ScreenshotHash;
+import android.service.screenshot.ScreenshotHasherService;
import android.util.Slog;
import android.view.MagnificationSpec;
@@ -59,30 +61,29 @@
import java.util.function.BiConsumer;
/**
- * Handles requests into {@link ImpressionAttestationService}
+ * Handles requests into {@link android.service.screenshot.ScreenshotHasherService}
*
* Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are
* blocking calls into another service.
*/
-public class ImpressionAttestationController {
- private static final String TAG =
- TAG_WITH_CLASS_NAME ? "ImpressionAttestationController" : TAG_WM;
+public class ScreenshotHashController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenshotHashController" : TAG_WM;
private static final boolean DEBUG = false;
private final Object mServiceConnectionLock = new Object();
@GuardedBy("mServiceConnectionLock")
- private ImpressionAttestationServiceConnection mServiceConnection;
+ private ScreenshotHasherServiceConnection mServiceConnection;
private final Context mContext;
/**
- * Lock used for the cached {@link #mImpressionAlgorithms} array
+ * Lock used for the cached {@link #mHashingAlgorithms} array
*/
- private final Object mImpressionAlgorithmsLock = new Object();
+ private final Object mHashingAlgorithmsLock = new Object();
- @GuardedBy("mImpressionAlgorithmsLock")
- private String[] mImpressionAlgorithms;
+ @GuardedBy("mHashingAlgorithmsLock")
+ private String[] mHashingAlgorithms;
private final Handler mHandler;
@@ -93,24 +94,24 @@
private final RectF mTmpRectF = new RectF();
private interface Command {
- void run(IImpressionAttestationService service) throws RemoteException;
+ void run(IScreenshotHasherService service) throws RemoteException;
}
- ImpressionAttestationController(Context context) {
+ ScreenshotHashController(Context context) {
mContext = context;
mHandler = new Handler(Looper.getMainLooper());
mSalt = UUID.randomUUID().toString().getBytes();
}
- String[] getSupportedImpressionAlgorithms() {
- // We have a separate lock for the impression algorithm array since it doesn't need to make
+ String[] getSupportedHashingAlgorithms() {
+ // We have a separate lock for the hashing algorithm array since it doesn't need to make
// the request through the service connection. Instead, we have a lock to ensure we can
- // properly cache the impression algorithms array so we don't need to call into the
+ // properly cache the hashing algorithms array so we don't need to call into the
// ExtServices process for each request.
- synchronized (mImpressionAlgorithmsLock) {
+ synchronized (mHashingAlgorithmsLock) {
// Already have cached values
- if (mImpressionAlgorithms != null) {
- return mImpressionAlgorithms;
+ if (mHashingAlgorithms != null) {
+ return mHashingAlgorithms;
}
final ServiceInfo serviceInfo = getServiceInfo();
@@ -127,54 +128,55 @@
final int resourceId = serviceInfo.metaData.getInt(
SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
- mImpressionAlgorithms = res.getStringArray(resourceId);
+ mHashingAlgorithms = res.getStringArray(resourceId);
- return mImpressionAlgorithms;
+ return mHashingAlgorithms;
}
}
- boolean verifyImpressionToken(ImpressionToken impressionToken) {
+ boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
final SyncCommand syncCommand = new SyncCommand();
Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
- service.verifyImpressionToken(mSalt, impressionToken, remoteCallback);
+ service.verifyScreenshotHash(mSalt, screenshotHash, remoteCallback);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke verifyImpressionToken command");
+ Slog.e(TAG, "Failed to invoke verifyScreenshotHash command");
}
});
- return results.getBoolean(ImpressionAttestationService.EXTRA_VERIFICATION_STATUS);
+ return results.getBoolean(EXTRA_VERIFICATION_STATUS);
}
- ImpressionToken generateImpressionToken(HardwareBuffer screenshot, Rect bounds,
+ ScreenshotHash generateScreenshotHash(HardwareBuffer screenshot, Rect bounds,
String hashAlgorithm) {
final SyncCommand syncCommand = new SyncCommand();
Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
- service.generateImpressionToken(mSalt, screenshot, bounds, hashAlgorithm,
+ service.generateScreenshotHash(mSalt, screenshot, bounds, hashAlgorithm,
remoteCallback);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke generateImpressionToken command", e);
+ Slog.e(TAG, "Failed to invoke generateScreenshotHash command", e);
}
});
- return results.getParcelable(ImpressionAttestationService.EXTRA_IMPRESSION_TOKEN);
+ return results.getParcelable(EXTRA_SCREENSHOT_HASH);
}
/**
- * Calculate the bounds to take the screenshot when generating the impression token. This takes
- * into account window transform, magnification, and display bounds.
+ * Calculate the bounds to take the screenshot when generating the ScreenshotHash. This
+ * takes into account window transform, magnification, and display bounds.
*
* Call while holding {@link WindowManagerService#mGlobalLock}
*
- * @param win Window that the impression token is generated for.
+ * @param win Window that the ScreenshotHash is generated for.
* @param boundsInWindow The bounds passed in about where in the window to take the screenshot.
* @param outBounds The result of the calculated bounds
*/
- void calculateImpressionTokenBoundsLocked(WindowState win, Rect boundsInWindow,
+ void calculateScreenshotHashBoundsLocked(WindowState win, Rect boundsInWindow,
Rect outBounds) {
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsInWindow=" + boundsInWindow);
+ Slog.d(TAG,
+ "calculateScreenshotHashBoundsLocked: boundsInWindow=" + boundsInWindow);
}
outBounds.set(boundsInWindow);
@@ -192,7 +194,8 @@
outBounds.intersectUnchecked(windowBounds);
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsIntersectWindow=" + outBounds);
+ Slog.d(TAG,
+ "calculateScreenshotHashBoundsLocked: boundsIntersectWindow=" + outBounds);
}
if (outBounds.isEmpty()) {
@@ -207,7 +210,7 @@
outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right,
(int) mTmpRectF.bottom);
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsInDisplay=" + outBounds);
+ Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsInDisplay=" + outBounds);
}
// Apply the magnification spec values to the bounds since the content could be magnified
@@ -218,7 +221,8 @@
}
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: boundsWithMagnification=" + outBounds);
+ Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsWithMagnification="
+ + outBounds);
}
if (outBounds.isEmpty()) {
@@ -230,7 +234,7 @@
final Rect displayBounds = displayContent.getBounds();
outBounds.intersectUnchecked(displayBounds);
if (DEBUG) {
- Slog.d(TAG, "calculateImpressionTokenBounds: finalBounds=" + outBounds);
+ Slog.d(TAG, "calculateScreenshotHashBoundsLocked: finalBounds=" + outBounds);
}
}
@@ -244,7 +248,7 @@
if (DEBUG) Slog.v(TAG, "creating connection");
// Create the connection
- mServiceConnection = new ImpressionAttestationServiceConnection();
+ mServiceConnection = new ScreenshotHasherServiceConnection();
final ComponentName component = getServiceComponentName();
if (DEBUG) Slog.v(TAG, "binding to: " + component);
@@ -275,7 +279,7 @@
return null;
}
- final Intent intent = new Intent(ImpressionAttestationService.SERVICE_INTERFACE);
+ final Intent intent = new Intent(ScreenshotHasherService.SERVICE_INTERFACE);
intent.setPackage(packageName);
final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
@@ -292,10 +296,10 @@
if (serviceInfo == null) return null;
final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- if (!Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE
+ if (!Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE
.equals(serviceInfo.permission)) {
Slog.w(TAG, name.flattenToShortString() + " requires permission "
- + Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE);
+ + Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE);
return null;
}
@@ -308,7 +312,7 @@
private Bundle mResult;
private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
- public Bundle run(BiConsumer<IImpressionAttestationService, RemoteCallback> func) {
+ public Bundle run(BiConsumer<IScreenshotHasherService, RemoteCallback> func) {
connectAndRun(service -> {
RemoteCallback callback = new RemoteCallback(result -> {
mResult = result;
@@ -327,9 +331,9 @@
}
}
- private class ImpressionAttestationServiceConnection implements ServiceConnection {
+ private class ScreenshotHasherServiceConnection implements ServiceConnection {
@GuardedBy("mServiceConnectionLock")
- private IImpressionAttestationService mRemoteService;
+ private IScreenshotHasherService mRemoteService;
@GuardedBy("mServiceConnectionLock")
private ArrayList<Command> mQueuedCommands;
@@ -338,7 +342,7 @@
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name);
synchronized (mServiceConnectionLock) {
- mRemoteService = IImpressionAttestationService.Stub.asInterface(service);
+ mRemoteService = IScreenshotHasherService.Stub.asInterface(service);
if (mQueuedCommands != null) {
final int size = mQueuedCommands.size();
if (DEBUG) Slog.d(TAG, "running " + size + " queued commands");
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 8d8bdcb..1f8daf6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -57,7 +57,7 @@
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.MergedConfiguration;
@@ -192,32 +192,30 @@
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
+ int viewVisibility, int displayId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
- UserHandle.getUserId(mUid), requestedVisibility, outFrame, outInputChannel,
- outInsetsState, outActiveControls);
+ UserHandle.getUserId(mUid), requestedVisibility, outInputChannel, outInsetsState,
+ outActiveControls);
}
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
- Rect outFrame, InputChannel outInputChannel, InsetsState outInsetsState,
+ InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
- requestedVisibility, outFrame, outInputChannel, outInsetsState,
- outActiveControls);
+ requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
}
@Override
public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, InsetsState outInsetsState) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
- UserHandle.getUserId(mUid), mDummyRequestedVisibility,
- new Rect() /* outFrame */, null /* outInputChannel */, outInsetsState,
- mDummyControls);
+ UserHandle.getUserId(mUid), mDummyRequestedVisibility, null /* outInputChannel */,
+ outInsetsState, mDummyControls);
}
@Override
@@ -862,11 +860,11 @@
}
@Override
- public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+ public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
String hashAlgorithm) {
final long origId = Binder.clearCallingIdentity();
try {
- return mService.generateImpressionToken(this, window, boundsInWindow, hashAlgorithm);
+ return mService.generateScreenshotHash(this, window, boundsInWindow, hashAlgorithm);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 62c0527..0902948 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -191,7 +191,7 @@
}
}
if (mDisplayContent.mWmService.mAccessibilityController != null) {
- mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(
+ mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
mDisplayContent.getDisplayId());
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0406bc9..e44a028 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -77,7 +77,6 @@
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -145,7 +144,6 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -921,10 +919,8 @@
return;
}
- if (isLeafTask()) {
- // This task is going away, so save the last state if necessary.
- saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
- }
+ // This task is going away, so save the last state if necessary.
+ saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
// TODO: VI what about activity?
final boolean isVoiceSession = voiceSession != null;
@@ -2092,7 +2088,9 @@
td.setEnsureNavigationBarContrastWhenTransparent(
atd.getEnsureNavigationBarContrastWhenTransparent());
}
-
+ if (td.getBackgroundColorFloating() == 0) {
+ td.setBackgroundColorFloating(atd.getBackgroundColorFloating());
+ }
}
// End search once we get to root.
@@ -2317,12 +2315,9 @@
return;
}
- final boolean windowingModeChanged = prevWindowingMode != getWindowingMode();
- final int overrideWindowingMode = getRequestedOverrideWindowingMode();
- // Update bounds if applicable
- boolean hasNewOverrideBounds = false;
// Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if ((overrideWindowingMode != WINDOWING_MODE_PINNED)
+ final int overrideWindowingMode = getRequestedOverrideWindowingMode();
+ if (overrideWindowingMode != WINDOWING_MODE_PINNED
&& !getRequestedOverrideBounds().isEmpty()) {
// If the parent (display) has rotated, rotate our bounds to best-fit where their
// bounds were on the pre-rotated display.
@@ -2332,22 +2327,10 @@
mDisplayContent.rotateBounds(
newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation,
newBounds);
- hasNewOverrideBounds = true;
+ setBounds(newBounds);
}
}
- if (windowingModeChanged) {
- taskDisplayArea.onRootTaskWindowingModeChanged(this);
- }
- if (hasNewOverrideBounds) {
- if (inSplitScreenWindowingMode()) {
- setBounds(newBounds);
- } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
- // For root pinned task, resize is now part of the {@link
- // WindowContainerTransaction}
- resize(new Rect(newBounds), PRESERVE_WINDOWS, true /* deferResume */);
- }
- }
if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
// Since always on top is only on when the root task is freeform or pinned, the state
// can be toggled when the windowing mode changes. We must make sure the root task is
@@ -2472,14 +2455,16 @@
/**
* Saves launching state if necessary so that we can launch the activity to its latest state.
- * It only saves state if this task has been shown to user and it's in fullscreen or freeform
- * mode on freeform displays.
*/
private void saveLaunchingStateIfNeeded() {
saveLaunchingStateIfNeeded(getDisplayContent());
}
private void saveLaunchingStateIfNeeded(DisplayContent display) {
+ if (!isLeafTask()) {
+ return;
+ }
+
if (!getHasBeenVisible()) {
// Not ever visible to user.
return;
@@ -2880,16 +2865,6 @@
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
outBounds.setEmpty();
computeLetterboxBounds(outBounds, newParentConfig);
- // Since the task is letterboxed due to mismatched orientation against its parent,
- // sandbox max bounds to the app bounds.
- if (!outBounds.isEmpty()) {
- if (DEBUG_CONFIGURATION) {
- ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched "
- + "orientation with parent, to %s vs DisplayArea %s", outBounds,
- getDisplayArea() != null ? getDisplayArea().getBounds() : "null");
- }
- getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds);
- }
}
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 3bd11ba..e18219e 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -23,7 +23,6 @@
import android.content.ComponentName;
import android.os.Binder;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
@@ -52,15 +51,14 @@
private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
- private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
- private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
- private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 22;
- private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 23;
- private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 24;
- private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 25;
- private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 26;
- private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 27;
- private static final int NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG = 28;
+ private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 20;
+ private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 21;
+ private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 22;
+ private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 23;
+ private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 24;
+ private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 25;
+ private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 26;
+ private static final int NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG = 27;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -151,10 +149,6 @@
l.onTaskSnapshotChanged(m.arg1, (TaskSnapshot) m.obj);
};
- private final TaskStackConsumer mOnSizeCompatModeActivityChanged = (l, m) -> {
- l.onSizeCompatModeActivityChanged(m.arg1, (IBinder) m.obj);
- };
-
private final TaskStackConsumer mNotifyTaskDisplayChanged = (l, m) -> {
l.onTaskDisplayChanged(m.arg1, m.arg2);
};
@@ -250,9 +244,6 @@
case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
break;
- case NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG:
- forAllRemoteListeners(mOnSizeCompatModeActivityChanged, msg);
- break;
case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
break;
@@ -490,17 +481,6 @@
}
/**
- * Notify listeners that whether a size compatibility mode activity is using the override
- * bounds which is not fit its parent.
- */
- void notifySizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
- final Message msg = mHandler.obtainMessage(NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG,
- displayId, 0 /* unused */, activityToken);
- forAllLocalListeners(mOnSizeCompatModeActivityChanged, msg);
- msg.sendToTarget();
- }
-
- /**
* Notify listeners that an activity received a back press when there are no other activities
* in the back stack.
*/
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4185407..40248c4 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -47,6 +47,7 @@
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
@@ -403,7 +404,11 @@
}
// We don't allow untrusted display to top when root task moves to top,
// until user tapping this display to change display position as top intentionally.
- if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) {
+ //
+ // Displays with {@code mDontMoveToTop} property set to {@code true} won't be
+ // allowed to top neither.
+ if ((!mDisplayContent.isTrusted() || mDisplayContent.mDontMoveToTop)
+ && !getParent().isOnTop()) {
includingParents = false;
}
final int targetPosition = findPositionForRootTask(position, child, false /* adding */);
@@ -905,6 +910,13 @@
}
}
+ @Override
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ final ActivityRecord activity = getTopMostActivity();
+ return activity != null ? activity.createRemoteAnimationTarget(record) : null;
+ }
+
SurfaceControl getSplitScreenDividerAnchor() {
return mSplitScreenDividerAnchor;
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index b3e0108..9fac3f0 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
@@ -33,6 +34,7 @@
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.RemoteException;
import android.util.Slog;
import android.view.SurfaceControl;
@@ -357,8 +359,14 @@
mGlobalLock = atm.mGlobalLock;
}
- private void enforceTaskPermission(String func) {
- mService.enforceTaskPermission(func);
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 09df71c..07610ab 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -226,9 +226,8 @@
}
int displayId = activity.getDisplayContent().getDisplayId();
try {
- final int res = session.addToDisplay(window, layoutParams,
- View.GONE, displayId, mTmpInsetsState, tmpFrames.frame,
- null /* outInputChannel */, mTmpInsetsState, mTempControls);
+ final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
+ mTmpInsetsState, null /* outInputChannel */, mTmpInsetsState, mTempControls);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return null;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 46aea23..98eb11f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -372,8 +372,6 @@
dc.mWallpaperController.startWallpaperAnimation(anim);
}
}
- }
- if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
dc.startKeyguardExitOnNonAppWindows(
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index f572e8e..43303d4 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -51,7 +50,7 @@
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) {
- super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID,
+ super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens,
false /* roundedCornerOverlay */, false /* fromClientToken */, options);
dc.mWallpaperController.addWallpaperToken(this);
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 52ed278..91a6664 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -164,7 +164,7 @@
dc.checkAppWindowsReadyToShow();
if (accessibilityController != null) {
- accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
+ accessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId,
mTransaction);
}
}
@@ -220,8 +220,6 @@
mRemoveReplacedWindows = false;
}
- mService.destroyPreservedSurfaceLocked();
-
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
executeAfterPrepareSurfacesRunnables();
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index a5ebf9a..015a0fb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -49,6 +49,10 @@
static final String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS =
"system_gesture_exclusion_log_debounce_millis";
+ // Enable logging from the sensor which publishes accel and gyro data generating a rotation
+ // event
+ private static final String KEY_RAW_SENSOR_LOGGING_ENABLED = "raw_sensor_logging_enabled";
+
private static final int MIN_GESTURE_EXCLUSION_LIMIT_DP = 200;
/** @see #KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS */
@@ -58,6 +62,8 @@
/** @see AndroidDeviceConfig#KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE */
boolean mSystemGestureExcludedByPreQStickyImmersive;
+ boolean mRawSensorLoggingEnabled;
+
private final WindowManagerGlobalLock mGlobalLock;
private final Runnable mUpdateSystemGestureExclusionCallback;
private final DeviceConfigInterface mDeviceConfig;
@@ -133,6 +139,9 @@
case KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS:
updateSystemGestureExclusionLogDebounceMillis();
break;
+ case KEY_RAW_SENSOR_LOGGING_ENABLED:
+ updateRawSensorDataLoggingEnabled();
+ break;
default:
break;
}
@@ -158,6 +167,12 @@
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
}
+ private void updateRawSensorDataLoggingEnabled() {
+ mRawSensorLoggingEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_RAW_SENSOR_LOGGING_ENABLED, false);
+ }
+
void dump(PrintWriter pw) {
pw.println("WINDOW MANAGER CONSTANTS (dumpsys window constants):");
@@ -167,6 +182,8 @@
pw.print("="); pw.println(mSystemGestureExclusionLimitDp);
pw.print(" "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE);
pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
+ pw.print(" "); pw.print(KEY_RAW_SENSOR_LOGGING_ENABLED);
+ pw.print("="); pw.println(mRawSensorLoggingEnabled);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a3a9eb7..bb04ace 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -23,12 +23,15 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
+import android.os.Bundle;
import android.os.IBinder;
import android.view.Display;
import android.view.IInputFilter;
+import android.view.IRemoteAnimationFinishedCallback;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.MagnificationSpec;
+import android.view.RemoteAnimationTarget;
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
@@ -46,6 +49,41 @@
public abstract class WindowManagerInternal {
/**
+ * Interface for accessibility features implemented by AccessibilityController inside
+ * WindowManager.
+ */
+ public interface AccessibilityControllerInternal {
+ /**
+ * Enable the accessibility trace logging.
+ */
+ void startTrace();
+
+ /**
+ * Disable the accessibility trace logging.
+ */
+ void stopTrace();
+
+ /**
+ * Is trace enabled or not.
+ */
+ boolean isEnabled();
+
+ /**
+ * Add an accessibility trace entry.
+ *
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ * @param callingParams The parameters for the method to be logged.
+ * @param a11yDump The proto byte array for a11y state when the entry is generated.
+ * @param callingUid The calling uid.
+ * @param stackTrace The stack trace, null if not needed.
+ */
+ void logTrace(
+ String where, String callingParams, byte[] a11yDump, int callingUid,
+ StackTraceElement[] stackTrace);
+ }
+
+ /**
* Interface to receive a callback when the windows reported for
* accessibility changed.
*/
@@ -154,6 +192,21 @@
}
/**
+ * An interface to be notified when keyguard exit animation should start.
+ */
+ public interface KeyguardExitAnimationStartListener {
+ /**
+ * Called when keyguard exit animation should start.
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ IRemoteAnimationFinishedCallback finishedCallback);
+ }
+
+ /**
* An interface to be notified about hardware keyboard status.
*/
public interface OnHardKeyboardStatusChangeListener {
@@ -207,6 +260,11 @@
}
/**
+ * Request the interface to access features implemented by AccessibilityController.
+ */
+ public abstract AccessibilityControllerInternal getAccessibilityController();
+
+ /**
* Request that the window manager call
* {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
* within a surface transaction at a later time.
@@ -351,8 +409,10 @@
* @param token The token to add.
* @param type The window type.
* @param displayId The display to add the token to.
+ * @param options A bundle used to pass window-related options.
*/
- public abstract void addWindowToken(android.os.IBinder token, int type, int displayId);
+ public abstract void addWindowToken(@NonNull android.os.IBinder token, int type, int displayId,
+ @Nullable Bundle options);
/**
* Removes a window token.
@@ -372,6 +432,14 @@
public abstract void registerAppTransitionListener(AppTransitionListener listener);
/**
+ * Registers a listener to be notified to start the keyguard exit animation.
+ *
+ * @param listener The listener to register.
+ */
+ public abstract void registerKeyguardExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener);
+
+ /**
* Reports that the password for the given user has changed.
*/
public abstract void reportPasswordChanged(int userId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1cc6239..a41761f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -23,7 +23,6 @@
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
-import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
@@ -35,7 +34,6 @@
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -82,8 +80,6 @@
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManagerGlobal.ADD_OKAY;
-import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
-import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
@@ -160,6 +156,7 @@
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -201,7 +198,7 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.sysprop.SurfaceFlingerProperties;
@@ -404,6 +401,8 @@
// trying to apply a new one.
private static final boolean ALWAYS_KEEP_CURRENT = true;
+ static final int LOGTAG_INPUT_FOCUS = 62001;
+
/**
* Restrict ability of activities overriding transition animation in a way such that
* an activity can do it only when the transition happens within a same task.
@@ -412,7 +411,6 @@
*/
private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
"persist.wm.disable_custom_task_animation";
- static final int LOGTAG_INPUT_FOCUS = 62001;
/**
* @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
@@ -420,6 +418,19 @@
static boolean sDisableCustomTaskAnimationProperty =
SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
+ /**
+ * Run Keyguard animation as remote animation in System UI instead of local animation in
+ * the server process.
+ */
+ private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+ "persist.wm.enable_remote_keyguard_animation";
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ public static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
"ro.sf.disable_triple_buffer";
@@ -443,12 +454,6 @@
private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000;
- /** The maximum count of window tokens without surface that an app can register. */
- private static final int MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE = 5;
-
- /** System UI can create more window context... */
- private static final int SYSTEM_UI_MULTIPLIER = 2;
-
/**
* Override of task letterbox aspect ratio that is set via ADB with
* set-task-letterbox-aspect-ratio or via {@link
@@ -457,7 +462,8 @@
*/
static final float MIN_TASK_LETTERBOX_ASPECT_RATIO = 1.0f;
- final WindowManagerConstants mConstants;
+ @VisibleForTesting
+ WindowManagerConstants mConstants;
final WindowTracing mWindowTracing;
@@ -624,13 +630,6 @@
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
- * Windows with a preserved surface waiting to be destroyed. These windows
- * are going through a surface change. We keep the old surface around until
- * the first frame on the new surface finishes drawing.
- */
- final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
-
- /**
* This is set when we have run out of memory, and will either be an empty
* list or contain windows that need to be force removed.
*/
@@ -767,8 +766,9 @@
final EmbeddedWindowController mEmbeddedWindowController;
final AnrController mAnrController;
- private final ImpressionAttestationController mImpressionAttestationController;
- private final WindowContextListenerController mWindowContextListenerController =
+ private final ScreenshotHashController mScreenshotHashController;
+ @VisibleForTesting
+ final WindowContextListenerController mWindowContextListenerController =
new WindowContextListenerController();
@VisibleForTesting
@@ -1010,10 +1010,30 @@
// Aspect ratio of task level letterboxing, values <= MIN_TASK_LETTERBOX_ASPECT_RATIO will be
// ignored.
- private float mTaskLetterboxAspectRatio;
+ private volatile float mTaskLetterboxAspectRatio;
+
+ /** Enum for Letterbox background type. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND,
+ LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING})
+ @interface LetterboxBackgroundType {};
+ /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */
+ static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0;
+
+ /** Color specified in R.attr.colorBackground for the letterboxed application. */
+ static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND = 1;
+
+ /** Color specified in R.attr.colorBackgroundFloating for the letterboxed application. */
+ static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING = 2;
// Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
- private int mLetterboxActivityCornersRadius;
+ private volatile int mLetterboxActivityCornersRadius;
+
+ // Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
+ private volatile Color mLetterboxBackgroundColor;
+
+ @LetterboxBackgroundType
+ private volatile int mLetterboxBackgroundType;
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
@@ -1240,10 +1260,15 @@
com.android.internal.R.bool.config_perDisplayFocusEnabled);
mAssistantOnTopOfDream = context.getResources().getBoolean(
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);
+ mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
+ com.android.internal.R.color.config_letterboxBackgroundColor));
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(context);
+
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -1392,7 +1417,7 @@
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
- mImpressionAttestationController = new ImpressionAttestationController(mContext);
+ mScreenshotHashController = new ScreenshotHashController(mContext);
setGlobalShadowSettings();
mAnrController = new AnrController(this);
mStartingSurfaceController = new StartingSurfaceController(this);
@@ -1461,7 +1486,7 @@
}
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
- int displayId, int requestUserId, InsetsState requestedVisibility, Rect outFrame,
+ int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
Arrays.fill(outActiveControls, null);
@@ -1576,7 +1601,7 @@
final Bundle options = mWindowContextListenerController
.getOptions(windowContextToken);
token = new WindowToken(this, binder, type, false /* persistOnEmpty */,
- displayContent, session.mCanAddInternalSystemWindow, callingUid,
+ displayContent, session.mCanAddInternalSystemWindow,
isRoundedCornerOverlay, true /* fromClientToken */, options);
} else {
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
@@ -1803,7 +1828,7 @@
prepareNoneTransitionForRelaunching(activity);
}
- if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outInsetsState,
+ if (displayPolicy.getLayoutHint(win.mAttrs, token, outInsetsState,
win.isClientLocal())) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
@@ -2142,6 +2167,7 @@
void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets,
Rect visibleInsets, Region touchableRegion) {
+ int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2167,8 +2193,8 @@
// We need to report touchable region changes to accessibility.
if (mAccessibilityController != null) {
- mAccessibilityController.onSomeWindowResizedOrMovedLocked(
- w.getDisplayContent().getDisplayId());
+ mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid(
+ uid, w.getDisplayContent().getDisplayId());
}
}
}
@@ -2182,7 +2208,7 @@
if (mAccessibilityController != null) {
WindowState window = mWindowMap.get(token);
if (window != null) {
- mAccessibilityController.onRectangleOnScreenRequestedLocked(
+ mAccessibilityController.onRectangleOnScreenRequested(
window.getDisplayId(), rectangle);
}
}
@@ -2285,8 +2311,8 @@
if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
&& (mAccessibilityController != null)) {
// No move or resize, but the controller checks for title changes as well
- mAccessibilityController.onSomeWindowResizedOrMovedLocked(
- win.getDisplayContent().getDisplayId());
+ mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid(
+ uid, win.getDisplayContent().getDisplayId());
}
if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
@@ -2301,7 +2327,6 @@
if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
+ " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
- winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
winAnimator.mAlpha = attrs.alpha;
}
@@ -2568,11 +2593,12 @@
} else if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = true;
win.mAnimatingExit = true;
- } else if (win.isAnimating(TRANSITION | PARENTS)) {
+ } else if (win.mDisplayContent.okToAnimate() && win.isAnimating(TRANSITION | PARENTS)) {
// Currently in a hide animation... turn this into
// an exit.
win.mAnimatingExit = true;
- } else if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
+ } else if (win.mDisplayContent.okToAnimate()
+ && win.mDisplayContent.mWallpaperController.isWallpaperTarget(win)) {
// If the wallpaper is currently behind this
// window, we need to change both of them inside
// of a transaction to avoid artifacts.
@@ -2586,7 +2612,7 @@
win.destroySurface(false, stopped);
}
if (mAccessibilityController != null) {
- mAccessibilityController.onWindowTransitionLocked(win, transit);
+ mAccessibilityController.onWindowTransition(win, transit);
}
return focusMayChange;
@@ -2679,93 +2705,49 @@
}
@Override
- public void addWindowToken(IBinder binder, int type, int displayId) {
- addWindowTokenWithOptions(binder, type, displayId, null /* options */,
- null /* packageName */, false /* fromClientToken */);
- }
-
- @Override
- public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options,
- String packageName) {
- if (tokenCountExceed()) {
- return ADD_TOO_MANY_TOKENS;
+ public void addWindowToken(@NonNull IBinder binder, int type, int displayId,
+ @Nullable Bundle options) {
+ if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
+ throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
- return addWindowTokenWithOptions(binder, type, displayId, options, packageName,
- true /* fromClientToken */);
- }
- private boolean tokenCountExceed() {
- final int callingUid = Binder.getCallingUid();
- // Don't check if caller is from system server.
- if (callingUid == myPid()) {
- return false;
- }
- final int limit =
- (checkCallingPermission(STATUS_BAR_SERVICE, "addWindowTokenWithOptions"))
- ? MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE * SYSTEM_UI_MULTIPLIER
- : MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE;
synchronized (mGlobalLock) {
- int[] count = new int[1];
- mRoot.forAllDisplays(d -> count[0] += d.getWindowTokensWithoutSurfaceCount(callingUid));
- return count[0] >= limit;
+ final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
+ if (dc == null) {
+ ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
+ + " for non-exiting displayId=%d", binder, displayId);
+ return;
+ }
+
+ WindowToken token = dc.getWindowToken(binder);
+ if (token != null) {
+ ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
+ + " for already created window token: %s"
+ + " displayId=%d", binder, token, displayId);
+ return;
+ }
+ if (type == TYPE_WALLPAPER) {
+ new WallpaperWindowToken(this, binder, true, dc,
+ true /* ownerCanManageAppTokens */, options);
+ } else {
+ new WindowToken(this, binder, type, true /* persistOnEmpty */, dc,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, options);
+ }
}
}
- private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options,
- String packageName, boolean fromClientToken) {
- final boolean callerCanManageAppTokens =
- checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
- // WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions
- // by checkAddPermission.
- if (!callerCanManageAppTokens) {
- final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
- packageName, new int[1]);
- if (res != ADD_OKAY) {
- return res;
- }
- }
-
- final int callingUid = Binder.getCallingUid();
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- if (!callerCanManageAppTokens) {
- if (packageName == null || !unprivilegedAppCanCreateTokenWith(
- null /* parentWindow */, callingUid, type, type, binder,
- packageName)) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
- }
-
- final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
- if (dc == null) {
- ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
- + " for non-exiting displayId=%d", binder, displayId);
- return WindowManagerGlobal.ADD_INVALID_DISPLAY;
- }
-
- WindowToken token = dc.getWindowToken(binder);
- if (token != null) {
- ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
- + " for already created window token: %s"
- + " displayId=%d", binder, token, displayId);
- return WindowManagerGlobal.ADD_DUPLICATE_ADD;
- }
- // TODO(window-container): Clean up dead tokens
- if (type == TYPE_WALLPAPER) {
- new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens,
- options);
- } else {
- new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
- callingUid, false /* roundedCornerOverlay */, fromClientToken, options);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- return WindowManagerGlobal.ADD_OKAY;
- }
-
+ /**
+ * Registers a listener for a {@link android.app.WindowContext} to subscribe to configuration
+ * changes of a {@link DisplayArea}.
+ *
+ * @param clientToken the window context's token
+ * @param type Window type of the window context
+ * @param displayId The display associated with the window context
+ * @param options A bundle used to pass window-related options and choose the right DisplayArea
+ *
+ * @return {@code true} if the listener was registered successfully.
+ */
@Override
public boolean registerWindowContextListener(IBinder clientToken, int type, int displayId,
Bundle options) {
@@ -2821,6 +2803,7 @@
}
}
+ /** Returns {@code true} if this binder is a registered window token. */
@Override
public boolean isWindowToken(IBinder binder) {
synchronized (mGlobalLock) {
@@ -2831,38 +2814,26 @@
@Override
public void removeWindowToken(IBinder binder, int displayId) {
- final boolean callerCanManageAppTokens =
- checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()");
- final WindowToken windowToken;
- synchronized (mGlobalLock) {
- windowToken = mRoot.getWindowToken(binder);
+ if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
+ throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
- if (windowToken == null) {
- ProtoLog.w(WM_ERROR,
- "removeWindowToken: Attempted to remove non-existing token: %s", binder);
- return;
- }
- final int callingUid = Binder.getCallingUid();
-
- // If MANAGE_APP_TOKEN permission is not held(usually from WindowContext), callers can only
- // remove the window tokens which they added themselves.
- if (!callerCanManageAppTokens && (windowToken.getOwnerUid() == INVALID_UID
- || callingUid != windowToken.getOwnerUid())) {
- throw new SecurityException("removeWindowToken: Requires MANAGE_APP_TOKENS permission"
- + " to remove token owned by another uid");
- }
-
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
+
if (dc == null) {
ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
+ " for non-exiting displayId=%d", binder, displayId);
return;
}
-
- dc.removeWindowToken(binder);
+ final WindowToken token = dc.removeWindowToken(binder);
+ if (token == null) {
+ ProtoLog.w(WM_ERROR,
+ "removeWindowToken: Attempted to remove non-existing token: %s",
+ binder);
+ return;
+ }
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
}
} finally {
@@ -3901,14 +3872,7 @@
* the framework implementation will be used to determine the aspect ratio.
*/
void setTaskLetterboxAspectRatio(float aspectRatio) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mTaskLetterboxAspectRatio = aspectRatio;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mTaskLetterboxAspectRatio = aspectRatio;
}
/**
@@ -3916,29 +3880,15 @@
* com.android.internal.R.dimen.config_taskLetterboxAspectRatio}.
*/
void resetTaskLetterboxAspectRatio() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mTaskLetterboxAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mTaskLetterboxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
}
/**
* Gets the aspect ratio of task level letterboxing.
*/
float getTaskLetterboxAspectRatio() {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- return mTaskLetterboxAspectRatio;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ return mTaskLetterboxAspectRatio;
}
/**
@@ -3948,14 +3898,7 @@
* 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);
- }
+ mLetterboxActivityCornersRadius = cornersRadius;
}
/**
@@ -3963,15 +3906,8 @@
* 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);
- }
+ mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxActivityCornersRadius);
}
/**
@@ -3985,14 +3921,67 @@
* 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);
- }
+ return mLetterboxActivityCornersRadius;
+ }
+
+ /**
+ * Gets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ Color getLetterboxBackgroundColor() {
+ return mLetterboxBackgroundColor;
+ }
+
+
+ /**
+ * Sets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ void setLetterboxBackgroundColor(Color color) {
+ mLetterboxBackgroundColor = color;
+ }
+
+ /**
+ * Resets color of letterbox background to {@link
+ * com.android.internal.R.color.config_letterboxBackgroundColor}.
+ */
+ void resetLetterboxBackgroundColor() {
+ mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
+ com.android.internal.R.color.config_letterboxBackgroundColor));
+ }
+
+ /**
+ * Gets {@link LetterboxBackgroundType} specified in {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
+ */
+ @LetterboxBackgroundType
+ int getLetterboxBackgroundType() {
+ return mLetterboxBackgroundType;
+ }
+
+ /** Sets letterbox background type. */
+ void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+ mLetterboxBackgroundType = backgroundType;
+ }
+
+ /**
+ * Resets cletterbox background type to {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType}.
+ */
+ void resetLetterboxBackgroundType() {
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+ }
+
+ @LetterboxBackgroundType
+ private static int readLetterboxBackgroundTypeFromConfig(Context context) {
+ int backgroundType = context.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxBackgroundType);
+ return backgroundType == LETTERBOX_BACKGROUND_SOLID_COLOR
+ || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND
+ || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING
+ ? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
}
@Override
@@ -5445,14 +5434,6 @@
}
}
- void destroyPreservedSurfaceLocked() {
- for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) {
- final WindowState w = mDestroyPreservedSurface.get(i);
- w.mWinAnimator.destroyPreservedSurfaceLocked(w.getSyncTransaction());
- }
- mDestroyPreservedSurface.clear();
- }
-
// -------------------------------------------------------------
// IWindowManager API
// -------------------------------------------------------------
@@ -7095,6 +7076,7 @@
checkCallerOwnsDisplay(displayId);
synchronized (mGlobalLock) {
+ int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
final WindowState win = windowForClientLocked(null, client, false);
@@ -7106,8 +7088,8 @@
// Notifies AccessibilityController to re-compute the window observer of
// this embedded display
if (mAccessibilityController != null) {
- mAccessibilityController.handleWindowObserverOfEmbeddedDisplayLocked(displayId,
- win);
+ mAccessibilityController.handleWindowObserverOfEmbeddedDisplay(
+ displayId, win, uid);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -7473,6 +7455,12 @@
private final class LocalService extends WindowManagerInternal {
@Override
+ public AccessibilityControllerInternal getAccessibilityController() {
+ return AccessibilityController.getAccessibilityControllerInternal(
+ WindowManagerService.this);
+ }
+
+ @Override
public void clearSnapshotCache() {
synchronized (mGlobalLock) {
mTaskSnapshotController.clearSnapshotCache();
@@ -7490,7 +7478,7 @@
public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
- mAccessibilityController.setMagnificationSpecLocked(displayId, spec);
+ mAccessibilityController.setMagnificationSpec(displayId, spec);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -7501,7 +7489,7 @@
public void setForceShowMagnifiableBounds(int displayId, boolean show) {
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
- mAccessibilityController.setForceShowMagnifiableBoundsLocked(displayId, show);
+ mAccessibilityController.setForceShowMagnifiableBounds(displayId, show);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -7512,8 +7500,7 @@
public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) {
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
- mAccessibilityController.getMagnificationRegionLocked(displayId,
- magnificationRegion);
+ mAccessibilityController.getMagnificationRegion(displayId, magnificationRegion);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -7529,7 +7516,7 @@
}
MagnificationSpec spec = null;
if (mAccessibilityController != null) {
- spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState);
+ spec = mAccessibilityController.getMagnificationSpecForWindow(windowState);
}
if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
return null;
@@ -7551,9 +7538,9 @@
mAccessibilityController = new AccessibilityController(
WindowManagerService.this);
}
- boolean result = mAccessibilityController.setMagnificationCallbacksLocked(
+ boolean result = mAccessibilityController.setMagnificationCallbacks(
displayId, callbacks);
- if (!mAccessibilityController.hasCallbacksLocked()) {
+ if (!mAccessibilityController.hasCallbacks()) {
mAccessibilityController = null;
}
return result;
@@ -7569,9 +7556,9 @@
WindowManagerService.this);
}
final boolean result =
- mAccessibilityController.setWindowsForAccessibilityCallbackLocked(
+ mAccessibilityController.setWindowsForAccessibilityCallback(
displayId, callback);
- if (!mAccessibilityController.hasCallbacksLocked()) {
+ if (!mAccessibilityController.hasCallbacks()) {
mAccessibilityController = null;
}
return result;
@@ -7660,8 +7647,9 @@
}
@Override
- public void addWindowToken(IBinder token, int type, int displayId) {
- WindowManagerService.this.addWindowToken(token, type, displayId);
+ public void addWindowToken(IBinder token, int type, int displayId,
+ @Nullable Bundle options) {
+ WindowManagerService.this.addWindowToken(token, type, displayId, options);
}
@Override
@@ -7704,6 +7692,15 @@
}
@Override
+ public void registerKeyguardExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener) {
+ synchronized (mGlobalLock) {
+ getDefaultDisplayContentLocked().mAppTransition
+ .registerKeygaurdExitAnimationStartListener(listener);
+ }
+ }
+
+ @Override
public void reportPasswordChanged(int userId) {
mKeyguardDisableHandler.updateKeyguardEnabled(userId);
}
@@ -7760,7 +7757,7 @@
accessibilityController = mAccessibilityController;
}
if (accessibilityController != null) {
- accessibilityController.performComputeChangedWindowsNotLocked(displayId, true);
+ accessibilityController.performComputeChangedWindowsNot(displayId, true);
}
}
@@ -8510,8 +8507,8 @@
+ "could not be found!");
}
final WindowToken windowToken = dc.getWindowToken(attrs.token);
- return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken,
- mTmpRect /* outFrame */, outInsetsState, fromLocal);
+ return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken, outInsetsState,
+ fromLocal);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -8616,38 +8613,38 @@
}
@Override
- public String[] getSupportedImpressionAlgorithms() {
- return mImpressionAttestationController.getSupportedImpressionAlgorithms();
+ public String[] getSupportedScreenshotHashingAlgorithms() {
+ return mScreenshotHashController.getSupportedHashingAlgorithms();
}
@Override
- public boolean verifyImpressionToken(ImpressionToken impressionToken) {
- return mImpressionAttestationController.verifyImpressionToken(impressionToken);
+ public boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
+ return mScreenshotHashController.verifyScreenshotHash(screenshotHash);
}
- ImpressionToken generateImpressionToken(Session session, IWindow window,
+ ScreenshotHash generateScreenshotHash(Session session, IWindow window,
Rect boundsInWindow, String hashAlgorithm) {
final SurfaceControl displaySurfaceControl;
final Rect boundsInDisplay = new Rect(boundsInWindow);
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, window, false);
if (win == null) {
- Slog.w(TAG, "Failed to generate impression token. Invalid window");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Invalid window");
return null;
}
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
- Slog.w(TAG, "Failed to generate impression token. Window is not on a display");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Window is not on a display");
return null;
}
displaySurfaceControl = displayContent.getSurfaceControl();
- mImpressionAttestationController.calculateImpressionTokenBoundsLocked(win,
+ mScreenshotHashController.calculateScreenshotHashBoundsLocked(win,
boundsInWindow, boundsInDisplay);
if (boundsInDisplay.isEmpty()) {
- Slog.w(TAG, "Failed to generate impression token. Bounds are not on screen");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Bounds are not on screen");
return null;
}
}
@@ -8667,11 +8664,11 @@
SurfaceControl.captureLayers(args);
if (screenshotHardwareBuffer == null
|| screenshotHardwareBuffer.getHardwareBuffer() == null) {
- Slog.w(TAG, "Failed to generate impression token. Failed to take screenshot");
+ Slog.w(TAG, "Failed to generate ScreenshotHash. Failed to take screenshot");
return null;
}
- return mImpressionAttestationController.generateImpressionToken(
+ return mScreenshotHashController.generateScreenshotHash(
screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index badd29a..645786c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -18,6 +18,11 @@
import static android.os.Build.IS_USER;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
+
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
@@ -34,6 +39,7 @@
import com.android.internal.protolog.ProtoLogImpl;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
import java.io.IOException;
import java.io.PrintWriter;
@@ -119,6 +125,14 @@
return runSetLetterboxActivityCornersRadius(pw);
case "get-letterbox-activity-corners-radius":
return runGetLetterboxActivityCornersRadius(pw);
+ case "set-letterbox-background-type":
+ return runSetLetterboxBackgroundType(pw);
+ case "get-letterbox-background-type":
+ return runGetLetterboxBackgroundType(pw);
+ case "set-letterbox-background-color":
+ return runSetLetterboxBackgroundColor(pw);
+ case "get-letterbox-background-color":
+ return runGetLetterboxBackgroundColor(pw);
case "reset":
return runReset(pw);
default:
@@ -581,6 +595,79 @@
return 0;
}
+ private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+ @LetterboxBackgroundType final int backgroundType;
+
+ String arg = getNextArgRequired();
+ if ("reset".equals(arg)) {
+ mInternal.resetLetterboxBackgroundType();
+ return 0;
+ }
+ switch (arg) {
+ case "solid_color":
+ backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
+ break;
+ case "app_color_background":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+ break;
+ case "app_color_background_floating":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'reset', 'solid_color' or 'app_color_background' should "
+ + "be provided as an argument");
+ return -1;
+ }
+
+ mInternal.setLetterboxBackgroundType(backgroundType);
+ return 0;
+ }
+
+ private int runGetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+ @LetterboxBackgroundType final int backgroundType = mInternal.getLetterboxBackgroundType();
+ switch (backgroundType) {
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ pw.println("Letterbox background type is 'solid_color'");
+ break;
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ pw.println("Letterbox background type is 'app_color_background'");
+ break;
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ pw.println("Letterbox background type is 'app_color_background_floating'");
+ break;
+ default:
+ throw new AssertionError("Unexpected letterbox background type: " + backgroundType);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+ final Color color;
+ String arg = getNextArgRequired();
+ try {
+ if ("reset".equals(arg)) {
+ mInternal.resetLetterboxBackgroundColor();
+ return 0;
+ }
+ color = Color.valueOf(Color.parseColor(arg));
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or color in #RRGGBB format should be provided as "
+ + "an argument " + e + " but got " + arg);
+ return -1;
+ }
+
+ mInternal.setLetterboxBackgroundColor(color);
+ return 0;
+ }
+
+ private int runGetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+ final Color color = mInternal.getLetterboxBackgroundColor();
+ pw.println("Letterbox background color is " + Integer.toHexString(color.toArgb()));
+ return 0;
+ }
+
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -611,6 +698,12 @@
// set-letterbox-activity-corners-radius
mInternal.resetLetterboxActivityCornersRadius();
+ // set-letterbox-background-type
+ mInternal.resetLetterboxBackgroundType();
+
+ // set-letterbox-background-color
+ mInternal.resetLetterboxBackgroundColor();
+
pw.println("Reset all settings for displayId=" + displayId);
return 0;
}
@@ -652,6 +745,16 @@
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(" set-letterbox-background-color [reset|colorName|'\\#RRGGBB']");
+ pw.println(" get-letterbox-background-color");
+ pw.println(" Color of letterbox background which is be used when letterbox background");
+ pw.println(" type is 'solid-color'. Use get(set)-letterbox-background-type to check");
+ pw.println(" and control letterbox background type. See Color#parseColor for allowed");
+ pw.println(" color formats (#RRGGBB and some colors by name, e.g. magenta or olive). ");
+ pw.println(" set-letterbox-background-type [reset|solid_color|app_color_background");
+ pw.println(" |app_color_background_floating]");
+ pw.println(" get-letterbox-background-type");
+ pw.println(" Type of background used in the letterbox mode.");
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 1b81914..63a0832 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -40,6 +40,7 @@
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
@@ -112,6 +113,16 @@
}
@Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e);
+ }
+ }
+
+ @Override
public void applyTransaction(WindowContainerTransaction t) {
enforceTaskPermission("applyTransaction()");
if (t == null) {
@@ -405,11 +416,7 @@
new Configuration(container.getRequestedOverrideConfiguration());
c.setTo(change.getConfiguration(), configMask, windowMask);
container.onRequestedOverrideConfigurationChanged(c);
- // TODO(b/145675353): remove the following once we could apply new bounds to the
- // root pinned task together with its children.
}
- resizeRootPinnedTaskIfNeeded(container, configMask, windowMask,
- container.getRequestedOverrideConfiguration());
effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
}
if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
@@ -659,19 +666,6 @@
return effects;
}
- private void resizeRootPinnedTaskIfNeeded(ConfigurationContainer container, int configMask,
- int windowMask, Configuration config) {
- if ((container instanceof Task)
- && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
- && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
- final Task rootTask = (Task) container;
- if (rootTask.inPinnedWindowingMode()) {
- rootTask.resize(config.windowConfiguration.getBounds(),
- PRESERVE_WINDOWS, true /* deferResume */);
- }
- }
- }
-
@Override
public ITaskOrganizerController getTaskOrganizerController() {
enforceTaskPermission("getTaskOrganizerController()");
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
similarity index 96%
rename from services/core/java/com/android/server/policy/WindowOrientationListener.java
rename to services/core/java/com/android/server/wm/WindowOrientationListener.java
index 17e81da..da31bb2 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.policy;
+package com.android.server.wm;
import static com.android.server.wm.WindowOrientationListenerProto.ENABLED;
import static com.android.server.wm.WindowOrientationListenerProto.ROTATION;
@@ -31,6 +31,8 @@
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.internal.util.FrameworkStatsLog;
+
import java.io.PrintWriter;
import java.util.List;
@@ -63,6 +65,7 @@
private OrientationJudge mOrientationJudge;
private int mCurrentRotation = -1;
private final Context mContext;
+ private final WindowManagerConstants mConstants;
private final Object mLock = new Object();
@@ -71,9 +74,11 @@
*
* @param context for the WindowOrientationListener.
* @param handler Provides the Looper for receiving sensor updates.
+ * @param wmService WindowManagerService to read the device config from.
*/
- public WindowOrientationListener(Context context, Handler handler) {
- this(context, handler, SensorManager.SENSOR_DELAY_UI);
+ public WindowOrientationListener(
+ Context context, Handler handler, WindowManagerService wmService) {
+ this(context, handler, wmService, SensorManager.SENSOR_DELAY_UI);
}
/**
@@ -81,6 +86,7 @@
*
* @param context for the WindowOrientationListener.
* @param handler Provides the Looper for receiving sensor updates.
+ * @param wmService WindowManagerService to read the device config from.
* @param rate at which sensor events are processed (see also
* {@link android.hardware.SensorManager SensorManager}). Use the default
* value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
@@ -88,10 +94,12 @@
*
* This constructor is private since no one uses it.
*/
- private WindowOrientationListener(Context context, Handler handler, int rate) {
+ private WindowOrientationListener(
+ Context context, Handler handler, WindowManagerService wmService, int rate) {
mContext = context;
mHandler = handler;
- mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+ mConstants = wmService.mConstants;
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
mRate = rate;
List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION);
Sensor wakeUpDeviceOrientationSensor = null;
@@ -497,7 +505,7 @@
private static final float MIN_ACCELERATION_MAGNITUDE =
SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE;
private static final float MAX_ACCELERATION_MAGNITUDE =
- SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
+ SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
// Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
// when screen is facing the sky or ground), we completely ignore orientation data
@@ -1072,8 +1080,14 @@
mDesiredRotation = reportedRotation;
newRotation = evaluateRotationChangeLocked();
}
- if (newRotation >=0) {
+ if (newRotation >= 0) {
onProposedRotationChanged(newRotation);
+ if (mConstants.mRawSensorLoggingEnabled) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DEVICE_ROTATED,
+ event.timestamp,
+ rotationToLogEnum(reportedRotation));
+ }
}
}
@@ -1180,5 +1194,20 @@
}
}
};
+
+ private int rotationToLogEnum(int rotation) {
+ switch (rotation) {
+ case 0:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_0;
+ case 1:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_90;
+ case 2:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_180;
+ case 3:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_270;
+ default:
+ return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__UNKNOWN;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c698591..c1f2d02 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -45,6 +45,7 @@
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
@@ -103,7 +104,6 @@
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
-import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -2020,7 +2020,7 @@
final int winTransit = TRANSIT_EXIT;
mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
if (accessibilityController != null) {
- accessibilityController.onWindowTransitionLocked(this, winTransit);
+ accessibilityController.onWindowTransition(this, winTransit);
}
}
setDisplayLayoutNeeded();
@@ -2034,7 +2034,7 @@
if (isVisibleNow()) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
+ mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT);
}
changed = true;
if (displayContent != null) {
@@ -2112,7 +2112,7 @@
}
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId());
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
}
updateLocationInParentDisplayIfNeeded();
@@ -2242,7 +2242,6 @@
disposeInputChannel();
- mWinAnimator.destroyDeferredSurfaceLocked(mTmpTransaction);
mWinAnimator.destroySurfaceLocked(mTmpTransaction);
mTmpTransaction.apply();
mSession.windowRemovedLocked();
@@ -2346,7 +2345,7 @@
mWmService.requestTraversal();
}
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+ mWmService.mAccessibilityController.onWindowTransition(this, transit);
}
}
final boolean isAnimating = mAnimatingExit || isAnimating(TRANSITION | PARENTS,
@@ -3308,11 +3307,6 @@
return destroyedSomething;
}
- if (appStopped || mWindowRemovalAllowed) {
- mWinAnimator.destroyPreservedSurfaceLocked(mTmpTransaction);
- mTmpTransaction.apply();
- }
-
if (mDestroying) {
ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s"
+ " destroySurfaces: appStopped=%b"
@@ -3678,7 +3672,7 @@
displayId);
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(displayId);
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId);
}
updateLocationInParentDisplayIfNeeded();
} catch (RemoteException e) {
@@ -4781,7 +4775,7 @@
return;
}
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId());
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
}
if (!isSelfOrAncestorWindowAnimatingExit()) {
@@ -5000,23 +4994,8 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- // When we change the Surface size, in scenarios which may require changing
- // the surface position in sync with the resize, we use a preserved surface
- // so we can freeze it while waiting for the client to report draw on the newly
- // sized surface. At the moment this logic is only in place for switching
- // in and out of the big surface for split screen resize.
if (isDragResizeChanged()) {
setDragResizing();
- // We can only change top level windows to the full-screen surface when
- // resizing (as we only have one full-screen surface). So there is no need
- // to preserve and destroy windows which are attached to another, they
- // will keep their surface and its size may change over time.
- if (mHasSurface && !isChildWindow()) {
- mWinAnimator.preserveSurfaceLocked(getSyncTransaction());
- result |= RELAYOUT_RES_SURFACE_CHANGED |
- RELAYOUT_RES_FIRST_TIME;
- scheduleAnimation();
- }
}
final boolean freeformResizing = isDragResizing()
&& getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
@@ -5269,7 +5248,7 @@
if (!mAnimatingExit && mAppDied) {
mIsDimming = true;
getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
- } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || mAttrs.backgroundBlurRadius != 0)
+ } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || (mAttrs.flags & FLAG_BLUR_BEHIND) != 0)
&& isVisibleNow() && !mHidden) {
// Only show the Dimmer when the following is satisfied:
// 1. The window has the flag FLAG_DIM_BEHIND or background blur is requested
@@ -5278,8 +5257,10 @@
// 4. The WS is not hidden.
mIsDimming = true;
final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
+ final int blurRadius =
+ (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 ? mAttrs.blurBehindRadius : 0;
getDimmer().dimBelow(
- getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.backgroundBlurRadius);
+ getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.blurBehindRadius);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c8b940a..2da3dda 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -116,15 +116,7 @@
boolean mAnimationIsEntrance;
WindowSurfaceController mSurfaceController;
- private WindowSurfaceController mPendingDestroySurface;
- /**
- * Set if the client has asked that the destroy of its surface be delayed
- * until it explicitly says it is okay.
- */
- boolean mSurfaceDestroyDeferred;
-
- private boolean mDestroyPreservedSurfaceUponRedraw;
float mShownAlpha = 0;
float mAlpha = 0;
float mLastAlpha = 0;
@@ -257,11 +249,6 @@
//dump();
mLastHidden = true;
- // We may have a preserved surface which we no longer need. If there was a quick
- // VISIBLE, GONE, VISIBLE, GONE sequence, the surface may never draw, so we don't mark
- // it to be destroyed in prepareSurfaceLocked.
- markPreservedSurfaceForDestroy();
-
if (mSurfaceController != null) {
mSurfaceController.hide(transaction, reason);
}
@@ -323,70 +310,6 @@
return result;
}
- void preserveSurfaceLocked(SurfaceControl.Transaction t) {
- if (mDestroyPreservedSurfaceUponRedraw) {
- // This could happen when switching the surface mode very fast. For example,
- // we preserved a surface when dragResizing changed to true. Then before the
- // preserved surface is removed, dragResizing changed to false again.
- // In this case, we need to leave the preserved surface alone, and destroy
- // the actual surface, so that the createSurface call could create a surface
- // of the proper size. The preserved surface will still be removed when client
- // finishes drawing to the new surface.
- mSurfaceDestroyDeferred = false;
-
- // Make sure to reparent any children of the new surface back to the preserved
- // surface before destroying it.
- if (mSurfaceController != null && mPendingDestroySurface != null) {
- mPostDrawTransaction.reparentChildren(
- mSurfaceController.mSurfaceControl,
- mPendingDestroySurface.mSurfaceControl).apply();
- }
- destroySurfaceLocked(t);
- mSurfaceDestroyDeferred = true;
- return;
- }
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SET FREEZE LAYER: %s", mWin);
- if (mSurfaceController != null) {
- // Our SurfaceControl is always at layer 0 within the parent Surface managed by
- // window-state. We want this old Surface to stay on top of the new one
- // until we do the swap, so we place it at a positive layer.
- t.setLayer(mSurfaceController.mSurfaceControl, PRESERVED_SURFACE_LAYER);
- }
- mDestroyPreservedSurfaceUponRedraw = true;
- mSurfaceDestroyDeferred = true;
- destroySurfaceLocked(t);
- }
-
- void destroyPreservedSurfaceLocked(SurfaceControl.Transaction t) {
- if (!mDestroyPreservedSurfaceUponRedraw) {
- return;
- }
-
- // If we are preserving a surface but we aren't relaunching that means
- // we are just doing an in-place switch. In that case any SurfaceFlinger side
- // child layers need to be reparented to the new surface to make this
- // transparent to the app.
- // If the children are detached, we don't want to reparent them to the new surface.
- // Instead let the children get removed when the old surface is deleted.
- if (mSurfaceController != null && mPendingDestroySurface != null
- && !mPendingDestroySurface.mChildrenDetached
- && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) {
- mPostDrawTransaction.reparentChildren(
- mPendingDestroySurface.mSurfaceControl,
- mSurfaceController.mSurfaceControl).apply();
- }
-
- destroyDeferredSurfaceLocked(t);
- mDestroyPreservedSurfaceUponRedraw = false;
- }
-
- private void markPreservedSurfaceForDestroy() {
- if (mDestroyPreservedSurfaceUponRedraw
- && !mService.mDestroyPreservedSurface.contains(mWin)) {
- mService.mDestroyPreservedSurface.add(mWin);
- }
- }
-
void resetDrawState() {
mDrawState = DRAW_PENDING;
@@ -508,39 +431,23 @@
return;
}
- // When destroying a surface we want to make sure child windows are hidden. If we are
- // preserving the surface until redraw though we intend to swap it out with another surface
- // for resizing. In this case the window always remains visible to the user and the child
- // windows should likewise remain visible.
- if (!mDestroyPreservedSurfaceUponRedraw) {
- mWin.mHidden = true;
- }
+ mWin.mHidden = true;
try {
- if (DEBUG_VISIBILITY) logWithStack(TAG, "Window " + this + " destroying surface "
- + mSurfaceController + ", session " + mSession);
- if (mSurfaceDestroyDeferred) {
- if (mSurfaceController != null && mPendingDestroySurface != mSurfaceController) {
- if (mPendingDestroySurface != null) {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
- mWin, new RuntimeException().fillInStackTrace());
- mPendingDestroySurface.destroy(t);
- }
- mPendingDestroySurface = mSurfaceController;
- }
- } else {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
- mWin, new RuntimeException().fillInStackTrace());
- destroySurface(t);
+ if (DEBUG_VISIBILITY) {
+ logWithStack(TAG, "Window " + this + " destroying surface "
+ + mSurfaceController + ", session " + mSession);
}
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
+ mWin, new RuntimeException().fillInStackTrace());
+ destroySurface(t);
// Don't hide wallpaper if we're deferring the surface destroy
// because of a surface change.
- if (!mDestroyPreservedSurfaceUponRedraw) {
- mWallpaperControllerLocked.hideWallpapers(mWin);
- }
+ mWallpaperControllerLocked.hideWallpapers(mWin);
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying Window " + this
- + " surface " + mSurfaceController + " session " + mSession + ": " + e.toString());
+ + " surface " + mSurfaceController + " session " + mSession + ": "
+ + e.toString());
}
// Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it
@@ -554,27 +461,6 @@
mDrawState = NO_SURFACE;
}
- void destroyDeferredSurfaceLocked(SurfaceControl.Transaction t) {
- try {
- if (mPendingDestroySurface != null) {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
- mWin, new RuntimeException().fillInStackTrace());
- mPendingDestroySurface.destroy(t);
- // Don't hide wallpaper if we're destroying a deferred surface
- // after a surface mode change.
- if (!mDestroyPreservedSurfaceUponRedraw) {
- mWallpaperControllerLocked.hideWallpapers(mWin);
- }
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Exception thrown when destroying Window "
- + this + " surface " + mPendingDestroySurface
- + " session " + mSession + ": " + e.toString());
- }
- mSurfaceDestroyDeferred = false;
- mPendingDestroySurface = null;
- }
-
void computeShownFrameLocked() {
if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
return;
@@ -744,7 +630,6 @@
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
if (showSurfaceRobustlyLocked(t)) {
- markPreservedSurfaceForDestroy();
mAnimator.requestRemovalOfReplacedWindows(w);
mLastHidden = false;
if (mIsWallpaper) {
@@ -905,20 +790,6 @@
if (!shown)
return false;
- // If we had a preserved surface it's no longer needed, and it may be harmful
- // if we are transparent.
- if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) {
- final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl;
- mPostDrawTransaction.reparent(pendingSurfaceControl, null);
- // If the children are detached, we don't want to reparent them to the new surface.
- // Instead let the children get removed when the old surface is deleted.
- if (!mPendingDestroySurface.mChildrenDetached) {
- mPostDrawTransaction.reparentChildren(
- mPendingDestroySurface.mSurfaceControl,
- mSurfaceController.mSurfaceControl);
- }
- }
-
t.merge(mPostDrawTransaction);
return true;
}
@@ -946,7 +817,7 @@
}
if (mService.mAccessibilityController != null) {
- mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
+ mService.mAccessibilityController.onWindowTransition(mWin, transit);
}
}
@@ -1058,13 +929,6 @@
pw.println();
}
- if (mPendingDestroySurface != null) {
- pw.print(prefix); pw.print("mPendingDestroySurface=");
- pw.println(mPendingDestroySurface);
- }
- if (mSurfaceDestroyDeferred) {
- pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
- }
if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
pw.print(" mAlpha="); pw.print(mAlpha);
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 5f4477d..82ba3c1 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -55,8 +55,6 @@
private boolean mSurfaceShown = false;
private float mSurfaceX = 0;
private float mSurfaceY = 0;
- private int mSurfaceW = 0;
- private int mSurfaceH = 0;
// Initialize to the identity matrix.
private float mLastDsdx = 1;
@@ -82,9 +80,6 @@
int flags, WindowStateAnimator animator, int windowType) {
mAnimator = animator;
- mSurfaceW = w;
- mSurfaceH = h;
-
title = name;
mService = animator.mService;
@@ -104,8 +99,8 @@
.setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
.setCallsite("WindowSurfaceController");
- final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags &
- WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
+ final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
+ & WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
if (useBLAST) {
b.setBLASTLayer();
@@ -119,7 +114,6 @@
void hide(SurfaceControl.Transaction transaction, String reason) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
- mAnimator.destroyPreservedSurfaceLocked(transaction);
if (mSurfaceShown) {
hideSurface(transaction);
}
@@ -335,9 +329,7 @@
pw.print(" layer="); pw.print(mSurfaceLayer);
pw.print(" alpha="); pw.print(mSurfaceAlpha);
pw.print(" rect=("); pw.print(mSurfaceX);
- pw.print(","); pw.print(mSurfaceY);
- pw.print(") "); pw.print(mSurfaceW);
- pw.print(" x "); pw.print(mSurfaceH);
+ pw.print(","); pw.print(mSurfaceY); pw.print(") ");
pw.print(" transform=("); pw.print(mLastDsdx); pw.print(", ");
pw.print(mLastDtdx); pw.print(", "); pw.print(mLastDsdy);
pw.print(", "); pw.print(mLastDtdy); pw.println(")");
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 8a512bc..c3a4609 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.os.Process.INVALID_UID;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -26,7 +24,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
-import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -41,7 +38,6 @@
import android.annotation.CallSuper;
import android.annotation.Nullable;
-import android.app.IWindowToken;
import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -58,7 +54,6 @@
import android.view.SurfaceControl;
import android.view.WindowManager;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
@@ -112,19 +107,10 @@
private FixedRotationTransformState mFixedRotationTransformState;
- private Configuration mLastReportedConfig;
- private int mLastReportedDisplay = INVALID_DISPLAY;
-
/**
* When set to {@code true}, this window token is created from {@link android.app.WindowContext}
*/
- @VisibleForTesting
- final boolean mFromClientToken;
-
- private DeathRecipient mDeathRecipient;
- private boolean mBinderDied = false;
-
- private final int mOwnerUid;
+ private final boolean mFromClientToken;
/**
* Used to fix the transform of the token to be rotated to a rotation different than it's
@@ -188,30 +174,6 @@
}
}
- private class DeathRecipient implements IBinder.DeathRecipient {
- private boolean mHasUnlinkToDeath = false;
-
- @Override
- public void binderDied() {
- synchronized (mWmService.mGlobalLock) {
- mBinderDied = true;
- removeImmediately();
- }
- }
-
- void linkToDeath() throws RemoteException {
- token.linkToDeath(DeathRecipient.this, 0);
- }
-
- void unlinkToDeath() {
- if (mHasUnlinkToDeath) {
- return;
- }
- token.unlinkToDeath(DeathRecipient.this, 0);
- mHasUnlinkToDeath = true;
- }
- }
-
/**
* Compares two child window of this token and returns -1 if the first is lesser than the
* second in terms of z-order and 1 otherwise.
@@ -240,43 +202,24 @@
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
- this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID,
- roundedCornerOverlay, false /* fromClientToken */);
+ this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
+ roundedCornerOverlay, false /* fromClientToken */, null /* options */);
}
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
- DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
- boolean roundedCornerOverlay, boolean fromClientToken) {
- this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, ownerUid,
- roundedCornerOverlay, fromClientToken, null /* options */);
- }
-
- WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
- DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
- boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
+ DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay,
+ boolean fromClientToken, @Nullable Bundle options) {
super(service);
token = _token;
windowType = type;
mOptions = options;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
- mOwnerUid = ownerUid;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
dc.addWindowToken(token, this);
}
- if (shouldReportToClient()) {
- try {
- mDeathRecipient = new DeathRecipient();
- mDeathRecipient.linkToDeath();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to add window token with type " + windowType + " on "
- + "display " + dc.getDisplayId(), e);
- mDeathRecipient = null;
- return;
- }
- }
}
void removeAllWindowsIfPossible() {
@@ -414,22 +357,6 @@
// Needs to occur after the token is removed from the display above to avoid attempt at
// duplicate removal of this window container from it's parent.
super.removeImmediately();
-
- reportWindowTokenRemovedToClient();
- }
-
- // TODO(b/159767464): Remove after we migrate to listener approach.
- private void reportWindowTokenRemovedToClient() {
- if (!shouldReportToClient()) {
- return;
- }
- mDeathRecipient.unlinkToDeath();
- IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
- try {
- windowTokenClient.onWindowTokenRemoved();
- } catch (RemoteException e) {
- ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
- }
}
@Override
@@ -441,51 +368,11 @@
// to another display before the window behind
// it is ready.
super.onDisplayChanged(dc);
- reportConfigToWindowTokenClient();
}
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
super.onConfigurationChanged(newParentConfig);
- reportConfigToWindowTokenClient();
- }
-
- void reportConfigToWindowTokenClient() {
- if (!shouldReportToClient()) {
- return;
- }
- if (mLastReportedConfig == null) {
- mLastReportedConfig = new Configuration();
- }
- final Configuration config = getConfiguration();
- final int displayId = getDisplayContent().getDisplayId();
- if (config.diff(mLastReportedConfig) == 0 && displayId == mLastReportedDisplay) {
- // No changes since last reported time.
- return;
- }
-
- mLastReportedConfig.setTo(config);
- mLastReportedDisplay = displayId;
-
- IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
- try {
- windowTokenClient.onConfigurationChanged(config, displayId);
- } catch (RemoteException e) {
- ProtoLog.w(WM_ERROR,
- "Could not report config changes to the window token client.");
- }
- }
-
- /**
- * @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and
- * registered from client side.
- */
- private boolean shouldReportToClient() {
- // Only report to client for WindowToken because Activities are updated through ATM
- // callbacks.
- return asActivityRecord() == null
- // Report to {@link android.view.WindowTokenClient} if this token was registered from it.
- && mFromClientToken && !mBinderDied;
}
@Override
@@ -841,10 +728,6 @@
mRoundedCornerOverlay);
}
- int getOwnerUid() {
- return mOwnerUid;
- }
-
boolean isFromClient() {
return mFromClientToken;
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index cc7e00a..91be056 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -134,7 +134,7 @@
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
- "android.hardware.gnss-cpp",
+ "android.hardware.gnss-V1-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
@@ -147,12 +147,12 @@
"android.hardware.light@2.0",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-cpp",
+ "android.hardware.power-V1-cpp",
"android.hardware.power.stats@1.0",
"android.hardware.power.stats-ndk_platform",
"android.hardware.thermal@1.0",
"android.hardware.tv.input@1.0",
- "android.hardware.vibrator-unstable-cpp",
+ "android.hardware.vibrator-V2-cpp",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
@@ -162,7 +162,7 @@
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
"android.frameworks.stats@1.0",
- "android.system.suspend.control-cpp",
+ "android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend@1.0",
"service.incremental",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 995cfe9..9a8942b 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -12,6 +12,9 @@
per-file com_android_server_HardwarePropertiesManagerService.cpp = michaelwr@google.com, santoscordon@google.com
per-file com_android_server_power_PowerManagerService.* = michaelwr@google.com, santoscordon@google.com
+# BatteryStats
+per-file com_android_server_am_BatteryStatsService.cpp = file:/BATTERY_STATS_OWNERS
+
per-file Android.bp = file:platform/build/soong:/OWNERS
per-file com_android_server_Usb* = file:/services/usb/OWNERS
per-file com_android_server_Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index c3e7c7a..16eaa77 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -55,15 +55,8 @@
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::power::stats::V1_0::IPowerStats;
-using android::hardware::power::V1_0::PowerStatePlatformSleepState;
-using android::hardware::power::V1_0::PowerStateVoter;
-using android::hardware::power::V1_0::Status;
-using android::hardware::power::V1_1::PowerStateSubsystem;
-using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
using android::system::suspend::BnSuspendCallback;
using android::system::suspend::ISuspendControlService;
-using IPowerV1_1 = android::hardware::power::V1_1::IPower;
-using IPowerV1_0 = android::hardware::power::V1_0::IPower;
namespace android
{
@@ -74,23 +67,9 @@
static sem_t wakeup_sem;
extern sp<ISuspendControlService> getSuspendControl();
-// Java methods used in getLowPowerStats
-static jmethodID jgetAndUpdatePlatformState = NULL;
-static jmethodID jgetSubsystem = NULL;
-static jmethodID jputVoter = NULL;
-static jmethodID jputState = NULL;
-
std::mutex gPowerStatsHalMutex;
-std::unordered_map<uint32_t, std::string> gPowerStatsHalEntityNames = {};
-std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>>
- gPowerStatsHalStateNames = {};
-std::vector<uint32_t> gPowerStatsHalPlatformIds = {};
-std::vector<uint32_t> gPowerStatsHalSubsystemIds = {};
sp<IPowerStats> gPowerStatsHalV1_0 = nullptr;
-std::function<void(JNIEnv*, jobject)> gGetLowPowerStatsImpl = {};
-std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {};
-std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {};
std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {};
// Cellular/Wifi power monitor rail information
@@ -222,68 +201,14 @@
return true;
}
-static bool checkPowerHalResult(const Return<void>& ret, const char* function) {
- if (!ret.isOk()) {
- ALOGE("%s failed: requested HAL service not available.", function);
- power::PowerHalLoader::unloadAll();
- return false;
- }
- return true;
-}
-
// gPowerStatsHalV1_0 must not be null
static bool initializePowerStatsLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
using android::hardware::power::stats::V1_0::Status;
- using android::hardware::power::stats::V1_0::PowerEntityType;
// Clear out previous content if we are re-initializing
- gPowerStatsHalEntityNames.clear();
- gPowerStatsHalStateNames.clear();
- gPowerStatsHalPlatformIds.clear();
- gPowerStatsHalSubsystemIds.clear();
gPowerStatsHalRailNames.clear();
Return<void> ret;
- ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting power entity info");
- return;
- }
-
- // construct lookup table of powerEntityId to power entity name
- // also construct vector of platform and subsystem IDs
- for (auto info : infos) {
- gPowerStatsHalEntityNames.emplace(info.powerEntityId, info.powerEntityName);
- if (info.type == PowerEntityType::POWER_DOMAIN) {
- gPowerStatsHalPlatformIds.emplace_back(info.powerEntityId);
- } else {
- gPowerStatsHalSubsystemIds.emplace_back(info.powerEntityId);
- }
- }
- });
- if (!checkPowerStatsHalResultLocked(ret, __func__)) {
- return false;
- }
-
- ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting state info");
- return;
- }
-
- // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
- for (auto stateSpace : stateSpaces) {
- std::unordered_map<uint32_t, std::string> stateNames = {};
- for (auto state : stateSpace.states) {
- stateNames.emplace(state.powerEntityStateId,
- state.powerEntityStateName);
- }
- gPowerStatsHalStateNames.emplace(stateSpace.powerEntityId, stateNames);
- }
- });
- if (!checkPowerStatsHalResultLocked(ret, __func__)) {
- return false;
- }
// Get Power monitor rails available
ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) {
@@ -306,7 +231,7 @@
return false;
}
- return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty());
+ return true;
}
static bool getPowerStatsHalLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
@@ -333,196 +258,6 @@
return true;
}
-static void getPowerStatsHalLowPowerDataLocked(JNIEnv* env, jobject jrpmStats)
- EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
- using android::hardware::power::stats::V1_0::Status;
-
- if (!getPowerStatsHalLocked()) {
- ALOGE("failed to get low power stats");
- return;
- }
-
- // Get power entity state residency data
- bool success = false;
- Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
- [&env, &jrpmStats, &success](auto results, auto status) {
- if (status == Status::NOT_SUPPORTED) {
- ALOGW("getPowerEntityStateResidencyData is not supported");
- success = false;
- return;
- }
-
- for (auto result : results) {
- jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
- env->NewStringUTF(gPowerStatsHalEntityNames.at(result.powerEntityId).c_str()));
- if (jsubsystem == NULL) {
- ALOGE("The rpmstats jni jobject jsubsystem is null.");
- return;
- }
- for (auto stateResidency : result.stateResidencyData) {
-
- env->CallVoidMethod(jsubsystem, jputState,
- env->NewStringUTF(gPowerStatsHalStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId).c_str()),
- stateResidency.totalTimeInStateMs,
- stateResidency.totalStateEntryCount);
- }
- }
- success = true;
- });
- checkPowerStatsHalResultLocked(ret, __func__);
- if (!success) {
- ALOGE("getPowerEntityStateResidencyData failed");
- }
-}
-
-static jint getPowerStatsHalPlatformDataLocked(JNIEnv* env, jobject outBuf)
- EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
- using android::hardware::power::stats::V1_0::Status;
- using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
- using hardware::power::stats::V1_0::PowerEntityStateResidencyData;
-
- if (!getPowerStatsHalLocked()) {
- ALOGE("failed to get low power stats");
- return -1;
- }
-
- char *output = (char*)env->GetDirectBufferAddress(outBuf);
- char *offset = output;
- int remaining = (int)env->GetDirectBufferCapacity(outBuf);
- int total_added = -1;
-
- // Get power entity state residency data
- Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
- gPowerStatsHalPlatformIds,
- [&offset, &remaining, &total_added](auto results, auto status) {
- if (status == Status::NOT_SUPPORTED) {
- ALOGW("getPowerEntityStateResidencyData is not supported");
- return;
- }
-
- for (size_t i = 0; i < results.size(); i++) {
- const PowerEntityStateResidencyResult& result = results[i];
-
- for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
- const PowerEntityStateResidencyData& stateResidency =
- result.stateResidencyData[j];
- int added = snprintf(offset, remaining,
- "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
- j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId).c_str(),
- stateResidency.totalTimeInStateMs,
- stateResidency.totalStateEntryCount);
- if (added < 0) {
- break;
- }
- if (added > remaining) {
- added = remaining;
- }
- offset += added;
- remaining -= added;
- total_added += added;
- }
- if (remaining <= 0) {
- /* rewrite NULL character*/
- offset--;
- total_added--;
- ALOGE("power.stats Hal: buffer not enough");
- break;
- }
- }
- });
- if (!checkPowerStatsHalResultLocked(ret, __func__)) {
- return -1;
- }
-
- total_added += 1;
- return total_added;
-}
-
-static jint getPowerStatsHalSubsystemDataLocked(JNIEnv* env, jobject outBuf)
- EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
- using android::hardware::power::stats::V1_0::Status;
- using hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
- using hardware::power::stats::V1_0::PowerEntityStateResidencyData;
-
- if (!getPowerStatsHalLocked()) {
- ALOGE("failed to get low power stats");
- return -1;
- }
-
- char *output = (char*)env->GetDirectBufferAddress(outBuf);
- char *offset = output;
- int remaining = (int)env->GetDirectBufferCapacity(outBuf);
- int total_added = -1;
-
- // Get power entity state residency data
- Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
- gPowerStatsHalSubsystemIds,
- [&offset, &remaining, &total_added](auto results, auto status) {
- if (status == Status::NOT_SUPPORTED) {
- ALOGW("getPowerEntityStateResidencyData is not supported");
- return;
- }
-
- int added = snprintf(offset, remaining, "SubsystemPowerState ");
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t i = 0; i < results.size(); i++) {
- const PowerEntityStateResidencyResult& result = results[i];
- added = snprintf(offset, remaining, "subsystem_%zu name=%s ",
- i + 1, gPowerStatsHalEntityNames.at(result.powerEntityId).c_str());
- if (added < 0) {
- break;
- }
-
- if (added > remaining) {
- added = remaining;
- }
-
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t j = 0; j < result.stateResidencyData.size(); j++) {
- const PowerEntityStateResidencyData& stateResidency =
- result.stateResidencyData[j];
- added = snprintf(offset, remaining,
- "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%"
- PRIu64 " ", j + 1, gPowerStatsHalStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId).c_str(),
- stateResidency.totalTimeInStateMs,
- stateResidency.totalStateEntryCount,
- stateResidency.lastEntryTimestampMs);
- if (added < 0) {
- break;
- }
- if (added > remaining) {
- added = remaining;
- }
- offset += added;
- remaining -= added;
- total_added += added;
- }
- if (remaining <= 0) {
- /* rewrite NULL character*/
- offset--;
- total_added--;
- ALOGE("power.stats Hal: buffer not enough");
- break;
- }
- }
- });
- if (!checkPowerStatsHalResultLocked(ret, __func__)) {
- return -1;
- }
-
- total_added += 1;
- return total_added;
-}
-
static void getPowerStatsHalRailEnergyDataLocked(JNIEnv* env, jobject jrailStats)
EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
using android::hardware::power::stats::V1_0::Status;
@@ -568,325 +303,17 @@
}
}
-static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
- sp<IPowerV1_0> powerHalV1_0 = power::PowerHalLoader::loadHidlV1_0();
- if (powerHalV1_0 == nullptr) {
- ALOGE("Power Hal not loaded");
- return;
- }
-
- Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
- [&env, &jrpmStats](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
-
- if (status != Status::SUCCESS) return;
-
- for (size_t i = 0; i < states.size(); i++) {
- const PowerStatePlatformSleepState& state = states[i];
-
- jobject jplatformState = env->CallObjectMethod(jrpmStats,
- jgetAndUpdatePlatformState,
- env->NewStringUTF(state.name.c_str()),
- state.residencyInMsecSinceBoot,
- state.totalTransitions);
- if (jplatformState == NULL) {
- ALOGE("The rpmstats jni jobject jplatformState is null.");
- return;
- }
-
- for (size_t j = 0; j < state.voters.size(); j++) {
- const PowerStateVoter& voter = state.voters[j];
- env->CallVoidMethod(jplatformState, jputVoter,
- env->NewStringUTF(voter.name.c_str()),
- voter.totalTimeInMsecVotedForSinceBoot,
- voter.totalNumberOfTimesVotedSinceBoot);
- }
- }
- });
- if (!checkPowerHalResult(ret, "getPlatformLowPowerStats")) {
- return;
- }
-
- // Trying to get IPower 1.1, this will succeed only for devices supporting 1.1
- sp<IPowerV1_1> powerHal_1_1 = power::PowerHalLoader::loadHidlV1_1();
- if (powerHal_1_1 == nullptr) {
- // This device does not support IPower@1.1, exiting gracefully
- return;
- }
- ret = powerHal_1_1->getSubsystemLowPowerStats(
- [&env, &jrpmStats](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
-
- if (status != Status::SUCCESS) return;
-
- if (subsystems.size() > 0) {
- for (size_t i = 0; i < subsystems.size(); i++) {
- const PowerStateSubsystem &subsystem = subsystems[i];
-
- jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
- env->NewStringUTF(subsystem.name.c_str()));
- if (jsubsystem == NULL) {
- ALOGE("The rpmstats jni jobject jsubsystem is null.");
- return;
- }
-
- for (size_t j = 0; j < subsystem.states.size(); j++) {
- const PowerStateSubsystemSleepState& state = subsystem.states[j];
- env->CallVoidMethod(jsubsystem, jputState,
- env->NewStringUTF(state.name.c_str()),
- state.residencyInMsecSinceBoot,
- state.totalTransitions);
- }
- }
- }
- });
- checkPowerHalResult(ret, "getSubsystemLowPowerStats");
-}
-
-static jint getPowerHalPlatformData(JNIEnv* env, jobject outBuf) {
- char *output = (char*)env->GetDirectBufferAddress(outBuf);
- char *offset = output;
- int remaining = (int)env->GetDirectBufferCapacity(outBuf);
- int total_added = -1;
-
- {
- sp<IPowerV1_0> powerHalV1_0 = power::PowerHalLoader::loadHidlV1_0();
- if (powerHalV1_0 == nullptr) {
- ALOGE("Power Hal not loaded");
- return -1;
- }
-
- Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
- [&offset, &remaining, &total_added](hidl_vec<PowerStatePlatformSleepState> states,
- Status status) {
- if (status != Status::SUCCESS)
- return;
- for (size_t i = 0; i < states.size(); i++) {
- int added;
- const PowerStatePlatformSleepState& state = states[i];
-
- added = snprintf(offset, remaining,
- "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
- i + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
- state.totalTransitions);
- if (added < 0) {
- break;
- }
- if (added > remaining) {
- added = remaining;
- }
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t j = 0; j < state.voters.size(); j++) {
- const PowerStateVoter& voter = state.voters[j];
- added = snprintf(offset, remaining,
- "voter_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
- j + 1, voter.name.c_str(),
- voter.totalTimeInMsecVotedForSinceBoot,
- voter.totalNumberOfTimesVotedSinceBoot);
- if (added < 0) {
- break;
- }
- if (added > remaining) {
- added = remaining;
- }
- offset += added;
- remaining -= added;
- total_added += added;
- }
-
- if (remaining <= 0) {
- /* rewrite NULL character*/
- offset--;
- total_added--;
- ALOGE("PowerHal: buffer not enough");
- break;
- }
- }
- }
- );
-
- if (!checkPowerHalResult(ret, "getPlatformLowPowerStats")) {
- return -1;
- }
- }
- *offset = 0;
- total_added += 1;
- return total_added;
-}
-
-static jint getPowerHalSubsystemData(JNIEnv* env, jobject outBuf) {
- char *output = (char*)env->GetDirectBufferAddress(outBuf);
- char *offset = output;
- int remaining = (int)env->GetDirectBufferCapacity(outBuf);
- int total_added = -1;
-
- // This is a IPower 1.1 API
- sp<IPowerV1_1> powerHal_1_1 = nullptr;
-
- {
- // Trying to get 1.1, this will succeed only for devices supporting 1.1
- powerHal_1_1 = power::PowerHalLoader::loadHidlV1_1();
- if (powerHal_1_1 == nullptr) {
- //This device does not support IPower@1.1, exiting gracefully
- return 0;
- }
-
- Return<void> ret = powerHal_1_1->getSubsystemLowPowerStats(
- [&offset, &remaining, &total_added](hidl_vec<PowerStateSubsystem> subsystems,
- Status status) {
-
- if (status != Status::SUCCESS)
- return;
-
- if (subsystems.size() > 0) {
- int added = snprintf(offset, remaining, "SubsystemPowerState ");
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t i = 0; i < subsystems.size(); i++) {
- const PowerStateSubsystem &subsystem = subsystems[i];
-
- added = snprintf(offset, remaining,
- "subsystem_%zu name=%s ", i + 1, subsystem.name.c_str());
- if (added < 0) {
- break;
- }
-
- if (added > remaining) {
- added = remaining;
- }
-
- offset += added;
- remaining -= added;
- total_added += added;
-
- for (size_t j = 0; j < subsystem.states.size(); j++) {
- const PowerStateSubsystemSleepState& state = subsystem.states[j];
- added = snprintf(offset, remaining,
- "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%" PRIu64 " ",
- j + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
- state.totalTransitions, state.lastEntryTimestampMs);
- if (added < 0) {
- break;
- }
-
- if (added > remaining) {
- added = remaining;
- }
-
- offset += added;
- remaining -= added;
- total_added += added;
- }
-
- if (remaining <= 0) {
- /* rewrite NULL character*/
- offset--;
- total_added--;
- ALOGE("PowerHal: buffer not enough");
- break;
- }
- }
- }
- }
- );
-
- if (!checkPowerHalResult(ret, "getSubsystemLowPowerStats")) {
- return -1;
- }
- }
-
- *offset = 0;
- total_added += 1;
- return total_added;
-}
-
static void setUpPowerStatsLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) {
// First see if power.stats HAL is available. Fall back to power HAL if
// power.stats HAL is unavailable.
if (IPowerStats::getService() != nullptr) {
ALOGI("Using power.stats HAL");
- gGetLowPowerStatsImpl = getPowerStatsHalLowPowerDataLocked;
- gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformDataLocked;
- gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemDataLocked;
gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyDataLocked;
- } else if (IPowerV1_0::getService() != nullptr) {
- ALOGI("Using power HAL");
- gGetLowPowerStatsImpl = getPowerHalLowPowerData;
- gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData;
- gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData;
+ } else {
gGetRailEnergyPowerStatsImpl = NULL;
}
}
-static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats) {
- if (jrpmStats == NULL) {
- jniThrowException(env, "java/lang/NullPointerException",
- "The rpmstats jni input jobject jrpmStats is null.");
- return;
- }
- if (jgetAndUpdatePlatformState == NULL || jgetSubsystem == NULL
- || jputVoter == NULL || jputState == NULL) {
- ALOGE("A rpmstats jni jmethodID is null.");
- return;
- }
-
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!gGetLowPowerStatsImpl) {
- setUpPowerStatsLocked();
- }
-
- if (gGetLowPowerStatsImpl) {
- return gGetLowPowerStatsImpl(env, jrpmStats);
- }
-
- ALOGE("Unable to load Power Hal or power.stats HAL");
- return;
-}
-
-static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
- if (outBuf == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "null argument");
- return -1;
- }
-
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!gGetPlatformLowPowerStatsImpl) {
- setUpPowerStatsLocked();
- }
-
- if (gGetPlatformLowPowerStatsImpl) {
- return gGetPlatformLowPowerStatsImpl(env, outBuf);
- }
-
- ALOGE("Unable to load Power Hal or power.stats HAL");
- return -1;
-}
-
-static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
- if (outBuf == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", "null argument");
- return -1;
- }
-
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!gGetSubsystemLowPowerStatsImpl) {
- setUpPowerStatsLocked();
- }
-
- if (gGetSubsystemLowPowerStatsImpl) {
- return gGetSubsystemLowPowerStatsImpl(env, outBuf);
- }
-
- ALOGE("Unable to load Power Hal or power.stats HAL");
- return -1;
-}
-
static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) {
if (jrailStats == NULL) {
jniThrowException(env, "java/lang/NullPointerException",
@@ -920,9 +347,6 @@
static const JNINativeMethod method_table[] = {
{ "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
- { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },
- { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
- { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats },
{ "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V",
(void*)getRailEnergyPowerStats },
};
@@ -930,24 +354,10 @@
int register_android_server_BatteryStatsService(JNIEnv *env)
{
// get java classes and methods
- jclass clsRpmStats = env->FindClass("com/android/internal/os/RpmStats");
- jclass clsPowerStatePlatformSleepState =
- env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState");
- jclass clsPowerStateSubsystem =
- env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem");
jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats");
- if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL
- || clsPowerStateSubsystem == NULL || clsRailStats == NULL) {
+ if (clsRailStats == NULL) {
ALOGE("A rpmstats jni jclass is null.");
} else {
- jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState",
- "(Ljava/lang/String;JI)Lcom/android/internal/os/RpmStats$PowerStatePlatformSleepState;");
- jgetSubsystem = env->GetMethodID(clsRpmStats, "getSubsystem",
- "(Ljava/lang/String;)Lcom/android/internal/os/RpmStats$PowerStateSubsystem;");
- jputVoter = env->GetMethodID(clsPowerStatePlatformSleepState, "putVoter",
- "(Ljava/lang/String;JI)V");
- jputState = env->GetMethodID(clsPowerStateSubsystem, "putState",
- "(Ljava/lang/String;JI)V");
jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData",
"(JLjava/lang/String;Ljava/lang/String;JJ)V");
jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 48f8b15..59b7367 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -136,6 +136,8 @@
private static final String TAG_PASSWORD_COMPLEXITY = "password-complexity";
private static final String TAG_ORGANIZATION_ID = "organization-id";
private static final String TAG_ENROLLMENT_SPECIFIC_ID = "enrollment-specific-id";
+ private static final String TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS =
+ "admin-can-grant-sensors-permissions";
private static final String ATTR_VALUE = "value";
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -277,6 +279,7 @@
boolean mCommonCriteriaMode;
public String mOrganizationId;
public String mEnrollmentSpecificId;
+ public boolean mAdminCanGrantSensorsPermissions;
ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
this.info = info;
@@ -543,6 +546,8 @@
if (!TextUtils.isEmpty(mEnrollmentSpecificId)) {
writeTextToXml(out, TAG_ENROLLMENT_SPECIFIC_ID, mEnrollmentSpecificId);
}
+ writeAttributeValueToXml(out, TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS,
+ mAdminCanGrantSensorsPermissions);
}
void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException {
@@ -792,6 +797,9 @@
Log.w(DevicePolicyManagerService.LOG_TAG,
"Missing Enrollment-specific ID.");
}
+ } else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
+ mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
+ false);
} else {
Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1143,5 +1151,8 @@
pw.print("mEnrollmentSpecificId=");
pw.println(mEnrollmentSpecificId);
}
+
+ pw.print("mAdminCanGrantSensorsPermissions");
+ pw.println(mAdminCanGrantSensorsPermissions);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 1194099..b52347f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.FullyManagedDeviceProvisioningParams;
import android.app.admin.IDevicePolicyManager;
@@ -120,11 +121,17 @@
@NonNull String callerPackage, @NonNull String enterpriseId, int userId) {}
public UserHandle createAndProvisionManagedProfile(
- @NonNull ManagedProfileProvisioningParams provisioningParams) {
+ @NonNull ManagedProfileProvisioningParams provisioningParams, String callerPackage) {
return null;
}
public void provisionFullyManagedDevice(
- FullyManagedDeviceProvisioningParams provisioningParams) {
+ FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
+ }
+
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {}
+
+ public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+ return false;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 8b2beb2..8ea21ec 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -46,11 +46,15 @@
@GuardedBy("mLock")
private final SparseIntArray mPermissionPolicy = new SparseIntArray();
+ @GuardedBy("mLock")
+ private final SparseBooleanArray mCanGrantSensorsPermissions = new SparseBooleanArray();
+
public void onUserRemoved(int userHandle) {
synchronized (mLock) {
mScreenCaptureDisabled.delete(userHandle);
mPasswordQuality.delete(userHandle);
mPermissionPolicy.delete(userHandle);
+ mCanGrantSensorsPermissions.delete(userHandle);
}
}
@@ -97,6 +101,21 @@
}
}
+ @Override
+ public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle) {
+ synchronized (mLock) {
+ return mCanGrantSensorsPermissions.get(userHandle, false);
+ }
+ }
+
+ /** Sets ahmin control over permission grants for user. */
+ public void setAdminCanGrantSensorsPermissions(@UserIdInt int userHandle,
+ boolean canGrant) {
+ synchronized (mLock) {
+ mCanGrantSensorsPermissions.put(userHandle, canGrant);
+ }
+ }
+
/** Dump content */
public void dump(IndentingPrintWriter pw) {
pw.println("Device policy cache:");
@@ -104,6 +123,8 @@
pw.println("Screen capture disabled: " + mScreenCaptureDisabled.toString());
pw.println("Password quality: " + mPasswordQuality.toString());
pw.println("Permission policy: " + mPermissionPolicy.toString());
+ pw.println("Admin can grant sensors permission: "
+ + mCanGrantSensorsPermissions.toString());
pw.decreaseIndent();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d235a7f..404b0cf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -92,6 +92,7 @@
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
+import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -128,6 +129,7 @@
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -157,6 +159,7 @@
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.DeviceStateCache;
@@ -310,6 +313,7 @@
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
+import com.android.server.devicepolicy.Owners.OwnerDto;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.RestrictionsSet;
@@ -558,6 +562,21 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long USE_SET_LOCATION_ENABLED = 117835097L;
+ // Only add to the end of the list. Do not change or rearrange these values, that will break
+ // historical data. Do not use negative numbers or zero, logger only handles positive
+ // integers.
+ private static final int COPY_ACCOUNT_SUCCEEDED = 1;
+ private static final int COPY_ACCOUNT_FAILED = 2;
+ private static final int COPY_ACCOUNT_TIMED_OUT = 3;
+ private static final int COPY_ACCOUNT_EXCEPTION = 4;
+
+ @IntDef({
+ COPY_ACCOUNT_SUCCEEDED,
+ COPY_ACCOUNT_FAILED,
+ COPY_ACCOUNT_TIMED_OUT,
+ COPY_ACCOUNT_EXCEPTION})
+ private @interface CopyAccountStatus {}
+
/**
* Admin apps targeting Android S+ may not use
* {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality
@@ -892,12 +911,20 @@
};
protected static class RestrictionsListener implements UserRestrictionsListener {
- private Context mContext;
+ private final Context mContext;
+ private final UserManagerInternal mUserManagerInternal;
+ private final DevicePolicyManagerService mDpms;
- public RestrictionsListener(Context context) {
+ public RestrictionsListener(
+ Context context,
+ UserManagerInternal userManagerInternal,
+ DevicePolicyManagerService dpms) {
mContext = context;
+ mUserManagerInternal = userManagerInternal;
+ mDpms = dpms;
}
+ @Override
public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
Bundle prevRestrictions) {
final boolean newlyDisallowed =
@@ -907,13 +934,19 @@
final boolean restrictionChanged = (newlyDisallowed != previouslyDisallowed);
if (restrictionChanged) {
- // Notify ManagedProvisioning to update the built-in cross profile intent filters.
- Intent intent = new Intent(
- DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
- intent.setPackage(getManagedProvisioningPackage(mContext));
- intent.putExtra(Intent.EXTRA_USER_ID, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ final int parentId = mUserManagerInternal.getProfileParentId(userId);
+ if (parentId == userId) {
+ return;
+ }
+
+ // Always reset filters on the parent user, which handles cross profile intent
+ // filters between the parent and its profiles.
+ Slog.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
+ + "change");
+ mDpms.resetDefaultCrossProfileIntentFilters(parentId);
+ mContext.sendBroadcastAsUser(new Intent(
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED),
+ UserHandle.of(userId));
}
}
}
@@ -1067,30 +1100,51 @@
* @throws UnsafeStateException if it's not safe to execute the operation.
*/
private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) {
- if (!canExecute(operation)) {
- if (mSafetyChecker == null) {
- // Happens on CTS after it's set just once (by OneTimeSafetyChecker)
- throw new UnsafeStateException(operation);
- }
- // Let mSafetyChecker customize it (for example, by explaining how to retry)
- throw mSafetyChecker.newUnsafeStateException(operation);
+ int reason = getUnsafeOperationReason(operation);
+ if (reason == UNSAFE_OPERATION_REASON_NONE) return;
+
+ if (mSafetyChecker == null) {
+ // Happens on CTS after it's set just once (by OneTimeSafetyChecker)
+ throw new UnsafeStateException(operation, reason);
}
+ // Let mSafetyChecker customize it (for example, by explaining how to retry)
+ throw mSafetyChecker.newUnsafeStateException(operation, reason);
}
/**
- * Returns whether it's safe to execute the given {@code operation}.
+ * Returns whether it's safe to execute the given {@code operation}, and why.
*/
- boolean canExecute(@DevicePolicyOperation int operation) {
- return mSafetyChecker == null || mSafetyChecker.isDevicePolicyOperationSafe(operation);
+ @UnsafeOperationReason
+ int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
+ return mSafetyChecker == null ? UNSAFE_OPERATION_REASON_NONE
+ : mSafetyChecker.getUnsafeOperationReason(operation);
}
@Override
- public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) {
+ public void setNextOperationSafety(@DevicePolicyOperation int operation,
+ @UnsafeOperationReason int reason) {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
- Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %b)",
- DevicePolicyManager.operationToString(operation), safe));
- mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe);
+ Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %s)",
+ DevicePolicyManager.operationToString(operation),
+ DevicePolicyManager.unsafeOperationReasonToString(reason)));
+ mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
+ }
+
+ // Used by DevicePolicyManagerServiceShellCommand
+ List<OwnerDto> listAllOwners() {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
+ List<OwnerDto> owners = mOwners.listAllOwners();
+ synchronized (getLockObject()) {
+ for (int i = 0; i < owners.size(); i++) {
+ OwnerDto owner = owners.get(i);
+ owner.isAffiliated = isUserAffiliatedWithDeviceLocked(owner.userId);
+ }
+ }
+
+ return owners;
}
/**
@@ -1598,7 +1652,8 @@
mSetupContentObserver = new SetupContentObserver(mHandler);
- mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
+ mUserManagerInternal.addUserRestrictionsListener(
+ new RestrictionsListener(mContext, mUserManagerInternal, this));
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
loadOwners();
@@ -2936,6 +2991,7 @@
updatePasswordQualityCacheForUserGroup(
userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId);
updatePermissionPolicyCache(userId);
+ updateAdminCanGrantSensorsPermissionCache(userId);
startOwnerService(userId, "start-user");
}
@@ -13428,15 +13484,15 @@
if (!mOwners.hasDeviceOwner()) {
return false;
}
- if (userId == mOwners.getDeviceOwnerUserId()) {
- // The user that the DO is installed on is always affiliated with the device.
- return true;
- }
if (userId == UserHandle.USER_SYSTEM) {
// The system user is always affiliated in a DO device,
// even if in headless system user mode.
return true;
}
+ if (userId == mOwners.getDeviceOwnerUserId()) {
+ // The user that the DO is installed on is always affiliated with the device.
+ return true;
+ }
final ComponentName profileOwner = getProfileOwnerAsUser(userId);
if (profileOwner == null) {
@@ -15927,11 +15983,12 @@
@Override
public UserHandle createAndProvisionManagedProfile(
- @NonNull ManagedProfileProvisioningParams provisioningParams) {
+ @NonNull ManagedProfileProvisioningParams provisioningParams,
+ @NonNull String callerPackage) {
final ComponentName admin = provisioningParams.getProfileAdminComponentName();
Objects.requireNonNull(admin, "admin is null");
- final CallerIdentity caller = getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentity(callerPackage);
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
@@ -15946,6 +16003,7 @@
"Provisioning preconditions failed with result: " + result);
}
+ final long startTime = SystemClock.elapsedRealtime();
final Set<String> nonRequiredApps = provisioningParams.isLeaveAllSystemAppsEnabled()
? Collections.emptySet()
: mOverlayPackagesProvider.getNonRequiredApps(
@@ -15962,8 +16020,12 @@
"Error creating profile, createProfileForUserEvenWhenDisallowed "
+ "returned null.");
}
-
resetInteractAcrossProfilesAppOps();
+ logEventDuration(
+ DevicePolicyEnums.PLATFORM_PROVISIONING_CREATE_PROFILE_MS,
+ startTime,
+ callerPackage);
+
installExistingAdminPackage(userInfo.id, admin.getPackageName());
if (!enableAdminAndSetProfileOwner(
userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
@@ -15973,28 +16035,22 @@
}
setUserSetupComplete(userInfo.id);
- startUser(userInfo.id);
+ startUser(userInfo.id, callerPackage);
maybeMigrateAccount(
userInfo.id, caller.getUserId(), provisioningParams.getAccountToMigrate(),
- provisioningParams.isKeepAccountMigrated());
+ provisioningParams.isKeepAccountMigrated(), callerPackage);
if (provisioningParams.isOrganizationOwnedProvisioning()) {
- markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id);
- restrictRemovalOfManagedProfile(admin, userInfo.id);
+ setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId());
}
- final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_PROFILE_CREATED)
- .putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id)
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- provisioningParams.isLeaveAllSystemAppsEnabled())
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
return userInfo.getUserHandle();
} catch (Exception e) {
- // in case of any errors during provisioning, remove the newly created profile.
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
+ .setStrings(callerPackage)
+ .write();
+ // In case of any errors during provisioning, remove the newly created profile.
if (userInfo != null) {
mUserManager.removeUserEvenWhenDisallowed(userInfo.id);
}
@@ -16099,7 +16155,9 @@
mContext.getContentResolver(), USER_SETUP_COMPLETE, 1, userId);
}
- private void startUser(@UserIdInt int userId) throws IllegalStateException {
+ private void startUser(@UserIdInt int userId, String callerPackage)
+ throws IllegalStateException {
+ final long startTime = SystemClock.elapsedRealtime();
final UserUnlockedBlockingReceiver unlockedReceiver = new UserUnlockedBlockingReceiver(
userId);
mContext.registerReceiverAsUser(
@@ -16118,6 +16176,10 @@
throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
String.format("Timeout whilst waiting for unlock of user %d.", userId));
}
+ logEventDuration(
+ DevicePolicyEnums.PLATFORM_PROVISIONING_START_PROFILE_MS,
+ startTime,
+ callerPackage);
} catch (RemoteException e) {
// Shouldn't happen.
} finally {
@@ -16125,9 +16187,9 @@
}
}
- void maybeMigrateAccount(
+ private void maybeMigrateAccount(
@UserIdInt int targetUserId, @UserIdInt int sourceUserId, Account accountToMigrate,
- boolean keepAccountMigrated) {
+ boolean keepAccountMigrated, String callerPackage) {
final UserHandle sourceUser = UserHandle.of(sourceUserId);
final UserHandle targetUser = UserHandle.of(targetUserId);
if (accountToMigrate == null) {
@@ -16138,13 +16200,16 @@
Slog.w(LOG_TAG, "sourceUser and targetUser are the same, won't migrate account.");
return;
}
- copyAccount(targetUser, sourceUser, accountToMigrate);
+ copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
if (!keepAccountMigrated) {
removeAccount(accountToMigrate);
}
}
- void copyAccount(UserHandle targetUser, UserHandle sourceUser, Account accountToMigrate) {
+ private void copyAccount(
+ UserHandle targetUser, UserHandle sourceUser, Account accountToMigrate,
+ String callerPackage) {
+ final long startTime = SystemClock.elapsedRealtime();
try {
final AccountManager accountManager = mContext.getSystemService(AccountManager.class);
final boolean copySucceeded = accountManager.copyAccountToUser(
@@ -16153,16 +16218,35 @@
targetUser,
/* callback= */ null, /* handler= */ null)
.getResult(60 * 3, TimeUnit.SECONDS);
- if (!copySucceeded) {
+ if (copySucceeded) {
+ logCopyAccountStatus(COPY_ACCOUNT_SUCCEEDED, callerPackage);
+ logEventDuration(
+ DevicePolicyEnums.PLATFORM_PROVISIONING_COPY_ACCOUNT_MS,
+ startTime,
+ callerPackage);
+ } else {
+ logCopyAccountStatus(COPY_ACCOUNT_FAILED, callerPackage);
Slog.e(LOG_TAG, "Failed to copy account to " + targetUser);
}
- } catch (OperationCanceledException | AuthenticatorException | IOException e) {
+ } catch (OperationCanceledException e) {
// Account migration is not considered a critical operation.
+ logCopyAccountStatus(COPY_ACCOUNT_TIMED_OUT, callerPackage);
+ Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
+ } catch (AuthenticatorException | IOException e) {
+ logCopyAccountStatus(COPY_ACCOUNT_EXCEPTION, callerPackage);
Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
}
}
- void removeAccount(Account account) {
+ private static void logCopyAccountStatus(@CopyAccountStatus int status, String callerPackage) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_COPY_ACCOUNT_STATUS)
+ .setInt(status)
+ .setStrings(callerPackage)
+ .write();
+ }
+
+ private void removeAccount(Account account) {
final AccountManager accountManager =
mContext.getSystemService(AccountManager.class);
final AccountManagerFuture<Bundle> bundle = accountManager.removeAccount(account,
@@ -16189,26 +16273,25 @@
}
}
- private void markIsProfileOwnerOnOrganizationOwnedDevice(
- ComponentName admin, @UserIdInt int profileId) {
- getDpmForProfile(profileId).markProfileOwnerOnOrganizationOwnedDevice(admin);
+ private void setProfileOwnerOnOrgOwnedDeviceState(
+ ComponentName admin, @UserIdInt int profileId, @UserIdInt int parentUserId) {
+ synchronized (getLockObject()) {
+ markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, profileId);
+ }
+ restrictRemovalOfManagedProfile(parentUserId);
}
- private void restrictRemovalOfManagedProfile(
- ComponentName admin, @UserIdInt int profileId) {
- getDpmForProfile(profileId).addUserRestriction(
- admin, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
- }
-
- private DevicePolicyManager getDpmForProfile(@UserIdInt int profileId) {
- final Context profileContext = mContext.createContextAsUser(
- UserHandle.of(profileId), /* flags= */ 0);
- return profileContext.getSystemService(DevicePolicyManager.class);
+ private void restrictRemovalOfManagedProfile(@UserIdInt int parentUserId) {
+ final UserHandle parentUserHandle = UserHandle.of(parentUserId);
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+ /* value= */ true,
+ parentUserHandle);
}
@Override
public void provisionFullyManagedDevice(
- FullyManagedDeviceProvisioningParams provisioningParams) {
+ FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
ComponentName deviceAdmin = provisioningParams.getDeviceAdminComponentName();
Objects.requireNonNull(deviceAdmin, "admin is null.");
@@ -16220,12 +16303,16 @@
final long identity = Binder.clearCallingIdentity();
try {
- int result = checkProvisioningPreConditionSkipPermission(
- ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
- if (result != CODE_OK) {
- throw new ServiceSpecificException(
- PROVISIONING_RESULT_PRE_CONDITION_FAILED,
- "Provisioning preconditions failed with result: " + result);
+ // TODO(b/178187130): This check fails silent provisioning, uncomment once silent
+ // provisioning is no longer used.
+ if (false) {
+ int result = checkProvisioningPreConditionSkipPermission(
+ ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
+ if (result != CODE_OK) {
+ throw new ServiceSpecificException(
+ PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+ "Provisioning preconditions failed with result: " + result);
+ }
}
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
@@ -16248,15 +16335,14 @@
}
disallowAddUser();
-
- final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONED_MANAGED_DEVICE)
- .putExtra(Intent.EXTRA_USER_HANDLE, caller.getUserId())
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- provisioningParams.isLeaveAllSystemAppsEnabled())
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ setAdminCanGrantSensorsPermissionForUserUnchecked(caller.getUserId(),
+ provisioningParams.canDeviceOwnerGrantSensorsPermissions());
+ } catch (Exception e) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
+ .setStrings(callerPackage)
+ .write();
+ throw e;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -16333,6 +16419,92 @@
private boolean setActiveAdminAndDeviceOwner(
@UserIdInt int userId, ComponentName adminComponent, String name) {
enableAndSetActiveAdmin(userId, userId, adminComponent);
- return setDeviceOwner(adminComponent, name, userId);
+ // TODO(b/178187130): Directly set DO and remove the check once silent provisioning is no
+ // longer used.
+ if (getDeviceOwnerComponent(/* callingUserOnly= */ true) == null) {
+ return setDeviceOwner(adminComponent, name, userId);
+ }
+ return true;
+ }
+
+ private static void logEventDuration(int eventId, long startTime, String callerPackage) {
+ final long duration = SystemClock.elapsedRealtime() - startTime;
+ DevicePolicyEventLogger
+ .createEvent(eventId)
+ .setTimePeriod(duration)
+ .setStrings(callerPackage)
+ .write();
+ }
+
+ @Override
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ try {
+ final List<UserInfo> profiles = mUserManager.getProfiles(userId);
+ final int numOfProfiles = profiles.size();
+ if (numOfProfiles <= 1) {
+ return;
+ }
+
+ final String managedProvisioningPackageName = getManagedProvisioningPackage(
+ mContext);
+ // Removes cross profile intent filters from the parent to all the profiles.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ userId, mContext.getOpPackageName());
+ // Setting and resetting default cross profile intent filters was previously handled
+ // by Managed Provisioning. For backwards compatibility, clear any intent filters
+ // that were set by ManagedProvisioning.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ userId, managedProvisioningPackageName);
+
+ // For each profile reset cross profile intent filters
+ for (int i = 0; i < numOfProfiles; i++) {
+ UserInfo profile = profiles.get(i);
+ mIPackageManager.clearCrossProfileIntentFilters(
+ profile.id, mContext.getOpPackageName());
+ // Clear any intent filters that were set by ManagedProvisioning.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ profile.id, managedProvisioningPackageName);
+
+ mUserManagerInternal.setDefaultCrossProfileIntentFilters(userId, profile.id);
+ }
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+ });
+ }
+
+ private void setAdminCanGrantSensorsPermissionForUserUnchecked(int userId, boolean canGrant) {
+ synchronized (getLockObject()) {
+ ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
+
+ Preconditions.checkState(
+ isDeviceOwner(owner) && owner.getUserHandle().getIdentifier() == userId,
+ "May only be set on a the user of a device owner.");
+
+ owner.mAdminCanGrantSensorsPermissions = canGrant;
+ mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
+ saveSettingsLocked(userId);
+ }
+ }
+
+ private void updateAdminCanGrantSensorsPermissionCache(int userId) {
+ synchronized (getLockObject()) {
+ ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
+ final boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
+ mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
+ }
+ }
+
+ @Override
+ public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+ if (!mHasFeature) {
+ return false;
+ }
+
+ return mPolicyCache.canAdminGrantSensorsPermissionsForUser(userId);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 22866b4..222c987 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -18,13 +18,17 @@
import android.app.admin.DevicePolicyManager;
import android.os.ShellCommand;
+import com.android.server.devicepolicy.Owners.OwnerDto;
+
import java.io.PrintWriter;
+import java.util.List;
import java.util.Objects;
final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe";
private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
+ private static final String CMD_LIST_OWNERS = "list-owners";
private final DevicePolicyManagerService mService;
@@ -51,6 +55,8 @@
return runIsSafeOperation(pw);
case CMD_SET_SAFE_OPERATION:
return runSetSafeOperation(pw);
+ case CMD_LIST_OWNERS:
+ return runListOwners(pw);
default:
return onInvalidCommand(pw, cmd);
}
@@ -73,25 +79,62 @@
pw.printf(" Prints this help text.\n\n");
pw.printf(" %s <OPERATION_ID>\n", CMD_IS_SAFE_OPERATION);
pw.printf(" Checks if the give operation is safe \n\n");
- pw.printf(" %s <OPERATION_ID> <true|false>\n", CMD_SET_SAFE_OPERATION);
+ pw.printf(" %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION);
pw.printf(" Emulates the result of the next call to check if the given operation is safe"
+ " \n\n");
+ pw.printf(" %s\n", CMD_LIST_OWNERS);
+ pw.printf(" Lists the device / profile owners per user \n\n");
}
private int runIsSafeOperation(PrintWriter pw) {
int operation = Integer.parseInt(getNextArgRequired());
- boolean safe = mService.canExecute(operation);
- pw.printf("Operation %s is %s\n", DevicePolicyManager.operationToString(operation),
- safe ? "SAFE" : "UNSAFE");
+ int reason = mService.getUnsafeOperationReason(operation);
+ boolean safe = reason == DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
+ pw.printf("Operation %s is %b. Reason: %s\n",
+ DevicePolicyManager.operationToString(operation), safe,
+ DevicePolicyManager.unsafeOperationReasonToString(reason));
return 0;
}
private int runSetSafeOperation(PrintWriter pw) {
int operation = Integer.parseInt(getNextArgRequired());
- boolean safe = getNextArg().equals("true");
- mService.setNextOperationSafety(operation, safe);
+ int reason = Integer.parseInt(getNextArgRequired());
+ mService.setNextOperationSafety(operation, reason);
pw.printf("Next call to check operation %s will return %s\n",
- DevicePolicyManager.operationToString(operation), safe ? "SAFE" : "UNSAFE");
+ DevicePolicyManager.operationToString(operation),
+ DevicePolicyManager.unsafeOperationReasonToString(reason));
return 0;
}
+
+ private int runListOwners(PrintWriter pw) {
+ List<OwnerDto> owners = mService.listAllOwners();
+ if (owners.isEmpty()) {
+ pw.println("none");
+ return 0;
+ }
+ int size = owners.size();
+ if (size == 1) {
+ pw.println("1 owner:");
+ } else {
+ pw.printf("%d owners:\n", size);
+ }
+
+ for (int i = 0; i < size; i++) {
+ OwnerDto owner = owners.get(i);
+ pw.printf("User %2d: admin=%s", owner.userId, owner.admin.flattenToShortString());
+ if (owner.isDeviceOwner) {
+ pw.print(",DeviceOwner");
+ }
+ if (owner.isProfileOwner) {
+ pw.print(",ProfileOwner");
+ }
+ if (owner.isAffiliated) {
+ pw.print(",Affiliated");
+ }
+ pw.println();
+ }
+
+ return 0;
+ }
+
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index f7a8261..883f95d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -15,9 +15,12 @@
*/
package com.android.server.devicepolicy;
+import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
import static android.app.admin.DevicePolicyManager.operationToString;
+import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.app.admin.DevicePolicySafetyChecker;
import android.util.Slog;
@@ -40,31 +43,33 @@
private final DevicePolicyManagerService mService;
private final DevicePolicySafetyChecker mRealSafetyChecker;
private final @DevicePolicyOperation int mOperation;
- private final boolean mSafe;
+ private final @UnsafeOperationReason int mReason;
OneTimeSafetyChecker(DevicePolicyManagerService service,
- @DevicePolicyOperation int operation, boolean safe) {
+ @DevicePolicyOperation int operation, @UnsafeOperationReason int reason) {
mService = Objects.requireNonNull(service);
mOperation = operation;
- mSafe = safe;
+ mReason = reason;
mRealSafetyChecker = service.getDevicePolicySafetyChecker();
Slog.i(TAG, "Saving real DevicePolicySafetyChecker as " + mRealSafetyChecker);
}
@Override
- public boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) {
+ @UnsafeOperationReason
+ public int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
String name = operationToString(operation);
- boolean safe = true;
+ int reason = UNSAFE_OPERATION_REASON_NONE;
if (operation == mOperation) {
- safe = mSafe;
+ reason = mReason;
} else {
Slog.wtf(TAG, "invalid call to isDevicePolicyOperationSafe(): asked for " + name
+ ", should be " + operationToString(mOperation));
}
- Slog.i(TAG, "isDevicePolicyOperationSafe(" + name + "): returning " + safe
+ Slog.i(TAG, "getDevicePolicyOperationSafety(" + name + "): returning "
+ + unsafeOperationReasonToString(reason)
+ " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker);
- return safe;
+ return reason;
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 809afe0..1e70d59 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManagerInternal;
import android.app.admin.SystemUpdateInfo;
@@ -57,6 +58,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -433,6 +435,23 @@
}
}
+ List<OwnerDto> listAllOwners() {
+ List<OwnerDto> owners = new ArrayList<>();
+ synchronized (mLock) {
+ if (mDeviceOwner != null) {
+ owners.add(new OwnerDto(mDeviceOwnerUserId, mDeviceOwner.admin,
+ /* isDeviceOwner= */ true));
+ }
+ for (int i = 0; i < mProfileOwners.size(); i++) {
+ int userId = mProfileOwners.keyAt(i);
+ OwnerInfo info = mProfileOwners.valueAt(i);
+ owners.add(new OwnerDto(userId, info.admin, /* isDeviceOwner= */ false));
+ }
+ }
+ return owners;
+ }
+
+
SystemUpdatePolicy getSystemUpdatePolicy() {
synchronized (mLock) {
return mSystemUpdatePolicy;
@@ -1076,6 +1095,24 @@
}
}
+ /**
+ * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}.
+ */
+ static final class OwnerDto {
+ public final @UserIdInt int userId;
+ public final ComponentName admin;
+ public final boolean isDeviceOwner;
+ public final boolean isProfileOwner;
+ public boolean isAffiliated;
+
+ private OwnerDto(@UserIdInt int userId, ComponentName admin, boolean isDeviceOwner) {
+ this.userId = userId;
+ this.admin = Objects.requireNonNull(admin, "admin must not be null");
+ this.isDeviceOwner = isDeviceOwner;
+ this.isProfileOwner = !isDeviceOwner;
+ }
+ }
+
public void dump(IndentingPrintWriter pw) {
boolean needBlank = false;
if (mDeviceOwner != null) {
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index e978ed4..7534c7c 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -51,9 +51,9 @@
static_libs: [
"libbase",
"libext2_uuid",
- "libdataloader_aidl-unstable-cpp",
- "libincremental_aidl-unstable-cpp",
- "libincremental_manager_aidl-unstable-cpp",
+ "libdataloader_aidl-cpp",
+ "libincremental_aidl-cpp",
+ "libincremental_manager_aidl-cpp",
"libprotobuf-cpp-lite",
"service.incremental.proto",
"libutils",
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index b2efc71..42360d8 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -88,7 +88,6 @@
}
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
- ps->giveThreadPoolName();
// sm->addService increments the reference count, and now we're OK with returning the pointer.
return self.get();
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 636be4a..7a4c611 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -147,6 +147,7 @@
import com.android.server.om.OverlayManagerService;
import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
+import com.android.server.os.NativeTombstoneManagerService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.people.PeopleService;
import com.android.server.pm.BackgroundDexOptService;
@@ -160,6 +161,7 @@
import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
import com.android.server.pm.dex.SystemServerDexLoadReporter;
+import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.policy.PermissionPolicyService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.policy.role.RoleServicePlatformHelperImpl;
@@ -206,12 +208,15 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
+import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
@@ -480,6 +485,50 @@
private static native void fdtrackAbort();
+ private static final File HEAP_DUMP_PATH = new File("/data/system/heapdump/");
+ private static final int MAX_HEAP_DUMPS = 2;
+
+ /**
+ * Dump system_server's heap.
+ *
+ * For privacy reasons, these aren't automatically pulled into bugreports:
+ * they must be manually pulled by the user.
+ */
+ private static void dumpHprof() {
+ // hprof dumps are rather large, so ensure we don't fill the disk by generating
+ // hundreds of these that will live forever.
+ TreeSet<File> existingTombstones = new TreeSet<>();
+ for (File file : HEAP_DUMP_PATH.listFiles()) {
+ if (!file.isFile()) {
+ continue;
+ }
+ if (!file.getName().startsWith("fdtrack-")) {
+ continue;
+ }
+ existingTombstones.add(file);
+ }
+ if (existingTombstones.size() >= MAX_HEAP_DUMPS) {
+ for (int i = 0; i < MAX_HEAP_DUMPS - 1; ++i) {
+ // Leave the newest `MAX_HEAP_DUMPS - 1` tombstones in place.
+ existingTombstones.pollLast();
+ }
+ for (File file : existingTombstones) {
+ if (!file.delete()) {
+ Slog.w("System", "Failed to clean up hprof " + file);
+ }
+ }
+ }
+
+ try {
+ String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
+ String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof";
+ Debug.dumpHprofData(filename);
+ } catch (IOException ex) {
+ Slog.e("System", "Failed to dump fdtrack hprof");
+ ex.printStackTrace();
+ }
+ }
+
/**
* Spawn a thread that monitors for fd leaks.
*/
@@ -504,6 +553,7 @@
enabled = true;
} else if (maxFd > abortThreshold) {
Slog.i("System", "fdtrack abort threshold reached, dumping and aborting");
+ dumpHprof();
fdtrackAbort();
}
@@ -1060,11 +1110,18 @@
SystemClock.elapsedRealtime());
}
+ t.traceBegin("StartDomainVerificationService");
+ DomainVerificationService domainVerificationService = new DomainVerificationService(
+ mSystemContext, SystemConfig.getInstance(), platformCompat);
+ mSystemServiceManager.startService(domainVerificationService);
+ t.traceEnd();
+
t.traceBegin("StartPackageManagerService");
try {
Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
- mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
+ domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
+ mOnlyCore);
} finally {
Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
}
@@ -1209,6 +1266,11 @@
mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ // Tracks native tombstones.
+ t.traceBegin("StartNativeTombstoneManagerService");
+ mSystemServiceManager.startService(NativeTombstoneManagerService.class);
+ t.traceEnd();
+
// Service to capture bugreports.
t.traceBegin("StartBugreportManagerService");
mSystemServiceManager.startService(BugreportManagerService.class);
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 21c863d..6c5c1d4 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -54,6 +54,7 @@
import org.mockito.Mockito.verify
import org.testng.Assert.assertThrows
import java.io.File
+import java.util.UUID
@RunWith(Parameterized::class)
class PackageManagerComponentLabelIconOverrideTest {
@@ -262,8 +263,13 @@
.apply(block)
.hideAsFinal()
- private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"),
- null, null, null, null, 0, 0, 0, 0, null, null, null)) {
+ private fun makePkgSetting(pkgName: String) = spy(
+ PackageSetting(
+ pkgName, null, File("/test"),
+ null, null, null, null, 0, 0, 0, 0, null, null, null,
+ UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c")
+ )
+ ) {
this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
}
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
new file mode 100644
index 0000000..4aa8abc
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "PackageManagerServiceUnitTests",
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "junit",
+ "services.core",
+ "servicestests-utils",
+ "testng",
+ "truth-prebuilt",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml
new file mode 100644
index 0000000..2ef7a1f
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test">
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.pm.test"
+ />
+
+</manifest>
+
diff --git a/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml b/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml
new file mode 100644
index 0000000..78dd1c5
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Test module config for PackageManagerServiceUnitTests">
+ <option name="test-tag" value="PackageManagerServiceUnitTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="PackageManagerServiceUnitTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.pm.test" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
new file mode 100644
index 0000000..e99b071
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.os.Build
+import android.os.PatternMatcher
+import android.util.ArraySet
+import com.android.server.SystemConfig
+import com.android.server.compat.PlatformCompat
+import com.android.server.pm.verify.domain.DomainVerificationCollector
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+
+class DomainVerificationCollectorTest {
+
+ companion object {
+ private const val TEST_PKG_NAME = "com.test.pkg"
+ }
+
+ private val platformCompat: PlatformCompat = mockThrowOnUnmocked {
+ whenever(isChangeEnabled(eq(DomainVerificationCollector.RESTRICT_DOMAINS), any())) {
+ (arguments[1] as ApplicationInfo).targetSdkVersion >= Build.VERSION_CODES.S
+ }
+ }
+
+ @Test
+ fun verifyV1() {
+ val pkg = mockPkg(useV2 = false, autoVerify = true)
+ val collector = mockCollector()
+ assertThat(collector.collectAllWebDomains(pkg))
+ .containsExactly("example1.com", "example2.com", "example3.com")
+ assertThat(collector.collectAutoVerifyDomains(pkg))
+ .containsExactly("example1.com", "example2.com", "example3.com", "example4.com")
+ }
+
+ @Test
+ fun verifyV1NoAutoVerify() {
+ val pkg = mockPkg(useV2 = false, autoVerify = false)
+ val collector = mockCollector()
+ assertThat(collector.collectAllWebDomains(pkg))
+ .containsExactly("example1.com", "example2.com", "example3.com")
+ assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+ }
+
+ @Test
+ fun verifyV1ForceAutoVerify() {
+ val pkg = mockPkg(useV2 = false, autoVerify = false)
+ val collector = mockCollector(linkedApps = setOf(TEST_PKG_NAME))
+ assertThat(collector.collectAllWebDomains(pkg))
+ .containsExactly("example1.com", "example2.com", "example3.com")
+ assertThat(collector.collectAutoVerifyDomains(pkg))
+ .containsExactly("example1.com", "example2.com", "example3.com", "example4.com")
+ }
+
+ @Test
+ fun verifyV1NoValidIntentFilter() {
+ val pkg = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { TEST_PKG_NAME }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.R }
+
+ val activityList = listOf(
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority("example1.com", null)
+ }
+ )
+ },
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ setAutoVerify(true)
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+
+ // The presence of a non-web-scheme as the only autoVerify
+ // intent-filter, when non-forced, means that v1 will not pick
+ // up the package for verification.
+ addDataScheme("nonWebScheme")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority("example2.com", null)
+ }
+ )
+ },
+ )
+
+ whenever(activities) { activityList }
+ }
+
+ val collector = mockCollector()
+ assertThat(collector.collectAllWebDomains(pkg))
+ .containsExactly("example1.com", "example2.com")
+ assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+ }
+
+ @Test
+ fun verifyV2() {
+ val pkg = mockPkg(useV2 = true, autoVerify = true)
+ val collector = mockCollector()
+
+ assertThat(collector.collectAllWebDomains(pkg))
+ .containsExactly("example1.com", "example2.com", "example3.com")
+ assertThat(collector.collectAutoVerifyDomains(pkg))
+ .containsExactly("example1.com", "example3.com")
+ }
+
+ @Test
+ fun verifyV2NoAutoVerify() {
+ val pkg = mockPkg(useV2 = true, autoVerify = false)
+ val collector = mockCollector()
+
+ assertThat(collector.collectAllWebDomains(pkg))
+ .containsExactly("example1.com", "example2.com", "example3.com")
+ assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+ }
+
+ @Test
+ fun verifyV2ForceAutoVerifyIgnored() {
+ val pkg = mockPkg(useV2 = true, autoVerify = false)
+ val collector = mockCollector(linkedApps = setOf(TEST_PKG_NAME))
+
+ assertThat(collector.collectAllWebDomains(pkg))
+ .containsExactly("example1.com", "example2.com", "example3.com")
+ assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+ }
+
+ private fun mockCollector(linkedApps: Set<String> = emptySet()): DomainVerificationCollector {
+ val systemConfig = mockThrowOnUnmocked<SystemConfig> {
+ whenever(this.linkedApps) { ArraySet(linkedApps) }
+ }
+
+ return DomainVerificationCollector(platformCompat, systemConfig)
+ }
+
+ private fun mockPkg(useV2: Boolean, autoVerify: Boolean): AndroidPackage {
+ // Translate equivalent of the following manifest declaration. This string isn't actually
+ // parsed, but it's a far easier to read representation of the test data.
+ // language=XML
+ """
+ <xml>
+ <intent-filter android:autoVerify="$autoVerify">
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="http"/>
+ <data android:scheme="https"/>
+ <data android:path="/sub"/>
+ <data android:host="example1.com"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="http"/>
+ <data android:path="/sub2"/>
+ <data android:host="example2.com"/>
+ </intent-filter>
+ <intent-filter android:autoVerify="$autoVerify">
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="https"/>
+ <data android:path="/sub3"/>
+ <data android:host="example3.com"/>
+ </intent-filter>
+ <intent-filter android:autoVerify="$autoVerify">
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <data android:scheme="https"/>
+ <data android:path="/sub4"/>
+ <data android:host="example4.com"/>
+ </intent-filter>
+ <intent-filter android:autoVerify="$autoVerify">
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="https"/>
+ <data android:path="/sub5"/>
+ <data android:host="example5.com"/>
+ </intent-filter>
+ <intent-filter android:autoVerify="$autoVerify">
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="https"/>
+ <data android:path="/sub5"/>
+ <data android:host="example5.com"/>
+ </intent-filter>
+ </xml>
+ """.trimIndent()
+
+ return mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { TEST_PKG_NAME }
+ whenever(targetSdkVersion) {
+ if (useV2) Build.VERSION_CODES.S else Build.VERSION_CODES.R
+ }
+
+ // The intents are split into separate Activities to test that multiple are collected
+ val activityList = listOf(
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ setAutoVerify(autoVerify)
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority("example1.com", null)
+ }
+ )
+ addIntent(
+ ParsedIntentInfo().apply {
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority("example2.com", null)
+ }
+ )
+ },
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ setAutoVerify(autoVerify)
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ addDataPath("/sub3", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority("example3.com", null)
+ }
+ )
+ },
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ setAutoVerify(autoVerify)
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addDataScheme("https")
+ addDataPath("/sub4", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority("example4.com", null)
+ }
+ )
+ addIntent(
+ ParsedIntentInfo().apply {
+ setAutoVerify(autoVerify)
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ addDataPath("/sub5", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority("example5.com", null)
+ }
+ )
+ addIntent(
+ ParsedIntentInfo().apply {
+ setAutoVerify(autoVerify)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ addDataPath("/sub6", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority("example6.com", null)
+ }
+ )
+ },
+ )
+
+ whenever(activities) { activityList }
+ }
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
new file mode 100644
index 0000000..deb3147
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationUserSelection
+import android.os.Parcel
+import android.os.Parcelable
+import android.os.UserHandle
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.UUID
+
+@RunWith(Parameterized::class)
+class DomainVerificationCoreApiTest {
+
+ companion object {
+ private val IS_EQUAL_TO: (value: Any, other: Any) -> Unit = { value, other ->
+ assertThat(value).isEqualTo(other)
+ }
+ private val IS_MAP_EQUAL_TO: (value: Map<*, *>, other: Map<*, *>) -> Unit = { value,
+ other ->
+ assertThat(value).containsExactlyEntriesIn(other)
+ }
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun parameters() = arrayOf(
+ Parameter(
+ initial = {
+ DomainVerificationRequest(
+ setOf(
+ "com.test.pkg.one",
+ "com.test.pkg.two"
+ )
+ )
+ },
+ unparcel = { DomainVerificationRequest.CREATOR.createFromParcel(it) },
+ assertion = { first, second ->
+ assertAll<DomainVerificationRequest, Set<String>>(first, second,
+ { it.packageNames }, { it.component1() }) { value, other ->
+ assertThat(value).containsExactlyElementsIn(other)
+ }
+ }
+ ),
+ Parameter(
+ initial = {
+ DomainVerificationInfo(
+ UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
+ "com.test.pkg",
+ mapOf(
+ "example.com" to 0,
+ "example.org" to 1,
+ "example.new" to 1000
+ )
+ )
+ },
+ unparcel = { DomainVerificationInfo.CREATOR.createFromParcel(it) },
+ assertion = { first, second ->
+ assertAll<DomainVerificationInfo, UUID>(first, second,
+ { it.identifier }, { it.component1() }, IS_EQUAL_TO
+ )
+ assertAll<DomainVerificationInfo, String>(first, second,
+ { it.packageName }, { it.component2() }, IS_EQUAL_TO
+ )
+ assertAll<DomainVerificationInfo, Map<String, Int?>>(first, second,
+ { it.hostToStateMap }, { it.component3() }, IS_MAP_EQUAL_TO
+ )
+ }
+ ),
+ Parameter(
+ initial = {
+ DomainVerificationUserSelection(
+ UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
+ "com.test.pkg",
+ UserHandle.of(10),
+ true,
+ mapOf(
+ "example.com" to true,
+ "example.org" to false,
+ "example.new" to true
+ )
+ )
+ },
+ unparcel = { DomainVerificationUserSelection.CREATOR.createFromParcel(it) },
+ assertion = { first, second ->
+ assertAll<DomainVerificationUserSelection, UUID>(first, second,
+ { it.identifier }, { it.component1() }, IS_EQUAL_TO
+ )
+ assertAll<DomainVerificationUserSelection, String>(first, second,
+ { it.packageName }, { it.component2() }, IS_EQUAL_TO
+ )
+ assertAll<DomainVerificationUserSelection, UserHandle>(first, second,
+ { it.user }, { it.component3() }, IS_EQUAL_TO
+ )
+ assertAll<DomainVerificationUserSelection, Boolean>(
+ first, second, { it.isLinkHandlingAllowed },
+ { it.component4() }, IS_EQUAL_TO
+ )
+ assertAll<DomainVerificationUserSelection, Map<String, Boolean>>(
+ first, second, { it.hostToUserSelectionMap },
+ { it.component5() }, IS_MAP_EQUAL_TO
+ )
+ }
+ )
+ )
+
+ class Parameter<T : Parcelable>(
+ val initial: () -> T,
+ val unparcel: (Parcel) -> T,
+ private val assertion: (first: T, second: T) -> Unit
+ ) {
+ @Suppress("UNCHECKED_CAST")
+ fun assert(first: Any, second: Any) = assertion(first as T, second as T)
+ }
+
+ private fun <T> assertAll(vararg values: T, block: (value: T, other: T) -> Unit) {
+ values.indices.drop(1).forEach {
+ block(values[0], values[it])
+ }
+ }
+
+ private fun <T, V : Any> assertAll(
+ first: T,
+ second: T,
+ fieldValue: (T) -> V,
+ componentValue: (T) -> V,
+ assertion: (value: V, other: V) -> Unit
+ ) {
+ val values = arrayOf<Any>(fieldValue(first), fieldValue(second),
+ componentValue(first), componentValue(second))
+ values.indices.drop(1).forEach {
+ @Suppress("UNCHECKED_CAST")
+ assertion(values[0] as V, values[it] as V)
+ }
+ }
+ }
+
+ @Parameterized.Parameter(0)
+ lateinit var parameter: Parameter<*>
+
+ @Test
+ fun parcel() {
+ val parcel = Parcel.obtain()
+ val initial = parameter.initial()
+ initial.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+
+ val newInitial = parameter.initial()
+ val unparceled = parameter.unparcel(parcel)
+ parameter.assert(newInitial, unparceled)
+
+ assertAll(initial, newInitial, unparceled) { value: Any, other: Any ->
+ assertThat(value).isEqualTo(other)
+ }
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
new file mode 100644
index 0000000..d863194
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageUserState
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.os.Build
+import android.os.Process
+import android.util.ArraySet
+import android.util.Singleton
+import android.util.SparseArray
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.verify.domain.DomainVerificationEnforcer
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.spyThrowOnUnmocked
+import com.android.server.testutils.whenever
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.testng.Assert.assertThrows
+import java.io.File
+import java.util.UUID
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
+
+private typealias Enforcer = DomainVerificationEnforcer
+
+@RunWith(Parameterized::class)
+class DomainVerificationEnforcerTest {
+
+ val context: Context = InstrumentationRegistry.getInstrumentation().context
+
+ companion object {
+ private val INTERNAL_UIDS = listOf(Process.ROOT_UID, Process.SHELL_UID, Process.SYSTEM_UID)
+ private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1
+ private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2
+
+ private const val TEST_PKG = "com.test"
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun parameters(): Array<Any> {
+ val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
+ DomainVerificationEnforcer(it)
+ }
+
+ val mockPkg = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { TEST_PKG }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+ whenever(activities) {
+ listOf(
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ addDataAuthority("example.com", null)
+ }
+ )
+ }
+ )
+ }
+ }
+
+ val uuid = UUID.randomUUID()
+
+ // TODO: PackageSetting field encapsulation to move to whenever(name)
+ val mockPkgSetting = spyThrowOnUnmocked(
+ PackageSetting(
+ TEST_PKG,
+ TEST_PKG,
+ File("/test"),
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ uuid
+ )
+ ) {
+ whenever(getPkg()) { mockPkg }
+ whenever(domainSetId) { uuid }
+ whenever(userState) {
+ SparseArray<PackageUserState>().apply {
+ this[0] = PackageUserState()
+ }
+ }
+ }
+
+ val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger, DomainVerificationService> =
+ {
+ val callingUidInt = AtomicInteger(-1)
+ val callingUserIdInt = AtomicInteger(-1)
+ Triple(
+ callingUidInt, callingUserIdInt, DomainVerificationService(
+ it,
+ mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+ mockThrowOnUnmocked {
+ whenever(
+ isChangeEnabled(
+ anyLong(),
+ any()
+ )
+ ) { true }
+ }).apply {
+ setConnection(mockThrowOnUnmocked {
+ whenever(callingUid) { callingUidInt.get() }
+ whenever(callingUserId) { callingUserIdInt.get() }
+ whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting }
+ whenever(getPackageLocked(TEST_PKG)) { mockPkg }
+ whenever(schedule(anyInt(), any()))
+ whenever(scheduleWriteSettings())
+ })
+ }
+ )
+ }
+
+ fun enforcer(
+ type: Type,
+ name: String,
+ block: DomainVerificationEnforcer.(
+ callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
+ ) -> Unit
+ ) = Params(
+ type,
+ makeEnforcer,
+ name
+ ) { enforcer, callingUid, callingUserId, userId, proxy ->
+ enforcer.block(callingUid, callingUserId, userId, proxy)
+ }
+
+ fun service(
+ type: Type,
+ name: String,
+ block: DomainVerificationService.(
+ callingUid: Int, callingUserId: Int, userId: Int
+ ) -> Unit
+ ) = Params(
+ type,
+ makeService,
+ name
+ ) { uidAndUserIdAndService, callingUid, callingUserId, userId, proxy ->
+ val (callingUidInt, callingUserIdInt, service) = uidAndUserIdAndService
+ callingUidInt.set(callingUid)
+ callingUserIdInt.set(callingUserId)
+ service.setProxy(proxy)
+ service.addPackage(mockPkgSetting)
+ service.block(callingUid, callingUserId, userId)
+ }
+
+ return arrayOf(
+ enforcer(Type.INTERNAL, "internal") { callingUid, _, _, _ ->
+ assertInternal(callingUid)
+ },
+ enforcer(Type.QUERENT, "approvedQuerent") { callingUid, _, _, proxy ->
+ assertApprovedQuerent(callingUid, proxy)
+ },
+ enforcer(Type.VERIFIER, "approvedVerifier") { callingUid, _, _, proxy ->
+ assertApprovedVerifier(callingUid, proxy)
+ },
+ enforcer(
+ Type.SELECTOR,
+ "approvedUserSelector"
+ ) { callingUid, callingUserId, userId, _ ->
+ assertApprovedUserSelector(callingUid, callingUserId, userId)
+ },
+
+ service(Type.INTERNAL, "setStatusInternalPackageName") { _, _, _ ->
+ setDomainVerificationStatusInternal(
+ TEST_PKG,
+ DomainVerificationManager.STATE_SUCCESS,
+ ArraySet(setOf("example.com"))
+ )
+ },
+ service(Type.INTERNAL, "setUserSelectionInternal") { _, _, userId ->
+ setDomainVerificationUserSelectionInternal(
+ userId,
+ TEST_PKG,
+ false,
+ ArraySet(setOf("example.com"))
+ )
+ },
+ service(Type.INTERNAL, "verifyPackages") { _, _, _ ->
+ verifyPackages(listOf(TEST_PKG), true)
+ },
+ service(Type.INTERNAL, "clearState") { _, _, _ ->
+ clearDomainVerificationState(listOf(TEST_PKG))
+ },
+ service(Type.INTERNAL, "clearUserSelections") { _, _, userId ->
+ clearUserSelections(listOf(TEST_PKG), userId)
+ },
+ service(Type.VERIFIER, "getPackageNames") { _, _, _ ->
+ validVerificationPackageNames
+ },
+ service(Type.QUERENT, "getInfo") { _, _, _ ->
+ getDomainVerificationInfo(TEST_PKG)
+ },
+ service(Type.VERIFIER, "setStatus") { _, _, _ ->
+ setDomainVerificationStatus(
+ uuid,
+ setOf("example.com"),
+ DomainVerificationManager.STATE_SUCCESS
+ )
+ },
+ service(Type.VERIFIER, "setStatusInternalUid") { callingUid, _, _ ->
+ setDomainVerificationStatusInternal(
+ callingUid,
+ uuid,
+ setOf("example.com"),
+ DomainVerificationManager.STATE_SUCCESS
+ )
+ },
+ service(Type.SELECTOR, "setLinkHandlingAllowed") { _, _, _ ->
+ setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+ },
+ service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { _, _, userId ->
+ setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, userId)
+ },
+ service(Type.SELECTOR, "getUserSelection") { _, _, _ ->
+ getDomainVerificationUserSelection(TEST_PKG)
+ },
+ service(Type.SELECTOR_USER, "getUserSelectionUserId") { _, _, userId ->
+ getDomainVerificationUserSelection(TEST_PKG, userId)
+ },
+ service(Type.SELECTOR, "setUserSelection") { _, _, _ ->
+ setDomainVerificationUserSelection(uuid, setOf("example.com"), true)
+ },
+ service(Type.SELECTOR_USER, "setUserSelectionUserId") { _, _, userId ->
+ setDomainVerificationUserSelection(uuid, setOf("example.com"), true, userId)
+ },
+ )
+ }
+
+ data class Params<T : Any>(
+ val type: Type,
+ val construct: (context: Context) -> T,
+ val name: String,
+ private val method: (
+ T, callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
+ ) -> Unit
+ ) {
+ override fun toString() = "${type}_$name"
+
+ fun runMethod(
+ target: Any,
+ callingUid: Int,
+ callingUserId: Int,
+ userId: Int,
+ proxy: DomainVerificationProxy
+ ) {
+ @Suppress("UNCHECKED_CAST")
+ method(target as T, callingUid, callingUserId, userId, proxy)
+ }
+ }
+ }
+
+ @Parameterized.Parameter(0)
+ lateinit var params: Params<*>
+
+ private val proxy: DomainVerificationProxy = mockThrowOnUnmocked {
+ whenever(isCallerVerifier(VERIFIER_UID)) { true }
+ whenever(isCallerVerifier(NON_VERIFIER_UID)) { false }
+ whenever(sendBroadcastForPackages(any()))
+ }
+
+ @Test
+ fun verify() {
+ when (params.type) {
+ Type.INTERNAL -> internal()
+ Type.QUERENT -> approvedQuerent()
+ Type.VERIFIER -> approvedVerifier()
+ Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false)
+ Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
+ }.run { /*exhaust*/ }
+ }
+
+ fun internal() {
+ val context: Context = mockThrowOnUnmocked()
+ val target = params.construct(context)
+
+ INTERNAL_UIDS.forEach { runMethod(target, it) }
+ assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
+ assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ }
+
+ fun approvedQuerent() {
+ val allowUserSelection = AtomicBoolean(false)
+ val context: Context = mockThrowOnUnmocked {
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
+ anyInt(), anyInt(), anyString()
+ )
+ ) {
+ if (!allowUserSelection.get()) {
+ throw SecurityException()
+ }
+ }
+ }
+ val target = params.construct(context)
+
+ INTERNAL_UIDS.forEach { runMethod(target, it) }
+
+ verifyNoMoreInteractions(context)
+
+ runMethod(target, VERIFIER_UID)
+ assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+
+ allowUserSelection.set(true)
+
+ runMethod(target, NON_VERIFIER_UID)
+ }
+
+ fun approvedVerifier() {
+ val shouldThrow = AtomicBoolean(false)
+ val context: Context = mockThrowOnUnmocked {
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
+ anyInt(), anyInt(), anyString()
+ )
+ ) {
+ if (shouldThrow.get()) {
+ throw SecurityException()
+ }
+ }
+ }
+ val target = params.construct(context)
+
+ INTERNAL_UIDS.forEach { runMethod(target, it) }
+
+ verifyNoMoreInteractions(context)
+
+ runMethod(target, VERIFIER_UID)
+ assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+
+ shouldThrow.set(true)
+
+ assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
+ assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ }
+
+ fun approvedUserSelector(verifyCrossUser: Boolean) {
+ val allowUserSelection = AtomicBoolean(true)
+ val allowInteractAcrossUsers = AtomicBoolean(true)
+ val context: Context = mockThrowOnUnmocked {
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
+ anyInt(), anyInt(), anyString()
+ )
+ ) {
+ if (!allowUserSelection.get()) {
+ throw SecurityException()
+ }
+ }
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
+ anyInt(), anyInt(), anyString()
+ )
+ ) {
+ if (!allowInteractAcrossUsers.get()) {
+ throw SecurityException()
+ }
+ }
+ }
+ val target = params.construct(context)
+
+ fun runEachTestCaseWrapped(
+ callingUserId: Int,
+ targetUserId: Int,
+ block: (testCase: () -> Unit) -> Unit = { it.invoke() }
+ ) {
+ block { runMethod(target, VERIFIER_UID, callingUserId, targetUserId) }
+ block { runMethod(target, NON_VERIFIER_UID, callingUserId, targetUserId) }
+ }
+
+ val callingUserId = 0
+ val notCallingUserId = 1
+
+ runEachTestCaseWrapped(callingUserId, callingUserId)
+ if (verifyCrossUser) {
+ runEachTestCaseWrapped(callingUserId, notCallingUserId)
+ }
+
+ allowInteractAcrossUsers.set(false)
+
+ runEachTestCaseWrapped(callingUserId, callingUserId)
+
+ if (verifyCrossUser) {
+ runEachTestCaseWrapped(callingUserId, notCallingUserId) {
+ assertThrows(SecurityException::class.java, it)
+ }
+ }
+
+ allowUserSelection.set(false)
+
+ runEachTestCaseWrapped(callingUserId, callingUserId) {
+ assertThrows(SecurityException::class.java, it)
+ }
+ if (verifyCrossUser) {
+ runEachTestCaseWrapped(callingUserId, notCallingUserId) {
+ assertThrows(SecurityException::class.java, it)
+ }
+ }
+
+ allowInteractAcrossUsers.set(true)
+
+ runEachTestCaseWrapped(callingUserId, callingUserId) {
+ assertThrows(SecurityException::class.java, it)
+ }
+ if (verifyCrossUser) {
+ runEachTestCaseWrapped(callingUserId, notCallingUserId) {
+ assertThrows(SecurityException::class.java, it)
+ }
+ }
+ }
+
+ private fun runMethod(target: Any, callingUid: Int, callingUserId: Int = 0, userId: Int = 0) {
+ params.runMethod(target, callingUid, callingUserId, userId, proxy)
+ }
+
+ enum class Type {
+ // System/shell only
+ INTERNAL,
+
+ // INTERNAL || domain verification agent || user setting permission holder
+ QUERENT,
+
+ // INTERNAL || domain verification agent
+ VERIFIER,
+
+ // Holding the user setting permission
+ SELECTOR,
+
+ // Holding the user setting permission, but targeting cross user
+ SELECTOR_USER
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
new file mode 100644
index 0000000..9a3bd99
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationLegacySettingsTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.pm.IntentFilterVerificationInfo
+import android.content.pm.PackageManager
+import android.util.ArraySet
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings
+import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.readXml
+import com.android.server.pm.test.verify.domain.DomainVerificationPersistenceTest.Companion.writeXml
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+class DomainVerificationLegacySettingsTest {
+
+ @Rule
+ @JvmField
+ val tempFolder = TemporaryFolder()
+
+ @Test
+ fun writeAndReadBackNormal() {
+ val settings = DomainVerificationLegacySettings().apply {
+ add(
+ "com.test.one",
+ IntentFilterVerificationInfo(
+ "com.test.one",
+ ArraySet(setOf("example1.com", "example2.com"))
+ ).apply {
+ status = PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK
+ }
+ )
+ add(
+ "com.test.one",
+ 0, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
+ )
+ add(
+ "com.test.one",
+ 10, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+ )
+
+ add(
+ "com.test.two",
+ IntentFilterVerificationInfo(
+ "com.test.two",
+ ArraySet(setOf("example3.com"))
+ )
+ )
+
+ add(
+ "com.test.three",
+ 11, PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
+ )
+ }
+
+
+ val file = tempFolder.newFile().writeXml(settings::writeSettings)
+ val newSettings = file.readXml {
+ DomainVerificationLegacySettings().apply {
+ readSettings(it)
+ }
+ }
+
+ val xml = file.readText()
+
+ // Legacy migrated settings doesn't bother writing the legacy verification info
+ assertWithMessage(xml).that(newSettings.remove("com.test.one")).isNull()
+ assertWithMessage(xml).that(newSettings.getUserState("com.test.one", 0))
+ .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS)
+ assertWithMessage(xml).that(newSettings.getUserState("com.test.one", 10))
+ .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)
+
+ val firstUserStates = newSettings.getUserStates("com.test.one")
+ assertWithMessage(xml).that(firstUserStates).isNotNull()
+ assertWithMessage(xml).that(firstUserStates!!.size()).isEqualTo(2)
+ assertWithMessage(xml).that(firstUserStates[0])
+ .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS)
+ assertWithMessage(xml).that(firstUserStates[10])
+ .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)
+
+ assertWithMessage(xml).that(newSettings.remove("com.test.two")).isNull()
+ assertWithMessage(xml).that(newSettings.getUserStates("com.test.two")).isNull()
+
+ assertWithMessage(xml).that(newSettings.remove("com.test.three")).isNull()
+ assertWithMessage(xml).that(newSettings.getUserState("com.test.three", 11))
+ .isEqualTo(PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS)
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
new file mode 100644
index 0000000..a76d8ce
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationUserSelection
+import com.android.server.pm.verify.domain.DomainVerificationPersistence
+
+operator fun <F> android.util.Pair<F, *>.component1() = first
+operator fun <S> android.util.Pair<*, S>.component2() = second
+
+operator fun DomainVerificationRequest.component1() = packageNames
+
+operator fun DomainVerificationInfo.component1() = identifier
+operator fun DomainVerificationInfo.component2() = packageName
+operator fun DomainVerificationInfo.component3() = hostToStateMap
+
+operator fun DomainVerificationUserSelection.component1() = identifier
+operator fun DomainVerificationUserSelection.component2() = packageName
+operator fun DomainVerificationUserSelection.component3() = user
+operator fun DomainVerificationUserSelection.component4() = isLinkHandlingAllowed
+operator fun DomainVerificationUserSelection.component5() = hostToUserSelectionMap
+
+operator fun DomainVerificationPersistence.ReadResult.component1() = active
+operator fun DomainVerificationPersistence.ReadResult.component2() = restored
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
new file mode 100644
index 0000000..a76152c
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.util.ArrayMap
+import android.util.TypedXmlPullParser
+import android.util.TypedXmlSerializer
+import android.util.Xml
+import com.android.server.pm.verify.domain.DomainVerificationPersistence
+import com.android.server.pm.verify.domain.models.DomainVerificationPkgState
+import com.android.server.pm.verify.domain.models.DomainVerificationStateMap
+import com.android.server.pm.verify.domain.models.DomainVerificationUserState
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import java.io.File
+import java.nio.charset.StandardCharsets
+import java.util.UUID
+
+class DomainVerificationPersistenceTest {
+
+ companion object {
+ private val PKG_PREFIX = DomainVerificationPersistenceTest::class.java.`package`!!.name
+
+ internal fun File.writeXml(block: (serializer: TypedXmlSerializer) -> Unit) = apply {
+ outputStream().use {
+ // Explicitly use string based XML so it can printed in the test failure output
+ Xml.newFastSerializer()
+ .apply {
+ setOutput(it, StandardCharsets.UTF_8.name())
+ startDocument(null, true)
+ setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true)
+ }
+ .apply(block)
+ .endDocument()
+ }
+ }
+
+ internal fun <T> File.readXml(block: (parser: TypedXmlPullParser) -> T) =
+ inputStream().use {
+ block(Xml.resolvePullParser(it))
+ }
+ }
+
+ @Rule
+ @JvmField
+ val tempFolder = TemporaryFolder()
+
+ @Test
+ fun writeAndReadBackNormal() {
+ val attached = DomainVerificationStateMap<DomainVerificationPkgState>().apply {
+ mockPkgState(0).let { put(it.packageName, it.id, it) }
+ mockPkgState(1).let { put(it.packageName, it.id, it) }
+ }
+ val pending = ArrayMap<String, DomainVerificationPkgState>().apply {
+ mockPkgState(2).let { put(it.packageName, it) }
+ mockPkgState(3).let { put(it.packageName, it) }
+ }
+ val restored = ArrayMap<String, DomainVerificationPkgState>().apply {
+ mockPkgState(4).let { put(it.packageName, it) }
+ mockPkgState(5).let { put(it.packageName, it) }
+ }
+
+ val file = tempFolder.newFile().writeXml {
+ DomainVerificationPersistence.writeToXml(it, attached, pending, restored)
+ }
+
+ val xml = file.readText()
+
+ val (readActive, readRestored) = file.readXml {
+ DomainVerificationPersistence.readFromXml(it)
+ }
+
+ assertWithMessage(xml).that(readActive.values)
+ .containsExactlyElementsIn(attached.values() + pending.values)
+ assertWithMessage(xml).that(readRestored.values).containsExactlyElementsIn(restored.values)
+ }
+
+ @Test
+ fun readMalformed() {
+ val stateZero = mockEmptyPkgState(0).apply {
+ stateMap["example.com"] = DomainVerificationManager.STATE_SUCCESS
+ stateMap["example.org"] = DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED
+
+ // A domain without a written state falls back to default
+ stateMap["missing-state.com"] = DomainVerificationManager.STATE_NO_RESPONSE
+
+ userSelectionStates[1] = DomainVerificationUserState(1).apply {
+ addHosts(setOf("example-user1.com", "example-user1.org"))
+ isDisallowLinkHandling = false
+ }
+ }
+ val stateOne = mockEmptyPkgState(1).apply {
+ // It's valid to have a user selection without any autoVerify domains
+ userSelectionStates[1] = DomainVerificationUserState(1).apply {
+ addHosts(setOf("example-user1.com", "example-user1.org"))
+ isDisallowLinkHandling = true
+ }
+ }
+
+ // Also valid to have neither autoVerify domains nor any active user states
+ val stateTwo = mockEmptyPkgState(2, hasAutoVerifyDomains = false)
+
+ // language=XML
+ val xml = """
+ <?xml?>
+ <domain-verifications>
+ <active>
+ <package-state
+ packageName="${stateZero.packageName}"
+ id="${stateZero.id}"
+ >
+ <state>
+ <domain name="duplicate-takes-last.com" state="1"/>
+ </state>
+ </package-state>
+ <package-state
+ packageName="${stateZero.packageName}"
+ id="${stateZero.id}"
+ hasAutoVerifyDomains="true"
+ >
+ <state>
+ <domain name="example.com" state="${DomainVerificationManager.STATE_SUCCESS}"/>
+ <domain name="example.org" state="${DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED}"/>
+ <not-domain name="not-domain.com" state="1"/>
+ <domain name="missing-state.com"/>
+ </state>
+ <user-states>
+ <user-state userId="1" disallowLinkHandling="false">
+ <enabled-hosts>
+ <host name="example-user1.com"/>
+ <not-host name="not-host.com"/>
+ <host/>
+ </enabled-hosts>
+ <enabled-hosts>
+ <host name="example-user1.org"/>
+ </enabled-hosts>
+ <enabled-hosts/>
+ </user-state>
+ <user-state>
+ <enabled-hosts>
+ <host name="no-user-id.com"/>
+ </enabled-hosts>
+ </user-state>
+ </user-states>
+ </package-state>
+ </active>
+ <not-active/>
+ <restored>
+ <package-state
+ packageName="${stateOne.packageName}"
+ id="${stateOne.id}"
+ hasAutoVerifyDomains="true"
+ >
+ <state/>
+ <user-states>
+ <user-state userId="1" disallowLinkHandling="true">
+ <enabled-hosts>
+ <host name="example-user1.com"/>
+ <host name="example-user1.org"/>
+ </enabled-hosts>
+ </user-state>
+ </user-states>
+ </package-state>
+ <package-state packageName="${stateTwo.packageName}"/>
+ <package-state id="${stateTwo.id}"/>
+ <package-state
+ packageName="${stateTwo.packageName}"
+ id="${stateTwo.id}"
+ hasAutoVerifyDomains="false"
+ >
+ <state/>
+ <user-states/>
+ </package-state>
+ </restore>
+ <not-restored/>
+ </domain-verifications>
+ """.trimIndent()
+
+ val (active, restored) = DomainVerificationPersistence
+ .readFromXml(Xml.resolvePullParser(xml.byteInputStream()))
+
+ assertThat(active.values).containsExactly(stateZero)
+ assertThat(restored.values).containsExactly(stateOne, stateTwo)
+ }
+
+ private fun mockEmptyPkgState(
+ id: Int,
+ hasAutoVerifyDomains: Boolean = true
+ ): DomainVerificationPkgState {
+ val pkgName = pkgName(id)
+ val domainSetId = UUID(0L, id.toLong())
+ return DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains)
+ }
+
+ private fun mockPkgState(id: Int) = mockEmptyPkgState(id).apply {
+ stateMap["$packageName.com"] = id
+ userSelectionStates[id] = DomainVerificationUserState(id).apply {
+ addHosts(setOf("$packageName-user.com"))
+ isDisallowLinkHandling = true
+ }
+ }
+
+ private fun pkgName(id: Int) = "${PKG_PREFIX}.pkg$id"
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
new file mode 100644
index 0000000..db541f6
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationState
+import android.os.Bundle
+import android.os.UserHandle
+import android.util.ArraySet
+import com.android.server.DeviceIdleInternal
+import com.android.server.pm.verify.domain.DomainVerificationCollector
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.util.UUID
+
+@Suppress("DEPRECATION")
+class DomainVerificationProxyTest {
+
+ companion object {
+ private const val TEST_PKG_NAME_ONE = "com.test.pkg.one"
+ private const val TEST_PKG_NAME_TWO = "com.test.pkg.two"
+ private const val TEST_PKG_NAME_TARGET_ONE = "com.test.target.one"
+ private const val TEST_PKG_NAME_TARGET_TWO = "com.test.target.two"
+ private const val TEST_CALLING_UID_ACCEPT = 40
+ private const val TEST_CALLING_UID_REJECT = 41
+ private val TEST_UUID_ONE = UUID.fromString("f7fbb7dd-7b5f-4609-a95e-c6c7765fb9cd")
+ private val TEST_UUID_TWO = UUID.fromString("4a09b361-a967-43ac-9d18-07a385dff740")
+ }
+
+ private val componentOne = ComponentName(TEST_PKG_NAME_ONE, ".ReceiverOne")
+ private val componentTwo = ComponentName(TEST_PKG_NAME_TWO, ".ReceiverTwo")
+ private val componentThree = ComponentName(TEST_PKG_NAME_TWO, ".ReceiverThree")
+
+ private lateinit var context: Context
+ private lateinit var manager: DomainVerificationManagerInternal
+ private lateinit var collector: DomainVerificationCollector
+
+ // Must be declared as field to support generics
+ @Captor
+ lateinit var hostCaptor: ArgumentCaptor<Set<String>>
+
+ @Before
+ fun setUpMocks() {
+ MockitoAnnotations.initMocks(this)
+ context = mockThrowOnUnmocked {
+ whenever(sendBroadcastAsUser(any(), any(), any(), any<Bundle>()))
+ whenever(
+ enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT),
+ anyString()
+ )
+ )
+ }
+ manager = mockThrowOnUnmocked {
+ whenever(getDomainVerificationInfoId(any())) {
+ when (val pkgName = arguments[0] as String) {
+ TEST_PKG_NAME_TARGET_ONE -> TEST_UUID_ONE
+ TEST_PKG_NAME_TARGET_TWO -> TEST_UUID_TWO
+ else -> throw IllegalArgumentException("Unexpected package name $pkgName")
+ }
+ }
+ whenever(getDomainVerificationInfo(anyString())) {
+ when (val pkgName = arguments[0] as String) {
+ TEST_PKG_NAME_TARGET_ONE -> DomainVerificationInfo(
+ TEST_UUID_ONE, pkgName, mapOf(
+ "example1.com" to DomainVerificationManager.STATE_NO_RESPONSE,
+ "example2.com" to DomainVerificationManager.STATE_NO_RESPONSE
+ )
+ )
+ TEST_PKG_NAME_TARGET_TWO -> DomainVerificationInfo(
+ TEST_UUID_TWO, pkgName, mapOf(
+ "example3.com" to DomainVerificationManager.STATE_NO_RESPONSE,
+ "example4.com" to DomainVerificationManager.STATE_NO_RESPONSE
+ )
+ )
+ else -> throw IllegalArgumentException("Unexpected package name $pkgName")
+ }
+ }
+ whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt()))
+ }
+ collector = mockThrowOnUnmocked {
+ whenever(collectAutoVerifyDomains(any())) {
+ when (val pkgName = (arguments[0] as AndroidPackage).packageName) {
+ TEST_PKG_NAME_TARGET_ONE -> ArraySet(setOf("example1.com", "example2.com"))
+ TEST_PKG_NAME_TARGET_TWO -> ArraySet(setOf("example3.com", "example4.com"))
+ else -> throw IllegalArgumentException("Unexpected package name $pkgName")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun isCallerVerifierV1() {
+ val connection = mockConnection()
+ val proxyV1 = DomainVerificationProxy.makeProxy<Connection>(
+ componentOne, null, context,
+ manager, collector, connection
+ )
+
+ assertThat(proxyV1.isCallerVerifier(TEST_CALLING_UID_ACCEPT)).isTrue()
+ verify(connection).isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_ONE)
+ verifyNoMoreInteractions(connection)
+ clearInvocations(connection)
+
+ assertThat(proxyV1.isCallerVerifier(TEST_CALLING_UID_REJECT)).isFalse()
+ verify(connection).isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_ONE)
+ verifyNoMoreInteractions(connection)
+ }
+
+ @Test
+ fun isCallerVerifierV2() {
+ val connection = mockConnection()
+ val proxyV2 = DomainVerificationProxy.makeProxy<Connection>(
+ null, componentTwo, context,
+ manager, collector, connection
+ )
+
+ assertThat(proxyV2.isCallerVerifier(TEST_CALLING_UID_ACCEPT)).isTrue()
+ verify(connection).isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_TWO)
+ verifyNoMoreInteractions(connection)
+ clearInvocations(connection)
+
+ assertThat(proxyV2.isCallerVerifier(TEST_CALLING_UID_REJECT)).isFalse()
+ verify(connection).isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_TWO)
+ verifyNoMoreInteractions(connection)
+ }
+
+ @Test
+ fun isCallerVerifierBoth() {
+ val connection = mockConnection()
+ val proxyBoth = DomainVerificationProxy.makeProxy<Connection>(
+ componentTwo, componentThree,
+ context, manager, collector, connection
+ )
+
+ // The combined proxy should only ever call v2 when it succeeds
+ assertThat(proxyBoth.isCallerVerifier(TEST_CALLING_UID_ACCEPT)).isTrue()
+ verify(connection).isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_TWO)
+ verifyNoMoreInteractions(connection)
+ clearInvocations(connection)
+
+ val callingUidCaptor = ArgumentCaptor.forClass(Int::class.java)
+
+ // But will call both when v2 fails
+ assertThat(proxyBoth.isCallerVerifier(TEST_CALLING_UID_REJECT)).isFalse()
+ verify(connection, times(2))
+ .isCallerPackage(callingUidCaptor.capture(), eq(TEST_PKG_NAME_TWO))
+ verifyNoMoreInteractions(connection)
+
+ assertThat(callingUidCaptor.allValues.toSet()).containsExactly(TEST_CALLING_UID_REJECT)
+ }
+
+ @Test
+ fun differentPackagesResolvesOnlyV2() {
+ assertThat(DomainVerificationProxy.makeProxy<Connection>(
+ componentOne, componentTwo,
+ context, manager, collector, mockConnection()
+ )).isInstanceOf(DomainVerificationProxyV2::class.java)
+ }
+
+ private fun prepareProxyV1(): ProxyV1Setup {
+ val messages = mutableListOf<Pair<Int, Any?>>()
+ val connection = mockConnection {
+ whenever(schedule(anyInt(), any())) {
+ messages.add((arguments[0] as Int) to arguments[1])
+ }
+ }
+
+ val proxy = DomainVerificationProxy.makeProxy<Connection>(
+ componentOne,
+ null,
+ context,
+ manager,
+ collector,
+ connection
+ )
+ return ProxyV1Setup(messages, connection, proxy)
+ }
+
+ @Test
+ fun sendBroadcastForPackagesV1() {
+ val (messages, _, proxy) = prepareProxyV1()
+
+ proxy.sendBroadcastForPackages(setOf(TEST_PKG_NAME_TARGET_ONE, TEST_PKG_NAME_TARGET_TWO))
+ messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+ val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+
+ verify(context, times(2)).sendBroadcastAsUser(
+ intentCaptor.capture(), eq(UserHandle.SYSTEM), isNull(), any<Bundle>()
+ )
+ verifyNoMoreInteractions(context)
+
+ val intents = intentCaptor.allValues
+ assertThat(intents).hasSize(2)
+ intents.forEach {
+ assertThat(it.action).isEqualTo(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION)
+ assertThat(it.getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME))
+ .isEqualTo(IntentFilter.SCHEME_HTTPS)
+ assertThat(it.getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1))
+ .isNotEqualTo(-1)
+ }
+
+ intents[0].apply {
+ assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME))
+ .isEqualTo(TEST_PKG_NAME_TARGET_ONE)
+ assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS))
+ .isEqualTo("example1.com example2.com")
+ }
+
+ intents[1].apply {
+ assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME))
+ .isEqualTo(TEST_PKG_NAME_TARGET_TWO)
+ assertThat(getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS))
+ .isEqualTo("example3.com example4.com")
+ }
+ }
+
+ private fun prepareProxyOnIntentFilterVerifiedV1(): Pair<ProxyV1Setup, Pair<Int, Int>> {
+ val (messages, connection, proxy) = prepareProxyV1()
+
+ proxy.sendBroadcastForPackages(setOf(TEST_PKG_NAME_TARGET_ONE, TEST_PKG_NAME_TARGET_TWO))
+ messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+ messages.clear()
+
+ val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+
+ verify(context, times(2)).sendBroadcastAsUser(
+ intentCaptor.capture(), eq(UserHandle.SYSTEM), isNull(), any<Bundle>()
+ )
+
+ val verificationIds = intentCaptor.allValues.map {
+ it.getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1)
+ }
+
+ assertThat(verificationIds).doesNotContain(-1)
+
+ return ProxyV1Setup(messages, connection, proxy) to
+ (verificationIds[0] to verificationIds[1])
+ }
+
+ @Test
+ fun proxyOnIntentFilterVerifiedFullSuccessV1() {
+ val setup = prepareProxyOnIntentFilterVerifiedV1()
+ val (messages, connection, proxy) = setup.first
+ val (idOne, idTwo) = setup.second
+
+ DomainVerificationProxyV1.queueLegacyVerifyResult(
+ context,
+ connection,
+ idOne,
+ PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS,
+ emptyList(),
+ TEST_CALLING_UID_ACCEPT
+ )
+
+ DomainVerificationProxyV1.queueLegacyVerifyResult(
+ context,
+ connection,
+ idTwo,
+ PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS,
+ emptyList(),
+ TEST_CALLING_UID_ACCEPT
+ )
+
+ assertThat(messages).hasSize(2)
+ messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+ val idCaptor = ArgumentCaptor.forClass(UUID::class.java)
+
+ @Suppress("UNCHECKED_CAST")
+ verify(manager, times(2)).setDomainVerificationStatusInternal(
+ eq(TEST_CALLING_UID_ACCEPT),
+ idCaptor.capture(),
+ hostCaptor.capture(),
+ eq(DomainVerificationManager.STATE_SUCCESS)
+ )
+
+ assertThat(idCaptor.allValues).containsExactly(TEST_UUID_ONE, TEST_UUID_TWO)
+
+ assertThat(hostCaptor.allValues.toSet()).containsExactly(
+ setOf("example1.com", "example2.com"),
+ setOf("example3.com", "example4.com")
+ )
+ }
+
+ @Test
+ fun proxyOnIntentFilterVerifiedPartialSuccessV1() {
+ val setup = prepareProxyOnIntentFilterVerifiedV1()
+ val (messages, connection, proxy) = setup.first
+ val (idOne, idTwo) = setup.second
+
+ DomainVerificationProxyV1.queueLegacyVerifyResult(
+ context,
+ connection,
+ idOne,
+ PackageManager.INTENT_FILTER_VERIFICATION_FAILURE,
+ listOf("example1.com"),
+ TEST_CALLING_UID_ACCEPT
+ )
+
+ DomainVerificationProxyV1.queueLegacyVerifyResult(
+ context,
+ connection,
+ idTwo,
+ PackageManager.INTENT_FILTER_VERIFICATION_FAILURE,
+ listOf("example3.com"),
+ TEST_CALLING_UID_ACCEPT
+ )
+
+ messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+ val idCaptor = ArgumentCaptor.forClass(UUID::class.java)
+ val stateCaptor = ArgumentCaptor.forClass(Int::class.java)
+
+ @Suppress("UNCHECKED_CAST")
+ verify(manager, times(4)).setDomainVerificationStatusInternal(
+ eq(TEST_CALLING_UID_ACCEPT),
+ idCaptor.capture(),
+ hostCaptor.capture(),
+ stateCaptor.capture()
+ )
+
+ assertThat(idCaptor.allValues)
+ .containsExactly(TEST_UUID_ONE, TEST_UUID_ONE, TEST_UUID_TWO, TEST_UUID_TWO)
+
+ val hostToStates: Map<Set<*>, Int> = hostCaptor.allValues.zip(stateCaptor.allValues).toMap()
+ assertThat(hostToStates).isEqualTo(mapOf(
+ setOf("example1.com") to DomainVerificationState.STATE_LEGACY_FAILURE,
+ setOf("example2.com") to DomainVerificationState.STATE_SUCCESS,
+ setOf("example3.com") to DomainVerificationState.STATE_LEGACY_FAILURE,
+ setOf("example4.com") to DomainVerificationState.STATE_SUCCESS,
+ ))
+ }
+
+ @Test
+ fun proxyOnIntentFilterVerifiedFailureV1() {
+ val setup = prepareProxyOnIntentFilterVerifiedV1()
+ val (messages, connection, proxy) = setup.first
+ val (idOne, idTwo) = setup.second
+
+ DomainVerificationProxyV1.queueLegacyVerifyResult(
+ context,
+ connection,
+ idOne,
+ PackageManager.INTENT_FILTER_VERIFICATION_FAILURE,
+ listOf("example1.com", "example2.com"),
+ TEST_CALLING_UID_ACCEPT
+ )
+
+ DomainVerificationProxyV1.queueLegacyVerifyResult(
+ context,
+ connection,
+ idTwo,
+ PackageManager.INTENT_FILTER_VERIFICATION_FAILURE,
+ listOf("example3.com", "example4.com"),
+ TEST_CALLING_UID_ACCEPT
+ )
+
+ messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+ val idCaptor = ArgumentCaptor.forClass(UUID::class.java)
+
+ @Suppress("UNCHECKED_CAST")
+ verify(manager, times(2)).setDomainVerificationStatusInternal(
+ eq(TEST_CALLING_UID_ACCEPT),
+ idCaptor.capture(),
+ hostCaptor.capture(),
+ eq(DomainVerificationState.STATE_LEGACY_FAILURE)
+ )
+
+ assertThat(idCaptor.allValues).containsExactly(TEST_UUID_ONE, TEST_UUID_TWO)
+
+ assertThat(hostCaptor.allValues.toSet()).containsExactly(
+ setOf("example1.com", "example2.com"),
+ setOf("example3.com", "example4.com")
+ )
+ }
+
+ @Test
+ fun sendBroadcastForPackagesV2() {
+ val componentTwo = ComponentName(TEST_PKG_NAME_TWO, ".ReceiverOne")
+ val messages = mutableListOf<Pair<Int, Any?>>()
+
+ val connection = mockConnection {
+ whenever(schedule(anyInt(), any())) {
+ messages.add((arguments[0] as Int) to arguments[1])
+ }
+ }
+
+ val proxy = DomainVerificationProxy.makeProxy<Connection>(
+ null,
+ componentTwo,
+ context,
+ manager,
+ collector,
+ connection
+ )
+
+ proxy.sendBroadcastForPackages(setOf(TEST_PKG_NAME_TARGET_ONE, TEST_PKG_NAME_TARGET_TWO))
+
+ messages.forEach { (code, value) -> proxy.runMessage(code, value) }
+
+ val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+
+ verify(context).sendBroadcastAsUser(
+ intentCaptor.capture(), eq(UserHandle.SYSTEM), isNull(), any<Bundle>()
+ )
+ verifyNoMoreInteractions(context)
+
+ val intents = intentCaptor.allValues
+ assertThat(intents).hasSize(1)
+ intents.single().apply {
+ assertThat(this.action).isEqualTo(Intent.ACTION_DOMAINS_NEED_VERIFICATION)
+ val request: DomainVerificationRequest? =
+ getParcelableExtra(DomainVerificationManager.EXTRA_VERIFICATION_REQUEST)
+ assertThat(request?.packageNames).containsExactly(
+ TEST_PKG_NAME_TARGET_ONE,
+ TEST_PKG_NAME_TARGET_TWO
+ )
+ }
+ }
+
+ private fun mockConnection(block: Connection.() -> Unit = {}) =
+ mockThrowOnUnmocked<Connection> {
+ whenever(isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_ONE)) { true }
+ whenever(isCallerPackage(TEST_CALLING_UID_ACCEPT, TEST_PKG_NAME_TWO)) { true }
+ whenever(isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_ONE)) { false }
+ whenever(isCallerPackage(TEST_CALLING_UID_REJECT, TEST_PKG_NAME_TWO)) { false }
+ whenever(getPackage(anyString())) { mockPkg(arguments[0] as String) }
+ whenever(powerSaveTempWhitelistAppDuration) { 1000 }
+ whenever(deviceIdleInternal) {
+ mockThrowOnUnmocked<DeviceIdleInternal> {
+ whenever(
+ addPowerSaveTempWhitelistApp(
+ anyInt(), anyString(), anyLong(), anyInt(),
+ anyBoolean(), anyString()
+ )
+ )
+ }
+ }
+ block()
+ }
+
+ private fun mockPkg(pkgName: String): AndroidPackage {
+ return mockThrowOnUnmocked { whenever(packageName) { pkgName } }
+ }
+
+ private data class ProxyV1Setup(
+ val messages: MutableList<Pair<Int, Any?>>,
+ val connection: Connection,
+ val proxy: DomainVerificationProxy
+ )
+
+ interface Connection : DomainVerificationProxyV1.Connection,
+ DomainVerificationProxyV2.Connection
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
index a18632b..961fc18 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -232,8 +232,8 @@
ai.packageName = packageName;
ai.uid = uid;
ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
- app.pid = pid;
- mAms.mPidsSelfLocked.doAddInternal(app);
+ app.setPid(pid);
+ mAms.mPidsSelfLocked.doAddInternal(app.getPid(), app);
mPhantomInjector.addToProcess(uid, pid, pid);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 9441ecf..22b2f7e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -798,7 +798,7 @@
final int traceEnd = 8192;
createRandomFile(traceFile, traceSize);
assertEquals(traceSize, traceFile.length());
- mAppExitInfoTracker.handleLogAnrTrace(app.pid, app.uid, app.getPackageList(),
+ mAppExitInfoTracker.handleLogAnrTrace(app.getPid(), app.uid, app.getPackageList(),
traceFile, traceStart, traceEnd);
noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
@@ -991,16 +991,16 @@
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = packageName;
ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
- app.pid = pid;
+ app.setPid(pid);
app.info.uid = packageUid;
if (definingUid != null) {
final String dummyPackageName = "com.android.test";
final String dummyClassName = ".Foo";
- app.hostingRecord = HostingRecord.byAppZygote(new ComponentName(
- dummyPackageName, dummyClassName), "", definingUid);
+ app.setHostingRecord(HostingRecord.byAppZygote(new ComponentName(
+ dummyPackageName, dummyClassName), "", definingUid));
}
- app.connectionGroup = connectionGroup;
- app.setProcState = procState;
+ app.mServices.setConnectionGroup(connectionGroup);
+ app.mState.setSetProcState(procState);
app.mProfile.setLastMemInfo(spy(new Debug.MemoryInfo()));
app.mProfile.setLastPss(pss);
app.mProfile.setLastRss(rss);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index c82db73..022fadc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -170,7 +170,7 @@
/* lruWeight= */1.0f);
ProcessList list = new ProcessList();
- ArrayList<ProcessRecord> processList = list.mLruProcesses;
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
processList.add(lastUsed40MinutesAgo);
@@ -191,7 +191,7 @@
Duration.ofMinutes(30).toMillis(), 1024L, 20);
processList.add(lastUsed30MinutesAgo);
- mCacheOomRanker.reRankLruCachedApps(list);
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 5 ordered by least recently used first, then last processes position unchanged.
assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo,
@@ -207,7 +207,7 @@
/* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
- ArrayList<ProcessRecord> processList = list.mLruProcesses;
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
processList.add(rss10k);
@@ -231,7 +231,7 @@
Duration.ofMinutes(30).toMillis(), 16 * 1024L, 20);
processList.add(rss16k);
- mCacheOomRanker.reRankLruCachedApps(list);
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 6 ordered by largest pss, then last processes position unchanged.
assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
@@ -246,8 +246,8 @@
/* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
- list.mLruProcessServiceStart = 1;
- ArrayList<ProcessRecord> processList = list.mLruProcesses;
+ list.setLruProcessServiceStartLSP(1);
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
processList.add(used1000);
@@ -268,7 +268,7 @@
Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
processList.add(used200);
- mCacheOomRanker.reRankLruCachedApps(list);
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 4 ordered by uses, then last processes position unchanged.
assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500,
@@ -283,7 +283,7 @@
/* lruWeight= */ 0.3f);
ProcessList list = new ProcessList();
- ArrayList<ProcessRecord> processList = list.mLruProcesses;
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord unknownAdj1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
processList.add(unknownAdj1);
@@ -304,7 +304,7 @@
processList.add(systemAdj);
// 6 Processes but only 3 in eligible for cache so no re-ranking.
- mCacheOomRanker.reRankLruCachedApps(list);
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// All positions unchanged.
assertThat(processList).containsExactly(unknownAdj1, unknownAdj2, unknownAdj3,
@@ -319,8 +319,8 @@
/* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
- list.mLruProcessServiceStart = 4;
- ArrayList<ProcessRecord> processList = list.mLruProcesses;
+ list.setLruProcessServiceStartLSP(4);
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
processList.add(used1000);
@@ -340,7 +340,7 @@
Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
processList.add(used200);
- mCacheOomRanker.reRankLruCachedApps(list);
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// All positions unchanged.
assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
@@ -378,17 +378,17 @@
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = "a.package.name" + mNextPackageName++;
ProcessRecord app = new ProcessRecord(mAms, ai, ai.packageName + ":process", mNextUid++);
- app.pid = mNextPid++;
+ app.setPid(mNextPid++);
app.info.uid = mNextPackageUid++;
// Exact value does not mater, it can be any state for which compaction is allowed.
- app.setProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- app.setAdj = setAdj;
- app.lastActivityTime = lastActivityTime;
+ app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ app.mState.setSetAdj(setAdj);
+ app.setLastActivityTime(lastActivityTime);
app.mProfile.setLastRss(lastRss);
- app.setCached(false);
+ app.mState.setCached(false);
for (int i = 0; i < returnedToCacheCount; ++i) {
- app.setCached(false);
- app.setCached(true);
+ app.mState.setCached(false);
+ app.mState.setCached(true);
}
return app;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 96a44a4..d860326 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -134,11 +134,11 @@
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = packageName;
ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
- app.pid = pid;
+ app.setPid(pid);
app.info.uid = packageUid;
// Exact value does not mater, it can be any state for which compaction is allowed.
- app.setProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- app.setAdj = 905;
+ app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ app.mState.setSetAdj(905);
return app;
}
@@ -875,7 +875,8 @@
mProcessDependencies.setRss(rssBefore2);
mProcessDependencies.setRssAfterCompaction(rssAfter2);
// This is to avoid throttle of compacting too soon.
- processRecord.lastCompactTime = processRecord.lastCompactTime - 10_000;
+ processRecord.mOptRecord.setLastCompactTime(
+ processRecord.mOptRecord.getLastCompactTime() - 10_000);
// WHEN we try to run compaction.
mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
waitForHandler();
@@ -890,7 +891,8 @@
mProcessDependencies.setRss(rssBefore3);
mProcessDependencies.setRssAfterCompaction(rssAfter3);
// This is to avoid throttle of compacting too soon.
- processRecord.lastCompactTime = processRecord.lastCompactTime - 10_000;
+ processRecord.mOptRecord.setLastCompactTime(
+ processRecord.mOptRecord.getLastCompactTime() - 10_000);
// WHEN we try to run compaction
mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
waitForHandler();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 7daf357..27825a4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -176,6 +176,8 @@
setFieldValue(ActivityManagerService.class, sService, "mUserController",
mock(UserController.class));
setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
+ setFieldValue(ActivityManagerService.class, sService, "mProcLock",
+ new ActivityManagerProcLock());
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
@@ -207,8 +209,8 @@
public void testUpdateOomAdj_DoOne_Persistent_TopUi_Sleeping() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.maxAdj = PERSISTENT_PROC_ADJ;
- app.setHasTopUi(true);
+ app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+ app.mState.setHasTopUi(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -222,8 +224,8 @@
public void testUpdateOomAdj_DoOne_Persistent_TopUi_Awake() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.maxAdj = PERSISTENT_PROC_ADJ;
- app.setHasTopUi(true);
+ app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+ app.mState.setHasTopUi(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -236,7 +238,7 @@
public void testUpdateOomAdj_DoOne_Persistent_TopApp() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.maxAdj = PERSISTENT_PROC_ADJ;
+ app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
doReturn(app).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -266,7 +268,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState();
- app.runningRemoteAnimation = true;
+ app.mState.setRunningRemoteAnimation(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
@@ -308,7 +310,7 @@
public void testUpdateOomAdj_DoOne_ExecutingService() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.executingServices.add(mock(ServiceRecord.class));
+ app.mServices.startExecutingService(mock(ServiceRecord.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -337,7 +339,7 @@
public void testUpdateOomAdj_DoOne_CachedEmpty() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.setCurRawAdj(CACHED_APP_MIN_ADJ);
+ app.mState.setCurRawAdj(CACHED_APP_MIN_ADJ);
doReturn(null).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -351,7 +353,6 @@
public void testUpdateOomAdj_DoOne_VisibleActivities() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasActivities();
doAnswer(answer(callback -> {
@@ -368,7 +369,6 @@
any(WindowProcessController.ComputeOomAdjCallback.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doCallRealMethod().when(app).getWindowProcessController();
assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
}
@@ -378,15 +378,14 @@
public void testUpdateOomAdj_DoOne_RecentTasks() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasRecentTasks();
- app.lastTopTime = SystemClock.uptimeMillis();
+ app.mState.setLastTopTime(SystemClock.uptimeMillis());
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doCallRealMethod().when(wpc).hasRecentTasks();
- assertEquals(PROCESS_STATE_CACHED_RECENT, app.setProcState);
+ assertEquals(PROCESS_STATE_CACHED_RECENT, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -394,7 +393,7 @@
public void testUpdateOomAdj_DoOne_FgServiceLocation() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
+ app.mServices.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -407,7 +406,7 @@
public void testUpdateOomAdj_DoOne_FgService() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.setHasForegroundServices(true, 0);
+ app.mServices.setHasForegroundServices(true, 0);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -420,7 +419,7 @@
public void testUpdateOomAdj_DoOne_OverlayUi() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.setHasOverlayUi(true);
+ app.mState.setHasOverlayUi(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -433,8 +432,8 @@
public void testUpdateOomAdj_DoOne_PerceptibleRecent() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.setHasForegroundServices(true, 0);
- app.lastTopTime = SystemClock.uptimeMillis();
+ app.mServices.setHasForegroundServices(true, 0);
+ app.mState.setLastTopTime(SystemClock.uptimeMillis());
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -447,7 +446,7 @@
public void testUpdateOomAdj_DoOne_Toast() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.forcingToImportant = new Object();
+ app.mState.setForcingToImportant(new Object());
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -460,7 +459,6 @@
public void testUpdateOomAdj_DoOne_HeavyWeight() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isHeavyWeightProcess();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -476,7 +474,6 @@
public void testUpdateOomAdj_DoOne_HomeApp() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -490,7 +487,6 @@
public void testUpdateOomAdj_DoOne_PreviousApp() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isPreviousProcess();
doReturn(true).when(wpc).hasActivities();
@@ -522,11 +518,11 @@
public void testUpdateOomAdj_DoOne_ClientActivities() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(true).when(app).hasClientActivities();
+ app.mServices.setHasClientActivities(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.setProcState);
+ assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -534,11 +530,11 @@
public void testUpdateOomAdj_DoOne_TreatLikeActivity() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- app.treatLikeActivity = true;
+ app.mServices.setTreatLikeActivity(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.setProcState);
+ assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -546,12 +542,12 @@
public void testUpdateOomAdj_DoOne_ServiceB() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- app.serviceb = true;
+ app.mState.setServiceB(true);
ServiceRecord s = mock(ServiceRecord.class);
doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
- app.startService(s);
+ app.mServices.startService(s);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -563,7 +559,7 @@
public void testUpdateOomAdj_DoOne_MaxAdj() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- app.maxAdj = PERCEPTIBLE_LOW_APP_ADJ;
+ app.mState.setMaxAdj(PERCEPTIBLE_LOW_APP_ADJ);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -576,14 +572,14 @@
public void testUpdateOomAdj_DoOne_NonCachedToCached() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- app.setCached(false);
- app.setCurRawAdj(SERVICE_ADJ);
+ app.mState.setCached(false);
+ app.mState.setCurRawAdj(SERVICE_ADJ);
doReturn(null).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.setAdj);
- assertTrue(ProcessList.CACHED_APP_MAX_ADJ >= app.setAdj);
+ assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.mState.getSetAdj());
+ assertTrue(ProcessList.CACHED_APP_MAX_ADJ >= app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -595,7 +591,7 @@
doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
- app.startService(s);
+ app.mServices.startService(s);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -633,7 +629,7 @@
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.setProcState);
+ assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -653,8 +649,8 @@
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(FOREGROUND_APP_ADJ, app.setAdj);
- assertEquals(SCHED_GROUP_TOP_APP_BOUND, app.setSchedGroup);
+ assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
+ assertEquals(SCHED_GROUP_TOP_APP_BOUND, app.mState.getSetSchedGroup());
}
@SuppressWarnings("GuardedBy")
@@ -676,12 +672,12 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- client.treatLikeActivity = true;
+ client.mServices.setTreatLikeActivity(true);
bindService(app, client, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PROCESS_STATE_CACHED_EMPTY, app.setProcState);
+ assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -689,7 +685,6 @@
public void testUpdateOomAdj_DoOne_Service_AllowOomManagement() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(false).when(wpc).isHomeProcess();
doReturn(true).when(wpc).isPreviousProcess();
@@ -703,7 +698,7 @@
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(null).when(sService).getTopApp();
- assertEquals(PREVIOUS_APP_ADJ, app.setAdj);
+ assertEquals(PREVIOUS_APP_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -714,8 +709,8 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
- client.maxAdj = PERSISTENT_PROC_ADJ;
- client.setHasTopUi(true);
+ client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+ client.mState.setHasTopUi(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -731,11 +726,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_IMPORTANT, mock(IBinder.class));
- client.executingServices.add(mock(ServiceRecord.class));
+ client.mServices.startExecutingService(mock(ServiceRecord.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(FOREGROUND_APP_ADJ, app.setAdj);
+ assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -763,11 +758,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
- client.maxAdj = PERSISTENT_PROC_ADJ;
+ client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.setProcState);
+ assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -778,11 +773,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class));
- client.maxAdj = PERSISTENT_PROC_ADJ;
+ client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.setProcState);
+ assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -793,11 +788,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, 0, mock(IBinder.class));
- client.setHasForegroundServices(true, 0);
+ client.mServices.setHasForegroundServices(true, 0);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, app.setProcState);
+ assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -815,12 +810,12 @@
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(null).when(sService.mBackupTargets).get(anyInt());
- assertEquals(BACKUP_APP_ADJ, app.setAdj);
+ assertEquals(BACKUP_APP_ADJ, app.mState.getSetAdj());
- client.maxAdj = PERSISTENT_PROC_ADJ;
+ client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PERSISTENT_SERVICE_ADJ, app.setAdj);
+ assertEquals(PERSISTENT_SERVICE_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -831,11 +826,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
- client.runningRemoteAnimation = true;
+ client.mState.setRunningRemoteAnimation(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.setAdj);
+ assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -846,11 +841,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class));
- client.runningRemoteAnimation = true;
+ client.mState.setRunningRemoteAnimation(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PERCEPTIBLE_APP_ADJ, app.setAdj);
+ assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -861,11 +856,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, 0, mock(IBinder.class));
- client.setHasOverlayUi(true);
+ client.mState.setHasOverlayUi(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PERCEPTIBLE_APP_ADJ, app.setAdj);
+ assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -876,11 +871,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, 0, mock(IBinder.class));
- client.runningRemoteAnimation = true;
+ client.mState.setRunningRemoteAnimation(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(VISIBLE_APP_ADJ, app.setAdj);
+ assertEquals(VISIBLE_APP_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -891,11 +886,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_IMPORTANT_BACKGROUND, mock(IBinder.class));
- client.setHasOverlayUi(true);
+ client.mState.setHasOverlayUi(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.setProcState);
+ assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -917,7 +912,7 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindProvider(app, client, null, null, false);
- client.treatLikeActivity = true;
+ client.mServices.setTreatLikeActivity(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -948,7 +943,7 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- client.setHasForegroundServices(true, 0);
+ client.mServices.setHasForegroundServices(true, 0);
bindProvider(app, client, null, null, false);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -977,7 +972,7 @@
public void testUpdateOomAdj_DoOne_Provider_Retention() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- app.lastProviderTime = SystemClock.uptimeMillis();
+ app.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1017,7 +1012,7 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(app, client2, null, 0, mock(IBinder.class));
- client2.setHasForegroundServices(true, 0);
+ client2.mServices.setHasForegroundServices(true, 0);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1036,7 +1031,7 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, 0, mock(IBinder.class));
- client2.setHasForegroundServices(true, 0);
+ client2.mServices.setHasForegroundServices(true, 0);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1055,9 +1050,9 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, 0, mock(IBinder.class));
- client2.setHasForegroundServices(true, 0);
+ client2.mServices.setHasForegroundServices(true, 0);
bindService(client2, app, null, 0, mock(IBinder.class));
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app);
lru.add(client);
@@ -1072,13 +1067,13 @@
assertProcStates(client2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
SCHED_GROUP_DEFAULT);
- client2.setHasForegroundServices(false, 0);
+ client2.mServices.setHasForegroundServices(false, 0);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(client2, true, OomAdjuster.OOM_ADJ_REASON_NONE);
- assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.setProcState);
- assertEquals(PROCESS_STATE_CACHED_EMPTY, client.setProcState);
- assertEquals(PROCESS_STATE_CACHED_EMPTY, app.setProcState);
+ assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.mState.getSetProcState());
+ assertEquals(PROCESS_STATE_CACHED_EMPTY, client.mState.getSetProcState());
+ assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState());
}
@SuppressWarnings("GuardedBy")
@@ -1093,8 +1088,8 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client2, client, null, 0, mock(IBinder.class));
- client.setHasForegroundServices(true, 0);
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ client.mServices.setHasForegroundServices(true, 0);
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app);
lru.add(client);
@@ -1121,11 +1116,11 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, 0, mock(IBinder.class));
- client2.setHasForegroundServices(true, 0);
+ client2.mServices.setHasForegroundServices(true, 0);
bindService(client2, app, null, 0, mock(IBinder.class));
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- client3.forcingToImportant = new Object();
+ client3.mState.setForcingToImportant(new Object());
bindService(app, client3, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1146,12 +1141,11 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, 0, mock(IBinder.class));
bindService(client2, app, null, 0, mock(IBinder.class));
- doReturn(mock(WindowProcessController.class)).when(client2).getWindowProcessController();
WindowProcessController wpc = client2.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- client3.forcingToImportant = new Object();
+ client3.mState.setForcingToImportant(new Object());
bindService(app, client3, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1172,14 +1166,13 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, 0, mock(IBinder.class));
bindService(client2, app, null, 0, mock(IBinder.class));
- doReturn(mock(WindowProcessController.class)).when(client2).getWindowProcessController();
WindowProcessController wpc = client2.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
- client4.forcingToImportant = new Object();
+ client4.mState.setForcingToImportant(new Object());
bindService(app, client4, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1200,16 +1193,15 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, 0, mock(IBinder.class));
bindService(client2, app, null, 0, mock(IBinder.class));
- doReturn(mock(WindowProcessController.class)).when(client2).getWindowProcessController();
WindowProcessController wpc = client2.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- client3.forcingToImportant = new Object();
+ client3.mState.setForcingToImportant(new Object());
bindService(app, client3, null, 0, mock(IBinder.class));
ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
- client4.setHasForegroundServices(true, 0);
+ client4.mServices.setHasForegroundServices(true, 0);
bindService(app, client4, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1225,17 +1217,16 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- doReturn(mock(WindowProcessController.class)).when(client).getWindowProcessController();
WindowProcessController wpc = client.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
bindService(app, client, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(app, client2, null, 0, mock(IBinder.class));
- client2.setHasForegroundServices(true, 0);
+ client2.mServices.setHasForegroundServices(true, 0);
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- client3.forcingToImportant = new Object();
+ client3.mState.setForcingToImportant(new Object());
bindService(app, client3, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1255,7 +1246,7 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
- client2.setHasForegroundServices(true, 0);
+ client2.mServices.setHasForegroundServices(true, 0);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1274,7 +1265,7 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
- client2.setHasForegroundServices(true, 0);
+ client2.mServices.setHasForegroundServices(true, 0);
bindService(client2, app, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1294,7 +1285,7 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
- client2.setHasForegroundServices(true, 0);
+ client2.mServices.setHasForegroundServices(true, 0);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1313,7 +1304,7 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
- client2.setHasForegroundServices(true, 0);
+ client2.mServices.setHasForegroundServices(true, 0);
bindProvider(client2, app, null, null, false);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1327,11 +1318,11 @@
public void testUpdateOomAdj_DoAll_Unbound() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- app.forcingToImportant = new Object();
+ app.mState.setForcingToImportant(new Object());
ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- app2.setHasForegroundServices(true, 0);
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ app2.mServices.setHasForegroundServices(true, 0);
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app);
lru.add(app2);
@@ -1350,12 +1341,12 @@
public void testUpdateOomAdj_DoAll_BoundFgService() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- app.forcingToImportant = new Object();
+ app.mState.setForcingToImportant(new Object());
ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- app2.setHasForegroundServices(true, 0);
+ app2.mServices.setHasForegroundServices(true, 0);
bindService(app, app2, null, 0, mock(IBinder.class));
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app);
lru.add(app2);
@@ -1380,9 +1371,9 @@
ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(app2, app3, null, 0, mock(IBinder.class));
- app3.setHasForegroundServices(true, 0);
+ app3.mServices.setHasForegroundServices(true, 0);
bindService(app3, app, null, 0, mock(IBinder.class));
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app);
lru.add(app2);
@@ -1397,15 +1388,15 @@
SCHED_GROUP_DEFAULT);
assertProcStates(app3, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
SCHED_GROUP_DEFAULT);
- assertEquals("service", app.adjType);
- assertEquals("service", app2.adjType);
- assertEquals("fg-service", app3.adjType);
+ assertEquals("service", app.mState.getAdjType());
+ assertEquals("service", app2.mState.getAdjType());
+ assertEquals("fg-service", app3.mState.getAdjType());
assertEquals(false, app.isCached());
assertEquals(false, app2.isCached());
assertEquals(false, app3.isCached());
- assertEquals(false, app.empty);
- assertEquals(false, app2.empty);
- assertEquals(false, app3.empty);
+ assertEquals(false, app.mState.isEmpty());
+ assertEquals(false, app2.mState.isEmpty());
+ assertEquals(false, app3.mState.isEmpty());
}
@SuppressWarnings("GuardedBy")
@@ -1420,18 +1411,17 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(app2, app3, null, 0, mock(IBinder.class));
bindService(app3, app, null, 0, mock(IBinder.class));
- doReturn(mock(WindowProcessController.class)).when(app3).getWindowProcessController();
WindowProcessController wpc = app3.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- app4.setHasOverlayUi(true);
+ app4.mState.setHasOverlayUi(true);
bindService(app, app4, s, 0, mock(IBinder.class));
ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
- app5.setHasForegroundServices(true, 0);
+ app5.mServices.setHasForegroundServices(true, 0);
bindService(app, app5, s, 0, mock(IBinder.class));
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app);
lru.add(app2);
@@ -1466,18 +1456,17 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(app2, app3, null, 0, mock(IBinder.class));
bindService(app3, app, null, 0, mock(IBinder.class));
- doReturn(mock(WindowProcessController.class)).when(app3).getWindowProcessController();
WindowProcessController wpc = app3.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- app4.setHasOverlayUi(true);
+ app4.mState.setHasOverlayUi(true);
bindService(app, app4, s, 0, mock(IBinder.class));
ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
- app5.setHasForegroundServices(true, 0);
+ app5.mServices.setHasForegroundServices(true, 0);
bindService(app, app5, s, 0, mock(IBinder.class));
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app5);
lru.add(app4);
@@ -1512,18 +1501,17 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(app2, app3, null, 0, mock(IBinder.class));
bindService(app3, app, null, 0, mock(IBinder.class));
- doReturn(mock(WindowProcessController.class)).when(app3).getWindowProcessController();
WindowProcessController wpc = app3.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- app4.setHasOverlayUi(true);
+ app4.mState.setHasOverlayUi(true);
bindService(app, app4, s, 0, mock(IBinder.class));
ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
- app5.setHasForegroundServices(true, 0);
+ app5.mServices.setHasForegroundServices(true, 0);
bindService(app, app5, s, 0, mock(IBinder.class));
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app3);
lru.add(app4);
@@ -1558,18 +1546,17 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(app2, app3, null, null, false);
bindProvider(app3, app, null, null, false);
- doReturn(mock(WindowProcessController.class)).when(app3).getWindowProcessController();
WindowProcessController wpc = app3.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- app4.setHasOverlayUi(true);
+ app4.mState.setHasOverlayUi(true);
bindProvider(app, app4, cr, null, false);
ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
- app5.setHasForegroundServices(true, 0);
+ app5.mServices.setHasForegroundServices(true, 0);
bindProvider(app, app5, cr, null, false);
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app);
lru.add(app2);
@@ -1612,11 +1599,11 @@
s.app = app3;
setFieldValue(ServiceRecord.class, s, "connections",
new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
- app3.startService(s);
+ app3.mServices.startService(s);
doCallRealMethod().when(s).getConnections();
s.startRequested = true;
s.lastActivity = now;
- ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app3);
lru.add(app2);
@@ -1626,9 +1613,9 @@
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
lru.clear();
- assertEquals(SERVICE_B_ADJ, app3.setAdj);
- assertEquals(SERVICE_ADJ, app2.setAdj);
- assertEquals(SERVICE_ADJ, app.setAdj);
+ assertEquals(SERVICE_B_ADJ, app3.mState.getSetAdj());
+ assertEquals(SERVICE_ADJ, app2.mState.getSetAdj());
+ assertEquals(SERVICE_ADJ, app.mState.getSetAdj());
}
@SuppressWarnings("GuardedBy")
@@ -1644,7 +1631,7 @@
final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
doReturn(userOwner).when(sService.mUserController).getCurrentUserId();
- final ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app2);
lru.add(app);
@@ -1664,9 +1651,9 @@
s.startRequested = true;
s.lastActivity = now;
- app.setCached(false);
- app.startService(s);
- app.hasShownUi = true;
+ app.mState.setCached(false);
+ app.mServices.startService(s);
+ app.mState.setHasShownUi(true);
final ServiceInfo si2 = mock(ServiceInfo.class);
si2.applicationInfo = mock(ApplicationInfo.class);
@@ -1677,9 +1664,9 @@
s2.startRequested = true;
s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
- app2.setCached(false);
- app2.startService(s2);
- app2.hasShownUi = false;
+ app2.mState.setCached(false);
+ app2.mServices.startService(s2);
+ app2.mState.setHasShownUi(false);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1687,28 +1674,28 @@
assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj2, "cch-started-services");
- app.setProcState = PROCESS_STATE_NONEXISTENT;
- app.adjType = null;
- app.setAdj = UNKNOWN_ADJ;
- app.hasShownUi = false;
+ app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
+ app.mState.setAdjType(null);
+ app.mState.setSetAdj(UNKNOWN_ADJ);
+ app.mState.setHasShownUi(false);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
- app.setCached(false);
- app.setProcState = PROCESS_STATE_NONEXISTENT;
- app.adjType = null;
- app.setAdj = UNKNOWN_ADJ;
+ app.mState.setCached(false);
+ app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
+ app.mState.setAdjType(null);
+ app.mState.setSetAdj(UNKNOWN_ADJ);
s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
- app.stopService(s);
- app.setProcState = PROCESS_STATE_NONEXISTENT;
- app.adjType = null;
- app.setAdj = UNKNOWN_ADJ;
- app.hasShownUi = true;
+ app.mServices.stopService(s);
+ app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
+ app.mState.setAdjType(null);
+ app.mState.setSetAdj(UNKNOWN_ADJ);
+ app.mState.setHasShownUi(true);
sService.mConstants.KEEP_WARMING_SERVICES.add(cn);
sService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
s = spy(new ServiceRecord(sService, cn, cn, null, 0, null,
@@ -1717,17 +1704,17 @@
s.startRequested = true;
s.lastActivity = now;
- app.startService(s);
+ app.mServices.startService(s);
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
- app.setCached(true);
- app.setProcState = PROCESS_STATE_NONEXISTENT;
- app.adjType = null;
- app.setAdj = UNKNOWN_ADJ;
- app.hasShownUi = false;
+ app.mState.setCached(true);
+ app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
+ app.mState.setAdjType(null);
+ app.mState.setSetAdj(UNKNOWN_ADJ);
+ app.mState.setHasShownUi(false);
s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -1776,49 +1763,55 @@
ai.longVersionCode = versionCode;
ai.targetSdkVersion = targetSdkVersion;
ProcessRecord app = new ProcessRecord(service, ai, processName, uid);
+ final ProcessStateRecord state = app.mState;
+ final ProcessServiceRecord services = app.mServices;
+ final ProcessReceiverRecord receivers = app.mReceivers;
final ProcessProfileRecord profile = app.mProfile;
- app.thread = mock(IApplicationThread.class);
- app.lastActivityTime = lastActivityTime;
+ final ProcessProviderRecord providers = app.mProviders;
+ app.makeActive(mock(IApplicationThread.class), sService.mProcessStats);
+ app.setLastActivityTime(lastActivityTime);
+ app.setKilledByAm(killedByAm);
+ app.setIsolatedEntryPoint(isolatedEntryPoint);
+ setFieldValue(ProcessRecord.class, app, "mWindowProcessController",
+ mock(WindowProcessController.class));
profile.setLastPssTime(lastPssTime);
profile.setNextPssTime(nextPssTime);
profile.setLastPss(lastPss);
- app.maxAdj = maxAdj;
- app.setRawAdj = setRawAdj;
- app.curAdj = curAdj;
- app.setAdj = setAdj;
- app.setCurrentSchedulingGroup(curSchedGroup);
- app.setSchedGroup = setSchedGroup;
- app.setCurProcState(curProcState);
- app.setReportedProcState(repProcState);
- app.setCurRawProcState(curRawProcState);
- app.setProcState = setProcState;
- app.connectionGroup = connectionGroup;
- app.connectionImportance = connectionImportance;
- app.serviceb = serviceb;
- app.setHasClientActivities(hasClientActivities);
- app.setHasForegroundServices(hasForegroundServices, fgServiceTypes);
- app.setHasClientActivities(hasForegroundActivities);
- app.repForegroundActivities = repForegroundActivities;
- app.systemNoUi = systemNoUi;
- app.hasShownUi = hasShownUi;
- app.setHasTopUi(hasTopUi);
- app.setHasOverlayUi(hasOverlayUi);
- app.runningRemoteAnimation = runningRemoteAnimation;
- app.hasAboveClient = hasAboveClient;
- app.treatLikeActivity = treatLikeActivity;
- app.killedByAm = killedByAm;
- app.forcingToImportant = forcingToImportant;
- for (int i = 0; i < numOfCurReceivers; i++) {
- app.curReceivers.add(mock(BroadcastRecord.class));
- }
- app.lastProviderTime = lastProviderTime;
- app.lastTopTime = lastTopTime;
- app.setCached(cached);
+ state.setMaxAdj(maxAdj);
+ state.setSetRawAdj(setRawAdj);
+ state.setCurAdj(curAdj);
+ state.setSetAdj(setAdj);
+ state.setCurrentSchedulingGroup(curSchedGroup);
+ state.setSetSchedGroup(setSchedGroup);
+ state.setCurProcState(curProcState);
+ state.setReportedProcState(repProcState);
+ state.setCurRawProcState(curRawProcState);
+ state.setSetProcState(setProcState);
+ state.setServiceB(serviceb);
+ state.setRepForegroundActivities(repForegroundActivities);
+ state.setHasForegroundActivities(hasForegroundActivities);
+ state.setSystemNoUi(systemNoUi);
+ state.setHasShownUi(hasShownUi);
+ state.setHasTopUi(hasTopUi);
+ state.setRunningRemoteAnimation(runningRemoteAnimation);
+ state.setHasOverlayUi(hasOverlayUi);
+ state.setCached(cached);
+ state.setLastTopTime(lastTopTime);
+ state.setForcingToImportant(forcingToImportant);
+ services.setConnectionGroup(connectionGroup);
+ services.setConnectionImportance(connectionImportance);
+ services.setHasClientActivities(hasClientActivities);
+ services.setHasForegroundServices(hasForegroundServices, fgServiceTypes);
+ services.setHasAboveClient(hasAboveClient);
+ services.setTreatLikeActivity(treatLikeActivity);
+ services.setExecServicesFg(execServicesFg);
for (int i = 0; i < numOfExecutingServices; i++) {
- app.executingServices.add(mock(ServiceRecord.class));
+ services.startExecutingService(mock(ServiceRecord.class));
}
- app.isolatedEntryPoint = isolatedEntryPoint;
- app.execServicesFg = execServicesFg;
+ for (int i = 0; i < numOfCurReceivers; i++) {
+ receivers.addCurReceiver(mock(BroadcastRecord.class));
+ }
+ providers.setLastProviderTime(lastProviderTime);
return app;
}
@@ -1829,7 +1822,7 @@
record.app = service;
setFieldValue(ServiceRecord.class, record, "connections",
new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
- service.startService(record);
+ service.mServices.startService(record);
doCallRealMethod().when(record).getConnections();
}
AppBindRecord binding = new AppBindRecord(record, null, client);
@@ -1840,7 +1833,7 @@
doCallRealMethod().when(record).addConnection(any(IBinder.class),
any(ConnectionRecord.class));
record.addConnection(binder, cr);
- client.connections.add(cr);
+ client.mServices.addConnection(cr);
binding.connections.add(cr);
doNothing().when(cr).trackProcState(anyInt(), anyInt(), anyLong());
return record;
@@ -1850,7 +1843,7 @@
ContentProviderRecord record, String name, boolean hasExternalProviders) {
if (record == null) {
record = mock(ContentProviderRecord.class);
- publisher.pubProviders.put(name, record);
+ publisher.mProviders.installProvider(name, record);
record.proc = publisher;
setFieldValue(ContentProviderRecord.class, record, "connections",
new ArrayList<ContentProviderConnection>());
@@ -1859,22 +1852,24 @@
ContentProviderConnection conn = spy(new ContentProviderConnection(record, client,
client.info.packageName));
record.connections.add(conn);
- client.conProviders.add(conn);
+ client.mProviders.addProviderConnection(conn);
return record;
}
private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
int expectedSchedGroup) {
- assertEquals(expectedProcState, app.setProcState);
- assertEquals(expectedAdj, app.setAdj);
- assertEquals(expectedSchedGroup, app.setSchedGroup);
+ final ProcessStateRecord state = app.mState;
+ assertEquals(expectedProcState, state.getSetProcState());
+ assertEquals(expectedAdj, state.getSetAdj());
+ assertEquals(expectedSchedGroup, state.getSetSchedGroup());
}
private void assertProcStates(ProcessRecord app, boolean expectedCached,
int expectedProcState, int expectedAdj, String expectedAdjType) {
- assertEquals(expectedCached, app.isCached());
- assertEquals(expectedProcState, app.setProcState);
- assertEquals(expectedAdj, app.setAdj);
- assertEquals(expectedAdjType, app.adjType);
+ final ProcessStateRecord state = app.mState;
+ assertEquals(expectedCached, state.isCached());
+ assertEquals(expectedProcState, state.getSetProcState());
+ assertEquals(expectedAdj, state.getSetAdj());
+ assertEquals(expectedAdjType, state.getAdjType());
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 84bfc9b..3b5cc88 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -96,6 +96,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -383,7 +384,7 @@
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener).onLocationChanged(eq(loc), nullable(IRemoteCallback.class));
+ verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
}
@Test
@@ -406,13 +407,13 @@
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener).onLocationChanged(eq(loc), nullable(IRemoteCallback.class));
+ verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, false);
loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener, times(1)).onLocationChanged(any(LocationResult.class),
+ verify(listener, times(1)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
@@ -422,7 +423,7 @@
verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, false);
loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener, times(1)).onLocationChanged(any(LocationResult.class),
+ verify(listener, times(1)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
mProvider.setAllowed(true);
@@ -430,7 +431,7 @@
loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener).onLocationChanged(eq(loc), nullable(IRemoteCallback.class));
+ verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
}
@Test
@@ -447,7 +448,7 @@
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener, timeout(TIMEOUT_MS).times(1)).onLocationChanged(eq(loc),
+ verify(listener, timeout(TIMEOUT_MS).times(1)).onLocationChanged(eq(loc.asList()),
nullable(IRemoteCallback.class));
}
@@ -462,7 +463,7 @@
mManager.unregisterLocationRequest(listener);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
@@ -493,7 +494,7 @@
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mManager.unregisterLocationRequest(listener);
blocker.countDown();
- verify(listener, after(TIMEOUT_MS).never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, after(TIMEOUT_MS).never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -513,7 +514,7 @@
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, times(5)).onLocationChanged(any(LocationResult.class),
+ verify(listener, times(5)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -528,7 +529,7 @@
mInjector.getAlarmHelper().incrementAlarmTime(5000);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -544,7 +545,7 @@
Thread.sleep(25);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -561,7 +562,7 @@
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1)).onLocationChanged(
- any(LocationResult.class), nullable(IRemoteCallback.class));
+ any(List.class), nullable(IRemoteCallback.class));
}
@Test
@@ -578,7 +579,7 @@
mProvider.setProviderLocation(loc);
verify(listener, times(1)).onLocationChanged(
- any(LocationResult.class), nullable(IRemoteCallback.class));
+ any(List.class), nullable(IRemoteCallback.class));
}
@Test
@@ -592,7 +593,7 @@
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -622,7 +623,7 @@
verify(mWakeLock, never()).release();
blocker.countDown();
- verify(listener, timeout(TIMEOUT_MS)).onLocationChanged(any(LocationResult.class),
+ verify(listener, timeout(TIMEOUT_MS)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
verify(mWakeLock).acquire(anyLong());
verify(mWakeLock, timeout(TIMEOUT_MS)).release();
@@ -640,7 +641,7 @@
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1))
- .onLocationChanged(any(LocationResult.class), nullable(IRemoteCallback.class));
+ .onLocationChanged(any(List.class), nullable(IRemoteCallback.class));
}
@Test
@@ -657,7 +658,7 @@
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1))
- .onLocationChanged(any(LocationResult.class), nullable(IRemoteCallback.class));
+ .onLocationChanged(any(List.class), nullable(IRemoteCallback.class));
}
@Test
@@ -746,7 +747,7 @@
mProvider.completeFlushes();
InOrder inOrder = inOrder(listener);
- inOrder.verify(listener).onLocationChanged(eq(loc), any(IRemoteCallback.class));
+ inOrder.verify(listener).onLocationChanged(eq(loc.asList()), any(IRemoteCallback.class));
inOrder.verify(listener).onFlushComplete(99);
}
@@ -838,7 +839,7 @@
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
- verify(listener1).onLocationChanged(any(LocationResult.class),
+ verify(listener1).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
assertThat(mProvider.getRequest().isActive()).isFalse();
@@ -989,7 +990,7 @@
private ILocationListener createMockLocationListener() {
return spy(new ILocationListener.Stub() {
@Override
- public void onLocationChanged(LocationResult location,
+ public void onLocationChanged(List<Location> locations,
IRemoteCallback onCompleteCallback) {
if (onCompleteCallback != null) {
try {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index 99846c5..07170da 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -185,8 +185,8 @@
@Test
public void testReportLocation() {
- LocationResult realLocation = LocationResult.create(new Location("real"));
- LocationResult mockLocation = LocationResult.create(new Location("mock"));
+ LocationResult realLocation = LocationResult.wrap(new Location("real"));
+ LocationResult mockLocation = LocationResult.wrap(new Location("mock"));
mRealProvider.reportLocation(realLocation);
assertThat(mListener.getNextLocationResult()).isEqualTo(realLocation);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index c522541..6e27b3a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -64,6 +64,8 @@
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.pm.permission.PermissionManagerServiceInternal
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.testutils.TestHandler
import com.android.server.testutils.mock
import com.android.server.testutils.nullable
import com.android.server.testutils.whenever
@@ -142,7 +144,7 @@
}
whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(),
nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(),
- nullable(), nullable(), nullable())) {
+ nullable(), nullable(), nullable(), nullable())) {
val name: String = getArgument(0)
val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name }
?: return@whenever null
@@ -183,6 +185,8 @@
val dexManager: DexManager = mock()
val installer: Installer = mock()
val displayMetrics: DisplayMetrics = mock()
+ val domainVerificationManagerInternal: DomainVerificationManagerInternal = mock()
+ val handler = TestHandler(null)
}
companion object {
@@ -258,6 +262,9 @@
whenever(mocks.injector.userManagerInternal).thenReturn(mocks.userManagerInternal)
whenever(mocks.injector.installer).thenReturn(mocks.installer)
whenever(mocks.injector.displayMetrics).thenReturn(mocks.displayMetrics)
+ whenever(mocks.injector.domainVerificationManagerInternal)
+ .thenReturn(mocks.domainVerificationManagerInternal)
+ whenever(mocks.injector.handler) { mocks.handler }
wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig)
whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP)
whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST)
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 6daa381..7a0cb8e 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -59,9 +59,9 @@
},
libs: [
- "android.hardware.power-java",
+ "android.hardware.power-V1-java",
"android.hardware.tv.cec-V1.0-java",
- "android.hardware.vibrator-java",
+ "android.hardware.vibrator-V1-java",
"android.hidl.manager-V1.0-java",
"android.test.mock",
"android.test.base",
@@ -88,7 +88,7 @@
"libui",
"libunwindstack",
"libutils",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-V5-cpp",
],
dxflags: ["--multi-dex"],
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 3b4699e..8c21a39 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -134,7 +134,7 @@
private UidRecord addActiveUidRecord(int uid, long curProcStateSeq,
long lastNetworkUpdatedProcStateSeq) {
- final UidRecord record = new UidRecord(uid);
+ final UidRecord record = new UidRecord(uid, mAms);
record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
record.curProcStateSeq = curProcStateSeq;
record.waitingForNetwork = true;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index e119d52..f314008 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -273,7 +273,7 @@
}
private UidRecord addUidRecord(int uid) {
- final UidRecord uidRec = new UidRecord(uid);
+ final UidRecord uidRec = new UidRecord(uid, mAms);
uidRec.waitingForNetwork = true;
uidRec.hasInternetPermission = true;
mAms.mProcessList.mActiveUids.put(uid, uidRec);
@@ -282,8 +282,9 @@
info.packageName = "";
final ProcessRecord appRec = new ProcessRecord(mAms, info, TAG, uid);
- appRec.thread = mock(IApplicationThread.class);
- mAms.mProcessList.mLruProcesses.add(appRec);
+ final ProcessStatsService tracker = new ProcessStatsService(mAms, mContext.getCacheDir());
+ appRec.makeActive(mock(IApplicationThread.class), tracker);
+ mAms.mProcessList.getLruProcessesLSP().add(appRec);
return uidRec;
}
@@ -295,23 +296,23 @@
CustomThread thread = new CustomThread(uidRec.networkStateLock);
thread.startAndWait("Unexpected state for " + uidRec);
- uidRec.setProcState = prevState;
+ uidRec.setSetProcState(prevState);
uidRec.setCurProcState(curState);
- mAms.mProcessList.incrementProcStateSeqAndNotifyAppsLocked(mAms.mProcessList.mActiveUids);
+ mAms.mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(mAms.mProcessList.mActiveUids);
// @SuppressWarnings("GuardedBy")
assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
- for (int i = mAms.mProcessList.getLruSizeLocked() - 1; i >= 0; --i) {
- final ProcessRecord app = mAms.mProcessList.mLruProcesses.get(i);
+ for (int i = mAms.mProcessList.getLruSizeLOSP() - 1; i >= 0; --i) {
+ final ProcessRecord app = mAms.mProcessList.getLruProcessesLOSP().get(i);
// AMS should notify apps only for block states other than NETWORK_STATE_NO_CHANGE.
- if (app.uid == uidRec.uid && expectedBlockState == NETWORK_STATE_BLOCK) {
- verify(app.thread).setNetworkBlockSeq(uidRec.curProcStateSeq);
+ if (app.uid == uidRec.getUid() && expectedBlockState == NETWORK_STATE_BLOCK) {
+ verify(app.getThread()).setNetworkBlockSeq(uidRec.curProcStateSeq);
} else {
- verifyZeroInteractions(app.thread);
+ verifyZeroInteractions(app.getThread());
}
- Mockito.reset(app.thread);
+ Mockito.reset(app.getThread());
}
if (expectNotify) {
@@ -446,55 +447,56 @@
@Test
public void testBlockStateForUid() {
- final UidRecord uidRec = new UidRecord(TEST_UID);
+ final UidRecord uidRec = new UidRecord(TEST_UID, mAms);
int expectedBlockState;
final String errorTemplate = "Block state should be %s, prevState: %s, curState: %s";
Function<Integer, String> errorMsg = (blockState) -> {
return String.format(errorTemplate,
valueToString(ActivityManagerService.class, "NETWORK_STATE_", blockState),
- valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.setProcState),
+ valueToString(ActivityManager.class, "PROCESS_STATE_",
+ uidRec.getSetProcState()),
valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.getCurProcState())
);
};
// No change in uid state
- uidRec.setProcState = PROCESS_STATE_RECEIVER;
+ uidRec.setSetProcState(PROCESS_STATE_RECEIVER);
uidRec.setCurProcState(PROCESS_STATE_RECEIVER);
expectedBlockState = NETWORK_STATE_NO_CHANGE;
assertEquals(errorMsg.apply(expectedBlockState),
expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Foreground to foreground
- uidRec.setProcState = PROCESS_STATE_FOREGROUND_SERVICE;
+ uidRec.setSetProcState(PROCESS_STATE_FOREGROUND_SERVICE);
uidRec.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
expectedBlockState = NETWORK_STATE_NO_CHANGE;
assertEquals(errorMsg.apply(expectedBlockState),
expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Background to background
- uidRec.setProcState = PROCESS_STATE_CACHED_ACTIVITY;
+ uidRec.setSetProcState(PROCESS_STATE_CACHED_ACTIVITY);
uidRec.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
expectedBlockState = NETWORK_STATE_NO_CHANGE;
assertEquals(errorMsg.apply(expectedBlockState),
expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Background to background
- uidRec.setProcState = PROCESS_STATE_NONEXISTENT;
+ uidRec.setSetProcState(PROCESS_STATE_NONEXISTENT);
uidRec.setCurProcState(PROCESS_STATE_CACHED_ACTIVITY);
expectedBlockState = NETWORK_STATE_NO_CHANGE;
assertEquals(errorMsg.apply(expectedBlockState),
expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Background to foreground
- uidRec.setProcState = PROCESS_STATE_SERVICE;
+ uidRec.setSetProcState(PROCESS_STATE_SERVICE);
uidRec.setCurProcState(PROCESS_STATE_FOREGROUND_SERVICE);
expectedBlockState = NETWORK_STATE_BLOCK;
assertEquals(errorMsg.apply(expectedBlockState),
expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Foreground to background
- uidRec.setProcState = PROCESS_STATE_TOP;
+ uidRec.setSetProcState(PROCESS_STATE_TOP);
uidRec.setCurProcState(PROCESS_STATE_LAST_ACTIVITY);
expectedBlockState = NETWORK_STATE_UNBLOCK;
assertEquals(errorMsg.apply(expectedBlockState),
@@ -748,13 +750,13 @@
item.procState, validateUidRecord.getCurProcState());
assertEquals("processState: " + item.procState + " setProcState: "
+ validateUidRecord.getCurProcState() + " should have been equal",
- item.procState, validateUidRecord.setProcState);
+ item.procState, validateUidRecord.getSetProcState());
if (item.change == UidRecord.CHANGE_IDLE) {
assertTrue("UidRecord.idle should be updated to true for CHANGE_IDLE",
- validateUidRecord.idle);
+ validateUidRecord.isIdle());
} else if (item.change == UidRecord.CHANGE_ACTIVE) {
assertFalse("UidRecord.idle should be updated to false for CHANGE_ACTIVE",
- validateUidRecord.idle);
+ validateUidRecord.isIdle());
}
}
}
@@ -779,7 +781,7 @@
@Test
public void testEnqueueUidChangeLocked_procStateSeqUpdated() {
- final UidRecord uidRecord = new UidRecord(TEST_UID);
+ final UidRecord uidRecord = new UidRecord(TEST_UID, mAms);
uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
// Verify with no pending changes for TEST_UID.
@@ -823,9 +825,9 @@
@MediumTest
@Test
public void testEnqueueUidChangeLocked_dispatchUidsChanged() {
- final UidRecord uidRecord = new UidRecord(TEST_UID);
+ final UidRecord uidRecord = new UidRecord(TEST_UID, mAms);
final int expectedProcState = PROCESS_STATE_SERVICE;
- uidRecord.setProcState = expectedProcState;
+ uidRecord.setSetProcState(expectedProcState);
uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
// Test with no pending uid records.
@@ -895,7 +897,7 @@
private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
long lastDispatchedProcStateSeq, long lastNetworkUpdatedProcStateSeq,
final long procStateSeqToWait, boolean expectWait) throws Exception {
- final UidRecord record = new UidRecord(Process.myUid());
+ final UidRecord record = new UidRecord(Process.myUid(), mAms);
record.curProcStateSeq = curProcStateSeq;
record.lastDispatchedProcStateSeq = lastDispatchedProcStateSeq;
record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index 52c824a..432f6d6 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -33,6 +33,8 @@
import org.junit.Before;
import org.junit.Test;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.util.concurrent.TimeUnit;
/**
@@ -48,7 +50,26 @@
@Before
public void setUp() {
- runWithDexmakerShareClassLoader(() -> mAnrApp = mock(ProcessRecord.class));
+ runWithDexmakerShareClassLoader(() -> {
+ mAnrApp = mock(ProcessRecord.class);
+ final ActivityManagerService service = mock(ActivityManagerService.class);
+ final ProcessErrorStateRecord errorState = mock(ProcessErrorStateRecord.class);
+ setFieldValue(ProcessErrorStateRecord.class, errorState, "mProcLock",
+ new ActivityManagerProcLock());
+ setFieldValue(ProcessRecord.class, mAnrApp, "mErrorState", errorState);
+ });
+ }
+
+ private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ Field mfield = Field.class.getDeclaredField("accessFlags");
+ mfield.setAccessible(true);
+ mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
+ field.set(obj, val);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ }
}
@Test
@@ -62,7 +83,7 @@
mAnrHelper.appNotResponding(mAnrApp, activityShortComponentName, appInfo,
parentShortComponentName, parentProcess, aboveSystem, annotation);
- verify(mAnrApp, timeout(TimeUnit.SECONDS.toMillis(5))).appNotResponding(
+ verify(mAnrApp.mErrorState, timeout(TimeUnit.SECONDS.toMillis(5))).appNotResponding(
eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
eq(parentProcess), eq(aboveSystem), eq(annotation), eq(false) /* onlyDumpSelf */);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
index e44b0836..fdf5095 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -21,9 +21,13 @@
import static org.junit.Assert.assertNotNull;
import android.content.Context;
+import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
import android.power.PowerStatsInternal;
import android.util.SparseArray;
import android.util.SparseLongArray;
@@ -146,6 +150,28 @@
return future;
}
+ @Override
+ public PowerEntity[] getPowerEntityInfo() {
+ return new PowerEntity[0];
+ }
+
+ @Override
+ public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+ int[] powerEntityIds) {
+ return new CompletableFuture<>();
+ }
+
+ @Override
+ public Channel[] getEnergyMeterInfo() {
+ return new Channel[0];
+ }
+
+ @Override
+ public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+ int[] channelIds) {
+ return new CompletableFuture<>();
+ }
+
/**
* Util method to add a new EnergyConsumer for testing
*
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
index 7355b80..638b1b4 100644
--- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -77,6 +77,8 @@
sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
sService.mAtmInternal = sService.mActivityTaskManager.getAtmInternal();
+ setFieldValue(ActivityManagerService.class, sService, "mProcLock",
+ new ActivityManagerProcLock());
sService.mConstants = new ActivityManagerConstants(sContext, sService,
sContext.getMainThreadHandler());
final AppProfiler profiler = mock(AppProfiler.class);
@@ -124,7 +126,7 @@
@Test
public void testMaybeUpdateUsageStats_ProcStatePersistentUI() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, true, elapsedTime);
@@ -133,7 +135,7 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateTop() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, true, elapsedTime);
@@ -142,8 +144,8 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateTop_PreviousInteraction() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
- mProcessRecord.reportedInteraction = true;
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
+ mProcessRecord.mState.setReportedInteraction(true);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, true, ZERO);
@@ -152,8 +154,8 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateTop_PastUsageInterval() {
final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
- mProcessRecord.reportedInteraction = true;
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
+ mProcessRecord.mState.setReportedInteraction(true);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, true, elapsedTime);
@@ -162,7 +164,7 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateBoundTop() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_TOP);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_TOP);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, true, elapsedTime);
@@ -171,7 +173,7 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateFGS() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(elapsedTime, false, ZERO);
@@ -181,8 +183,8 @@
public void testMaybeUpdateUsageStats_ProcStateFGS_ShortInteraction() {
final long elapsedTime = ZERO;
final long fgInteractionTime = 1000L;
- mProcessRecord.setFgInteractionTime(fgInteractionTime);
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(fgInteractionTime, false, ZERO);
@@ -192,8 +194,8 @@
public void testMaybeUpdateUsageStats_ProcStateFGS_LongInteraction() {
final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
final long fgInteractionTime = 1000L;
- mProcessRecord.setFgInteractionTime(fgInteractionTime);
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(fgInteractionTime, true, elapsedTime);
@@ -203,9 +205,9 @@
public void testMaybeUpdateUsageStats_ProcStateFGS_PreviousLongInteraction() {
final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
final long fgInteractionTime = 1000L;
- mProcessRecord.setFgInteractionTime(fgInteractionTime);
- mProcessRecord.reportedInteraction = true;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
+ mProcessRecord.mState.setReportedInteraction(true);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(fgInteractionTime, true, ZERO);
@@ -214,7 +216,7 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateFGSLocation() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(elapsedTime, false, ZERO);
@@ -223,7 +225,8 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateBFGS() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ mProcessRecord.mState.setCurProcState(
+ ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, true, elapsedTime);
@@ -232,7 +235,7 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateImportantFG() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, true, elapsedTime);
@@ -241,8 +244,8 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateImportantFG_PreviousInteraction() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
- mProcessRecord.reportedInteraction = true;
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mProcessRecord.mState.setReportedInteraction(true);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, true, ZERO);
@@ -251,8 +254,8 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateImportantFG_PastUsageInterval() {
final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
- mProcessRecord.reportedInteraction = true;
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mProcessRecord.mState.setReportedInteraction(true);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, true, elapsedTime);
@@ -261,7 +264,7 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateImportantBG() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, false, ZERO);
@@ -270,7 +273,7 @@
@Test
public void testMaybeUpdateUsageStats_ProcStateService() {
final long elapsedTime = ZERO;
- mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_SERVICE);
+ mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_SERVICE);
sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
assertProcessRecordState(ZERO, false, ZERO);
@@ -279,10 +282,10 @@
private void assertProcessRecordState(long fgInteractionTime, boolean reportedInteraction,
long interactionEventTime) {
assertEquals("Foreground interaction time was not updated correctly.",
- fgInteractionTime, mProcessRecord.getFgInteractionTime());
+ fgInteractionTime, mProcessRecord.mState.getFgInteractionTime());
assertEquals("Interaction was not updated correctly.",
- reportedInteraction, mProcessRecord.reportedInteraction);
+ reportedInteraction, mProcessRecord.mState.hasReportedInteraction());
assertEquals("Interaction event time was not updated correctly.",
- interactionEventTime, mProcessRecord.getInteractionEventTime());
+ interactionEventTime, mProcessRecord.mState.getInteractionEventTime());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
index 263efa6..6538a17 100644
--- a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
@@ -44,7 +44,6 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
-import java.util.Collections;
/**
* Build/Install/Run:
@@ -57,6 +56,7 @@
private static ActivityManagerService sService;
private ProcessRecord mProcessRecord;
+ private ProcessErrorStateRecord mProcessErrorState;
@BeforeClass
public static void setUpOnce() throws Exception {
@@ -72,6 +72,8 @@
final AppProfiler profiler = mock(AppProfiler.class);
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
+ setFieldValue(ActivityManagerService.class, sService, "mProcLock",
+ new ActivityManagerProcLock());
final ProcessList processList = new ProcessList();
setFieldValue(ActivityManagerService.class, sService, "mProcessList", processList);
});
@@ -104,12 +106,12 @@
public void setUpProcess() throws Exception {
// Need to run with dexmaker share class loader to mock package private class.
runWithDexmakerShareClassLoader(() -> {
- mProcessRecord = spy(new ProcessRecord(sService, sContext.getApplicationInfo(),
- "name", 12345));
- doNothing().when(mProcessRecord).startAppProblemLocked();
- doReturn(false).when(mProcessRecord).isSilentAnr();
- doReturn(false).when(mProcessRecord).isMonitorCpuUsage();
- doReturn(Collections.emptyList()).when(mProcessRecord).getLruProcessList();
+ mProcessRecord = new ProcessRecord(sService, sContext.getApplicationInfo(),
+ "name", 12345);
+ mProcessErrorState = spy(mProcessRecord.mErrorState);
+ doNothing().when(mProcessErrorState).startAppProblemLSP();
+ doReturn(false).when(mProcessErrorState).isSilentAnr();
+ doReturn(false).when(mProcessErrorState).isMonitorCpuUsage();
});
}
@@ -120,10 +122,10 @@
*/
@Test
public void testProcessDefaultAnrRelatedStatus() {
- assertFalse(mProcessRecord.isNotResponding());
- assertFalse(mProcessRecord.isCrashing());
- assertFalse(mProcessRecord.killedByAm);
- assertFalse(mProcessRecord.killed);
+ assertFalse(mProcessErrorState.isNotResponding());
+ assertFalse(mProcessErrorState.isCrashing());
+ assertFalse(mProcessRecord.isKilledByAm());
+ assertFalse(mProcessRecord.isKilled());
}
/**
@@ -131,12 +133,12 @@
*/
@Test
public void testAnrWhenCrash() {
- mProcessRecord.setCrashing(true);
- assertTrue(mProcessRecord.isCrashing());
- appNotResponding(mProcessRecord, "Test ANR when crash");
- assertFalse(mProcessRecord.isNotResponding());
- assertFalse(mProcessRecord.killedByAm);
- assertFalse(mProcessRecord.killed);
+ mProcessErrorState.setCrashing(true);
+ assertTrue(mProcessErrorState.isCrashing());
+ appNotResponding(mProcessErrorState, "Test ANR when crash");
+ assertFalse(mProcessErrorState.isNotResponding());
+ assertFalse(mProcessRecord.isKilledByAm());
+ assertFalse(mProcessRecord.isKilled());
}
/**
@@ -144,11 +146,11 @@
*/
@Test
public void testAnrWhenKilledByAm() {
- mProcessRecord.killedByAm = true;
- appNotResponding(mProcessRecord, "Test ANR when killed by AM");
- assertFalse(mProcessRecord.isNotResponding());
- assertFalse(mProcessRecord.isCrashing());
- assertFalse(mProcessRecord.killed);
+ mProcessRecord.setKilledByAm(true);
+ appNotResponding(mProcessErrorState, "Test ANR when killed by AM");
+ assertFalse(mProcessErrorState.isNotResponding());
+ assertFalse(mProcessErrorState.isCrashing());
+ assertFalse(mProcessRecord.isKilled());
}
/**
@@ -156,11 +158,11 @@
*/
@Test
public void testAnrWhenKilled() {
- mProcessRecord.killed = true;
- appNotResponding(mProcessRecord, "Test ANR when killed");
- assertFalse(mProcessRecord.isNotResponding());
- assertFalse(mProcessRecord.isCrashing());
- assertFalse(mProcessRecord.killedByAm);
+ mProcessRecord.setKilled(true);
+ appNotResponding(mProcessErrorState, "Test ANR when killed");
+ assertFalse(mProcessErrorState.isNotResponding());
+ assertFalse(mProcessErrorState.isCrashing());
+ assertFalse(mProcessRecord.isKilledByAm());
}
/**
@@ -169,11 +171,11 @@
*/
@Test
public void testNonSilentAnr() {
- appNotResponding(mProcessRecord, "Test non-silent ANR");
- assertTrue(mProcessRecord.isNotResponding());
- assertFalse(mProcessRecord.isCrashing());
- assertFalse(mProcessRecord.killedByAm);
- assertFalse(mProcessRecord.killed);
+ appNotResponding(mProcessErrorState, "Test non-silent ANR");
+ assertTrue(mProcessErrorState.isNotResponding());
+ assertFalse(mProcessErrorState.isCrashing());
+ assertFalse(mProcessRecord.isKilledByAm());
+ assertFalse(mProcessRecord.isKilled());
}
/**
@@ -183,16 +185,17 @@
@Test
public void testSilentAnr() {
// Silent Anr will run through even without a parent process, and directly killed by AM.
- doReturn(true).when(mProcessRecord).isSilentAnr();
- appNotResponding(mProcessRecord, "Test silent ANR");
- assertTrue(mProcessRecord.isNotResponding());
- assertFalse(mProcessRecord.isCrashing());
- assertTrue(mProcessRecord.killedByAm);
- assertTrue(mProcessRecord.killed);
+ doReturn(true).when(mProcessErrorState).isSilentAnr();
+ appNotResponding(mProcessErrorState, "Test silent ANR");
+ assertTrue(mProcessErrorState.isNotResponding());
+ assertFalse(mProcessErrorState.isCrashing());
+ assertTrue(mProcessRecord.isKilledByAm());
+ assertTrue(mProcessRecord.isKilled());
}
- private static void appNotResponding(ProcessRecord processRecord, String annotation) {
- processRecord.appNotResponding(null /* activityShortComponentName */, null /* aInfo */,
+ private static void appNotResponding(ProcessErrorStateRecord processErrorState,
+ String annotation) {
+ processErrorState.appNotResponding(null /* activityShortComponentName */, null /* aInfo */,
null /* parentShortComponentName */, null /* parentProcess */,
false /* aboveSystem */, annotation, false /* onlyDumpSelf */);
}
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 45bca68..1328b91 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -16,16 +16,18 @@
package com.android.server.apphibernation;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.returnsArgAt;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.intThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
-import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
@@ -48,6 +50,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -76,18 +79,21 @@
private IActivityManager mIActivityManager;
@Mock
private UserManager mUserManager;
+ @Mock
+ private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore;
@Captor
private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
@Before
public void setUp() throws RemoteException {
+ // Share class loader to allow access to package-private classes
+ System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
- mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
- mIActivityManager, mUserManager);
+ mAppHibernationService = new AppHibernationService(new MockInjector(mContext));
- verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any());
+ verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
mBroadcastReceiver = mReceiverCaptor.getValue();
doReturn(mUserInfos).when(mUserManager).getUsers();
@@ -95,12 +101,19 @@
doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
anyInt(), anyBoolean(), anyBoolean(), any(), any());
- addUser(USER_ID_1);
+ List<PackageInfo> packages = new ArrayList<>();
+ packages.add(makePackageInfo(PACKAGE_NAME_1));
+ doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
+ intThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ UserInfo userInfo = addUser(USER_ID_1);
+ mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
}
@Test
- public void testSetHibernatingForUser_packageIsHibernating() throws RemoteException {
+ public void testSetHibernatingForUser_packageIsHibernating() {
// WHEN we hibernate a package for a user
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
@@ -109,8 +122,7 @@
}
@Test
- public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating()
- throws RemoteException {
+ public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating() {
// WHEN a new package is added and it is hibernated
Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
@@ -124,17 +136,12 @@
}
@Test
- public void testSetHibernatingForUser_newUserAdded_packageIsHibernating()
+ public void testSetHibernatingForUser_newUserUnlocked_packageIsHibernating()
throws RemoteException {
// WHEN a new user is added and a package from the user is hibernated
- List<PackageInfo> userPackages = new ArrayList<>();
- userPackages.add(makePackageInfo(PACKAGE_NAME_1));
- doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
- .getInstalledPackages(anyInt(), eq(USER_ID_2));
- Intent intent = new Intent(Intent.ACTION_USER_ADDED);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2);
- mBroadcastReceiver.onReceive(mContext, intent);
-
+ UserInfo user2 = addUser(USER_ID_2);
+ mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
// THEN the new user's package is hibernated
@@ -142,8 +149,7 @@
}
@Test
- public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating()
- throws RemoteException {
+ public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() {
// GIVEN a package is currently hibernated
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
@@ -168,25 +174,25 @@
}
/**
- * Add a mock user with one package. Must be called before
- * {@link AppHibernationService#onBootPhase(int)} to work properly.
+ * Add a mock user with one package.
*/
- private void addUser(int userId) throws RemoteException {
- addUser(userId, new String[]{PACKAGE_NAME_1});
+ private UserInfo addUser(int userId) throws RemoteException {
+ return addUser(userId, new String[]{PACKAGE_NAME_1});
}
/**
- * Add a mock user with the packages specified. Must be called before
- * {@link AppHibernationService#onBootPhase(int)} to work properly
+ * Add a mock user with the packages specified.
*/
- private void addUser(int userId, String[] packageNames) throws RemoteException {
- mUserInfos.add(new UserInfo(userId, "user_" + userId, 0 /* flags */));
+ private UserInfo addUser(int userId, String[] packageNames) throws RemoteException {
+ UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */);
+ mUserInfos.add(userInfo);
List<PackageInfo> userPackages = new ArrayList<>();
for (String pkgName : packageNames) {
userPackages.add(makePackageInfo(pkgName));
}
doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
- .getInstalledPackages(anyInt(), eq(userId));
+ .getInstalledPackages(intThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId));
+ return userInfo;
}
private static PackageInfo makePackageInfo(String packageName) {
@@ -194,4 +200,42 @@
pkg.packageName = packageName;
return pkg;
}
+
+ private class MockInjector implements AppHibernationService.Injector {
+ private final Context mContext;
+
+ MockInjector(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public IActivityManager getActivityManager() {
+ return mIActivityManager;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public IPackageManager getPackageManager() {
+ return mIPackageManager;
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mUserManager;
+ }
+
+ @Override
+ public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+ return Mockito.mock(HibernationStateDiskStore.class);
+ }
+
+ @Override
+ public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+ return Mockito.mock(HibernationStateDiskStore.class);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
new file mode 100644
index 0000000..59f3c35
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.FileUtils;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+
+@SmallTest
+public class HibernationStateDiskStoreTest {
+ private static final String STATES_FILE_NAME = "states";
+ private final MockScheduledExecutorService mMockScheduledExecutorService =
+ new MockScheduledExecutorService();
+
+ private File mFile;
+ private HibernationStateDiskStore<String> mHibernationStateDiskStore;
+
+
+ @Before
+ public void setUp() {
+ mFile = new File(InstrumentationRegistry.getContext().getCacheDir(), "test");
+ mHibernationStateDiskStore = new HibernationStateDiskStore<>(mFile,
+ new MockProtoReadWriter(), mMockScheduledExecutorService, STATES_FILE_NAME);
+ }
+
+ @After
+ public void tearDown() {
+ FileUtils.deleteContentsAndDir(mFile);
+ }
+
+ @Test
+ public void testScheduleWriteHibernationStates_writesDataThatCanBeRead() {
+ // GIVEN some data to be written
+ List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+
+ // WHEN the data is written
+ mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+ mMockScheduledExecutorService.executeScheduledTask();
+
+ // THEN the read data is equal to what was written
+ List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+ for (int i = 0; i < toWrite.size(); i++) {
+ assertEquals(toWrite.get(i), storedStrings.get(i));
+ }
+ }
+
+ @Test
+ public void testScheduleWriteHibernationStates_laterWritesOverwritePrevious() {
+ // GIVEN store has some data it is scheduled to write
+ mHibernationStateDiskStore.scheduleWriteHibernationStates(
+ new ArrayList<>(Arrays.asList("C", "D")));
+
+ // WHEN a write is scheduled with new data
+ List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+ mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+ mMockScheduledExecutorService.executeScheduledTask();
+
+ // THEN the written data is the last scheduled data
+ List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+ for (int i = 0; i < toWrite.size(); i++) {
+ assertEquals(toWrite.get(i), storedStrings.get(i));
+ }
+ }
+
+ /**
+ * Mock proto read / writer that just writes and reads a list of String data.
+ */
+ private final class MockProtoReadWriter implements ProtoReadWriter<List<String>> {
+ private static final long FIELD_ID = 1;
+
+ @Override
+ public void writeToProto(@NonNull ProtoOutputStream stream,
+ @NonNull List<String> data) {
+ for (int i = 0, size = data.size(); i < size; i++) {
+ stream.write(FIELD_ID, data.get(i));
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<String> readFromProto(@NonNull ProtoInputStream stream)
+ throws IOException {
+ ArrayList<String> list = new ArrayList<>();
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ list.add(stream.readString(FIELD_ID));
+ }
+ return list;
+ }
+ }
+
+ /**
+ * Mock scheduled executor service that has minimum implementation and can synchronously
+ * execute scheduled tasks.
+ */
+ private final class MockScheduledExecutorService implements ScheduledExecutorService {
+
+ Runnable mScheduledRunnable = null;
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ mScheduledRunnable = command;
+ return Mockito.mock(ScheduledFuture.class);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
+ long period, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
+ long delay, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void shutdown() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return false;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return false;
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
+ TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ throw new UnsupportedOperationException();
+ }
+
+ void executeScheduledTask() {
+ mScheduledRunnable.run();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8c853cc..7597cbf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5616,43 +5616,52 @@
public void testDisallowSharingIntoProfileSetRestriction() {
when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
.thenReturn("com.android.managedprovisioning");
+ when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+ .thenReturn(UserHandle.USER_SYSTEM);
+ mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+ mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
Bundle restriction = new Bundle();
restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
- mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mServiceContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, restriction, new Bundle());
- verifyDataSharingChangedBroadcast();
+
+ verifyDataSharingAppliedBroadcast();
}
@Test
public void testDisallowSharingIntoProfileClearRestriction() {
when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
.thenReturn("com.android.managedprovisioning");
+ when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+ .thenReturn(UserHandle.USER_SYSTEM);
+ mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+ mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
Bundle restriction = new Bundle();
restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
- mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mServiceContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), restriction);
- verifyDataSharingChangedBroadcast();
+
+ verifyDataSharingAppliedBroadcast();
}
@Test
public void testDisallowSharingIntoProfileUnchanged() {
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), new Bundle());
verify(mContext.spiedContext, never()).sendBroadcastAsUser(any(), any());
}
- private void verifyDataSharingChangedBroadcast() {
+ private void verifyDataSharingAppliedBroadcast() {
Intent expectedIntent = new Intent(
- DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
- expectedIntent.setPackage("com.android.managedprovisioning");
- expectedIntent.putExtra(Intent.EXTRA_USER_ID, CALLER_USER_HANDLE);
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED);
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntent(expectedIntent),
- MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 95aac60..1a22661 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -19,10 +19,14 @@
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertThrows;
+import android.hardware.devicestate.DeviceStateRequest;
import android.hardware.devicestate.IDeviceStateManagerCallback;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -33,6 +37,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
+import java.util.Optional;
+
import javax.annotation.Nullable;
/**
@@ -43,9 +50,9 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public final class DeviceStateManagerServiceTest {
- private static final int DEFAULT_DEVICE_STATE = 0;
- private static final int OTHER_DEVICE_STATE = 1;
- private static final int UNSUPPORTED_DEVICE_STATE = 999;
+ private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
+ private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(1, "OTHER");
+ private static final DeviceState UNSUPPORTED_DEVICE_STATE = new DeviceState(999, "UNSUPPORTED");
private TestDeviceStatePolicy mPolicy;
private TestDeviceStateProvider mProvider;
@@ -59,139 +66,106 @@
}
@Test
- public void requestStateChange() {
+ public void baseStateChanged() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
}
@Test
- public void requestStateChange_pendingState() {
+ public void baseStateChanged_withStatePendingPolicyCallback() {
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
+ mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
mPolicy.resumeConfigure();
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
- public void requestStateChange_unsupportedState() {
- mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE);
- assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
- }
-
- @Test
- public void requestStateChange_invalidState() {
+ public void baseStateChanged_unsupportedState() {
assertThrows(IllegalArgumentException.class, () -> {
- mProvider.notifyRequestState(INVALID_DEVICE_STATE);
+ mProvider.setState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
});
+
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
- public void requestOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE);
- // Committed state changes as there is a requested override.
- assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+ public void baseStateChanged_invalidState() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ mProvider.setState(INVALID_DEVICE_STATE);
+ });
- // Committed state is set back to the requested state once the override is cleared.
- mService.clearOverrideState();
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
- }
-
- @Test
- public void requestOverrideState_unsupportedState() {
- mService.setOverrideState(UNSUPPORTED_DEVICE_STATE);
- // Committed state remains the same as the override state is unsupported.
- assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
public void supportedStatesChanged() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
- mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
// The current committed and requests states do not change because the current state remains
// supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
}
@Test
- public void supportedStatesChanged_invalidState() {
- assertThrows(IllegalArgumentException.class, () -> {
- mProvider.notifySupportedDeviceStates(new int []{ INVALID_DEVICE_STATE });
- });
- }
-
- @Test
- public void supportedStatesChanged_unsupportedRequestedState() {
+ public void supportedStatesChanged_unsupportedBaseState() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
- mProvider.notifySupportedDeviceStates(new int []{ OTHER_DEVICE_STATE });
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE });
// The current requested state is cleared because it is no longer supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), INVALID_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState(), Optional.empty());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
- }
-
- @Test
- public void supportedStatesChanged_unsupportedOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE);
- // Committed state changes as there is a requested override.
- assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
-
- mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
-
- // Committed state is set back to the requested state as the override state is no longer
- // supported.
- assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
}
@Test
@@ -199,23 +173,27 @@
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertNotNull(callback.getLastNotifiedValue());
- assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE);
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
- assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
+ mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
// The callback should not have been notified of the state change as the policy is still
// pending callback.
- assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
mPolicy.resumeConfigure();
// Now that the policy is finished processing the callback should be notified of the state
// change.
- assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE);
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ OTHER_DEVICE_STATE.getIdentifier());
}
@Test
@@ -223,7 +201,150 @@
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
assertNotNull(callback.getLastNotifiedValue());
- assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void getSupportedDeviceStates() throws RemoteException {
+ final int[] expectedStates = new int[] { 0, 1 };
+ assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates);
+ }
+
+ @Test
+ public void requestState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+
+ mService.getBinderService().cancelRequest(token);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set back to the requested state once the override is cleared.
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_flagCancelWhenBaseChanges() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+ DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+
+ // Request is canceled because the base state changed.
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set back to the requested state once the override is cleared.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_becomesUnsupported() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
+
+ // Request is canceled because the state is no longer supported.
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set back to the requested state as the override state is no longer
+ // supported.
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_unsupportedState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestState(token,
+ UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestState_invalidState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestState(token, INVALID_DEVICE_STATE, 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestState_beforeRegisteringCallback() {
+ assertThrows(IllegalStateException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ });
}
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
@@ -275,9 +396,8 @@
}
private static final class TestDeviceStateProvider implements DeviceStateProvider {
- private int[] mSupportedDeviceStates = new int[]{ DEFAULT_DEVICE_STATE,
+ private DeviceState[] mSupportedDeviceStates = new DeviceState[]{ DEFAULT_DEVICE_STATE,
OTHER_DEVICE_STATE };
- private int mCurrentDeviceState = DEFAULT_DEVICE_STATE;
private Listener mListener;
@Override
@@ -288,32 +408,56 @@
mListener = listener;
mListener.onSupportedDeviceStatesChanged(mSupportedDeviceStates);
- mListener.onStateChanged(mCurrentDeviceState);
+ mListener.onStateChanged(mSupportedDeviceStates[0].getIdentifier());
}
- public void notifySupportedDeviceStates(int[] supportedDeviceStates) {
+ public void notifySupportedDeviceStates(DeviceState[] supportedDeviceStates) {
mSupportedDeviceStates = supportedDeviceStates;
mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
}
- public void notifyRequestState(int state) {
- mCurrentDeviceState = state;
- mListener.onStateChanged(state);
+ public void setState(int identifier) {
+ mListener.onStateChanged(identifier);
}
}
private static final class TestDeviceStateManagerCallback extends
IDeviceStateManagerCallback.Stub {
- Integer mLastNotifiedValue;
+ public static final int STATUS_UNKNOWN = 0;
+ public static final int STATUS_ACTIVE = 1;
+ public static final int STATUS_SUSPENDED = 2;
+ public static final int STATUS_CANCELED = 3;
+
+ private Integer mLastNotifiedValue;
+ private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>();
@Override
public void onDeviceStateChanged(int deviceState) {
mLastNotifiedValue = deviceState;
}
+ @Override
+ public void onRequestActive(IBinder token) {
+ mLastNotifiedStatus.put(token, STATUS_ACTIVE);
+ }
+
+ @Override
+ public void onRequestSuspended(IBinder token) {
+ mLastNotifiedStatus.put(token, STATUS_SUSPENDED);
+ }
+
+ @Override
+ public void onRequestCanceled(IBinder token) {
+ mLastNotifiedStatus.put(token, STATUS_CANCELED);
+ }
+
@Nullable
Integer getLastNotifiedValue() {
return mLastNotifiedValue;
}
+
+ int getLastNotifiedStatus(IBinder requestToken) {
+ return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index ae966aa..f0b4f1b 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -227,6 +227,31 @@
}
@Test
+ public void testPhysicalStrategyRecalculateSplines() {
+ Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
+ BACKLIGHT_RANGE);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
+ float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length];
+ for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) {
+ adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f;
+ }
+
+ // Default is unadjusted
+ assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
+ assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+
+ // When adjustment is turned on, adjustment array is used
+ strategy.recalculateSplines(true, adjustedNits50p);
+ assertEquals(1.3425f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
+ assertEquals(239.25f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+
+ // When adjustment is turned off, adjustment array is ignored
+ strategy.recalculateSplines(false, adjustedNits50p);
+ assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
+ assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
+ }
+
+ @Test
public void testDefaultStrategyIsPhysical() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index e02a46af..d362791 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -335,6 +335,9 @@
assertEquals(0.5, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
assertEquals(3333, event.colorTemperature);
+ assertTrue(event.reduceBrightColors);
+ assertEquals(40, event.reduceBrightColorsStrength);
+ assertEquals(20f, event.reduceBrightColorsOffset, FLOAT_DELTA);
assertEquals("a.package", event.packageName);
assertEquals(0, event.userId);
assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
@@ -561,6 +564,9 @@
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
+ mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+ mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 40);
+
startTracker(mTracker);
mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
batteryChangeEvent(30, 100));
@@ -592,6 +598,9 @@
assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
assertEquals(3339, event.colorTemperature);
+ assertTrue(event.reduceBrightColors);
+ assertEquals(40, event.reduceBrightColorsStrength);
+ assertEquals(20f, event.reduceBrightColorsOffset, FLOAT_DELTA);
assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
assertTrue(event.isUserSetBrightness);
assertFalse(event.isDefaultBrightnessConfig);
@@ -606,6 +615,9 @@
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
+ mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+ mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 40);
+
startTracker(mTracker);
mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
batteryChangeEvent(30, 100));
@@ -639,6 +651,7 @@
assertEquals(brightness, event.brightness, FLOAT_DELTA);
assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
+ assertTrue(event.reduceBrightColors);
assertEquals(3339, event.colorTemperature);
}
@@ -661,6 +674,9 @@
builder.setBatteryLevel(0.7f);
builder.setNightMode(false);
builder.setColorTemperature(345);
+ builder.setReduceBrightColors(false);
+ builder.setReduceBrightColorsStrength(40);
+ builder.setReduceBrightColorsOffset(20f);
builder.setLastBrightness(50f);
builder.setColorValues(new long[] {23, 34, 45}, 1000L);
BrightnessChangeEvent event = builder.build();
@@ -684,6 +700,9 @@
assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA);
assertEquals(event.nightMode, event2.nightMode);
assertEquals(event.colorTemperature, event2.colorTemperature);
+ assertEquals(event.reduceBrightColors, event2.reduceBrightColors);
+ assertEquals(event.reduceBrightColorsStrength, event2.reduceBrightColorsStrength);
+ assertEquals(event.reduceBrightColorsOffset, event2.reduceBrightColorsOffset, FLOAT_DELTA);
assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA);
assertArrayEquals(event.colorValueBuckets, event2.colorValueBuckets);
assertEquals(event.colorSampleDuration, event2.colorSampleDuration);
@@ -1020,6 +1039,18 @@
}
@Override
+ public int getReduceBrightColorsStrength(Context context) {
+ return mSecureIntSettings.getOrDefault(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL,
+ 0);
+ }
+
+ @Override
+ public boolean isReduceBrightColorsActivated(Context context) {
+ return mSecureIntSettings.getOrDefault(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+ 0) == 1;
+ }
+
+ @Override
public DisplayedContentSample sampleColor(int noFramesToSample) {
return new DisplayedContentSample(600L,
null,
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
new file mode 100644
index 0000000..35014dc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.color;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ReduceBrightColorsTintControllerTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ final Resources mockResources = mock(Resources.class);
+ when(mockResources.getStringArray(
+ com.android.internal.R.array.config_reduceBrightColorsCoefficients))
+ .thenReturn(new String[]{"-0.000000000000001", "-0.955555555555554",
+ "1.000000000000000"});
+ when(mockResources.getStringArray(
+ com.android.internal.R.array.config_reduceBrightColorsCoefficientsNonlinear))
+ .thenReturn(new String[]{"-0.4429953456", "-0.2434077725", "0.9809063061"});
+ mContext = mock(Context.class);
+ when(mContext.getResources()).thenReturn(mockResources);
+ }
+
+ @Test
+ public void setAndGetMatrix() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(50);
+ tintController.setActivated(true);
+ assertThat(tintController.getStrength()).isEqualTo(50);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 0.5222222f, 0f, 0f, 0f,
+ 0f, 0.5222222f, 0f, 0f,
+ 0f, 0f, 0.5222222f, 0f,
+ 0f, 0f, 0f, 1f)
+ .inOrder();
+ }
+
+ @Test
+ public void setAndGetMatrixClampToZero() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(-50);
+ tintController.setActivated(true);
+ assertThat(tintController.getStrength()).isEqualTo(0);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 1f, 0f, 0f, 0f,
+ 0f, 1f, 0f, 0f,
+ 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f)
+ .inOrder();
+ }
+
+ @Test
+ public void setAndGetMatrixClampTo100() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(120);
+ tintController.setActivated(true);
+ assertThat(tintController.getStrength()).isEqualTo(100);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 0.04444444f, 0f, 0f, 0f,
+ 0f, 0.04444444f, 0f, 0f,
+ 0f, 0f, 0.04444444f, 0f,
+ 0f, 0f, 0f, 1f)
+ .inOrder();
+ }
+
+ @Test
+ public void returnsIdentityMatrixWhenNotActivated() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(50);
+ tintController.setActivated(true);
+ tintController.setActivated(false);
+ assertThat(tintController.getStrength()).isEqualTo(50);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 1f, 0f, 0f, 0f,
+ 0f, 1f, 0f, 0f,
+ 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f)
+ .inOrder();
+ }
+
+ @Test
+ public void getAdjustedBrightnessZeroRbcStrengthFullBrightness() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(0);
+ assertThat(tintController.getAdjustedBrightness(450f)).isEqualTo(450f);
+ }
+
+ @Test
+ public void getAdjustedBrightnessFullRbcStrengthFullBrightness() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(100);
+ assertThat(tintController.getAdjustedBrightness(450f)).isEqualTo(19.999998f);
+ }
+
+ @Test
+ public void getAdjustedBrightnessZeroRbcStrengthLowBrightness() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(0);
+ assertThat(tintController.getAdjustedBrightness(2.2f)).isEqualTo(2.2f);
+ }
+
+ @Test
+ public void getAdjustedBrightnessFullRbcStrengthLowBrightness() {
+ final ReduceBrightColorsTintController tintController =
+ new ReduceBrightColorsTintController();
+ tintController.setUp(mContext, /* needsLinear= */ true);
+ tintController.setMatrix(100);
+ assertThat(tintController.getAdjustedBrightness(2.2f)).isEqualTo(0.09777778f);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
index c10cee9..86054e4 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
@@ -42,6 +42,8 @@
long expectedModifiedDate = 1234567890;
PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
config.lastModifiedDate = expectedModifiedDate;
+ config.updatedFontDirs.add("~~abc");
+ config.updatedFontDirs.add("~~def");
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
PersistentSystemFontConfig.writeToXml(baos, config);
@@ -54,6 +56,7 @@
PersistentSystemFontConfig.loadFromXml(bais, another);
assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate);
+ assertThat(another.updatedFontDirs).containsExactly("~~abc", "~~def");
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 8331031..cb83b0f 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -23,7 +23,9 @@
import android.content.Context;
import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontUpdateRequest;
import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
import android.system.Os;
@@ -37,11 +39,12 @@
import org.junit.runner.RunWith;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -150,13 +153,13 @@
dirForPreparation.loadFontFileMap();
assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
.isEqualTo(expectedModifiedDate);
- installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+ dirForPreparation.update(Arrays.asList(
+ newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
- //
assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
.isNotEqualTo(expectedModifiedDate);
@@ -191,10 +194,11 @@
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
dirForPreparation.loadFontFileMap();
- installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+ dirForPreparation.update(Arrays.asList(
+ newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
@@ -217,10 +221,11 @@
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
dirForPreparation.loadFontFileMap();
- installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+ dirForPreparation.update(Arrays.asList(
+ newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
@@ -244,10 +249,11 @@
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
dirForPreparation.loadFontFileMap();
- installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
- installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
+ dirForPreparation.update(Arrays.asList(
+ newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
@@ -282,6 +288,34 @@
}
@Test
+ public void construct_afterBatchFailure() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dirForPreparation = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ dirForPreparation.loadFontFileMap();
+ dirForPreparation.update(
+ Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+ try {
+ dirForPreparation.update(Arrays.asList(
+ newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,2", "Invalid signature")));
+ fail("Batch update with invalid signature should fail");
+ } catch (FontManagerService.SystemFontException e) {
+ // Expected
+ }
+
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ dir.loadFontFileMap();
+ // The state should be rolled back as a whole if one of the update requests fail.
+ assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
+ assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
+ }
+
+ @Test
public void installFontFile() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
@@ -290,7 +324,7 @@
mConfigFile);
dir.loadFontFileMap();
- installFontFile(dir, "test,1", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
assertThat(dir.getFontFileMap()).containsKey("test.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
File fontFile = dir.getFontFileMap().get("test.ttf");
@@ -308,9 +342,9 @@
mConfigFile);
dir.loadFontFileMap();
- installFontFile(dir, "test,1", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
- installFontFile(dir, "test,2", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
assertThat(dir.getFontFileMap()).containsKey("test.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
assertThat(mapBeforeUpgrade).containsKey("test.ttf");
@@ -327,9 +361,9 @@
mConfigFile);
dir.loadFontFileMap();
- installFontFile(dir, "test,2", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
try {
- installFontFile(dir, "test,1", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
fail("Expect IllegalArgumentException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -348,8 +382,26 @@
mConfigFile);
dir.loadFontFileMap();
- installFontFile(dir, "foo,1", GOOD_SIGNATURE);
- installFontFile(dir, "bar,2", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+ dir.update(Collections.singletonList(newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
+ assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
+ assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
+ assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
+ assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(2);
+ }
+
+ @Test
+ public void installFontFile_batch() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ dir.loadFontFileMap();
+
+ dir.update(Arrays.asList(
+ newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -366,7 +418,8 @@
dir.loadFontFileMap();
try {
- installFontFile(dir, "test,1", "Invalid signature");
+ dir.update(
+ Collections.singletonList(newFontUpdateRequest("test,1", "Invalid signature")));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
@@ -386,7 +439,7 @@
dir.loadFontFileMap();
try {
- installFontFile(dir, "test,1", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
fail("Expect IllegalArgumentException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -417,7 +470,8 @@
dir.loadFontFileMap();
try {
- installFontFile(dir, "test,2", GOOD_SIGNATURE);
+ dir.update(
+ Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
.isEqualTo(FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG);
@@ -449,7 +503,7 @@
dir.loadFontFileMap();
try {
- installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
@@ -477,7 +531,7 @@
dir.loadFontFileMap();
try {
- installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
@@ -513,7 +567,7 @@
dir.loadFontFileMap();
try {
- installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode())
@@ -522,13 +576,36 @@
assertThat(dir.getFontFileMap()).isEmpty();
}
- private void installFontFile(UpdatableFontDir dir, String content, String signature)
+ @Test
+ public void installFontFile_batchFailure() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ dir.loadFontFileMap();
+
+ dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+ try {
+ dir.update(Arrays.asList(
+ newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
+ newFontUpdateRequest("bar,2", "Invalid signature")));
+ fail("Batch update with invalid signature should fail");
+ } catch (FontManagerService.SystemFontException e) {
+ // Expected
+ }
+ // The state should be rolled back as a whole if one of the update requests fail.
+ assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
+ assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
+ }
+
+ private FontUpdateRequest newFontUpdateRequest(String content, String signature)
throws Exception {
File file = File.createTempFile("font", "ttf", mCacheDir);
FileUtils.stringToFile(file, content);
- try (FileInputStream in = new FileInputStream(file)) {
- dir.installFontFile(in.getFD(), signature.getBytes());
- }
+ return new FontUpdateRequest(
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY),
+ signature.getBytes());
}
private void writeConfig(PersistentSystemFontConfig.Config config,
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 6208801..263cf48 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -77,18 +77,19 @@
for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
}
}
for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
}
}
}
}
-
- private void startPendingJobs(Jobs jobs, int totalMax,
+ private void recount(Jobs jobs, int totalMax,
@NonNull List<Pair<Integer, Integer>> minLimits,
@NonNull List<Pair<Integer, Integer>> maxLimits) {
mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig(
@@ -113,7 +114,9 @@
}
mWorkCountTracker.onCountDone();
+ }
+ private void startPendingJobs(Jobs jobs) {
while ((jobs.pending.get(WORK_TYPE_TOP) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
|| (jobs.pending.get(WORK_TYPE_BG) > 0
@@ -151,7 +154,8 @@
jobs.maybeFinishJobs(stopRatio);
jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
- startPendingJobs(jobs, totalMax, minLimits, maxLimits);
+ recount(jobs, totalMax, minLimits, maxLimits);
+ startPendingJobs(jobs);
int totalRunning = 0;
for (int r = 0; r < jobs.running.size(); ++r) {
@@ -316,7 +320,8 @@
jobs.pending.put(pend.first, pend.second);
}
- startPendingJobs(jobs, totalMax, minLimits, maxLimits);
+ recount(jobs, totalMax, minLimits, maxLimits);
+ startPendingJobs(jobs);
for (Pair<Integer, Integer> run : resultRunning) {
assertWithMessage("Incorrect running result for work type " + run.first)
@@ -421,4 +426,81 @@
/* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
}
+
+ /** Tests that the counter updates properly when jobs are stopped. */
+ @Test
+ public void testJobLifecycleLoop() {
+ final Jobs jobs = new Jobs();
+ jobs.pending.put(WORK_TYPE_TOP, 11);
+ jobs.pending.put(WORK_TYPE_BG, 10);
+
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 1));
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 5));
+
+ recount(jobs, totalMax, minLimits, maxLimits);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(6);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9);
+
+ // Stop all jobs
+ jobs.maybeFinishJobs(1);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(8);
+
+ // Stop only a bg job and make sure the counter only allows another bg job to start.
+ jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_NONE);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(7);
+
+ // Stop only a top job and make sure the counter only allows another top job to start.
+ jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_NONE);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(5);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(7);
+
+ // Now that there are no more TOP jobs pending, BG should be able to start when TOP stops.
+ for (int i = jobs.running.get(WORK_TYPE_TOP); i > 0; --i) {
+ jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+ }
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(5);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(3);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index df19aeb..58ba907 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1829,11 +1829,11 @@
}
/**
- * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+ * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external
* conditions.
*/
@Test
- public void testIsUidNetworkingBlocked() {
+ public void testCheckUidNetworkingBlocked() {
final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
// Metered network. Data saver on.
@@ -1877,17 +1877,16 @@
private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
- final NetworkPolicyManagerInternal npmi = LocalServices
- .getService(NetworkPolicyManagerInternal.class);
for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
final boolean expectedResult = pair.first;
final int rule = pair.second;
assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
- expectedResult,
- npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+ expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule,
+ metered, backgroundRestricted));
assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
- npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+ mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered,
+ backgroundRestricted));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
new file mode 100644
index 0000000..764c504
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions;
+import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BundleUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest com.android.server.pm.BundleUtilsTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BundleUtilsTest {
+
+ @Test
+ public void testIsEmpty() {
+ assertThat(BundleUtils.isEmpty(null)).isTrue();
+ assertThat(BundleUtils.isEmpty(new Bundle())).isTrue();
+ assertThat(BundleUtils.isEmpty(newRestrictions("a"))).isFalse();
+ }
+
+ @Test
+ public void testClone() {
+ Bundle in = new Bundle();
+ Bundle out = BundleUtils.clone(in);
+ assertThat(in).isNotSameInstanceAs(out);
+ assertRestrictions(out, new Bundle());
+
+ out = BundleUtils.clone(null);
+ assertThat(out).isNotNull();
+ out.putBoolean("a", true); // Should not be Bundle.EMPTY.
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
index 9ba0967..7b9a00d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
@@ -71,12 +71,12 @@
@Before
public void setUp() {
mIncrementalStates = new IncrementalStates();
- assertFalse(mIncrementalStates.isStartable());
+ assertFalse(mIncrementalStates.getIncrementalStatesInfo().isStartable());
mIncrementalStates.setCallback(mCallback);
mIncrementalStates.onCommit(true);
// Test that package is now startable and loading
- assertTrue(mIncrementalStates.isStartable());
- assertTrue(mIncrementalStates.isLoading());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isLoading());
mUnstartableCalled.close();
mFullyLoadedCalled.close();
}
@@ -90,7 +90,7 @@
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get());
}
@@ -104,7 +104,7 @@
IStorageHealthListener.HEALTH_STATUS_READS_PENDING);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
}
/**
@@ -116,7 +116,7 @@
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
@@ -130,7 +130,7 @@
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
@@ -145,12 +145,12 @@
mIncrementalStates.setProgress(1.0f);
// Test that package is now fully loaded
assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isLoading());
+ assertFalse(mIncrementalStates.getIncrementalStatesInfo().isLoading());
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
}
/**
@@ -159,6 +159,6 @@
@Test
public void testStartableTransition_AppCrashOrAnr() {
mIncrementalStates.onCrashOrAnr();
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 90edaef..709b009 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -37,9 +37,10 @@
private KeySetManagerService mKsms;
public PackageSetting generateFakePackageSetting(String name) {
- return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
- "", "", "", "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
- null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/);
+ return new PackageSettingBuilder()
+ .setName(name)
+ .setCodePath(new File(mContext.getCacheDir(), "fakeCodePath").getAbsolutePath())
+ .build();
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 4ce1bbc..558fb30 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -107,10 +107,15 @@
// Create a real (non-null) PackageSetting and confirm that the removed
// users are copied properly
- setting = new PackageSetting("name", "realName", new File("codePath"),
- "legacyNativeLibraryPathString", "primaryCpuAbiString", "secondaryCpuAbiString",
- "cpuAbiOverrideString", 0, 0, 0, 0,
- null, null, null);
+ setting = new PackageSettingBuilder()
+ .setName("name")
+ .setRealName("realName")
+ .setCodePath("codePath")
+ .setLegacyNativeLibraryPathString("legacyNativeLibraryPathString")
+ .setPrimaryCpuAbiString("primaryCpuAbiString")
+ .setSecondaryCpuAbiString("secondaryCpuAbiString")
+ .setCpuAbiOverrideString("cpuAbiOverrideString")
+ .build();
pri.populateUsers(new int[] {
1, 2, 3, 4, 5
}, setting);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 333ec929..59458e8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -33,10 +33,10 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.PropertyInvalidatedCache;
-import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -59,6 +59,7 @@
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.server.LocalServices;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
@@ -80,6 +81,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -94,10 +96,14 @@
RuntimePermissionsPersistence mRuntimePermissionsPersistence;
@Mock
LegacyPermissionDataProvider mPermissionDataProvider;
+ @Mock
+ DomainVerificationManagerInternal mDomainVerificationManager;
@Before
public void initializeMocks() {
MockitoAnnotations.initMocks(this);
+ when(mDomainVerificationManager.generateNewId())
+ .thenAnswer(invocation -> UUID.randomUUID());
}
@Before
@@ -112,10 +118,7 @@
throws ReflectiveOperationException, IllegalAccessException {
/* write out files and read */
writeOldFiles();
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(),
- mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+ Settings settings = makeSettings();
assertThat(settings.readLPw(createFakeUsers()), is(true));
verifyKeySetMetaData(settings);
}
@@ -126,10 +129,7 @@
throws ReflectiveOperationException, IllegalAccessException {
// write out files and read
writeOldFiles();
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(),
- mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+ Settings settings = makeSettings();
assertThat(settings.readLPw(createFakeUsers()), is(true));
// write out, read back in and verify the same
@@ -142,10 +142,7 @@
public void testSettingsReadOld() {
// Write delegateshellthe package files and make sure they're parsed properly the first time
writeOldFiles();
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(),
- mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+ Settings settings = makeSettings();
assertThat(settings.readLPw(createFakeUsers()), is(true));
assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue()));
assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue()));
@@ -164,16 +161,12 @@
public void testNewPackageRestrictionsFile() throws ReflectiveOperationException {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(),
- mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+ Settings settings = makeSettings();
assertThat(settings.readLPw(createFakeUsers()), is(true));
settings.writeLPr();
// Create Settings again to make it read from the new files
- settings = new Settings(context.getFilesDir(),
- mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+ settings = makeSettings();
assertThat(settings.readLPw(createFakeUsers()), is(true));
PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2);
@@ -200,10 +193,7 @@
@Test
public void testReadPackageRestrictions_noSuspendingPackage() {
writePackageRestrictions_noSuspendingPackageXml(0);
- final Object lock = new Object();
- final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
- lock);
+ Settings settingsUnderTest = makeSettings();
final WatchableTester watcher =
new WatchableTester(settingsUnderTest, "noSuspendingPackage");
watcher.register();
@@ -244,10 +234,7 @@
@Test
public void testReadPackageRestrictions_noSuspendParamsMap() {
writePackageRestrictions_noSuspendParamsMapXml(0);
- final Object lock = new Object();
- final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
- lock);
+ final Settings settingsUnderTest = makeSettings();
final WatchableTester watcher =
new WatchableTester(settingsUnderTest, "noSuspendParamsMap");
watcher.register();
@@ -281,9 +268,7 @@
@Test
public void testReadWritePackageRestrictions_suspendInfo() {
- final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
- new Object());
+ final Settings settingsUnderTest = makeSettings();
final WatchableTester watcher = new WatchableTester(settingsUnderTest, "suspendInfo");
watcher.register();
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
@@ -397,9 +382,7 @@
@Test
public void testReadWritePackageRestrictions_distractionFlags() {
- final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
- new Object());
+ final Settings settingsUnderTest = makeSettings();
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3);
@@ -440,10 +423,7 @@
@Test
public void testWriteReadUsesStaticLibraries() {
- final Context context = InstrumentationRegistry.getTargetContext();
- final Object lock = new Object();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(),
- mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+ final Settings settingsUnderTest = makeSettings();
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
ps1.appId = Process.FIRST_APPLICATION_UID;
ps1.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
@@ -516,10 +496,7 @@
public void testEnableDisable() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- Settings settings = new Settings(context.getFilesDir(),
- mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+ Settings settings = makeSettings();
final WatchableTester watcher = new WatchableTester(settings, "testEnableDisable");
watcher.register();
assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -585,7 +562,8 @@
0,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
final PackageSetting testPkgSetting01 = new PackageSetting(origPkgSetting01);
verifySettingCopy(origPkgSetting01, testPkgSetting01);
}
@@ -606,7 +584,8 @@
0,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
final PackageSetting testPkgSetting01 = new PackageSetting(
PACKAGE_NAME /*pkgName*/,
REAL_PACKAGE_NAME /*realPkgName*/,
@@ -621,7 +600,8 @@
0,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
testPkgSetting01.copyFrom(origPkgSetting01);
verifySettingCopy(origPkgSetting01, testPkgSetting01);
}
@@ -648,7 +628,8 @@
UserManagerService.getInstance(),
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
assertThat(testPkgSetting01.pkgFlags, is(0));
@@ -681,7 +662,8 @@
UserManagerService.getInstance(),
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
@@ -698,12 +680,9 @@
/** Update package; changing shared user throws exception */
@Test
public void testUpdatePackageSetting03() {
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- final Settings testSettings01 = new Settings(context.getFilesDir(),
- mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+ Settings settings = makeSettings();
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
- testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
+ settings, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
final PackageSetting testPkgSetting01 =
createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
try {
@@ -720,7 +699,8 @@
UserManagerService.getInstance(),
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
fail("Expected a PackageManagerException");
} catch (PackageManagerException expected) {
}
@@ -752,7 +732,8 @@
UserManagerService.getInstance(),
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
@@ -790,7 +771,8 @@
UserManagerService.getInstance(),
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
assertThat(testPkgSetting01.appId, is(0));
assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
@@ -808,12 +790,9 @@
/** Create PackageSetting for a shared user */
@Test
public void testCreateNewSetting03() {
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- final Settings testSettings01 = new Settings(context.getFilesDir(),
- mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
+ Settings settings = makeSettings();
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
- testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
+ settings, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
final PackageSetting testPkgSetting01 = Settings.createNewSetting(
PACKAGE_NAME,
null /*originalPkg*/,
@@ -834,7 +813,8 @@
UserManagerService.getInstance(),
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
assertThat(testPkgSetting01.appId, is(10064));
assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
@@ -875,7 +855,8 @@
UserManagerService.getInstance(),
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
assertThat(testPkgSetting01.appId, is(10064));
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
@@ -934,6 +915,7 @@
assertThat(origPkgSetting.getPathString(), is(testPkgSetting.getPathString()));
assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
+ assertThat(origPkgSetting.getDomainSetId(), is(testPkgSetting.getDomainSetId()));
assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
assertSame(origPkgSetting.installSource, testPkgSetting.installSource);
assertThat(origPkgSetting.installPermissionsFixed,
@@ -976,8 +958,6 @@
assertNotSame(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
// No equals() method for SparseArray object
// assertThat(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
- assertSame(origPkgSetting.verificationInfo, testPkgSetting.verificationInfo);
- assertThat(origPkgSetting.verificationInfo, is(testPkgSetting.verificationInfo));
assertThat(origPkgSetting.versionCode, is(testPkgSetting.versionCode));
assertSame(origPkgSetting.volumeUuid, testPkgSetting.volumeUuid);
assertThat(origPkgSetting.volumeUuid, is(testPkgSetting.volumeUuid));
@@ -1006,7 +986,8 @@
sharedUserId,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
}
private PackageSetting createPackageSetting(String packageName) {
@@ -1024,7 +1005,8 @@
0,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/);
+ null /*mimeGroups*/,
+ UUID.randomUUID());
}
private @NonNull List<UserInfo> createFakeUsers() {
@@ -1212,6 +1194,12 @@
deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir());
}
+ private Settings makeSettings() {
+ return new Settings(InstrumentationRegistry.getContext().getFilesDir(),
+ mRuntimePermissionsPersistence, mPermissionDataProvider,
+ mDomainVerificationManager, new Object());
+ }
+
private void verifyKeySetMetaData(Settings settings)
throws ReflectiveOperationException, IllegalAccessException {
ArrayMap<String, PackageSetting> packages =
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 90c2982..e6a238a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -546,12 +546,17 @@
}
private static PackageSetting mockPkgSetting(AndroidPackage pkg) {
- return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(),
- new File(pkg.getPath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
- null, pkg.getVersionCode(),
- PackageInfoUtils.appInfoFlags(pkg, null),
- PackageInfoUtils.appInfoPrivateFlags(pkg, null),
- pkg.getSharedUserLabel(), null, null, null);
+ return new PackageSettingBuilder()
+ .setName(pkg.getPackageName())
+ .setRealName(pkg.getRealPackage())
+ .setCodePath(pkg.getPath())
+ .setPrimaryCpuAbiString(pkg.getPrimaryCpuAbi())
+ .setSecondaryCpuAbiString(pkg.getSecondaryCpuAbi())
+ .setPVersionCode(pkg.getLongVersionCode())
+ .setPkgFlags(PackageInfoUtils.appInfoFlags(pkg, null))
+ .setPrivateFlags(PackageInfoUtils.appInfoPrivateFlags(pkg, null))
+ .setSharedUserId(pkg.getSharedUserLabel())
+ .build();
}
// NOTE: The equality assertions below are based on code autogenerated by IntelliJ.
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 84551c5..f75751b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -22,10 +22,10 @@
import android.util.SparseArray;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.PackageImpl;
import java.io.File;
import java.util.Map;
+import java.util.UUID;
public class PackageSettingBuilder {
private String mName;
@@ -48,6 +48,7 @@
private long[] mUsesStaticLibrariesVersions;
private Map<String, ArraySet<String>> mMimeGroups;
private PackageParser.SigningDetails mSigningDetails;
+ private UUID mDomainSetId = UUID.randomUUID();
public PackageSettingBuilder setPackage(AndroidPackage pkg) {
this.mPkg = pkg;
@@ -163,12 +164,17 @@
return this;
}
+ public PackageSettingBuilder setDomainSetId(UUID domainSetId) {
+ mDomainSetId = domainSetId;
+ return this;
+ }
+
public PackageSetting build() {
final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString,
mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags,
mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions,
- mMimeGroups);
+ mMimeGroups, mDomainSetId);
packageSetting.signatures = mSigningDetails != null
? new PackageSignatures(mSigningDetails)
: new PackageSignatures();
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 90658055..27f3eec 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -465,10 +465,11 @@
private static PackageSetting createPackageSetting() {
// Generic PackageSetting object with values from a test app installed on a device to be
// used to test the methods under the PackageSignatures signatures data member.
- File appPath = new File("/data/app/app");
- PackageSetting result = new PackageSetting("test.app", null, appPath,
- "/data/app/app", null, null, null, 1, 940097092, 0, 0 /*userId*/, null, null,
- null /*mimeGroups*/);
- return result;
+ return new PackageSettingBuilder()
+ .setName("test.app")
+ .setCodePath("/data/app/app")
+ .setPVersionCode(1)
+ .setPkgFlags(940097092)
+ .build();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 1cfbad9..938e4cc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -53,18 +53,10 @@
assertThat(testUserState.equals(oldUserState), is(true));
oldUserState = new PackageUserState();
- oldUserState.appLinkGeneration = 6;
- assertThat(testUserState.equals(oldUserState), is(false));
-
- oldUserState = new PackageUserState();
oldUserState.ceDataInode = 4000L;
assertThat(testUserState.equals(oldUserState), is(false));
oldUserState = new PackageUserState();
- oldUserState.domainVerificationStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
- assertThat(testUserState.equals(oldUserState), is(false));
-
- oldUserState = new PackageUserState();
oldUserState.enabled = COMPONENT_ENABLED_STATE_ENABLED;
assertThat(testUserState.equals(oldUserState), is(false));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index d8c3979..b5add84 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -50,6 +50,7 @@
import android.util.Pair;
import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
@@ -91,6 +92,14 @@
when(mMockInjector.getAbiHelper()).thenReturn(mMockPackageAbiHelper);
when(mMockInjector.getUserManagerInternal()).thenReturn(mMockUserManager);
when(mMockInjector.getCompatibility()).thenReturn(mMockCompatibility);
+
+ DomainVerificationManagerInternal domainVerificationManager =
+ mock(DomainVerificationManagerInternal.class);
+ when(domainVerificationManager.generateNewId())
+ .thenAnswer(invocation -> UUID.randomUUID());
+
+ when(mMockInjector.getDomainVerificationManagerInternal())
+ .thenReturn(domainVerificationManager);
}
@Before
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index e6c3d7c..b21b049 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -18,6 +18,7 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.array;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertHaveIds;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
@@ -25,6 +26,8 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resultContains;
import android.content.ComponentName;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -47,6 +50,9 @@
*/
@SmallTest
public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
+
+ private static final int CACHE_OWNER = LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
+
private List<String> callShellCommand(String... args) throws IOException, RemoteException {
// For reset to work, the current time needs to be incrementing.
@@ -215,11 +221,13 @@
// This command is deprecated. Will remove the test later.
public void testLauncherCommands() throws Exception {
+ prepareGetRoleHoldersAsUser(getSystemLauncher().activityInfo.packageName, USER_0);
prepareGetHomeActivitiesAsUser(
/* preferred */ getSystemLauncher().activityInfo.getComponentName(),
list(getSystemLauncher(), getFallbackLauncher()),
USER_0);
+ prepareGetRoleHoldersAsUser(CALLING_PACKAGE_2, USER_10);
prepareGetHomeActivitiesAsUser(
/* preferred */ cn(CALLING_PACKAGE_2, "name"),
list(getSystemLauncher(), getFallbackLauncher(),
@@ -241,6 +249,7 @@
"Launcher: ComponentInfo{com.android.test.2/name}");
// Change user-0's launcher.
+ prepareGetRoleHoldersAsUser(CALLING_PACKAGE_1, USER_0);
prepareGetHomeActivitiesAsUser(
/* preferred */ cn(CALLING_PACKAGE_1, "name"),
list(ri(CALLING_PACKAGE_1, "name", false, 0)),
@@ -323,6 +332,72 @@
});
}
+ public void testGetShortcuts() throws Exception {
+
+ mRunningUsers.put(USER_10, true);
+
+ // Add two manifests and two dynamics.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.addDynamicShortcuts(list(
+ makeLongLivedShortcut("s1"), makeShortcut("s2"))));
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_10,
+ CACHE_OWNER);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllEnabled()
+
+ .selectPinned()
+ .haveIds("ms2", "s2");
+ });
+
+
+ mRunningUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_10, true);
+
+ mInjectedCallingUid = Process.SHELL_UID;
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1),
+ "s1");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC), CALLING_PACKAGE_1),
+ "s1", "s2");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST), CALLING_PACKAGE_1),
+ "ms1", "ms2");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1),
+ "ms2", "s2");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC
+ | ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1),
+ "ms2", "s1", "s2");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST
+ | ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1),
+ "ms1", "ms2", "s1");
+
+ }
+
public void testDumpsysArgs() {
checkDumpsysArgs(null, true, false, false);
checkDumpsysArgs(array("-u"), true, true, false);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index ee30f68..cd98d44 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -72,11 +72,18 @@
@Test
public void testUserTypeBuilder_createUserType() {
final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
+ final Bundle systemSettings = makeSettingsBundle("s1", "s2");
+ final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
+ final List<DefaultCrossProfileIntentFilter> filters = List.of(
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */false).build());
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
.setEnabled(true)
.setMaxAllowed(21)
- .setBaseType(FLAG_FULL)
+ .setBaseType(FLAG_PROFILE)
.setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
.setBadgeLabels(23, 24, 25)
.setBadgeColors(26, 27)
@@ -86,20 +93,45 @@
.setLabel(31)
.setMaxAllowedPerParent(32)
.setDefaultRestrictions(restrictions)
+ .setDefaultSystemSettings(systemSettings)
+ .setDefaultSecureSettings(secureSettings)
+ .setDefaultCrossProfileIntentFilters(filters)
.createUserTypeDetails();
assertEquals("a.name", type.getName());
assertTrue(type.isEnabled());
assertEquals(21, type.getMaxAllowed());
- assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
+ assertEquals(FLAG_PROFILE | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
assertEquals(28, type.getIconBadge());
assertEquals(29, type.getBadgePlain());
assertEquals(30, type.getBadgeNoBackground());
assertEquals(31, type.getLabel());
assertEquals(32, type.getMaxAllowedPerParent());
+
assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
assertNotSame(restrictions, type.getDefaultRestrictions());
+ assertNotSame(systemSettings, type.getDefaultSystemSettings());
+ assertEquals(systemSettings.size(), type.getDefaultSystemSettings().size());
+ for (String key : systemSettings.keySet()) {
+ assertEquals(
+ systemSettings.getString(key),
+ type.getDefaultSystemSettings().getString(key));
+ }
+
+ assertNotSame(secureSettings, type.getDefaultSecureSettings());
+ assertEquals(secureSettings.size(), type.getDefaultSecureSettings().size());
+ for (String key : secureSettings.keySet()) {
+ assertEquals(
+ secureSettings.getString(key),
+ type.getDefaultSecureSettings().getString(key));
+ }
+
+ assertNotSame(filters, type.getDefaultCrossProfileIntentFilters());
+ assertEquals(filters.size(), type.getDefaultCrossProfileIntentFilters().size());
+ for (int i = 0; i < filters.size(); i++) {
+ assertEquals(filters.get(i), type.getDefaultCrossProfileIntentFilters().get(i));
+ }
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -135,6 +167,9 @@
assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
assertEquals(Resources.ID_NULL, type.getLabel());
assertTrue(type.getDefaultRestrictions().isEmpty());
+ assertTrue(type.getDefaultSystemSettings().isEmpty());
+ assertTrue(type.getDefaultSecureSettings().isEmpty());
+ assertTrue(type.getDefaultCrossProfileIntentFilters().isEmpty());
assertFalse(type.hasBadge());
}
@@ -416,4 +451,13 @@
}
return bundle;
}
+
+ /** Creates a Bundle of the given settings keys and puts true for the value. */
+ private static Bundle makeSettingsBundle(String ... settings) {
+ final Bundle bundle = new Bundle();
+ for (String setting : settings) {
+ bundle.putBoolean(setting, true);
+ }
+ return bundle;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index dc181a9..ddf0cd0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -48,23 +48,6 @@
assertSame(in, UserRestrictionsUtils.nonNull(in));
}
- public void testIsEmpty() {
- assertTrue(UserRestrictionsUtils.isEmpty(null));
- assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
- assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a")));
- }
-
- public void testClone() {
- Bundle in = new Bundle();
- Bundle out = UserRestrictionsUtils.clone(in);
- assertNotSame(in, out);
- assertRestrictions(out, new Bundle());
-
- out = UserRestrictionsUtils.clone(null);
- assertNotNull(out);
- out.putBoolean("a", true); // Should not be Bundle.EMPTY.
- }
-
public void testMerge() {
Bundle a = newRestrictions("a", "d");
Bundle b = newRestrictions("b", "d", "e");
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 0bea584..4f36c8a 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -40,6 +40,7 @@
import androidx.annotation.NonNull;
import com.android.server.LocalServices;
+import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
import org.junit.After;
@@ -61,7 +62,8 @@
* Run with <code>atest DeviceStateProviderImplTest</code>.
*/
public final class DeviceStateProviderImplTest {
- private final ArgumentCaptor<int[]> mIntArrayCaptor = ArgumentCaptor.forClass(int[].class);
+ private final ArgumentCaptor<DeviceState[]> mDeviceStateArrayCaptor = ArgumentCaptor.forClass(
+ DeviceState[].class);
private final ArgumentCaptor<Integer> mIntegerCaptor = ArgumentCaptor.forClass(Integer.class);
private Context mContext;
@@ -120,11 +122,12 @@
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
provider.setListener(listener);
- verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
- assertArrayEquals(new int[] { DEFAULT_DEVICE_STATE }, mIntArrayCaptor.getValue());
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ assertArrayEquals(new DeviceState[]{DEFAULT_DEVICE_STATE},
+ mDeviceStateArrayCaptor.getValue());
verify(listener).onStateChanged(mIntegerCaptor.capture());
- assertEquals(DEFAULT_DEVICE_STATE, mIntegerCaptor.getValue().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE.getIdentifier(), mIntegerCaptor.getValue().intValue());
}
@Test
@@ -146,8 +149,10 @@
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
provider.setListener(listener);
- verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
- assertArrayEquals(new int[] { 1, 2 }, mIntArrayCaptor.getValue());
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, null),
+ new DeviceState(2, null) };
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(1, mIntegerCaptor.getValue().intValue());
@@ -166,6 +171,7 @@
+ " </device-state>\n"
+ " <device-state>\n"
+ " <identifier>2</identifier>\n"
+ + " <name>CLOSED</name>\n"
+ " <conditions>\n"
+ " <lid-switch>\n"
+ " <open>false</open>\n"
@@ -180,8 +186,10 @@
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
provider.setListener(listener);
- verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
- assertArrayEquals(new int[] { 1, 2 }, mIntArrayCaptor.getValue());
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, null),
+ new DeviceState(2, "CLOSED") };
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(2, mIntegerCaptor.getValue().intValue());
@@ -189,8 +197,7 @@
Mockito.clearInvocations(listener);
provider.notifyLidSwitchChanged(0, true /* lidOpen */);
-
- verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(1, mIntegerCaptor.getValue().intValue());
}
@@ -203,6 +210,7 @@
String configString = "<device-state-config>\n"
+ " <device-state>\n"
+ " <identifier>1</identifier>\n"
+ + " <name>CLOSED</name>\n"
+ " <conditions>\n"
+ " <sensor>\n"
+ " <type>" + sensor.getStringType() + "</type>\n"
@@ -215,6 +223,7 @@
+ " </device-state>\n"
+ " <device-state>\n"
+ " <identifier>2</identifier>\n"
+ + " <name>HALF_OPENED</name>\n"
+ " <conditions>\n"
+ " <sensor>\n"
+ " <type>" + sensor.getStringType() + "</type>\n"
@@ -228,6 +237,7 @@
+ " </device-state>\n"
+ " <device-state>\n"
+ " <identifier>3</identifier>\n"
+ + " <name>OPENED</name>\n"
+ " <conditions>\n"
+ " <sensor>\n"
+ " <type>" + sensor.getStringType() + "</type>\n"
@@ -246,8 +256,10 @@
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
provider.setListener(listener);
- verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
- assertArrayEquals(new int[] { 1, 2, 3 }, mIntArrayCaptor.getValue());
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ assertArrayEquals(
+ new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"),
+ new DeviceState(3, "OPENED") }, mDeviceStateArrayCaptor.getValue());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(1, mIntegerCaptor.getValue().intValue());
@@ -256,11 +268,11 @@
SensorEvent event0 = mock(SensorEvent.class);
event0.sensor = sensor;
- FieldSetter.setField(event0, event0.getClass().getField("values"), new float[] { 180 });
+ FieldSetter.setField(event0, event0.getClass().getField("values"), new float[]{180});
provider.onSensorChanged(event0);
- verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(3, mIntegerCaptor.getValue().intValue());
@@ -268,11 +280,11 @@
SensorEvent event1 = mock(SensorEvent.class);
event1.sensor = sensor;
- FieldSetter.setField(event1, event1.getClass().getField("values"), new float[] { 90 });
+ FieldSetter.setField(event1, event1.getClass().getField("values"), new float[]{90});
provider.onSensorChanged(event1);
- verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(2, mIntegerCaptor.getValue().intValue());
@@ -280,11 +292,11 @@
SensorEvent event2 = mock(SensorEvent.class);
event2.sensor = sensor;
- FieldSetter.setField(event2, event2.getClass().getField("values"), new float[] { 0 });
+ FieldSetter.setField(event2, event2.getClass().getField("values"), new float[]{0});
provider.onSensorChanged(event2);
- verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture());
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
verify(listener).onStateChanged(mIntegerCaptor.capture());
assertEquals(1, mIntegerCaptor.getValue().intValue());
}
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 84b690f..03e60af 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.PowerEntity;
@@ -73,6 +74,7 @@
private static final String ENERGY_CONSUMER_NAME = "energyconsumer";
private static final int ENERGY_METER_COUNT = 8;
private static final int ENERGY_CONSUMER_COUNT = 2;
+ private static final int ENERGY_CONSUMER_ATTRIBUTION_COUNT = 5;
private static final int POWER_ENTITY_COUNT = 3;
private static final int STATE_INFO_COUNT = 5;
private static final int STATE_RESIDENCY_COUNT = 4;
@@ -204,6 +206,13 @@
energyConsumedList[i].id = i;
energyConsumedList[i].timestampMs = i;
energyConsumedList[i].energyUWs = i;
+ energyConsumedList[i].attribution =
+ new EnergyConsumerAttribution[ENERGY_CONSUMER_ATTRIBUTION_COUNT];
+ for (int j = 0; j < energyConsumedList[i].attribution.length; j++) {
+ energyConsumedList[i].attribution[j] = new EnergyConsumerAttribution();
+ energyConsumedList[i].attribution[j].uid = j;
+ energyConsumedList[i].attribution[j].energyUWs = j;
+ }
}
return energyConsumedList;
}
@@ -221,7 +230,7 @@
}
@Override
- public EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+ public EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
EnergyMeasurement[] energyMeasurementList = new EnergyMeasurement[ENERGY_METER_COUNT];
for (int i = 0; i < energyMeasurementList.length; i++) {
energyMeasurementList[i] = new EnergyMeasurement();
@@ -250,7 +259,7 @@
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Write data to on-device storage.
- mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER);
+ mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
// The above call puts a message on a handler. Wait for
// it to be processed.
@@ -293,7 +302,7 @@
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Write data to on-device storage.
- mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER);
+ mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
// The above call puts a message on a handler. Wait for
// it to be processed.
@@ -324,6 +333,12 @@
assertTrue(pssProto.energyConsumerResult[i].id == i);
assertTrue(pssProto.energyConsumerResult[i].timestampMs == i);
assertTrue(pssProto.energyConsumerResult[i].energyUws == i);
+ assertTrue(pssProto.energyConsumerResult[i].attribution.length
+ == ENERGY_CONSUMER_ATTRIBUTION_COUNT);
+ for (int j = 0; j < pssProto.energyConsumerResult[i].attribution.length; j++) {
+ assertTrue(pssProto.energyConsumerResult[i].attribution[j].uid == j);
+ assertTrue(pssProto.energyConsumerResult[i].attribution[j].energyUws == j);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index c59ead9..6dcfb42 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -1123,10 +1123,10 @@
}
/**
- * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
- * like the real thing should, it also asserts preconditions.
+ * A fake implementation of {@link TimeDetectorStrategyImpl.Environment}. Besides tracking
+ * changes and behaving like the real thing should, it also asserts preconditions.
*/
- private static class FakeCallback implements TimeDetectorStrategyImpl.Callback {
+ private static class FakeEnvironment implements TimeDetectorStrategyImpl.Environment {
private boolean mAutoTimeDetectionEnabled;
private boolean mWakeLockAcquired;
private long mElapsedRealtimeMillis;
@@ -1254,41 +1254,41 @@
*/
private class Script {
- private final FakeCallback mFakeCallback;
+ private final FakeEnvironment mFakeEnvironment;
private final TimeDetectorStrategyImpl mTimeDetectorStrategy;
Script() {
- mFakeCallback = new FakeCallback();
- mTimeDetectorStrategy = new TimeDetectorStrategyImpl(mFakeCallback);
+ mFakeEnvironment = new FakeEnvironment();
+ mTimeDetectorStrategy = new TimeDetectorStrategyImpl(mFakeEnvironment);
}
Script pokeAutoTimeDetectionEnabled(boolean enabled) {
- mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
+ mFakeEnvironment.pokeAutoTimeDetectionEnabled(enabled);
return this;
}
Script pokeFakeClocks(TimestampedValue<Instant> timeInfo) {
- mFakeCallback.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis());
- mFakeCallback.pokeSystemClockMillis(timeInfo.getValue().toEpochMilli());
+ mFakeEnvironment.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis());
+ mFakeEnvironment.pokeSystemClockMillis(timeInfo.getValue().toEpochMilli());
return this;
}
Script pokeThresholds(int systemClockUpdateThreshold) {
- mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
+ mFakeEnvironment.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
return this;
}
Script pokeAutoOriginPriorities(@Origin int... autoOriginPriorities) {
- mFakeCallback.pokeAutoOriginPriorities(autoOriginPriorities);
+ mFakeEnvironment.pokeAutoOriginPriorities(autoOriginPriorities);
return this;
}
long peekElapsedRealtimeMillis() {
- return mFakeCallback.peekElapsedRealtimeMillis();
+ return mFakeEnvironment.peekElapsedRealtimeMillis();
}
long peekSystemClockMillis() {
- return mFakeCallback.peekSystemClockMillis();
+ return mFakeEnvironment.peekSystemClockMillis();
}
Script simulateTelephonyTimeSuggestion(TelephonyTimeSuggestion timeSuggestion) {
@@ -1324,13 +1324,13 @@
}
Script simulateAutoTimeDetectionToggle() {
- mFakeCallback.simulateAutoTimeZoneDetectionToggle();
+ mFakeEnvironment.simulateAutoTimeZoneDetectionToggle();
mTimeDetectorStrategy.handleAutoTimeConfigChanged();
return this;
}
Script simulateTimePassing(long clockIncrementMillis) {
- mFakeCallback.simulateTimePassing(clockIncrementMillis);
+ mFakeEnvironment.simulateTimePassing(clockIncrementMillis);
return this;
}
@@ -1342,14 +1342,14 @@
}
Script verifySystemClockWasNotSetAndResetCallTracking() {
- mFakeCallback.verifySystemClockNotSet();
- mFakeCallback.resetCallTracking();
+ mFakeEnvironment.verifySystemClockNotSet();
+ mFakeEnvironment.resetCallTracking();
return this;
}
Script verifySystemClockWasSetAndResetCallTracking(long expectedSystemClockMillis) {
- mFakeCallback.verifySystemClockWasSet(expectedSystemClockMillis);
- mFakeCallback.resetCallTracking();
+ mFakeEnvironment.verifySystemClockWasSet(expectedSystemClockMillis);
+ mFakeEnvironment.resetCallTracking();
return this;
}
@@ -1427,7 +1427,7 @@
ManualTimeSuggestion generateManualTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new ManualTimeSuggestion(utcTime);
}
@@ -1461,7 +1461,7 @@
NetworkTimeSuggestion generateNetworkTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new NetworkTimeSuggestion(utcTime);
}
@@ -1473,7 +1473,7 @@
GnssTimeSuggestion generateGnssTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new GnssTimeSuggestion(utcTime);
}
@@ -1485,7 +1485,7 @@
ExternalTimeSuggestion generateExternalTimeSuggestion(Instant suggestedTime) {
TimestampedValue<Long> utcTime =
new TimestampedValue<>(
- mFakeCallback.peekElapsedRealtimeMillis(),
+ mFakeEnvironment.peekElapsedRealtimeMillis(),
suggestedTime.toEpochMilli());
return new ExternalTimeSuggestion(utcTime);
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index a6ffd20..c8dba5f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -167,15 +167,15 @@
createConfig(null, false /* geoDetection */);
private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
- private FakeCallback mFakeCallback;
+ private FakeEnvironment mFakeEnvironment;
private MockConfigChangeListener mMockConfigChangeListener;
@Before
public void setUp() {
- mFakeCallback = new FakeCallback();
+ mFakeEnvironment = new FakeEnvironment();
mMockConfigChangeListener = new MockConfigChangeListener();
- mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(mFakeCallback);
+ mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(mFakeEnvironment);
mTimeZoneDetectorStrategy.addConfigChangeListener(mMockConfigChangeListener);
}
@@ -183,7 +183,7 @@
public void testGetCurrentUserConfiguration() {
new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
ConfigurationInternal expectedConfiguration =
- mFakeCallback.getConfigurationInternal(USER_ID);
+ mFakeEnvironment.getConfigurationInternal(USER_ID);
assertEquals(expectedConfiguration,
mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal());
}
@@ -490,7 +490,7 @@
private void makeSlotIndex1SuggestionAndCheckState(Script script, TelephonyTestCase testCase) {
// Give the next suggestion a different zone from the currently set device time zone;
- String currentZoneId = mFakeCallback.getDeviceTimeZone();
+ String currentZoneId = mFakeEnvironment.getDeviceTimeZone();
String suggestionZoneId =
"Europe/London".equals(currentZoneId) ? "Europe/Paris" : "Europe/London";
TelephonyTimeZoneSuggestion zoneSlotIndex1Suggestion =
@@ -610,9 +610,9 @@
}
/**
- * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
- * zone is actually necessary. This test proves that the strategy doesn't assume it knows the
- * current settings.
+ * The {@link TimeZoneDetectorStrategyImpl.Environment} is left to detect whether changing the
+ * time zone is actually necessary. This test proves that the strategy doesn't assume it knows
+ * the current settings.
*/
@Test
public void testTelephonySuggestionStrategyDoesNotAssumeCurrentSetting_autoTelephony() {
@@ -958,7 +958,7 @@
return builder.build();
}
- static class FakeCallback implements TimeZoneDetectorStrategyImpl.Callback {
+ static class FakeEnvironment implements TimeZoneDetectorStrategyImpl.Environment {
private final TestState<ConfigurationInternal> mConfigurationInternal = new TestState<>();
private final TestState<String> mTimeZoneId = new TestState<>();
@@ -1051,12 +1051,12 @@
private class Script {
Script initializeConfig(ConfigurationInternal configuration) {
- mFakeCallback.initializeConfig(configuration);
+ mFakeEnvironment.initializeConfig(configuration);
return this;
}
Script initializeTimeZoneSetting(String zoneId) {
- mFakeCallback.initializeTimeZoneSetting(zoneId);
+ mFakeEnvironment.initializeTimeZoneSetting(zoneId);
return this;
}
@@ -1084,7 +1084,7 @@
Script simulateManualTimeZoneSuggestion(
@UserIdInt int userId, ManualTimeZoneSuggestion manualTimeZoneSuggestion,
boolean expectedResult) {
- mFakeCallback.assertKnownUser(userId);
+ mFakeEnvironment.assertKnownUser(userId);
boolean actualResult = mTimeZoneDetectorStrategy.suggestManualTimeZone(
userId, manualTimeZoneSuggestion);
assertEquals(expectedResult, actualResult);
@@ -1104,26 +1104,26 @@
* state was last reset.
*/
Script verifyTimeZoneNotChanged() {
- mFakeCallback.assertTimeZoneNotChanged();
+ mFakeEnvironment.assertTimeZoneNotChanged();
return this;
}
/** Verifies the device's time zone has been set and clears change tracking history. */
Script verifyTimeZoneChangedAndReset(String zoneId) {
- mFakeCallback.assertTimeZoneChangedTo(zoneId);
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.assertTimeZoneChangedTo(zoneId);
+ mFakeEnvironment.commitAllChanges();
return this;
}
Script verifyTimeZoneChangedAndReset(ManualTimeZoneSuggestion suggestion) {
- mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneId());
+ mFakeEnvironment.commitAllChanges();
return this;
}
Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) {
- mFakeCallback.assertTimeZoneChangedTo(suggestion.getZoneId());
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneId());
+ mFakeEnvironment.commitAllChanges();
return this;
}
@@ -1131,9 +1131,9 @@
* Verifies that the configuration has been changed to the expected value.
*/
Script verifyConfigurationChangedAndReset(ConfigurationInternal expected) {
- mFakeCallback.mConfigurationInternal.assertHasBeenSet();
- assertEquals(expected, mFakeCallback.mConfigurationInternal.getLatest());
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.mConfigurationInternal.assertHasBeenSet();
+ assertEquals(expected, mFakeEnvironment.mConfigurationInternal.getLatest());
+ mFakeEnvironment.commitAllChanges();
// Also confirm the listener triggered.
mMockConfigChangeListener.verifyOnChangeCalled();
@@ -1146,7 +1146,7 @@
* {@link TimeZoneConfiguration} have been changed.
*/
Script verifyConfigurationNotChanged() {
- mFakeCallback.mConfigurationInternal.assertHasNotBeenSet();
+ mFakeEnvironment.mConfigurationInternal.assertHasNotBeenSet();
// Also confirm the listener did not trigger.
mMockConfigChangeListener.verifyOnChangeNotCalled();
@@ -1154,7 +1154,7 @@
}
Script resetConfigurationTracking() {
- mFakeCallback.commitAllChanges();
+ mFakeEnvironment.commitAllChanges();
return this;
}
}
diff --git a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
index 4c82818..c6e35cf 100644
--- a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
+++ b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
@@ -26,21 +26,17 @@
object MockitoUtils {
val ANSWER_THROWS = Answer<Any?> {
when (val name = it.method.name) {
- "toString" -> return@Answer Answers.CALLS_REAL_METHODS.answer(it)
+ "toString" -> return@Answer try {
+ Answers.CALLS_REAL_METHODS.answer(it)
+ } catch (e: Exception) {
+ "failure calling toString"
+ }
else -> {
val arguments = it.arguments
?.takeUnless { it.isEmpty() }
- ?.mapIndexed { index, arg ->
- try {
- arg?.toString()
- } catch (e: Exception) {
- "toString[$index] threw ${e.message}"
- }
- }
- ?.joinToString()
- ?.let {
- "with $it"
- }
+ ?.mapIndexed { index, arg -> arg.attemptToString(index) }
+ ?.joinToString { it.attemptToString(null) }
+ ?.let { "with $it" }
.orEmpty()
throw UnsupportedOperationException("${it.mock::class.java.simpleName}#$name " +
@@ -48,6 +44,19 @@
}
}
}
+
+ // Sometimes mocks won't have a toString method, so try-catch and return some default
+ private fun Any?.attemptToString(id: Any? = null): String {
+ return try {
+ toString()
+ } catch (e: Exception) {
+ if (id == null) {
+ e.message ?: "ERROR"
+ } else {
+ "$id ${e.message}"
+ }
+ }
+ }
}
inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.java).apply(block)
@@ -83,4 +92,4 @@
inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit = {}) =
spyThrowOnUnmocked<T>(null, block)
-inline fun <reified T : Any> nullable() = ArgumentMatchers.nullable(T::class.java)
\ No newline at end of file
+inline fun <reified T : Any> nullable() = ArgumentMatchers.nullable(T::class.java)
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
index c2cb6881..35ca354 100644
--- a/services/tests/shortcutmanagerutils/Android.bp
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -22,5 +22,9 @@
"android.test.runner.stubs",
],
+ static_libs: [
+ "compatibility-device-util-axt",
+ ],
+
sdk_version: "test_current",
}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 907f887..5182b3b 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -15,6 +15,8 @@
*/
package com.android.server.pm.shortcutmanagertest;
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -34,6 +36,7 @@
import static org.mockito.Mockito.verify;
import android.app.Instrumentation;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.LocusId;
@@ -50,6 +53,7 @@
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.UserHandle;
+import android.os.UserManager;
import android.test.MoreAsserts;
import android.util.Log;
@@ -136,6 +140,20 @@
return sb.toString();
}
+ public static List<String> extractShortcutIds(List<String> result) {
+ final String prefix = "ShortcutInfo {id=";
+ final String postfix = ", ";
+
+ List<String> ids = new ArrayList<>();
+ for (String line : result) {
+ if (line.contains(prefix)) {
+ ids.add(line.substring(
+ line.indexOf(prefix) + prefix.length(), line.indexOf(postfix)));
+ }
+ }
+ return ids;
+ }
+
public static boolean resultContains(List<String> result, String expected) {
for (String line : result) {
if (line.contains(expected)) {
@@ -160,6 +178,16 @@
return result;
}
+ public static List<String> assertHaveIds(List<String> result, String... expectedIds) {
+ assertSuccess(result);
+
+ final SortedSet<String> expected = new TreeSet<>(list(expectedIds));
+ final SortedSet<String> actual = new TreeSet<>(extractShortcutIds(result));
+ assertEquals(expected, actual);
+
+ return result;
+ }
+
public static List<String> runCommand(Instrumentation instrumentation, String command) {
return runCommand(instrumentation, command, null);
}
@@ -193,30 +221,60 @@
return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
}
- public static String getDefaultLauncher(Instrumentation instrumentation) {
- final String PREFIX = "Launcher: ComponentInfo{";
- final String POSTFIX = "}";
- final List<String> result = runShortcutCommandForSuccess(
- instrumentation, "get-default-launcher --user "
- + instrumentation.getContext().getUserId());
- for (String s : result) {
- if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
- return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
+ private static UserHandle getParentUser(Context context) {
+ final UserHandle user = context.getUser();
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ if (!userManager.isManagedProfile(user.getIdentifier())) {
+ return user;
+ }
+
+ final List<UserHandle> profiles = userManager.getUserProfiles();
+ for (UserHandle handle : profiles) {
+ if (!userManager.isManagedProfile(handle.getIdentifier())) {
+ return handle;
}
}
- fail("Default launcher not found");
return null;
}
- public static void setDefaultLauncher(Instrumentation instrumentation, String component) {
- runCommand(instrumentation, "cmd package set-home-activity --user "
- + instrumentation.getContext().getUserId() + " " + component,
- result -> result.contains("Success"));
+ public static String getDefaultLauncher(Instrumentation instrumentation) throws Exception {
+ final Context context = instrumentation.getContext();
+ final RoleManager roleManager = context.getSystemService(RoleManager.class);
+ final UserHandle user = getParentUser(context);
+ List<String> roleHolders = callWithShellPermissionIdentity(
+ () -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user));
+ if (roleHolders.size() == 1) {
+ return roleHolders.get(0);
+ }
+ fail("Failed to get the default launcher for user " + context.getUserId());
+ return null;
+ }
+
+ public static void setDefaultLauncher(Instrumentation instrumentation, String packageName) {
+ runCommandForNoOutput(instrumentation, "cmd role add-role-holder --user "
+ + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " "
+ + packageName + " 0");
+ waitUntil("Failed to get shortcut access",
+ () -> hasShortcutAccess(instrumentation, packageName), 20);
}
public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
- setDefaultLauncher(instrumentation, packageContext.getPackageName()
- + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
+ setDefaultLauncher(instrumentation, packageContext.getPackageName());
+ }
+
+ public static boolean hasShortcutAccess(Instrumentation instrumentation, String packageName) {
+ final List<String> result = runShortcutCommandForSuccess(instrumentation,
+ "has-shortcut-access --user " + instrumentation.getContext().getUserId()
+ + " " + packageName);
+ for (String s : result) {
+ if (s.startsWith("true")) {
+ return true;
+ } else if (s.startsWith("false")) {
+ return false;
+ }
+ }
+ fail("Failed to check shortcut access");
+ return false;
}
public static void overrideConfig(Instrumentation instrumentation, String config) {
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index e5646db..1dd4212 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -60,6 +60,6 @@
"libui",
"libunwindstack",
"libutils",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-V5-cpp",
],
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index fc1cb70..acda4d5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -29,8 +29,8 @@
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 57d5323..72c6028 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -33,8 +33,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER;
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index cf977b4..ddf284401 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -51,7 +51,7 @@
],
libs: [
- "android.hardware.power-java",
+ "android.hardware.power-V1-java",
"android.test.mock",
"android.test.base",
"android.test.runner",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index de2cc76..9385110 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -679,8 +679,10 @@
new RemoteAnimationAdapter(new Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
}
@@ -1715,9 +1717,8 @@
doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
any() /* window */, any() /* attrs */,
anyInt() /* viewVisibility */, anyInt() /* displayId */,
- any() /* requestedVisibility */, any() /* outFrame */,
- any() /* outInputChannel */, any() /* outInsetsState */,
- any() /* outActiveControls */);
+ any() /* requestedVisibility */, any() /* outInputChannel */,
+ any() /* outInsetsState */, any() /* outActiveControls */);
mAtm.mWindowManager.mStartingSurfaceController
.createTaskSnapshotSurface(activity, snapshot);
} catch (RemoteException ignored) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 91b9449..71f1914 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -36,6 +36,7 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -70,8 +71,10 @@
class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
for (RemoteAnimationTarget target : apps) {
assertNotNull(target.startBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f1e3609..83aca5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -265,8 +265,10 @@
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
boolean mCancelled = false;
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 2f4c8e2..d13e4dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -15,7 +15,7 @@
*/
package com.android.server.wm;
-import static android.os.Process.INVALID_UID;
+
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -587,7 +587,7 @@
final WindowToken token = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
policy.addWindow(token);
@@ -621,11 +621,11 @@
final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
TYPE_WALLPAPER, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
policy.addWindow(token1);
policy.addWindow(token2);
@@ -672,15 +672,15 @@
options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId);
final WindowToken token0 = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, options1);
final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, options2);
policy.addWindow(token0);
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 4bea9a2..4c2d124 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1608,6 +1608,16 @@
}
@Test
+ public void testGetOrCreateRootHomeTask_dontMoveToTop() {
+ DisplayContent display = createNewDisplay();
+ display.mDontMoveToTop = true;
+ TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+
+ assertNull(taskDisplayArea.getRootHomeTask());
+ assertNull(taskDisplayArea.getOrCreateRootHomeTask());
+ }
+
+ @Test
public void testValidWindowingLayer() {
final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer();
assertNotNull(windowingLayer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 37fb0e9..b1ea4a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -55,7 +55,6 @@
import static org.mockito.Mockito.spy;
import static org.testng.Assert.expectThrows;
-import android.app.WindowConfiguration;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -66,6 +65,7 @@
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsState;
+import android.view.RoundedCorners;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
@@ -99,6 +99,7 @@
private int mRotation = ROTATION_0;
private boolean mHasDisplayCutout;
private boolean mIsLongEdgeDisplayCutout;
+ private boolean mHasRoundedCorners;
private final Rect mDisplayBounds = new Rect();
@@ -153,6 +154,11 @@
updateDisplayFrames();
}
+ public void addRoundedCorners() {
+ mHasRoundedCorners = true;
+ updateDisplayFrames();
+ }
+
private void updateDisplayFrames() {
mFrames = createDisplayFrames(
mDisplayContent.getInsetsStateController().getRawInsetsState());
@@ -166,9 +172,11 @@
private DisplayFrames createDisplayFrames(InsetsState insetsState) {
final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
mHasDisplayCutout, mIsLongEdgeDisplayCutout);
+ final RoundedCorners roundedCorners = mHasRoundedCorners
+ ? mDisplayContent.calculateRoundedCornersForRotation(mRotation)
+ : RoundedCorners.NO_ROUNDED_CORNERS;
return new DisplayFrames(mDisplayContent.getDisplayId(),
- insetsState,
- info.first, info.second);
+ insetsState, info.first, info.second, roundedCorners);
}
@Test
@@ -664,77 +672,13 @@
public void layoutHint_appWindow() {
mWindow.mAttrs.setFitInsetsTypes(0);
- final Rect outFrame = new Rect();
final DisplayCutout.ParcelableWrapper outDisplayCutout =
new DisplayCutout.ParcelableWrapper();
final InsetsState outState = new InsetsState();
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outFrame,
- outState, true /* localClient */);
-
- assertThat(outFrame, is(outState.getDisplayFrame()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
- is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
- assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
- is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
- }
-
- @Test
- public void layoutHint_appWindowInTask() {
- mWindow.mAttrs.setFitInsetsTypes(0);
-
- final Rect taskBounds = new Rect(100, 100, 200, 200);
- final Task task = mWindow.getTask();
- // Force the bounds because the task may resolve different bounds from Task#setBounds.
- task.getWindowConfiguration().setBounds(taskBounds);
-
- final Rect outFrame = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
- final InsetsState outState = new InsetsState();
-
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outState,
+ mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outState,
true /* localClient */);
- assertThat(outFrame, is(taskBounds));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
- is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
- assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
- is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
- }
-
- @Test
- public void layoutHint_appWindowInTask_outsideContentFrame() {
- mWindow.mAttrs.setFitInsetsTypes(0);
-
- final InsetsState state =
- mDisplayContent.getInsetsStateController().getRawInsetsState();
- final Rect contentFrame = new Rect(state.getDisplayFrame());
- contentFrame.inset(state.calculateInsets(contentFrame, Type.systemBars(),
- false /* ignoreVisibility */));
-
- // Task is in the nav bar area (usually does not happen, but this is similar enough to
- // the possible overlap with the IME)
- final Rect taskBounds = new Rect(100, contentFrame.bottom + 1,
- 200, contentFrame.bottom + 10);
-
- final Task task = mWindow.getTask();
- // Make the task floating.
- task.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
- // Force the bounds because the task may resolve different bounds from Task#setBounds.
- task.getWindowConfiguration().setBounds(taskBounds);
-
- final Rect outFrame = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
- final InsetsState outState = new InsetsState();
-
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outState,
- true /* localClient */);
-
- assertThat(outFrame, is(taskBounds));
assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
@@ -753,6 +697,8 @@
assertSimulateLayoutSameDisplayFrames();
addDisplayCutout();
assertSimulateLayoutSameDisplayFrames();
+ addRoundedCorners();
+ assertSimulateLayoutSameDisplayFrames();
}
private void assertSimulateLayoutSameDisplayFrames() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 499507e..2163661 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -302,7 +302,7 @@
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
mImeWindow.mAboveInsetsState = state;
mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
- state, displayInfo, null /* displayCutout */);
+ state, displayInfo, null /* displayCutout */, null /* roundedCorners*/);
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index e1aca55..2321a73 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -121,6 +121,8 @@
sMockWm = mock(WindowManagerService.class);
sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
sMockWm.mPolicy = mock(WindowManagerPolicy.class);
+ sMockWm.mConstants = mock(WindowManagerConstants.class);
+ sMockWm.mConstants.mRawSensorLoggingEnabled = true;
}
@AfterClass
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index 33e8fc0..3e05c86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -217,6 +217,7 @@
SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
overrideSettings.mShouldShowSystemDecors = true;
overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
+ overrideSettings.mDontMoveToTop = true;
provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
assertTrue(mOverrideSettingsStorage.wasWriteSuccessful());
@@ -227,6 +228,8 @@
getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowSystemDecors"));
assertEquals("Attribute value must be stored", "0",
getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy"));
+ assertEquals("Attribute value must be stored", "true",
+ getStoredDisplayAttributeValue(mOverrideSettingsStorage, "dontMoveToTop"));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index f75c98f..78074d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -26,7 +27,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -70,13 +70,15 @@
spyOn(wms);
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
- final IBinder token = (IBinder) args[0];
- final int windowType = (int) args[1];
- new WindowToken(mWm, token, windowType, true /* persistOnEmpty */,
- mDefaultDisplay, true /* ownerCanManageAppTokens */, 1000 /* ownerUid */,
- false /* roundedCornerOverlay */, true /* fromClientToken */);
- return WindowManagerGlobal.ADD_OKAY;
- }).when(wms).addWindowTokenWithOptions(any(), anyInt(), anyInt(), any(), anyString());
+ IBinder clientToken = (IBinder) args[0];
+ int displayId = (int) args[2];
+ DisplayContent dc = mWm.mRoot.getDisplayContent(displayId);
+ mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
+ dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
+ null /* options */);
+ return true;
+ }).when(wms).registerWindowContextListener(any(), eq(TYPE_INPUT_METHOD_DIALOG),
+ anyInt(), any());
mSecondaryDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1000).build();
@@ -95,14 +97,12 @@
assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
- // Obtain the context again and check they are the same instance and match the display
- // metrics of the secondary display.
+ // Obtain the context again and check if the window metrics match the IME container bounds
+ // of the secondary display.
final Context contextOnSecondaryDisplay = mController.getSettingsContext(
mSecondaryDisplay.getDisplayId());
assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mSecondaryDisplay);
- assertThat(contextOnDefaultDisplay.getWindowContextToken())
- .isEqualTo(contextOnSecondaryDisplay.getWindowContextToken());
}
private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 2107ab1e..ee293fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -86,6 +86,7 @@
assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_IME));
}
@Test
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 a045100..7714a6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -51,11 +52,13 @@
SurfaceControl.Transaction mTransaction;
private boolean mAreCornersRounded = false;
+ private int mColor = Color.BLACK;
@Before
public void setUp() throws Exception {
mSurfaces = new SurfaceControlMocker();
- mLetterbox = new Letterbox(mSurfaces, StubTransaction::new, () -> mAreCornersRounded);
+ mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
+ () -> mAreCornersRounded, () -> Color.valueOf(mColor));
mTransaction = spy(StubTransaction.class);
}
@@ -171,6 +174,22 @@
}
@Test
+ public void testApplySurfaceChanges_setColor() {
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 0, 0});
+
+ mColor = Color.GREEN;
+
+ assertTrue(mLetterbox.needsApplySurfaceChanges());
+
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
+ }
+
+ @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);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 409bad4..c6be987 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -493,7 +493,7 @@
initializeRecentsAnimationController(mController, homeActivity);
// Verify RecentsAnimationController will animate visible leaf task by default.
- verify(mController).addAnimation(eq(leafTask), anyBoolean(), anyBoolean(), eq(null));
+ verify(mController).addAnimation(eq(leafTask), anyBoolean(), anyBoolean(), any());
assertTrue(leafTask.isAnimatingByRecents());
// Make sure isAnimatingByRecents will also return true when it called by the parent task.
@@ -543,6 +543,35 @@
verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
}
+ @Test
+ public void testCleanupAnimation_expectExitAnimationDone() {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityRecord homeActivity = createHomeActivity();
+ final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
+
+ initializeRecentsAnimationController(mController, homeActivity);
+ mController.startAnimation();
+
+ spyOn(win1);
+ spyOn(win1.mWinAnimator);
+ // Simulate when the window is exiting and cleanupAnimation invoked
+ // (e.g. screen off during RecentsAnimation animating), will expect the window receives
+ // onExitAnimationDone to destroy the surface when the removal is allowed.
+ win1.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
+ win1.mHasSurface = true;
+ win1.mAnimatingExit = true;
+ win1.mRemoveOnExit = true;
+ win1.mWindowRemovalAllowed = true;
+ mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ verify(win1).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), any());
+ verify(win1).onExitAnimationDone();
+ verify(win1).destroySurface(eq(false), eq(false));
+ assertFalse(win1.mAnimatingExit);
+ assertFalse(win1.mHasSurface);
+ }
+
private ActivityRecord createHomeActivity() {
final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
.setParentTask(mRootHomeTask)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 2efd4b5..15e045c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -100,15 +103,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -136,7 +142,7 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
adapter.onAnimationCancelled(mMockLeash);
verify(mMockRunner).onAnimationCancelled();
@@ -149,7 +155,7 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mClock.fastForward(2500);
mHandler.timeAdvance();
@@ -170,7 +176,7 @@
null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mClock.fastForward(2500);
mHandler.timeAdvance();
@@ -190,7 +196,7 @@
@Test
public void testZeroAnimations() {
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_NONE);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -199,7 +205,7 @@
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -213,15 +219,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
@@ -235,7 +244,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
win.mActivityRecord.removeImmediately();
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
eq(adapter));
@@ -255,15 +264,18 @@
mFinishedCallback);
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -305,15 +317,18 @@
mFinishedCallback);
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -354,15 +369,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, wallpapersCaptor.getValue().length);
} finally {
@@ -383,15 +401,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAPpsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAPpsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, wallpapersCaptor.getValue().length);
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 6f775cf..cc4d4ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -39,8 +39,6 @@
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -119,13 +117,13 @@
@Test
public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
removeGlobalMinSizeRestriction();
- // Create landscape freeform display and a freeform app.
+ // create freeform display and a freeform app
DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
.setCanRotate(false)
.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
setUpApp(display);
- // Put app window into portrait freeform and then make it a compat app.
+ // Put app window into freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -138,7 +136,7 @@
final int density = mActivity.getConfiguration().densityDpi;
- // Change display configuration to fullscreen.
+ // change display configuration to fullscreen
Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
display.onRequestedOverrideConfigurationChanged(c);
@@ -148,8 +146,6 @@
assertEquals(bounds.width(), mActivity.getBounds().width());
assertEquals(bounds.height(), mActivity.getBounds().height());
assertEquals(density, mActivity.getConfiguration().densityDpi);
- // Size compat mode is sandboxed at the activity level.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -175,12 +171,6 @@
assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
// The decor height should be a part of the effective bounds.
assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
- // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
- // Activity max bounds ignore notch, since an app can be shown past the notch (although app
- // is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
- .isEqualTo(displayBounds.height());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
@@ -190,17 +180,9 @@
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
// The notch is no longer on top.
assertEquals(appBounds, mActivity.getBounds());
- // Activity max bounds are sandboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
- // Activity max bounds ignore notch, since an app can be shown past the notch (although app
- // is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
- .isEqualTo(displayBounds.height());
}
@Test
@@ -228,9 +210,6 @@
assertEquals(originalBounds.width(), mActivity.getBounds().width());
assertEquals(originalBounds.height(), mActivity.getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
- // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
- // max aspect ratio.
- assertActivityMaxBoundsSandboxedForSizeCompat();
assertScaled();
}
@@ -238,13 +217,11 @@
public void testFixedScreenBoundsWhenDisplaySizeChanged() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
- final DisplayContent display = mActivity.mDisplayContent;
assertFitted();
- // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
final Rect origBounds = new Rect(mActivity.getBounds());
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final DisplayContent display = mActivity.mDisplayContent;
// Change the size of current display.
resizeDisplay(display, 1000, 2000);
@@ -261,8 +238,6 @@
// The position of configuration bounds should be the same as compat bounds.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
- // Activity is sandboxed to the offset size compat bounds.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display size to a different orientation
resizeDisplay(display, 2000, 1000);
@@ -271,8 +246,6 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
- // Activity is sandboxed to the offset size compat bounds.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// The previous resize operation doesn't consider the rotation change after size changed.
// These setups apply the requested orientation to rotation as real case that the top fixed
@@ -292,8 +265,6 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(offsetX, currentBounds.left);
assertScaled();
- // Activity is sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -309,8 +280,6 @@
assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
- // Activity max bounds should be sandboxed since it is letterboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -322,8 +291,6 @@
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
- // Activity max bounds should be sandboxed since it is letterboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -349,13 +316,14 @@
}
@Test
- public void testMoveToDifferentOrientationDisplay() {
+ public void testMoveToDifferentOrientDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
- final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
+ final int origWidth = configBounds.width();
+ final int origHeight = configBounds.height();
final int notchHeight = 100;
final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -364,44 +332,37 @@
// Move the non-resizable activity to the new display.
mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
// The configuration bounds [820, 0 - 1820, 2500] should keep the same.
- assertEquals(originalBounds.width(), currentBounds.width());
- assertEquals(originalBounds.height(), currentBounds.height());
+ assertEquals(origWidth, configBounds.width());
+ assertEquals(origHeight, configBounds.height());
assertScaled();
- // Activity max bounds are sandboxed due to size compat mode on the new display.
- assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
// The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
assertEquals(newDisplayBounds.height() - notchHeight,
- (int) ((float) mActivity.getBounds().width() * originalBounds.height()
- / originalBounds.width()));
+ (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
// Recompute the natural configuration in the new display.
mActivity.clearSizeCompatMode();
mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
- assertEquals(newDisplayBounds.height(), currentBounds.height());
- assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- currentBounds.width());
+ assertEquals(newDisplayBounds.height(), configBounds.height());
+ assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+ configBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
- assertEquals(currentBounds.width(), appBounds.width());
- assertEquals(currentBounds.height() - notchHeight, appBounds.height());
- // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display.
- assertTaskMaxBoundsSandboxed();
+ assertEquals(configBounds.width(), appBounds.width());
+ assertEquals(configBounds.height() - notchHeight, appBounds.height());
}
@Test
- public void testFixedOrientationRotateCutoutDisplay() {
+ public void testFixedOrientRotateCutoutDisplay() {
// Create a display with a notch/cutout
final int notchHeight = 60;
- final int width = 1000;
- setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
+ setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
.setNotch(notchHeight).build());
- // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
- final float maxAspect = 1.4f;
+ // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -409,11 +370,6 @@
final Rect origBounds = new Rect(currentBounds);
final Rect origAppBounds = new Rect(appBounds);
- // Activity is sandboxed, and bounds include the area consumed by the notch.
- assertActivityMaxBoundsSandboxedForLetterbox();
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
- .isEqualTo(Math.round(width * maxAspect) + notchHeight);
-
// Although the activity is fixed orientation, force rotate the display.
rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -429,13 +385,10 @@
// The position in configuration should be global coordinates.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
-
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
- public void testFixedAspectRatioOrientationChangeOrientation() {
+ public void testFixedAspOrientChangeOrient() {
setUpDisplaySizeWithApp(1000, 2500);
final float maxAspect = 1.4f;
@@ -447,8 +400,6 @@
final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
- // Activity is sandboxed due to fixed aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
// Change the fixed orientation.
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -460,8 +411,6 @@
mActivity.getWindowConfiguration().getAppBounds().height());
assertEquals(originalAppBounds.height(),
mActivity.getWindowConfiguration().getAppBounds().width());
- // Activity is sandboxed due to fixed aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -510,8 +459,6 @@
// restarted and the override configuration won't be cleared.
verify(mActivity, never()).restartProcessIfVisible();
assertScaled();
- // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display density
display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -586,16 +533,12 @@
// in multi-window mode.
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldUseSizeCompatMode());
- // Activity and task should not be sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldUseSizeCompatMode());
- // Activity and task should not be sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
}
@Test
@@ -659,9 +602,6 @@
// be transparent.
assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
- // Activity is sandboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
-
// Make the activity fill the display.
prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -671,7 +611,6 @@
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -696,8 +635,6 @@
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(taskBounds, activityBounds);
- // Activity inherits max bounds from task, since sandboxing applied to task.
- assertTaskMaxBoundsSandboxed();
// Task bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), taskBounds.height());
@@ -728,8 +665,6 @@
assertScaled();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -741,30 +676,29 @@
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
+ Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ Rect activityBounds = new Rect(mActivity.getBounds());
+
// App should launch in fullscreen.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Activity and task inherit max bounds from TaskDisplayArea.
- assertMaxBoundsInheritDisplayAreaBounds();
+ assertEquals(displayBounds, activityBounds);
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
- assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
+ displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ activityBounds = new Rect(mActivity.getBounds());
+ assertTrue(displayBounds.width() > displayBounds.height());
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- assertThat(mActivity.inSizeCompatMode()).isTrue();
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// App bounds should be 700x1400 with the ratio as the display.
- assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
- assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
- / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ activityBounds.width());
}
@Test
@@ -797,17 +731,14 @@
final Rect displayBounds = new Rect(display.getBounds());
final Rect taskBounds = new Rect(mTask.getBounds());
final Rect newActivityBounds = new Rect(newActivity.getBounds());
- final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width();
// Task and app bounds should be 700x1400 with the ratio as the display.
assertTrue(mTask.isTaskLetterboxed());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(taskBounds, newActivityBounds);
assertEquals(displayBounds.height(), taskBounds.height());
- assertThat(taskBounds.width())
- .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio));
- // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
- assertTaskMaxBoundsSandboxed();
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ taskBounds.width());
}
@Test
@@ -847,14 +778,6 @@
assertEquals(displayBounds.height(), taskBounds.height());
assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
taskBounds.width());
- // New activity max bounds are sandboxed due to letterbox.
- assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskBounds);
- // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height())
- .isEqualTo(displayBounds.height());
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width())
- .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio));
// App bounds should be fullscreen in Task bounds.
assertFalse(newActivity.inSizeCompatMode());
@@ -883,9 +806,6 @@
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
- assertThat(mActivity.inSizeCompatMode()).isTrue();
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect activityBounds = new Rect(mActivity.getBounds());
mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -896,8 +816,6 @@
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
assertEquals(activityBounds, mActivity.getBounds());
- // Activity max bounds are sandboxed due to size compat.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -913,7 +831,6 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertTaskMaxBoundsSandboxed();
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
@@ -921,7 +838,6 @@
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
@@ -929,7 +845,6 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertTaskMaxBoundsSandboxed();
}
@Test
@@ -947,26 +862,20 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Task is letterboxed due to mismatched orientation request.
- assertTaskMaxBoundsSandboxed();
- // Rotate display to landscape.
+ // Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- // Activity max bounds are sandboxed due to unresizable app.
- assertActivityMaxBoundsSandboxedForSizeCompat();
- // Rotate display to portrait.
+ // Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Task is letterboxed, as in first case.
- assertTaskMaxBoundsSandboxed();
}
@Test
@@ -983,18 +892,12 @@
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(2800, displayBounds.width());
assertEquals(1400, displayBounds.height());
- Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
- taskDisplayArea.setBounds(displayAreaBounds);
+ taskDisplayArea.setBounds(0, 0, 2400, 1000);
final Rect activityBounds = new Rect(mActivity.getBounds());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(2400, activityBounds.width());
assertEquals(1000, activityBounds.height());
- // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(displayAreaBounds);
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(displayAreaBounds);
}
@Test
@@ -1142,48 +1045,6 @@
assertFalse(mActivity.hasSizeCompatBounds());
}
- /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */
- private void assertMaxBoundsInheritDisplayAreaBounds() {
- final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds();
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskDisplayAreaBounds);
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskDisplayAreaBounds);
- }
-
- /**
- * Asserts task-level letterboxing, so both activity and task max bounds
- * are sandboxed to the letterbox bounds.
- */
- private void assertTaskMaxBoundsSandboxed() {
- // Activity inherits max bounds from task, since sandboxing applied to task.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getBounds());
- // Task max bounds are sandboxed due to letterbox.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getBounds());
- }
-
- /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */
- private void assertActivityMaxBoundsSandboxedForSizeCompat() {
- // Activity max bounds are sandboxed due to size compat mode.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mActivity.getWindowConfiguration().getBounds());
- // Task inherits max bounds from display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getDisplayContent().getBounds());
- }
-
- /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */
- private void assertActivityMaxBoundsSandboxedForLetterbox() {
- // Activity is sandboxed due to fixed aspect ratio.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mActivity.getBounds());
- // Task inherits bounds from display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getDisplayContent().getBounds());
- }
-
static Configuration rotateDisplay(DisplayContent display, int rotation) {
final Configuration c = new Configuration();
display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b8d44f6..6c72249 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -152,12 +152,6 @@
}
@Override
- public SurfaceControl.Transaction reparentChildren(SurfaceControl sc,
- SurfaceControl newParent) {
- return this;
- }
-
- @Override
public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) {
return this;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index d83e9c2..a3ade51 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -156,6 +156,9 @@
spyOn(mDisplayContent);
doReturn(true).when(mDisplayContent).isTrusted();
+ // Allow child stack to move to top.
+ mDisplayContent.mDontMoveToTop = false;
+
// The display contains pinned stack that was added in {@link #setUp}.
final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
@@ -173,6 +176,65 @@
}
@Test
+ public void testMovingChildTaskOnTop() {
+ // Make sure the display is trusted display which capable to move the stack to top.
+ spyOn(mDisplayContent);
+ doReturn(true).when(mDisplayContent).isTrusted();
+
+ // Allow child stack to move to top.
+ mDisplayContent.mDontMoveToTop = false;
+
+ // The display contains pinned stack that was added in {@link #setUp}.
+ Task stack = createTaskStackOnDisplay(mDisplayContent);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+
+ // Add another display at top.
+ mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+ false /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) is not on top.
+ assertEquals("Testing DisplayContent should not be on the top",
+ mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+
+ // Move the task of {@code mDisplayContent} to top.
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) is now on the top.
+ assertEquals("The testing DisplayContent should be moved to top with task",
+ mWm.mRoot.getChildCount() - 1, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+ }
+
+ @Test
+ public void testDontMovingChildTaskOnTop() {
+ // Make sure the display is trusted display which capable to move the stack to top.
+ spyOn(mDisplayContent);
+ doReturn(true).when(mDisplayContent).isTrusted();
+
+ // Allow child stack to move to top.
+ mDisplayContent.mDontMoveToTop = true;
+
+ // The display contains pinned stack that was added in {@link #setUp}.
+ Task stack = createTaskStackOnDisplay(mDisplayContent);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+
+ // Add another display at top.
+ mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+ false /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) is not on top.
+ assertEquals("Testing DisplayContent should not be on the top",
+ mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+
+ // Try moving the task of {@code mDisplayContent} to top.
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) hasn't moved and is not
+ // on the top.
+ assertEquals("The testing DisplayContent should not be moved to top with task",
+ mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+ }
+
+ @Test
public void testReuseTaskAsRootTask() {
final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index f20513d..0eb8c8d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -1004,6 +1004,26 @@
}
@Test
+ public void testNotSaveLaunchingStateForNonLeafTask() {
+ LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+ spyOn(persister);
+
+ final Task task = getTestTask();
+ task.setHasBeenVisible(false);
+ task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ final Task leafTask = createTaskInStack(task, 0 /* userId */);
+
+ leafTask.setHasBeenVisible(true);
+ task.setHasBeenVisible(true);
+ task.onConfigurationChanged(task.getParent().getConfiguration());
+
+ verify(persister, never()).saveTask(same(task), any());
+ verify(persister).saveTask(same(leafTask), any());
+ }
+
+ @Test
public void testNotSpecifyOrientationByFloatingTask() {
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true).setCreateParentTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index d49956a..9372530 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -145,7 +145,7 @@
assertThat(surface).isNotNull();
verify(session).addToDisplay(any(), argThat(this::isTrustedOverlay), anyInt(), anyInt(),
- any(), any(), any(), any(), any());
+ any(), any(), any(), any());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d71993d..ae85ceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,11 +136,6 @@
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
- // Ensure letterbox aspect ratio is not overridden on any device target.
- // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
- // the below method, is set on some device form factors.
- mService.mWindowManager.setTaskLetterboxAspectRatio(0);
-
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 53cea5a..d1d0ac6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -47,6 +47,7 @@
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsState;
+import android.view.RoundedCorners;
import android.view.Surface;
import android.view.WindowManager;
@@ -146,7 +147,7 @@
final DisplayInfo info = dc.computeScreenConfiguration(config, Surface.ROTATION_0);
final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0);
final DisplayFrames displayFrames = new DisplayFrames(dc.getDisplayId(), new InsetsState(),
- info, cutout);
+ info, cutout, RoundedCorners.NO_ROUNDED_CORNERS);
wallpaperWindowToken.applyFixedRotationTransform(info, displayFrames, config);
// Check that the wallpaper has the same frame in landscape than in portrait
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 df5b48a..99c96bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -66,6 +66,7 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -905,8 +906,10 @@
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new IRemoteAnimationRunner.Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
try {
finishedCallback.onAnimationFinished();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index f8a89c6..6d0e510 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -20,7 +20,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.os.Process.INVALID_UID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -37,12 +36,10 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.os.IBinder;
@@ -75,7 +72,7 @@
@Test
public void testAddWindowToken() {
IBinder token = mock(IBinder.class);
- mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId());
+ mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId(), null /* options */);
WindowToken windowToken = mWm.mRoot.getWindowToken(token);
assertFalse(windowToken.mRoundedCornerOverlay);
@@ -83,32 +80,6 @@
}
@Test
- public void testAddWindowTokenWithOptions() {
- IBinder token = mock(IBinder.class);
- mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
- null /* options */, null /* options */);
-
- WindowToken windowToken = mWm.mRoot.getWindowToken(token);
- assertFalse(windowToken.mRoundedCornerOverlay);
- assertTrue(windowToken.isFromClient());
- }
-
- @Test(expected = SecurityException.class)
- public void testRemoveWindowToken_ownerUidNotMatch_throwException() {
- IBinder token = mock(IBinder.class);
- mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
- null /* options */, null /* options */);
-
- spyOn(mWm);
- when(mWm.checkCallingPermission(anyString(), anyString())).thenReturn(false);
- WindowToken windowToken = mWm.mRoot.getWindowToken(token);
- spyOn(windowToken);
- when(windowToken.getOwnerUid()).thenReturn(INVALID_UID);
-
- mWm.removeWindowToken(token, mDisplayContent.getDisplayId());
- }
-
- @Test
public void testTaskFocusChange_stackNotHomeType_focusChanges() throws RemoteException {
DisplayContent display = createNewDisplay();
// Current focused window
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 2d27331..e2585e5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -201,7 +200,7 @@
token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST,
true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */,
- INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */);
+ true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */);
assertTrue(token.mRoundedCornerOverlay);
assertTrue(token.isFromClient());
}
@@ -215,8 +214,8 @@
public void testSurfaceCreatedForWindowToken() {
final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */,
- mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID,
- true /* roundedCornerOverlay */, true /* fromClientToken */);
+ mDisplayContent, true /* ownerCanManageAppTokens */,
+ true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */);
assertNull(fromClientToken.mSurfaceControl);
createWindow(null, TYPE_APPLICATION_OVERLAY, fromClientToken, "window");
@@ -224,8 +223,8 @@
final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
- false /* fromClientToken */);
+ true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
assertNotNull(nonClientToken.mSurfaceControl);
}
@@ -238,7 +237,7 @@
final WindowToken token1 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
verify(selectFunc).apply(token1.windowType, null);
@@ -246,7 +245,7 @@
final Bundle options = new Bundle();
final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
false /* fromClientToken */, options /* options */);
verify(selectFunc).apply(token2.windowType, options);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 9e8f8eaa..04dea3f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -163,8 +163,13 @@
mValid = true;
mSessionComponentName = new ComponentName(service.getPackageName(),
mInfo.getSessionService());
- // TODO : Need to get the hotword detection service from the xml metadata
- mHotwordDetectionComponentName = null;
+ final String hotwordDetectionServiceName = mInfo.getHotwordDetectionService();
+ if (hotwordDetectionServiceName != null) {
+ mHotwordDetectionComponentName = new ComponentName(service.getPackageName(),
+ hotwordDetectionServiceName);
+ } else {
+ mHotwordDetectionComponentName = null;
+ }
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
IntentFilter filter = new IntentFilter();
@@ -388,7 +393,11 @@
if (DEBUG) {
Slog.d(TAG, "setHotwordDetectionConfigLocked");
}
-
+ if (mHotwordDetectionComponentName == null) {
+ Slog.e(TAG, "Calling setHotwordDetectionConfigLocked, but hotword detection service"
+ + " name not found");
+ return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+ }
if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) {
return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index cfdc568..84f4f6a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -165,7 +165,8 @@
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
if (mBound) {
try {
- mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY);
+ mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY,
+ null /* options */);
} catch (RemoteException e) {
Slog.w(TAG, "Failed adding window token", e);
}
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 7cc233b..cac7b82 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -84,7 +84,6 @@
static_libs: [
"libviewcompiler",
],
- test_suites: ["general-tests"],
}
cc_binary_host {
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
index 5f7d3f9..791e471 100644
--- a/startop/view_compiler/TEST_MAPPING
+++ b/startop/view_compiler/TEST_MAPPING
@@ -10,10 +10,6 @@
"include-filter": "android.view.cts.PrecompiledLayoutTest"
}
]
- },
- {
- "name": "view-compiler-tests",
- "host": true
}
]
}
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
index eaa3e04..5cb0c17 100644
--- a/startop/view_compiler/apk_layout_compiler.cc
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -80,7 +80,7 @@
}
namespace {
-void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets,
+void CompileApkAssetsLayouts(const std::unique_ptr<android::ApkAssets>& assets,
CompilationTarget target, std::ostream& target_out) {
android::AssetManager2 resources;
resources.SetApkAssets({assets.get()});
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index b73ef9b..be5fae4 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.net.Uri;
@@ -67,7 +68,8 @@
* Sets the participants for the resulting {@link ConnectionRequest}
* @param participants The participants to which the {@link Connection} is to connect.
*/
- public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+ public @NonNull Builder setParticipants(
+ @SuppressLint("NullableCollection") @Nullable List<Uri> participants) {
this.mParticipants = participants;
return this;
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 5c75a2f..3b06fd3 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2459,7 +2459,7 @@
}
private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts) {
- Log.i(this, "onCallFilteringCompleted(%s, %b, %b)", isBlocked, isInContacts);
+ Log.i(this, "onCallFilteringCompleted(%b, %b)", isBlocked, isInContacts);
Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
if (connection != null) {
connection.onCallFilteringCompleted(isBlocked, isInContacts);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 6dc096d..88ef1b0 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -333,6 +333,8 @@
void cleanupStuckCalls();
+ void resetCarMode();
+
void setTestDefaultCallRedirectionApp(String packageName);
void setTestPhoneAcctSuggestionComponent(String flattenedComponentName);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7215cd5..ac584c1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4038,6 +4038,20 @@
public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
KEY_PREFIX + "enable_presence_group_subscribe_bool";
+ /**
+ * An integer key associated with the period of time in seconds the non-rcs capability
+ * information of each contact is cached on the device.
+ * <p>
+ * The rcs capability cache expiration sec is managed by
+ * {@code android.telephony.ims.ProvisioningManager} but non-rcs capability is managed by
+ * {@link CarrierConfigManager} since non-rcs capability will be provided via ACS or carrier
+ * config.
+ * <p>
+ * The default value is 2592000 secs (30 days), see RCC.07 Annex A.1.9.
+ */
+ public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT =
+ KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -4048,6 +4062,7 @@
defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
+ defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
return defaults;
}
}
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 8507d85..706e3cb 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -344,6 +344,7 @@
// TODO: Instead of doing this, we should create a formal way for cloning cell identity.
// Cell identity is not an immutable object so we have to deep copy it.
mCellIdentity = CellIdentity.CREATOR.createFromParcel(p);
+ p.recycle();
}
if (nri.mVoiceSpecificInfo != null) {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2734ad1..1473b7a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -47,6 +47,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
@@ -150,6 +151,22 @@
public static final String CACHE_KEY_SLOT_INDEX_PROPERTY =
"cache_key.telephony.get_slot_index";
+ /** @hide */
+ public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
+
+ /** @hide */
+ public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME =
+ "restoreSimSpecificSettings";
+
+ /**
+ * Key to the backup & restore data byte array in the Bundle that is returned by {@link
+ * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link
+ * #restoreAllSimSpecificSettings()}.
+ *
+ * @hide
+ */
+ public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA";
+
private static final int MAX_CACHE_SIZE = 4;
private static class VoidPropertyInvalidatedCache<T>
@@ -372,6 +389,28 @@
public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
CONTENT_URI, "wfc_roaming_enabled");
+
+ /**
+ * A content {@link uri} used to call the appropriate backup or restore method for sim-specific
+ * settings
+ * <p>
+ * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link
+ * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call.
+ * @hide
+ */
+ @NonNull
+ public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+ CONTENT_URI, "backup_and_restore");
+
+ /**
+ * A content {@link uri} used to notify contentobservers listening to siminfo restore during
+ * SuW.
+ * @hide
+ */
+ @NonNull
+ public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore");
+
/**
* A content {@link Uri} used to receive updates on cross sim enabled user setting.
* <p>
@@ -3455,4 +3494,71 @@
sSlotIndexCache.clear();
sPhoneIdCache.clear();
}
+
+ /**
+ * Called to retrieve SIM-specific settings data to be backed up.
+ *
+ * @return data in byte[] to be backed up.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public byte[] getAllSimSpecificSettingsForBackup() {
+ Bundle bundle = mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null);
+ return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA);
+ }
+
+ /**
+ * Called to attempt to restore the backed up sim-specific configs to device for specific sim.
+ * This will try to restore the data that was stored internally when {@link
+ * #restoreAllSimSpecificSettingsFromBackup(byte[] data)} was called during setup wizard.
+ * End result is SimInfoDB is modified to match any backed up configs for the requested
+ * inserted sim.
+ *
+ * <p>
+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+ * entry is updated as the result of this method call.
+ *
+ * @param iccId of the sim that a restore is requested for.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void restoreSimSpecificSettingsForIccIdFromBackup(@NonNull String iccId) {
+ mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ iccId, null);
+ }
+
+ /**
+ * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+ * configs to device for all existing SIMs in SimInfoDB. Internally, it will store the backup
+ * data in an internal file. This file will persist on device for device's lifetime and will be
+ * used later on when a SIM is inserted to restore that specific SIM's settings by calling
+ * {@link #restoreSimSpecificSettingsForIccIdFromBackup(String iccId)}. End result is
+ * SimInfoDB is modified to match any backed up configs for the appropriate inserted SIMs.
+ *
+ * <p>
+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+ * entry is updated as the result of this method call.
+ *
+ * @param data with the sim specific configs to be backed up.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
+ mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ null, bundle);
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e4386e75..65b8de2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -14242,7 +14242,7 @@
* If this policy is enabled, data will be temporarily enabled on the non-default data SIM
* during any voice calls.
*
- * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+ * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
* @hide
*/
@SystemApi
@@ -14256,7 +14256,7 @@
* will also return true for {@link ApnSetting#TYPE_MMS}.
* When disabled, the MMS APN will be governed by the same rules as all other APNs.
*
- * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+ * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
* @hide
*/
@SystemApi
@@ -14284,11 +14284,11 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void setMobileDataPolicyEnabledStatus(@MobileDataPolicy int policy, boolean enabled) {
+ public void setMobileDataPolicyEnabled(@MobileDataPolicy int policy, boolean enabled) {
try {
ITelephony service = getITelephony();
if (service != null) {
- service.setMobileDataPolicyEnabledStatus(getSubId(), policy, enabled);
+ service.setMobileDataPolicyEnabled(getSubId(), policy, enabled);
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index f48ddb0..bd4bf07 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -136,7 +136,7 @@
private final @HandoverFailureMode int mHandoverFailureMode;
private final int mPduSessionId;
private final Qos mDefaultQos;
- private final List<QosSession> mQosSessions;
+ private final List<QosBearerSession> mQosBearerSessions;
private final SliceInfo mSliceInfo;
/**
@@ -187,7 +187,7 @@
mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
mPduSessionId = PDU_SESSION_ID_NOT_SET;
mDefaultQos = null;
- mQosSessions = new ArrayList<>();
+ mQosBearerSessions = new ArrayList<>();
mSliceInfo = null;
}
@@ -197,7 +197,7 @@
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
@Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
@HandoverFailureMode int handoverFailureMode, int pduSessionId,
- @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions,
+ @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions,
@Nullable SliceInfo sliceInfo) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
@@ -219,7 +219,7 @@
mHandoverFailureMode = handoverFailureMode;
mPduSessionId = pduSessionId;
mDefaultQos = defaultQos;
- mQosSessions = qosSessions;
+ mQosBearerSessions = qosBearerSessions;
mSliceInfo = sliceInfo;
}
@@ -246,8 +246,8 @@
mHandoverFailureMode = source.readInt();
mPduSessionId = source.readInt();
mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
- mQosSessions = new ArrayList<>();
- source.readList(mQosSessions, QosSession.class.getClassLoader());
+ mQosBearerSessions = new ArrayList<>();
+ source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader());
mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader());
}
@@ -394,8 +394,8 @@
* @hide
*/
@NonNull
- public List<QosSession> getQosSessions() {
- return mQosSessions;
+ public List<QosBearerSession> getQosBearerSessions() {
+ return mQosBearerSessions;
}
/**
@@ -427,7 +427,7 @@
.append(" handoverFailureMode=").append(failureModeToString(mHandoverFailureMode))
.append(" pduSessionId=").append(getPduSessionId())
.append(" defaultQos=").append(mDefaultQos)
- .append(" qosSessions=").append(mQosSessions)
+ .append(" qosBearerSessions=").append(mQosBearerSessions)
.append(" sliceInfo=").append(mSliceInfo)
.append("}");
return sb.toString();
@@ -447,10 +447,10 @@
mDefaultQos == other.mDefaultQos :
mDefaultQos.equals(other.mDefaultQos);
- final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ?
- mQosSessions == other.mQosSessions :
- mQosSessions.size() == other.mQosSessions.size()
- && mQosSessions.containsAll(other.mQosSessions);
+ final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ?
+ mQosBearerSessions == other.mQosBearerSessions :
+ mQosBearerSessions.size() == other.mQosBearerSessions.size()
+ && mQosBearerSessions.containsAll(other.mQosBearerSessions);
return mCause == other.mCause
&& mSuggestedRetryTime == other.mSuggestedRetryTime
@@ -472,7 +472,7 @@
&& mHandoverFailureMode == other.mHandoverFailureMode
&& mPduSessionId == other.mPduSessionId
&& isQosSame
- && isQosSessionsSame
+ && isQosBearerSessionsSame
&& Objects.equals(mSliceInfo, other.mSliceInfo);
}
@@ -481,7 +481,7 @@
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
- mQosSessions, mSliceInfo);
+ mQosBearerSessions, mSliceInfo);
}
@Override
@@ -515,7 +515,7 @@
} else {
dest.writeParcelable(null, flags);
}
- dest.writeList(mQosSessions);
+ dest.writeList(mQosBearerSessions);
dest.writeParcelable(mSliceInfo, flags);
}
@@ -598,7 +598,7 @@
private Qos mDefaultQos;
- private List<QosSession> mQosSessions = new ArrayList<>();
+ private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
private SliceInfo mSliceInfo;
@@ -812,15 +812,16 @@
/**
* Set the dedicated bearer QOS sessions for this data connection.
*
- * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received
+ * @param qosBearerSessions Dedicated bearer QOS (Quality Of Service) sessions received
* from network.
*
* @return The same instance of the builder.
*
* @hide
*/
- public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) {
- mQosSessions = qosSessions;
+ public @NonNull Builder setQosBearerSessions(
+ @NonNull List<QosBearerSession> qosBearerSessions) {
+ mQosBearerSessions = qosBearerSessions;
return this;
}
@@ -848,7 +849,7 @@
return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
- mDefaultQos, mQosSessions, mSliceInfo);
+ mDefaultQos, mQosBearerSessions, mSliceInfo);
}
}
}
diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java
index ad43068..22c8b0a 100644
--- a/telephony/java/android/telephony/data/EpsQos.java
+++ b/telephony/java/android/telephony/data/EpsQos.java
@@ -48,6 +48,10 @@
qosClassId = source.readInt();
}
+ public int getQci() {
+ return qosClassId;
+ }
+
public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) {
return new EpsQos(in);
}
diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java
index c8bb91e..c286c621 100644
--- a/telephony/java/android/telephony/data/Qos.java
+++ b/telephony/java/android/telephony/data/Qos.java
@@ -56,7 +56,15 @@
this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps);
}
- static class QosBandwidth implements Parcelable {
+ public QosBandwidth getDownlinkBandwidth() {
+ return downlink;
+ }
+
+ public QosBandwidth getUplinkBandwidth() {
+ return uplink;
+ }
+
+ public static class QosBandwidth implements Parcelable {
int maxBitrateKbps;
int guaranteedBitrateKbps;
@@ -73,6 +81,14 @@
guaranteedBitrateKbps = source.readInt();
}
+ public int getMaxBitrateKbps() {
+ return maxBitrateKbps;
+ }
+
+ public int getGuaranteedBitrateKbps() {
+ return guaranteedBitrateKbps;
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(maxBitrateKbps);
diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java
similarity index 89%
rename from telephony/java/android/telephony/data/QosFilter.java
rename to telephony/java/android/telephony/data/QosBearerFilter.java
index 6927744..6c1c653 100644
--- a/telephony/java/android/telephony/data/QosFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -38,7 +38,7 @@
*
* @hide
*/
-public final class QosFilter implements Parcelable {
+public final class QosBearerFilter implements Parcelable {
private List<LinkAddress> localAddresses;
private List<LinkAddress> remoteAddresses;
@@ -74,7 +74,7 @@
@IntDef(prefix = "QOS_FILTER_DIRECTION_",
value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK,
QOS_FILTER_DIRECTION_BIDIRECTIONAL})
- public @interface QosFilterDirection {}
+ public @interface QosBearerFilterDirection {}
public static final int QOS_FILTER_DIRECTION_DOWNLINK =
android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK;
@@ -83,7 +83,7 @@
public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL =
android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL;
- @QosFilterDirection
+ @QosBearerFilterDirection
private int filterDirection;
/**
@@ -92,7 +92,7 @@
*/
private int precedence;
- QosFilter() {
+ QosBearerFilter() {
localAddresses = new ArrayList<>();
remoteAddresses = new ArrayList<>();
localPort = new PortRange();
@@ -101,7 +101,7 @@
filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL;
}
- public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
+ public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
PortRange localPort, PortRange remotePort, int protocol, int tos,
long flowLabel, long spi, int direction, int precedence) {
this.localAddresses = localAddresses;
@@ -116,10 +116,30 @@
this.precedence = precedence;
}
+ public List<LinkAddress> getLocalAddresses() {
+ return localAddresses;
+ }
+
+ public List<LinkAddress> getRemoteAddresses() {
+ return remoteAddresses;
+ }
+
+ public PortRange getLocalPortRange() {
+ return localPort;
+ }
+
+ public PortRange getRemotePortRange() {
+ return remotePort;
+ }
+
+ public int getPrecedence() {
+ return precedence;
+ }
+
/** @hide */
- public static @NonNull QosFilter create(
+ public static @NonNull QosBearerFilter create(
@NonNull android.hardware.radio.V1_6.QosFilter qosFilter) {
- QosFilter ret = new QosFilter();
+ QosBearerFilter ret = new QosBearerFilter();
String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new);
if (localAddresses != null) {
@@ -202,6 +222,14 @@
this.end = end;
}
+ public int getStart() {
+ return start;
+ }
+
+ public int getEnd() {
+ return end;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(start);
@@ -254,7 +282,7 @@
@Override
public String toString() {
- return "QosFilter {"
+ return "QosBearerFilter {"
+ " localAddresses=" + localAddresses
+ " remoteAddresses=" + remoteAddresses
+ " localPort=" + localPort
@@ -278,11 +306,11 @@
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || !(o instanceof QosFilter)) {
+ if (o == null || !(o instanceof QosBearerFilter)) {
return false;
}
- QosFilter other = (QosFilter) o;
+ QosBearerFilter other = (QosBearerFilter) o;
return localAddresses.size() == other.localAddresses.size()
&& localAddresses.containsAll(other.localAddresses)
@@ -324,7 +352,7 @@
LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN);
}
- private QosFilter(Parcel source) {
+ private QosBearerFilter(Parcel source) {
localAddresses = new ArrayList<>();
source.readList(localAddresses, LinkAddress.class.getClassLoader());
remoteAddresses = new ArrayList<>();
@@ -358,16 +386,16 @@
return 0;
}
- public static final @NonNull Parcelable.Creator<QosFilter> CREATOR =
- new Parcelable.Creator<QosFilter>() {
+ public static final @NonNull Parcelable.Creator<QosBearerFilter> CREATOR =
+ new Parcelable.Creator<QosBearerFilter>() {
@Override
- public QosFilter createFromParcel(Parcel source) {
- return new QosFilter(source);
+ public QosBearerFilter createFromParcel(Parcel source) {
+ return new QosBearerFilter(source);
}
@Override
- public QosFilter[] newArray(int size) {
- return new QosFilter[size];
+ public QosBearerFilter[] newArray(int size) {
+ return new QosBearerFilter[size];
}
};
}
diff --git a/telephony/java/android/telephony/data/QosBearerSession.java b/telephony/java/android/telephony/data/QosBearerSession.java
new file mode 100644
index 0000000..30effc9
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosBearerSession.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to QOS session.
+ *
+ * @hide
+ */
+public final class QosBearerSession implements Parcelable{
+
+ final int qosBearerSessionId;
+ final Qos qos;
+ final List<QosBearerFilter> qosBearerFilterList;
+
+ public QosBearerSession(int qosBearerSessionId, @NonNull Qos qos, @NonNull List<QosBearerFilter> qosBearerFilterList) {
+ this.qosBearerSessionId = qosBearerSessionId;
+ this.qos = qos;
+ this.qosBearerFilterList = qosBearerFilterList;
+ }
+
+ private QosBearerSession(Parcel source) {
+ qosBearerSessionId = source.readInt();
+ qos = source.readParcelable(Qos.class.getClassLoader());
+ qosBearerFilterList = new ArrayList<>();
+ source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader());
+ }
+
+ public int getQosBearerSessionId() {
+ return qosBearerSessionId;
+ }
+
+ public Qos getQos() {
+ return qos;
+ }
+
+ public List<QosBearerFilter> getQosBearerFilterList() {
+ return qosBearerFilterList;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(qosBearerSessionId);
+ if (qos.getType() == Qos.QOS_TYPE_EPS) {
+ dest.writeParcelable((EpsQos)qos, flags);
+ } else {
+ dest.writeParcelable((NrQos)qos, flags);
+ }
+ dest.writeList(qosBearerFilterList);
+ }
+
+ public static @NonNull QosBearerSession create(
+ @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
+ List<QosBearerFilter> qosBearerFilters = new ArrayList<>();
+
+ if (qosSession.qosFilters != null) {
+ for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
+ qosBearerFilters.add(QosBearerFilter.create(filter));
+ }
+ }
+
+ return new QosBearerSession(
+ qosSession.qosSessionId,
+ Qos.create(qosSession.qos),
+ qosBearerFilters);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "QosBearerSession {"
+ + " qosBearerSessionId=" + qosBearerSessionId
+ + " qos=" + qos
+ + " qosBearerFilterList=" + qosBearerFilterList + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(qosBearerSessionId, qos, qosBearerFilterList);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof QosBearerSession)) {
+ return false;
+ }
+
+ QosBearerSession other = (QosBearerSession) o;
+ return this.qosBearerSessionId == other.qosBearerSessionId
+ && this.qos.equals(other.qos)
+ && this.qosBearerFilterList.size() == other.qosBearerFilterList.size()
+ && this.qosBearerFilterList.containsAll(other.qosBearerFilterList);
+ }
+
+
+ public static final @NonNull Parcelable.Creator<QosBearerSession> CREATOR =
+ new Parcelable.Creator<QosBearerSession>() {
+ @Override
+ public QosBearerSession createFromParcel(Parcel source) {
+ return new QosBearerSession(source);
+ }
+
+ @Override
+ public QosBearerSession[] newArray(int size) {
+ return new QosBearerSession[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java
deleted file mode 100644
index f07b6a9..0000000
--- a/telephony/java/android/telephony/data/QosSession.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.data;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-
-/**
- * Class that stores information specific to QOS session.
- *
- * @hide
- */
-public final class QosSession implements Parcelable{
-
- final int qosSessionId;
- final Qos qos;
- final List<QosFilter> qosFilterList;
-
- public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) {
- this.qosSessionId = qosSessionId;
- this.qos = qos;
- this.qosFilterList = qosFilterList;
- }
-
- private QosSession(Parcel source) {
- qosSessionId = source.readInt();
- qos = source.readParcelable(Qos.class.getClassLoader());
- qosFilterList = new ArrayList<>();
- source.readList(qosFilterList, QosFilter.class.getClassLoader());
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(qosSessionId);
- if (qos.getType() == Qos.QOS_TYPE_EPS) {
- dest.writeParcelable((EpsQos)qos, flags);
- } else {
- dest.writeParcelable((NrQos)qos, flags);
- }
- dest.writeList(qosFilterList);
- }
-
- public static @NonNull QosSession create(
- @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
- List<QosFilter> qosFilters = new ArrayList<>();
-
- if (qosSession.qosFilters != null) {
- for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
- qosFilters.add(QosFilter.create(filter));
- }
- }
-
- return new QosSession(
- qosSession.qosSessionId,
- Qos.create(qosSession.qos),
- qosFilters);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public String toString() {
- return "QosSession {"
- + " qosSessionId=" + qosSessionId
- + " qos=" + qos
- + " qosFilterList=" + qosFilterList + "}";
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(qosSessionId, qos, qosFilterList);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
-
- if (o == null || !(o instanceof QosSession)) {
- return false;
- }
-
- QosSession other = (QosSession) o;
- return this.qosSessionId == other.qosSessionId
- && this.qos.equals(other.qos)
- && this.qosFilterList.size() == other.qosFilterList.size()
- && this.qosFilterList.containsAll(other.qosFilterList);
- }
-
-
- public static final @NonNull Parcelable.Creator<QosSession> CREATOR =
- new Parcelable.Creator<QosSession>() {
- @Override
- public QosSession createFromParcel(Parcel source) {
- return new QosSession(source);
- }
-
- @Override
- public QosSession[] newArray(int size) {
- return new QosSession[size];
- }
- };
-}
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index fb65949..6bf992e 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.telephony.ims.stub.SipDelegate;
import android.telephony.ims.stub.SipTransportImplBase;
@@ -52,7 +53,9 @@
* implementing this feature elsewhere. If all features of this {@link SipDelegate} are
* denied, this method should still be called.
*/
- void onCreated(@NonNull SipDelegate delegate, @Nullable Set<FeatureTagState> deniedTags);
+ void onCreated(@NonNull SipDelegate delegate,
+ @SuppressLint("NullableCollection") // TODO(b/154763999): Mark deniedTags @Nonnull
+ @Nullable Set<FeatureTagState> deniedTags);
/**
* This must be called by the ImsService after the framework calls
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index f444c62..31ba853 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -25,6 +25,7 @@
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -1325,7 +1326,7 @@
* the RCS VoLTE single registration feature. Only default messaging application may receive
* the intent.
*
- * <p>Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to specify the subscription index for which
+ * <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which
* the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
* status.
*/
@@ -1371,7 +1372,7 @@
* provisioning is done using autoconfiguration, then these parameters shall be
* sent in the HTTP get request to fetch the RCS provisioning. RCS client
* configuration must be provided by the application before registering for the
- * provisioning status events {@link #registerRcsProvisioningChangedCallback()}
+ * provisioning status events {@link #registerRcsProvisioningChangedCallback}
* @param rcc RCS client configuration {@link RcsClientConfiguration}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -1387,13 +1388,15 @@
}
/**
- * Returns a flag to indicate if the device software and the carrier
- * have the capability to support RCS Volte single IMS registration.
- * @return true if this single registration is capable, false otherwise
+ * Returns a flag to indicate whether or not the device supports IMS single registration for
+ * MMTEL and RCS features as well as if the carrier has provisioned the feature.
+ * @return true if IMS single registration is capable at this time, or false otherwise
* @throws ImsException If the remote ImsService is not available for
* any reason or the subscription associated with this instance is no
* longer active. See {@link ImsException#getCode()} for more
* information.
+ * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
+ * device supports IMS single registration.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
@@ -1430,7 +1433,7 @@
* available. This can happen if the service crashed, for example.
* It shall also throw this exception when the RCS client parameters for the
* application are not valid. In that case application must set the client
- * params (See {@link #setRcsClientConfiguration()}) and re register the
+ * params (See {@link #setRcsClientConfiguration}) and re register the
* callback.
* See {@link ImsException#getCode()} for a more detailed reason.
*/
@@ -1458,9 +1461,9 @@
* will result in a no-op.
* @param callback The existing {@link RcsProvisioningCallback} to be
* removed.
- * @see #registerRcsProvisioningChangedCallback(RcsClientConfiguration,
- * Executor, RcsProvisioningCallback) @throws IllegalArgumentException
- * if the subscription associated with this callback is invalid.
+ * @see #registerRcsProvisioningChangedCallback
+ * @throws IllegalArgumentException if the subscription associated with this callback is
+ * invalid.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterRcsProvisioningChangedCallback(
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 2e9eb94..04421c9 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -24,6 +24,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.telephony.BinderCacheManager;
@@ -47,6 +48,9 @@
* This allows multiple IMS applications to forward SIP messages to/from their application for the
* purposes of providing a single IMS registration to the carrier's IMS network from potentially
* many IMS stacks implementing a subset of the supported MMTEL/RCS features.
+ * <p>
+ * This API is only supported if the device supports the
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION} feature.
* @hide
*/
@SystemApi
@@ -269,6 +273,7 @@
* {@link ImsException#getCode()} for more information.
*
* @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL
+ * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isSupported() throws ImsException {
diff --git a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
index 481e7f8..b99d8a7d 100644
--- a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
@@ -26,4 +26,5 @@
oneway interface IPublishResponseCallback {
void onCommandError(int code);
void onNetworkResponse(int code, String reason);
+ void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
}
diff --git a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
index a141993..8cc8020 100644
--- a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
@@ -30,6 +30,7 @@
oneway interface ISubscribeResponseCallback {
void onCommandError(int code);
void onNetworkResponse(int code, in String reason);
+ void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
void onNotifyCapabilitiesUpdate(in List<String> pidfXmls);
void onResourceTerminated(in List<RcsContactTerminatedReason> uriTerminatedReason);
void onTerminated(in String reason, long retryAfterMilliseconds);
diff --git a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
index 22985d0..65415ea 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
@@ -34,10 +34,11 @@
}
@Override
- public void onCommandError(int code) {
+ public void onCommandError(int code) throws ImsException {
try {
mResponseBinder.onCommandError(code);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -46,6 +47,18 @@
try {
mResponseBinder.onNetworkResponse(code, reason);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ @Override
+ public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+ String reasonHeaderText) throws ImsException {
+ try {
+ mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
+ reasonHeaderText);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
}
diff --git a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
index 1fb339c..11118c0 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
@@ -40,10 +40,11 @@
}
@Override
- public void onCommandError(int code) {
+ public void onCommandError(int code) throws ImsException {
try {
mResponseBinder.onCommandError(code);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -52,6 +53,18 @@
try {
mResponseBinder.onNetworkResponse(code, reason);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ @Override
+ public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+ String reasonHeaderText) throws ImsException {
+ try {
+ mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
+ reasonHeaderText);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -60,6 +73,7 @@
try {
mResponseBinder.onNotifyCapabilitiesUpdate(pidfXmls);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -69,6 +83,7 @@
try {
mResponseBinder.onResourceTerminated(getTerminatedReasonList(uriTerminatedReason));
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -90,6 +105,7 @@
try {
mResponseBinder.onTerminated(reason, retryAfterMilliseconds);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 2e35d27..5f8e93d 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -44,8 +44,12 @@
@Override
public void setListener(IImsEcbmListener listener) {
synchronized (mLock) {
- if (mImsEcbm != null && listener != null && Objects.equals(
- mImsEcbm.asBinder(), listener.asBinder())) {
+ if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+ Log.w(TAG, "setListener: discarding dead Binder");
+ mListener = null;
+ }
+ if (mListener != null && listener != null && Objects.equals(
+ mListener.asBinder(), listener.asBinder())) {
return;
}
if (listener == null) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 555a47e..8e961ac 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -48,6 +48,10 @@
@Override
public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
synchronized (mLock) {
+ if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+ Log.w(TAG, "setListener: discarding dead Binder");
+ mListener = null;
+ }
if (mListener != null && listener != null && Objects.equals(
mListener.asBinder(), listener.asBinder())) {
return;
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index eef4fca..83b89aa 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.telephony.ims.ImsUtListener;
+import android.util.Log;
import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener;
@@ -41,6 +42,7 @@
// will break other implementations of ImsUt maintained by other ImsServices.
@SystemApi
public class ImsUtImplBase {
+ private static final String TAG = "ImsUtImplBase";
/**
* Bar all incoming calls. (See 3GPP TS 24.611)
* @hide
@@ -207,6 +209,11 @@
@Override
public void setListener(IImsUtListener listener) throws RemoteException {
synchronized (mLock) {
+ if (mUtListener != null
+ && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
+ Log.w(TAG, "setListener: discarding dead Binder");
+ mUtListener = null;
+ }
if (mUtListener != null && listener != null && Objects.equals(
mUtListener.getListenerInterface().asBinder(), listener.asBinder())) {
return;
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 7eba709..ec98be6 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -140,6 +140,9 @@
* Provide the framework with a subsequent network response update to
* {@link #publishCapabilities(String, PublishResponseCallback)}.
*
+ * If this network response also contains a “Reason” header, then the
+ * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+ *
* @param sipCode The SIP response code sent from the network for the operation
* token specified.
* @param reason The optional reason response from the network. If there is a reason header
@@ -154,6 +157,31 @@
*/
void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
@NonNull String reason) throws ImsException;
+
+ /**
+ * Provide the framework with a subsequent network response update to
+ * {@link #publishCapabilities(RcsContactUceCapability, int)} that also
+ * includes a reason provided in the “reason” header. See RFC3326 for more
+ * information.
+ *
+ * @param sipCode The SIP response code sent from the network.
+ * @param reasonPhrase The optional reason response from the network. If the
+ * network provided no reason with the sip code, the string should be empty.
+ * @param reasonHeaderCause The “cause” parameter of the “reason” header
+ * included in the SIP message.
+ * @param reasonHeaderText The “text” parameter of the “reason” header
+ * included in the SIP message.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework. This can happen if the
+ * {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+ * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+ * rare cases when the Telephony stack has crashed.
+ */
+ void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+ @NonNull String reasonPhrase,
+ @IntRange(from = 100, to = 699) int reasonHeaderCause,
+ @NonNull String reasonHeaderText) throws ImsException;
}
/**
@@ -222,6 +250,9 @@
* {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
* subsequent NOTIFY responses to the subscription.
*
+ * If this network response also contains a “Reason” header, then the
+ * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+ *
* @param sipCode The SIP response code sent from the network for the operation
* token specified.
* @param reason The optional reason response from the network. If the network
@@ -236,6 +267,31 @@
@NonNull String reason) throws ImsException;
/**
+ * Notify the framework of the response to the SUBSCRIBE request from
+ * {@link #subscribeForCapabilities(RcsContactUceCapability, int)} that also
+ * includes a reason provided in the “reason” header. See RFC3326 for more
+ * information.
+ *
+ * @param sipCode The SIP response code sent from the network,
+ * @param reasonPhrase The optional reason response from the network. If the
+ * network provided no reason with the sip code, the string should be empty.
+ * @param reasonHeaderCause The “cause” parameter of the “reason” header
+ * included in the SIP message.
+ * @param reasonHeaderText The “text” parameter of the “reason” header
+ * included in the SIP message.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework. This can happen if the
+ * {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+ * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+ * rare cases when the Telephony stack has crashed.
+ */
+ void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+ @NonNull String reasonPhrase,
+ @IntRange(from = 100, to = 699) int reasonHeaderCause,
+ @NonNull String reasonHeaderText) throws ImsException;
+
+ /**
* Notify the framework of the latest XML PIDF documents included in the network response
* for the requested contacts' capabilities requested by the Framework using
* {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d17415a..0cd17da3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2172,7 +2172,7 @@
*/
String getMmsUAProfUrl(int subId);
- void setMobileDataPolicyEnabledStatus(int subscriptionId, int policy, boolean enabled);
+ void setMobileDataPolicyEnabled(int subscriptionId, int policy, boolean enabled);
boolean isMobileDataPolicyEnabled(int subscriptionId, int policy);
diff --git a/tests/UpdatableSystemFontTest/OWNERS b/tests/UpdatableSystemFontTest/OWNERS
new file mode 100644
index 0000000..34ac813
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 24939
+
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/tests/UpdatableSystemFontTest/TEST_MAPPING b/tests/UpdatableSystemFontTest/TEST_MAPPING
new file mode 100644
index 0000000..a5c4479
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "UpdatableSystemFontTest"
+ }
+ ]
+}
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index cade5ba..d232a507 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -20,22 +20,20 @@
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Build;
-import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
@IgnoreUpTo(Build.VERSION_CODES.R)
@RunWith(DevSdkIgnoreRunner.class)
@@ -45,51 +43,51 @@
private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT;
private static final String TEST_PACKAGE = "com.google.apps.contacts";
- private final List<String> mPackages = new ArrayList<>();
private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder();
- @Before
- public void beforeEachTestMethod() {
- mPackages.add(TEST_PACKAGE);
- }
-
@Test
- public void builderAddNetworkPreferenceRequiresNonNullPackages() {
+ public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() {
assertThrows(NullPointerException.class,
- () -> mBuilder.addNetworkPreference(TEST_PREF, null));
+ () -> mBuilder.addNetworkPreference(null, TEST_PREF));
}
@Test
- public void getNetworkPreferencesReturnsCorrectValue() {
- final int expectedNumberOfMappings = 1;
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() {
+ assertThrows(NullPointerException.class,
+ () -> mBuilder.removeNetworkPreference(null));
+ }
- final SparseArray<List<String>> networkPreferences =
+ @Test
+ public void testGetNetworkPreferenceReturnsCorrectValue() {
+ final int expectedNumberOfMappings = 1;
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+
+ final Map<String, Integer> networkPreferences =
mBuilder.build().getNetworkPreferences();
assertEquals(expectedNumberOfMappings, networkPreferences.size());
- assertEquals(mPackages.size(), networkPreferences.get(TEST_PREF).size());
- assertEquals(mPackages.get(0), networkPreferences.get(TEST_PREF).get(0));
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
}
@Test
- public void getNetworkPreferencesReturnsUnmodifiableValue() {
+ public void testGetNetworkPreferenceReturnsUnmodifiableValue() {
final String newPackage = "new.com.google.apps.contacts";
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
- final SparseArray<List<String>> networkPreferences =
+ final Map<String, Integer> networkPreferences =
mBuilder.build().getNetworkPreferences();
assertThrows(UnsupportedOperationException.class,
- () -> networkPreferences.get(TEST_PREF).set(mPackages.size() - 1, newPackage));
+ () -> networkPreferences.put(newPackage, TEST_PREF));
assertThrows(UnsupportedOperationException.class,
- () -> networkPreferences.get(TEST_PREF).add(newPackage));
+ () -> networkPreferences.remove(TEST_PACKAGE));
+
}
@Test
- public void toStringReturnsCorrectValue() {
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ public void testToStringReturnsCorrectValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString();
@@ -99,10 +97,56 @@
@Test
public void testOemNetworkPreferencesParcelable() {
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
final OemNetworkPreferences prefs = mBuilder.build();
assertParcelSane(prefs, 1 /* fieldCount */);
}
+
+ @Test
+ public void testAddNetworkPreferenceOverwritesPriorPreference() {
+ final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ Map<String, Integer> networkPreferences =
+ mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+
+ mBuilder.addNetworkPreference(TEST_PACKAGE, newPref);
+ networkPreferences = mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref);
+ }
+
+ @Test
+ public void testRemoveNetworkPreferenceRemovesValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ Map<String, Integer> networkPreferences =
+ mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+
+ mBuilder.removeNetworkPreference(TEST_PACKAGE);
+ networkPreferences = mBuilder.build().getNetworkPreferences();
+
+ assertFalse(networkPreferences.containsKey(TEST_PACKAGE));
+ }
+
+ @Test
+ public void testConstructorByOemNetworkPreferencesSetsValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ OemNetworkPreferences networkPreference = mBuilder.build();
+
+ final Map<String, Integer> networkPreferences =
+ new OemNetworkPreferences
+ .Builder(networkPreference)
+ .build()
+ .getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+ }
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index bf73134..bcbc9e0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -132,6 +132,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.NotificationManager;
@@ -252,6 +253,7 @@
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.net.module.util.ArrayTrackRecord;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.MockableSystemProperties;
@@ -329,11 +331,12 @@
private static final String TAG = "ConnectivityServiceTest";
private static final int TIMEOUT_MS = 500;
- private static final int TEST_LINGER_DELAY_MS = 300;
- // Chosen to be less than the linger timeout. This ensures that we can distinguish between a
- // LOST callback that arrives immediately and a LOST callback that arrives after the linger
- // timeout. For this, our assertions should run fast enough to leave less than
- // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
+ private static final int TEST_LINGER_DELAY_MS = 400;
+ private static final int TEST_NASCENT_DELAY_MS = 300;
+ // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish
+ // between a LOST callback that arrives immediately and a LOST callback that arrives after
+ // the linger/nascent timeout. For this, our assertions should run fast enough to leave
+ // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
// supposedly fired, and the time we call expectCallback.
private static final int TEST_CALLBACK_TIMEOUT_MS = 250;
// Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
@@ -906,28 +909,69 @@
}
/**
- * A NetworkFactory that allows tests to wait until any in-flight NetworkRequest add or remove
- * operations have been processed. Before ConnectivityService can add or remove any requests,
- * the factory must be told to expect those operations by calling expectAddRequestsWithScores or
- * expectRemoveRequests.
+ * A NetworkFactory that allows to wait until any in-flight NetworkRequest add or remove
+ * operations have been processed and test for them.
*/
private static class MockNetworkFactory extends NetworkFactory {
private final ConditionVariable mNetworkStartedCV = new ConditionVariable();
private final ConditionVariable mNetworkStoppedCV = new ConditionVariable();
private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false);
- // Used to expect that requests be removed or added on a separate thread, without sleeping.
- // Callers can call either expectAddRequestsWithScores() or expectRemoveRequests() exactly
- // once, then cause some other thread to add or remove requests, then call
- // waitForRequests().
- // It is not possible to wait for both add and remove requests. When adding, the queue
- // contains the expected score. When removing, the value is unused, all matters is the
- // number of objects in the queue.
- private final LinkedBlockingQueue<Integer> mExpectations;
+ static class RequestEntry {
+ @NonNull
+ public final NetworkRequest request;
- // Whether we are currently expecting requests to be added or removed. Valid only if
- // mExpectations is non-empty.
- private boolean mExpectingAdditions;
+ RequestEntry(@NonNull final NetworkRequest request) {
+ this.request = request;
+ }
+
+ static final class Add extends RequestEntry {
+ public final int factorySerialNumber;
+
+ Add(@NonNull final NetworkRequest request, final int factorySerialNumber) {
+ super(request);
+ this.factorySerialNumber = factorySerialNumber;
+ }
+ }
+
+ static final class Remove extends RequestEntry {
+ Remove(@NonNull final NetworkRequest request) {
+ super(request);
+ }
+ }
+ }
+
+ // History of received requests adds and removes.
+ private final ArrayTrackRecord<RequestEntry>.ReadHead mRequestHistory =
+ new ArrayTrackRecord<RequestEntry>().newReadHead();
+
+ private static <T> T failIfNull(@Nullable final T obj, @Nullable final String message) {
+ if (null == obj) fail(null != message ? message : "Must not be null");
+ return obj;
+ }
+
+
+ public RequestEntry.Add expectRequestAdd() {
+ return failIfNull((RequestEntry.Add) mRequestHistory.poll(TIMEOUT_MS,
+ it -> it instanceof RequestEntry.Add), "Expected request add");
+ }
+
+ public void expectRequestAdds(final int count) {
+ for (int i = count; i > 0; --i) {
+ expectRequestAdd();
+ }
+ }
+
+ public RequestEntry.Remove expectRequestRemove() {
+ return failIfNull((RequestEntry.Remove) mRequestHistory.poll(TIMEOUT_MS,
+ it -> it instanceof RequestEntry.Remove), "Expected request remove");
+ }
+
+ public void expectRequestRemoves(final int count) {
+ for (int i = count; i > 0; --i) {
+ expectRequestRemove();
+ }
+ }
// Used to collect the networks requests managed by this factory. This is a duplicate of
// the internal information stored in the NetworkFactory (which is private).
@@ -936,7 +980,6 @@
public MockNetworkFactory(Looper looper, Context context, String logTag,
NetworkCapabilities filter) {
super(looper, context, logTag, filter);
- mExpectations = new LinkedBlockingQueue<>();
}
public int getMyRequestCount() {
@@ -970,95 +1013,33 @@
@Override
protected void handleAddRequest(NetworkRequest request, int score,
int factorySerialNumber) {
- synchronized (mExpectations) {
- final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
-
- assertNotNull("Added more requests than expected (" + request + " score : "
- + score + ")", expectedScore);
- // If we're expecting anything, we must be expecting additions.
- if (!mExpectingAdditions) {
- fail("Can't add requests while expecting requests to be removed");
- }
- if (expectedScore != score) {
- fail("Expected score was " + expectedScore + " but actual was " + score
- + " in added request");
- }
-
- // Add the request.
- mNetworkRequests.put(request.requestId, request);
- super.handleAddRequest(request, score, factorySerialNumber);
- mExpectations.notify();
- }
+ mNetworkRequests.put(request.requestId, request);
+ super.handleAddRequest(request, score, factorySerialNumber);
+ mRequestHistory.add(new RequestEntry.Add(request, factorySerialNumber));
}
@Override
protected void handleRemoveRequest(NetworkRequest request) {
- synchronized (mExpectations) {
- final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
+ mNetworkRequests.remove(request.requestId);
+ super.handleRemoveRequest(request);
+ mRequestHistory.add(new RequestEntry.Remove(request));
+ }
- assertTrue("Removed more requests than expected", expectedScore != null);
- // If we're expecting anything, we must be expecting removals.
- if (mExpectingAdditions) {
- fail("Can't remove requests while expecting requests to be added");
- }
+ public void assertRequestCountEquals(final int count) {
+ assertEquals(count, getMyRequestCount());
+ }
- // Remove the request.
- mNetworkRequests.remove(request.requestId);
- super.handleRemoveRequest(request);
- mExpectations.notify();
- }
+ @Override
+ public void terminate() {
+ super.terminate();
+ // Make sure there are no remaining requests unaccounted for.
+ assertNull(mRequestHistory.poll(TIMEOUT_MS, r -> true));
}
// Trigger releasing the request as unfulfillable
public void triggerUnfulfillable(NetworkRequest r) {
super.releaseRequestAsUnfulfillableByAnyFactory(r);
}
-
- private void assertNoExpectations() {
- if (mExpectations.size() != 0) {
- fail("Can't add expectation, " + mExpectations.size() + " already pending");
- }
- }
-
- // Expects that requests with the specified scores will be added.
- public void expectAddRequestsWithScores(final int... scores) {
- assertNoExpectations();
- mExpectingAdditions = true;
- for (int score : scores) {
- mExpectations.add(score);
- }
- }
-
- // Expects that count requests will be removed.
- public void expectRemoveRequests(final int count) {
- assertNoExpectations();
- mExpectingAdditions = false;
- for (int i = 0; i < count; ++i) {
- mExpectations.add(0); // For removals the score is ignored so any value will do.
- }
- }
-
- // Waits for the expected request additions or removals to happen within a timeout.
- public void waitForRequests() throws InterruptedException {
- final long deadline = SystemClock.elapsedRealtime() + TIMEOUT_MS;
- synchronized (mExpectations) {
- while (mExpectations.size() > 0 && SystemClock.elapsedRealtime() < deadline) {
- mExpectations.wait(deadline - SystemClock.elapsedRealtime());
- }
- }
- final long count = mExpectations.size();
- final String msg = count + " requests still not " +
- (mExpectingAdditions ? "added" : "removed") +
- " after " + TIMEOUT_MS + " ms";
- assertEquals(msg, 0, count);
- }
-
- public SparseArray<NetworkRequest> waitForNetworkRequests(final int count)
- throws InterruptedException {
- waitForRequests();
- assertEquals(count, getMyRequestCount());
- return mNetworkRequests;
- }
}
private Set<UidRange> uidRangesForUid(int uid) {
@@ -1282,22 +1263,35 @@
}
}
- private void updateUidNetworkingBlocked() {
- doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked(
- i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */,
- mRestrictBackground)
+ private void processBroadcastForVpn(Intent intent) {
+ // The BroadcastReceiver for this broadcast checks it is being run on the handler thread.
+ final Handler handler = new Handler(mCsHandlerThread.getLooper());
+ handler.post(() -> mServiceContext.sendBroadcast(intent));
+ waitForIdle();
+ }
+
+ private void mockUidNetworkingBlocked() {
+ doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
+ .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
+ i.getArgument(1) /* metered */, mRestrictBackground)
).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
+
+ doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class)
+ .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */,
+ inv.getArgument(1) /* uidRules */,
+ inv.getArgument(2) /* isNetworkMetered */,
+ inv.getArgument(3) /* isBackgroundRestricted */)
+ ).when(mNetworkPolicyManager).checkUidNetworkingBlocked(
+ anyInt(), anyInt(), anyBoolean(), anyBoolean());
}
private void setUidRulesChanged(int uidRules) throws RemoteException {
mUidRules = uidRules;
- updateUidNetworkingBlocked();
mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
}
private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
mRestrictBackground = restrictBackground;
- updateUidNetworkingBlocked();
mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
}
@@ -1409,6 +1403,7 @@
mMockNetd,
mDeps);
mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
+ mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
@@ -1751,6 +1746,108 @@
verifyNoNetwork();
}
+ /**
+ * Verify a newly created network will be inactive instead of torn down even if no one is
+ * requesting.
+ */
+ @Test
+ public void testNewNetworkInactive() throws Exception {
+ // Create a callback that monitoring the testing network.
+ final TestNetworkCallback listenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback);
+
+ // 1. Create a network that is not requested by anyone, and does not satisfy any of the
+ // default requests. Verify that the network will be inactive instead of torn down.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ listenCallback.assertNoCallback();
+
+ // Verify that the network will be torn down after nascent expiry. A small period of time
+ // is added in case of flakiness.
+ final int nascentTimeoutMs =
+ mService.mNascentDelayMs + mService.mNascentDelayMs / 4;
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs);
+
+ // 2. Create a network that is satisfied by a request comes later.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final TestNetworkCallback wifiCallback = new TestNetworkCallback();
+ mCm.requestNetwork(wifiRequest, wifiCallback);
+ wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Verify that the network will be kept since the request is still satisfied. And is able
+ // to get disconnected as usual if the request is released after the nascent timer expires.
+ listenCallback.assertNoCallback(nascentTimeoutMs);
+ mCm.unregisterNetworkCallback(wifiCallback);
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // 3. Create a network that is satisfied by a request comes later.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ mCm.requestNetwork(wifiRequest, wifiCallback);
+ wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Verify that the network will still be torn down after the request gets removed.
+ mCm.unregisterNetworkCallback(wifiCallback);
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // There is no need to ensure that LOSING is never sent in the common case that the
+ // network immediately satisfies a request that was already present, because it is already
+ // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called.
+
+ mCm.unregisterNetworkCallback(listenCallback);
+ }
+
+ /**
+ * Verify a newly created network will be inactive and switch to background if only background
+ * request is satisfied.
+ */
+ @Test
+ public void testNewNetworkInactive_bgNetwork() throws Exception {
+ // Create a callback that monitoring the wifi network.
+ final TestNetworkCallback wifiListenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback);
+
+ // Create callbacks that can monitor background and foreground mobile networks.
+ // This is done by granting using background networks permission before registration. Thus,
+ // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default.
+ grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
+ final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback();
+ final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback);
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback);
+
+ // Connect wifi, which satisfies default request.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+
+ // Connect a cellular network, verify that satisfies only the background callback.
+ setAlwaysOnNetworks(true);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ fgMobileListenCallback.assertNoCallback();
+ assertFalse(isForegroundNetwork(mCellNetworkAgent));
+
+ mCellNetworkAgent.disconnect();
+ bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ fgMobileListenCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(wifiListenCallback);
+ mCm.unregisterNetworkCallback(bgMobileListenCallback);
+ mCm.unregisterNetworkCallback(fgMobileListenCallback);
+ }
+
@Test
public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
// Test bringing up unvalidated WiFi
@@ -2595,12 +2692,6 @@
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
}
- private int[] makeIntArray(final int size, final int value) {
- final int[] array = new int[size];
- Arrays.fill(array, value);
- return array;
- }
-
private void tryNetworkFactoryRequests(int capability) throws Exception {
// Verify NOT_RESTRICTED is set appropriately
final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability)
@@ -2622,9 +2713,9 @@
mServiceContext, "testFactory", filter);
testFactory.setScoreFilter(40);
ConditionVariable cv = testFactory.getNetworkStartedCV();
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
int expectedRequestCount = 1;
NetworkCallback networkCallback = null;
// For non-INTERNET capabilities we cannot rely on the default request being present, so
@@ -2633,13 +2724,12 @@
assertFalse(testFactory.getMyStartRequested());
NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build();
networkCallback = new NetworkCallback();
- testFactory.expectAddRequestsWithScores(0); // New request
mCm.requestNetwork(request, networkCallback);
expectedRequestCount++;
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdd();
}
waitFor(cv);
- assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertTrue(testFactory.getMyStartRequested());
// Now bring in a higher scored network.
@@ -2653,15 +2743,14 @@
// When testAgent connects, ConnectivityService will re-send us all current requests with
// the new score. There are expectedRequestCount such requests, and we must wait for all of
// them.
- testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 50));
testAgent.connect(false);
testAgent.addCapability(capability);
waitFor(cv);
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdds(expectedRequestCount);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Bring in a bunch of requests.
- testFactory.expectAddRequestsWithScores(makeIntArray(10, 50));
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
ConnectivityManager.NetworkCallback[] networkCallbacks =
new ConnectivityManager.NetworkCallback[10];
@@ -2671,24 +2760,24 @@
builder.addCapability(capability);
mCm.requestNetwork(builder.build(), networkCallbacks[i]);
}
- testFactory.waitForNetworkRequests(10 + expectedRequestCount);
+ testFactory.expectRequestAdds(10);
+ testFactory.assertRequestCountEquals(10 + expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Remove the requests.
- testFactory.expectRemoveRequests(10);
for (int i = 0; i < networkCallbacks.length; i++) {
mCm.unregisterNetworkCallback(networkCallbacks[i]);
}
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestRemoves(10);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Drop the higher scored network.
cv = testFactory.getNetworkStartedCV();
- // With the default network disconnecting, the requests are sent with score 0 to factories.
- testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 0));
testAgent.disconnect();
waitFor(cv);
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdds(expectedRequestCount);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
assertTrue(testFactory.getMyStartRequested());
@@ -2731,9 +2820,8 @@
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter);
// Register the factory and don't be surprised when the default request arrives.
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
testFactory.setScoreFilter(42);
testFactory.terminate();
@@ -3876,38 +3964,37 @@
testFactory.setScoreFilter(40);
// Register the factory and expect it to start looking for a network.
- testFactory.expectAddRequestsWithScores(0); // Score 0 as the request is not served yet.
testFactory.register();
try {
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
assertTrue(testFactory.getMyStartRequested());
// Bring up wifi. The factory stops looking for a network.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
// Score 60 - 40 penalty for not validated yet, then 60 when it validates
- testFactory.expectAddRequestsWithScores(20, 60);
mWiFiNetworkAgent.connect(true);
- testFactory.waitForRequests();
+ // Default request and mobile always on request
+ testFactory.expectRequestAdds(2);
assertFalse(testFactory.getMyStartRequested());
- ContentResolver cr = mServiceContext.getContentResolver();
-
// Turn on mobile data always on. The factory starts looking again.
- testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0
setAlwaysOnNetworks(true);
- testFactory.waitForNetworkRequests(2);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(2);
+
assertTrue(testFactory.getMyStartRequested());
// Bring up cell data and check that the factory stops looking.
assertLength(1, mCm.getAllNetworks());
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- testFactory.waitForNetworkRequests(2);
- assertFalse(
- testFactory.getMyStartRequested()); // Because the cell network outscores us.
+ testFactory.expectRequestAdds(2); // Unvalidated and validated
+ testFactory.assertRequestCountEquals(2);
+ // The cell network outscores the factory filter, so start is not requested.
+ assertFalse(testFactory.getMyStartRequested());
// Check that cell data stays up.
waitForIdle();
@@ -3915,12 +4002,12 @@
assertLength(2, mCm.getAllNetworks());
// Turn off mobile data always on and expect the request to disappear...
- testFactory.expectRemoveRequests(1);
setAlwaysOnNetworks(false);
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestRemove();
- // ... and cell data to be torn down.
- cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ // ... and cell data to be torn down after nascent network timeout.
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+ mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS);
assertLength(1, mCm.getAllNetworks());
} finally {
testFactory.terminate();
@@ -4224,46 +4311,33 @@
testFactory.setScoreFilter(40);
// Register the factory and expect it to receive the default request.
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- SparseArray<NetworkRequest> requests = testFactory.waitForNetworkRequests(1);
-
- assertEquals(1, requests.size()); // have 1 request at this point
- int origRequestId = requests.valueAt(0).requestId;
+ testFactory.expectRequestAdd();
// Now file the test request and expect it.
- testFactory.expectAddRequestsWithScores(0);
mCm.requestNetwork(nr, networkCallback);
- requests = testFactory.waitForNetworkRequests(2); // have 2 requests at this point
+ final NetworkRequest newRequest = testFactory.expectRequestAdd().request;
- int newRequestId = 0;
- for (int i = 0; i < requests.size(); ++i) {
- if (requests.valueAt(i).requestId != origRequestId) {
- newRequestId = requests.valueAt(i).requestId;
- break;
- }
- }
-
- testFactory.expectRemoveRequests(1);
if (preUnregister) {
mCm.unregisterNetworkCallback(networkCallback);
// Simulate the factory releasing the request as unfulfillable: no-op since
// the callback has already been unregistered (but a test that no exceptions are
// thrown).
- testFactory.triggerUnfulfillable(requests.get(newRequestId));
+ testFactory.triggerUnfulfillable(newRequest);
} else {
// Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
- testFactory.triggerUnfulfillable(requests.get(newRequestId));
+ testFactory.triggerUnfulfillable(newRequest);
networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
- testFactory.waitForRequests();
// unregister network callback - a no-op (since already freed by the
// on-unavailable), but should not fail or throw exceptions.
mCm.unregisterNetworkCallback(networkCallback);
}
+ testFactory.expectRequestRemove();
+
testFactory.terminate();
handlerThread.quit();
}
@@ -5338,20 +5412,20 @@
// MOBILE_IFNAME even though the default network is wifi.
// TODO: fix this to pass in the actual default network interface. Whether or not the VPN
// applies to the system server UID should not have any bearing on network stats.
- mService.setUnderlyingNetworksForVpn(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME});
reset(mStatsService);
- mService.setUnderlyingNetworksForVpn(cellAndWifi);
+ mMockVpn.setUnderlyingNetworks(cellAndWifi);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
reset(mStatsService);
// Null underlying networks are ignored.
- mService.setUnderlyingNetworksForVpn(cellNullAndWifi);
+ mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
@@ -5400,25 +5474,25 @@
// is probably a performance improvement (though it's very unlikely that a VPN would declare
// no underlying networks).
// Also, for the same reason as above, the active interface passed in is null.
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Specifying only a null underlying network is the same as no networks.
- mService.setUnderlyingNetworksForVpn(onlyNull);
+ mMockVpn.setUnderlyingNetworks(onlyNull);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Specifying networks that are all disconnected is the same as specifying no networks.
- mService.setUnderlyingNetworksForVpn(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Passing in null again means follow the default network again.
- mService.setUnderlyingNetworksForVpn(null);
+ mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{WIFI_IFNAME});
@@ -5893,7 +5967,7 @@
mMockVpn.establishForMyUid(false, true, false);
assertUidRangesUpdatedForMyUid(true);
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
- mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
+ mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
@@ -6087,7 +6161,7 @@
final Set<UidRange> ranges = uidRangesForUid(uid);
mMockVpn.registerAgent(ranges);
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
@@ -6335,7 +6409,7 @@
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mCellNetworkAgent.connect(true);
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6350,7 +6424,7 @@
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mWiFiNetworkAgent.connect(true);
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6361,7 +6435,7 @@
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Don't disconnect, but note the VPN is not using wifi any more.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6392,7 +6466,7 @@
vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
// Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6403,7 +6477,7 @@
assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
// Use both again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6418,7 +6492,7 @@
vpnNetworkCallback.assertNoCallback();
// Stop using WiFi. The VPN is suspended again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
@@ -6429,7 +6503,7 @@
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Use both again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6564,9 +6638,7 @@
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
// Send a USER_ADDED broadcast for it.
- // The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+ processBroadcastForVpn(addedIntent);
// Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
// restricted user.
@@ -6590,7 +6662,7 @@
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
+ processBroadcastForVpn(removedIntent);
// Expect that the VPN gains the UID range for the restricted user, and that the capability
// change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
@@ -6647,9 +6719,7 @@
// TODO: check that VPN app within restricted profile still has access, etc.
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
- waitForIdle();
+ processBroadcastForVpn(addedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
@@ -6659,8 +6729,7 @@
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
- waitForIdle();
+ processBroadcastForVpn(removedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
@@ -6762,7 +6831,7 @@
// Ensure VPN is now the active network.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is using Cell
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
waitForIdle();
@@ -6770,7 +6839,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// VPN is now using WiFi
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6778,7 +6847,7 @@
assertFalse(mCm.isActiveNetworkMetered());
// VPN is using Cell | WiFi.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6786,7 +6855,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// VPN is using WiFi | Cell.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() });
waitForIdle();
@@ -6794,7 +6863,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// VPN is not using any underlying networks.
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
// VPN without underlying networks is treated as metered.
@@ -6821,7 +6890,7 @@
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is tracking current platform default (WiFi).
- mService.setUnderlyingNetworksForVpn(null);
+ mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
// Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
@@ -6829,7 +6898,7 @@
// VPN explicitly declares WiFi as its underlying network.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6853,6 +6922,7 @@
.addTransportType(TRANSPORT_CELLULAR)
.build();
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+ mockUidNetworkingBlocked();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
@@ -6935,6 +7005,7 @@
public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
+ mockUidNetworkingBlocked();
// No Networkcallbacks invoked before any network is active.
setUidRulesChanged(RULE_REJECT_ALL);
@@ -7204,6 +7275,13 @@
when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
}
+ private void establishLegacyLockdownVpn() throws Exception {
+ // The legacy lockdown VPN only supports userId 0.
+ final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ mMockVpn.registerAgent(ranges);
+ mMockVpn.connect(true);
+ }
+
@Test
public void testLegacyLockdownVpn() throws Exception {
mServiceContext.setPermission(
@@ -7228,9 +7306,7 @@
final int userId = UserHandle.getUserId(Process.myUid());
final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
- waitForIdle();
+ processBroadcastForVpn(addedIntent);
// Lockdown VPN disables teardown and enables lockdown.
assertFalse(mMockVpn.getEnableTeardown());
@@ -7298,22 +7374,30 @@
mMockVpn.expectStartLegacyVpnRunner();
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
- mMockVpn.establishForMyUid();
+ establishLegacyLockdownVpn();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
b1.expectBroadcast();
b2.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+ assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName("wlan0");
wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+ final NetworkCapabilities wifiNc = new NetworkCapabilities();
+ wifiNc.addTransportType(TRANSPORT_WIFI);
+ wifiNc.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc);
b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
// Wifi is CONNECTING because the VPN isn't up yet.
@@ -7346,16 +7430,20 @@
// The VPN comes up again on wifi.
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
- mMockVpn.establishForMyUid();
+ establishLegacyLockdownVpn();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
b1.expectBroadcast();
b2.expectBroadcast();
-
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
+ assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+ assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect cell. Nothing much happens since it's not the default network.
// Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
@@ -8399,13 +8487,14 @@
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ mMockVpn.setVpnType(vpnType);
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
- mMockVpn.setVpnType(vpnType);
final UnderlyingNetworkInfo underlyingNetworkInfo =
new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<String>());
mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo);
+ when(mDeps.getConnectionOwnerUid(anyInt(), any(), any())).thenReturn(42);
}
private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -8454,8 +8543,7 @@
final int myUid = Process.myUid();
setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
@Test
@@ -8465,8 +8553,7 @@
mServiceContext.setPermission(
android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
@Test
@@ -8477,8 +8564,7 @@
mServiceContext.setPermission(
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
@@ -8662,7 +8748,7 @@
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network}));
+ assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network}));
waitForIdle();
assertTrue(
"Active VPN permission not applied",
@@ -8670,7 +8756,7 @@
Process.myPid(), Process.myUid(), naiWithoutUid,
mContext.getOpPackageName()));
- assertTrue(mService.setUnderlyingNetworksForVpn(null));
+ assertTrue(mMockVpn.setUnderlyingNetworks(null));
waitForIdle();
assertFalse(
"VPN shouldn't receive callback on non-underlying network",
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index e590fb7..a10a3c8 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -55,7 +55,7 @@
private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
private val mMockService = mock(ConnectivityService::class.java).apply {
- doReturn(false).`when`(this).isFallbackNetwork(any())
+ doReturn(false).`when`(this).isDefaultNetwork(any())
}
private val mTracker = LegacyTypeTracker(mMockService).apply {
supportedTypes.forEach {
@@ -126,11 +126,11 @@
fun testBroadcastOnDisconnect() {
val mobileNai1 = mock(NetworkAgentInfo::class.java)
val mobileNai2 = mock(NetworkAgentInfo::class.java)
- doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai1)
+ doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1)
mTracker.add(TYPE_MOBILE, mobileNai1)
verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE)
reset(mMockService)
- doReturn(false).`when`(mMockService).isFallbackNetwork(mobileNai2)
+ doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2)
mTracker.add(TYPE_MOBILE, mobileNai2)
verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt())
mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */)
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 68aaaed..73cc9f1 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -49,6 +49,7 @@
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -119,6 +120,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -148,6 +150,7 @@
managedProfileA.profileGroupId = primaryUser.id;
}
+ static final Network EGRESS_NETWORK = new Network(101);
static final String EGRESS_IFACE = "wlan0";
static final String TEST_VPN_PKG = "com.testvpn.vpn";
private static final String TEST_VPN_SERVER = "1.2.3.4";
@@ -212,6 +215,8 @@
when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
+ when(mContext.getSystemServiceName(UserManager.class))
+ .thenReturn(Context.USER_SERVICE);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
when(mContext.getSystemServiceName(NotificationManager.class))
@@ -252,12 +257,14 @@
@Test
public void testRestrictedProfilesAreAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
final Vpn vpn = createVpn(primaryUser.id);
- final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
- null, null);
+
+ // Assume the user can have restricted profiles.
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ final Set<UidRange> ranges =
+ vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
@@ -266,7 +273,6 @@
@Test
public void testManagedProfilesAreNotAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, managedProfileA);
final Vpn vpn = createVpn(primaryUser.id);
@@ -289,7 +295,6 @@
@Test
public void testUidAllowAndDenylist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
@@ -315,7 +320,6 @@
@Test
public void testGetAlwaysAndOnGetLockDown() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
// Default state.
@@ -340,7 +344,6 @@
@Test
public void testLockdownChangingPackage() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -368,7 +371,6 @@
@Test
public void testLockdownAllowlist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -443,7 +445,6 @@
@Test
public void testLockdownRuleRepeatability() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
@@ -476,7 +477,6 @@
@Test
public void testLockdownRuleReversibility() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] entireUser = {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
@@ -963,7 +963,7 @@
InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE);
lp.addRoute(defaultRoute);
- vpn.startLegacyVpn(vpnProfile, mKeyStore, lp);
+ vpn.startLegacyVpn(vpnProfile, mKeyStore, EGRESS_NETWORK, lp);
return vpn;
}
@@ -996,14 +996,12 @@
profile.ipsecIdentifier = "id";
profile.ipsecSecret = "secret";
profile.l2tpSecret = "l2tpsecret";
+
when(mConnectivityManager.getAllNetworks())
.thenReturn(new Network[] { new Network(101) });
+
when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
- anyInt(), any(), anyInt())).thenAnswer(invocation -> {
- // The runner has registered an agent and is now ready.
- legacyRunnerReady.open();
- return new Network(102);
- });
+ anyInt(), any(), anyInt())).thenReturn(new Network(102));
final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
final TestDeps deps = (TestDeps) vpn.mDeps;
try {
@@ -1019,14 +1017,20 @@
"linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
"idle", "1800", "mtu", "1270", "mru", "1270" },
deps.mtpdArgs.get(10, TimeUnit.SECONDS));
+
// Now wait for the runner to be ready before testing for the route.
- legacyRunnerReady.block(10_000);
- // In this test the expected address is always v4 so /32
+ ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+ verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
+ lpCaptor.capture(), any(), anyInt(), any(), anyInt());
+
+ // In this test the expected address is always v4 so /32.
+ // Note that the interface needs to be specified because RouteInfo objects stored in
+ // LinkProperties objects always acquire the LinkProperties' interface.
final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"),
- RouteInfo.RTN_THROW);
- assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : "
- + vpn.mConfig.routes,
- vpn.mConfig.routes.contains(expectedRoute));
+ null, EGRESS_IFACE, RouteInfo.RTN_THROW);
+ final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes();
+ assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes,
+ actualRoutes.contains(expectedRoute));
} finally {
// Now interrupt the thread, unblock the runner and clean up.
vpn.mVpnRunner.exitVpnRunner();
@@ -1082,6 +1086,11 @@
}
@Override
+ public PendingIntent getIntentForStatusPanel(Context context) {
+ return null;
+ }
+
+ @Override
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final Vpn.RetryScheduler interruptChecker) throws IOException {
@@ -1178,11 +1187,6 @@
final int id = (int) invocation.getArguments()[0];
return userMap.get(id);
}).when(mUserManager).getUserInfo(anyInt());
-
- doAnswer(invocation -> {
- final int id = (int) invocation.getArguments()[0];
- return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
- }).when(mUserManager).canHaveRestrictedProfile();
}
/**
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index aeb142b..1fe13fe 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -28,8 +28,6 @@
import junit.framework.TestCase;
-import org.junit.Test;
-
/**
* TODO: Remove this. This is only a placeholder, need to implement this.
*/
@@ -56,7 +54,7 @@
}
try {
- mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY);
+ mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null /* options */);
fail("IWindowManager.addWindowToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -155,29 +153,4 @@
fail("Unexpected remote exception");
}
}
-
- @Test
- public void testADD_WINDOW_TOKEN_WITH_OPTIONS() {
- // Verify if addWindowTokenWithOptions throw SecurityException for privileged window type.
- try {
- mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, "");
- fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- // Verify if addWindowTokenWithOptions throw SecurityException for null packageName.
- try {
- mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, null);
- fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
- }
}
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index a7d0860..a07bce3 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -46,6 +46,8 @@
"android.view.InsetsSourceTest",
"android.view.InsetsSourceConsumerTest",
"android.view.InsetsStateTest",
+ "android.view.RoundedCornerTest",
+ "android.view.RoundedCornersTest",
"android.view.WindowMetricsTest",
"android.view.PendingInsetsControllerTest",
"android.app.WindowContextTest",
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 86a1591..3e659d0 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -59,12 +59,17 @@
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
+ return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+ }
+
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
final VcnGatewayConnectionConfig.Builder builder =
new VcnGatewayConnectionConfig.Builder()
.setRetryInterval(RETRY_INTERVALS_MS)
.setMaxMtu(MAX_MTU);
- for (int caps : EXPOSED_CAPS) {
+ for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index e7d334e..4859644 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -66,6 +66,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
@@ -142,6 +143,9 @@
private final TelephonySubscriptionTracker mSubscriptionTracker =
mock(TelephonySubscriptionTracker.class);
+ private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
+ ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+
private final VcnManagementService mVcnMgmtSvc;
private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
@@ -184,7 +188,7 @@
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(Vcn.class);
- }).when(mMockDeps).newVcn(any(), any(), any());
+ }).when(mMockDeps).newVcn(any(), any(), any(), any(), any());
final PersistableBundle bundle =
PersistableBundleUtils.fromMap(
@@ -304,14 +308,17 @@
@Test
public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
- triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
- verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG));
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+ verify(mMockDeps)
+ .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
}
@Test
public void testTelephonyNetworkTrackerCallbackStopsInstances() throws Exception {
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
@@ -319,6 +326,7 @@
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
mTestLooper.dispatchAll();
verify(vcn).teardownAsynchronously();
+ verify(mMockPolicyListener).onPolicyChanged();
}
@Test
@@ -389,6 +397,7 @@
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
fail("Expected security exception for non system user");
} catch (SecurityException expected) {
+ verify(mMockPolicyListener, never()).onPolicyChanged();
}
}
@@ -400,6 +409,7 @@
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
fail("Expected security exception for missing carrier privileges");
} catch (SecurityException expected) {
+ verify(mMockPolicyListener, never()).onPolicyChanged();
}
}
@@ -409,6 +419,7 @@
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage");
fail("Expected exception due to mismatched packages in config and method call");
} catch (IllegalArgumentException expected) {
+ verify(mMockPolicyListener, never()).onPolicyChanged();
}
}
@@ -473,7 +484,13 @@
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
// Verify Vcn is started
- verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG));
+ verify(mMockDeps)
+ .newVcn(
+ eq(mVcnContext),
+ eq(TEST_UUID_2),
+ eq(TEST_VCN_CONFIG),
+ eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT),
+ any());
// Verify Vcn is updated if it was previously started
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -520,7 +537,7 @@
Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
}
- private void verifyMergedNetworkCapabilities(
+ private void verifyMergedNetworkCapabilitiesIsVcnManaged(
NetworkCapabilities mergedCapabilities, @Transport int transportType) {
assertTrue(mergedCapabilities.hasTransport(transportType));
assertFalse(
@@ -529,7 +546,7 @@
}
@Test
- public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception {
+ public void testGetUnderlyingNetworkPolicyCellular() throws Exception {
setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
NetworkCapabilities nc =
@@ -542,12 +559,12 @@
mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
assertFalse(policy.isTeardownRequested());
- verifyMergedNetworkCapabilities(
+ verifyMergedNetworkCapabilitiesIsVcnManaged(
policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
}
@Test
- public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception {
+ public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
WifiInfo wifiInfo = mock(WifiInfo.class);
@@ -563,7 +580,7 @@
mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
assertFalse(policy.isTeardownRequested());
- verifyMergedNetworkCapabilities(
+ verifyMergedNetworkCapabilitiesIsVcnManaged(
policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
}
@@ -591,4 +608,56 @@
mVcnMgmtSvc.getUnderlyingNetworkPolicy(new NetworkCapabilities(), new LinkProperties());
}
+
+ @Test
+ public void testSubscriptionSnapshotUpdateNotifiesVcn() {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ final Map<ParcelUuid, Vcn> vcnInstances = mVcnMgmtSvc.getAllVcns();
+ final Vcn vcnInstance = vcnInstances.get(TEST_UUID_2);
+
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
+
+ verify(vcnInstance).updateSubscriptionSnapshot(eq(snapshot));
+ }
+
+ @Test
+ public void testAddNewVcnUpdatesPolicyListener() throws Exception {
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
+ @Test
+ public void testRemoveVcnUpdatesPolicyListener() throws Exception {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
+ @Test
+ public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+ verify(mMockDeps)
+ .newVcn(
+ eq(mVcnContext),
+ eq(TEST_UUID_1),
+ eq(TEST_VCN_CONFIG),
+ eq(snapshot),
+ mSafemodeCallbackCaptor.capture());
+
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
+ safemodeCallback.onEnteredSafemode();
+
+ assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index 48e068d..1d459a3 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -42,8 +42,9 @@
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
+import android.util.ArraySet;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
@@ -58,13 +59,19 @@
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
+import java.util.Set;
import java.util.UUID;
public class UnderlyingNetworkTrackerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int INITIAL_SUB_ID_1 = 1;
private static final int INITIAL_SUB_ID_2 = 2;
+ private static final int UPDATED_SUB_ID = 3;
+
+ private static final Set<Integer> INITIAL_SUB_IDS =
+ new ArraySet<>(Arrays.asList(INITIAL_SUB_ID_1, INITIAL_SUB_ID_2));
+ private static final Set<Integer> UPDATED_SUB_IDS =
+ new ArraySet<>(Arrays.asList(UPDATED_SUB_ID));
private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
@@ -90,7 +97,7 @@
@Mock private Context mContext;
@Mock private VcnNetworkProvider mVcnNetworkProvider;
@Mock private ConnectivityManager mConnectivityManager;
- @Mock private SubscriptionManager mSubscriptionManager;
+ @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
@Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
@Mock private Network mNetwork;
@@ -113,23 +120,14 @@
mConnectivityManager,
Context.CONNECTIVITY_SERVICE,
ConnectivityManager.class);
- setupSystemService(
- mContext,
- mSubscriptionManager,
- Context.TELEPHONY_SUBSCRIPTION_SERVICE,
- SubscriptionManager.class);
- List<SubscriptionInfo> initialSubInfos =
- Arrays.asList(
- getSubscriptionInfoForSubId(INITIAL_SUB_ID_1),
- getSubscriptionInfoForSubId(INITIAL_SUB_ID_2));
- when(mSubscriptionManager.getSubscriptionsInGroup(eq(SUB_GROUP)))
- .thenReturn(initialSubInfos);
+ when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
mUnderlyingNetworkTracker =
new UnderlyingNetworkTracker(
mVcnContext,
SUB_GROUP,
+ mSubscriptionSnapshot,
Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
mNetworkTrackerCb);
}
@@ -154,23 +152,45 @@
eq(getWifiRequest()),
any(),
any(NetworkBringupCallback.class));
- verify(mConnectivityManager)
- .requestBackgroundNetwork(
- eq(getCellRequestForSubId(INITIAL_SUB_ID_1)),
- any(),
- any(NetworkBringupCallback.class));
- verify(mConnectivityManager)
- .requestBackgroundNetwork(
- eq(getCellRequestForSubId(INITIAL_SUB_ID_2)),
- any(),
- any(NetworkBringupCallback.class));
+ verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
+
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getRouteSelectionRequest()),
any(),
any(RouteSelectionCallback.class));
+ }
- verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP));
+ private void verifyBackgroundCellRequests(
+ TelephonySubscriptionSnapshot snapshot,
+ ParcelUuid subGroup,
+ Set<Integer> expectedSubIds) {
+ verify(snapshot).getAllSubIdsInGroup(eq(subGroup));
+
+ for (final int subId : expectedSubIds) {
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getCellRequestForSubId(subId)),
+ any(),
+ any(NetworkBringupCallback.class));
+ }
+ }
+
+ @Test
+ public void testUpdateSubscriptionSnapshot() {
+ // Verify initial cell background requests filed
+ verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
+
+ TelephonySubscriptionSnapshot subscriptionUpdate =
+ mock(TelephonySubscriptionSnapshot.class);
+ when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS);
+
+ mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
+
+ // verify that initially-filed bringup requests are unregistered
+ verify(mConnectivityManager, times(INITIAL_SUB_IDS.size()))
+ .unregisterNetworkCallback(any(NetworkBringupCallback.class));
+ verifyBackgroundCellRequests(subscriptionUpdate, SUB_GROUP, UPDATED_SUB_IDS);
}
private NetworkRequest getWifiRequest() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
new file mode 100644
index 0000000..e20070e
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static android.net.IpSecManager.DIRECTION_IN;
+import static android.net.IpSecManager.DIRECTION_OUT;
+
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.ConnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
+ private VcnIkeSession mIkeSession;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+
+ mIkeSession = mGatewayConnection.buildIkeSession();
+ mGatewayConnection.setIkeSession(mIkeSession);
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mConnectedState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testEnterStateCreatesNewIkeSession() throws Exception {
+ verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession, never()).close();
+ }
+
+ @Test
+ public void testNewNetworkTriggersMigration() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession, never()).close();
+ verify(mIkeSession).setNetwork(TEST_UNDERLYING_NETWORK_RECORD_2.network);
+ }
+
+ @Test
+ public void testSameNetworkDoesNotTriggerMigration() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testCreatedTransformsAreApplied() throws Exception {
+ for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
+ getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+ mTestLooper.dispatchAll();
+
+ verify(mIpSecSvc)
+ .applyTunnelModeTransform(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
+ }
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testChildSessionClosedTriggersDisconnect() throws Exception {
+ getChildSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testIkeSessionClosedTriggersDisconnect() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+
+ // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 4ecd215..8643d8a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -44,7 +44,13 @@
@Test
public void testEnterWhileNotRunningTriggersQuit() throws Exception {
final VcnGatewayConnection vgc =
- new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+ new VcnGatewayConnection(
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
vgc.setIsRunning(false);
vgc.transitionTo(vgc.mDisconnectedState);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index d741e5c..bc6bee2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -16,20 +16,36 @@
package com.android.server.vcn;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import android.annotation.NonNull;
-import android.content.Context;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
import android.os.ParcelUuid;
-import android.os.test.TestLooper;
+import android.os.Process;
import android.telephony.SubscriptionInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,7 +57,9 @@
/** Tests for TelephonySubscriptionTracker */
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class VcnGatewayConnectionTest {
+public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
+ private static final int TEST_UID = Process.myUid();
+
private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
private static final int TEST_SIM_SLOT_INDEX = 1;
private static final int TEST_SUBSCRIPTION_ID_1 = 2;
@@ -57,26 +75,67 @@
TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
}
- @NonNull private final Context mContext;
- @NonNull private final TestLooper mTestLooper;
- @NonNull private final VcnNetworkProvider mVcnNetworkProvider;
- @NonNull private final VcnGatewayConnection.Dependencies mDeps;
+ private WifiInfo mWifiInfo;
- public VcnGatewayConnectionTest() {
- mContext = mock(Context.class);
- mTestLooper = new TestLooper();
- mVcnNetworkProvider = mock(VcnNetworkProvider.class);
- mDeps = mock(VcnGatewayConnection.Dependencies.class);
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mWifiInfo = mock(WifiInfo.class);
+ }
+
+ private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
+ final NetworkCapabilities underlyingCaps = new NetworkCapabilities();
+ underlyingCaps.addTransportType(transportType);
+ underlyingCaps.addCapability(NET_CAPABILITY_NOT_METERED);
+ underlyingCaps.addCapability(NET_CAPABILITY_NOT_ROAMING);
+
+ if (transportType == TRANSPORT_WIFI) {
+ underlyingCaps.setTransportInfo(mWifiInfo);
+ underlyingCaps.setOwnerUid(TEST_UID);
+ } else if (transportType == TRANSPORT_CELLULAR) {
+ underlyingCaps.setAdministratorUids(new int[] {TEST_UID});
+ underlyingCaps.setNetworkSpecifier(
+ new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_1));
+ }
+
+ UnderlyingNetworkRecord record =
+ new UnderlyingNetworkRecord(
+ new Network(0), underlyingCaps, new LinkProperties(), false);
+ final NetworkCapabilities vcnCaps =
+ VcnGatewayConnection.buildNetworkCapabilities(
+ VcnGatewayConnectionConfigTest.buildTestConfig(), record);
+
+ assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids());
+ assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo);
+
+ final VcnTransportInfo info = (VcnTransportInfo) vcnCaps.getTransportInfo();
+ if (transportType == TRANSPORT_WIFI) {
+ assertEquals(mWifiInfo, info.getWifiInfo());
+ } else if (transportType == TRANSPORT_CELLULAR) {
+ assertEquals(TEST_SUBSCRIPTION_ID_1, info.getSubId());
+ }
}
@Test
- public void testBuildNetworkCapabilities() throws Exception {
- final NetworkCapabilities caps =
- VcnGatewayConnection.buildNetworkCapabilities(
- VcnGatewayConnectionConfigTest.buildTestConfig());
+ public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception {
+ verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI);
+ }
- for (int exposedCapability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
- assertTrue(caps.hasCapability(exposedCapability));
- }
+ @Test
+ public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
+ verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR);
+ }
+
+ @Test
+ public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() {
+ final TelephonySubscriptionSnapshot updatedSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+ mGatewayConnection.updateSubscriptionSnapshot(updatedSnapshot);
+
+ verify(mUnderlyingNetworkTracker).updateSubscriptionSnapshot(eq(updatedSnapshot));
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 4d92fb9..333b5b9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -27,7 +27,9 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.net.IpSecConfig;
import android.net.IpSecManager;
+import android.net.IpSecTransform;
import android.net.IpSecTunnelInterfaceResponse;
import android.net.LinkProperties;
import android.net.Network;
@@ -40,15 +42,22 @@
import android.os.test.TestLooper;
import com.android.server.IpSecService;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
+import java.util.Collections;
import java.util.UUID;
public class VcnGatewayConnectionTestBase {
protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
- protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1;
+ protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
+ protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
+ protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
+ protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 3;
+ protected static final int TEST_SUB_ID = 5;
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
new UnderlyingNetworkRecord(
@@ -63,11 +72,16 @@
new LinkProperties(),
false /* blocked */);
+ protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
+ new TelephonySubscriptionSnapshot(
+ Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
+
@NonNull protected final Context mContext;
@NonNull protected final TestLooper mTestLooper;
@NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
@NonNull protected final VcnContext mVcnContext;
@NonNull protected final VcnGatewayConnectionConfig mConfig;
+ @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull protected final VcnGatewayConnection.Dependencies mDeps;
@NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@@ -82,6 +96,7 @@
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mVcnContext = mock(VcnContext.class);
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+ mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
@@ -94,7 +109,7 @@
doReturn(mUnderlyingNetworkTracker)
.when(mDeps)
- .newUnderlyingNetworkTracker(any(), any(), any(), any());
+ .newUnderlyingNetworkTracker(any(), any(), any(), any(), any());
}
@Before
@@ -109,7 +124,18 @@
mMockIkeSession = mock(VcnIkeSession.class);
doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
- mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+ mGatewayConnection =
+ new VcnGatewayConnection(
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
+ }
+
+ protected IpSecTransform makeDummyIpSecTransform() throws Exception {
+ return new IpSecTransform(mContext, new IpSecConfig());
}
protected IkeSessionCallback getIkeSessionCallback() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
new file mode 100644
index 0000000..66cbf84
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.NetworkRequest;
+import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Set;
+import java.util.UUID;
+
+public class VcnTest {
+ private static final String PKG_NAME = VcnTest.class.getPackage().getName();
+ private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+ private static final int NETWORK_SCORE = 0;
+ private static final int PROVIDER_ID = 5;
+
+ private Context mContext;
+ private VcnContext mVcnContext;
+ private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
+ private VcnNetworkProvider mVcnNetworkProvider;
+ private VcnSafemodeCallback mVcnSafemodeCallback;
+ private Vcn.Dependencies mDeps;
+
+ private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
+
+ private TestLooper mTestLooper;
+ private VcnGatewayConnectionConfig mGatewayConnectionConfig;
+ private VcnConfig mConfig;
+ private Vcn mVcn;
+
+ @Before
+ public void setUp() {
+ mContext = mock(Context.class);
+ mVcnContext = mock(VcnContext.class);
+ mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
+ mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
+ mDeps = mock(Vcn.Dependencies.class);
+
+ mTestLooper = new TestLooper();
+
+ doReturn(PKG_NAME).when(mContext).getOpPackageName();
+ doReturn(mContext).when(mVcnContext).getContext();
+ doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
+ doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+
+ // Setup VcnGatewayConnection instance generation
+ doAnswer((invocation) -> {
+ // Mock-within a doAnswer is safe, because it doesn't actually run nested.
+ return mock(VcnGatewayConnection.class);
+ }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
+
+ mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
+
+ final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ configBuilder.addGatewayConnectionConfig(
+ VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+ }
+ configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
+ mConfig = configBuilder.build();
+
+ mVcn =
+ new Vcn(
+ mVcnContext,
+ TEST_SUB_GROUP,
+ mConfig,
+ mSubscriptionSnapshot,
+ mVcnSafemodeCallback,
+ mDeps);
+ }
+
+ private NetworkRequestListener verifyAndGetRequestListener() {
+ ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor =
+ ArgumentCaptor.forClass(NetworkRequestListener.class);
+ verify(mVcnNetworkProvider).registerListener(mNetworkRequestListenerCaptor.capture());
+
+ return mNetworkRequestListenerCaptor.getValue();
+ }
+
+ private void startVcnGatewayWithCapabilities(
+ NetworkRequestListener requestListener, int... netCapabilities) {
+ final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+ for (final int netCapability : netCapabilities) {
+ requestBuilder.addCapability(netCapability);
+ }
+
+ requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ startVcnGatewayWithCapabilities(
+ requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
+
+ final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+ assertFalse(gatewayConnections.isEmpty());
+
+ final TelephonySubscriptionSnapshot updatedSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+
+ mVcn.updateSubscriptionSnapshot(updatedSnapshot);
+ mTestLooper.dispatchAll();
+
+ for (final VcnGatewayConnection gateway : gatewayConnections) {
+ verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
+ }
+ }
+
+ @Test
+ public void testGatewayEnteringSafemodeNotifiesVcn() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ startVcnGatewayWithCapabilities(requestListener, capability);
+ }
+
+ // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
+ // Expect one VcnGatewayConnection per capability.
+ final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+
+ final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+ assertEquals(numExpectedGateways, gatewayConnections.size());
+ verify(mDeps, times(numExpectedGateways))
+ .newVcnGatewayConnection(
+ eq(mVcnContext),
+ eq(TEST_SUB_GROUP),
+ eq(mSubscriptionSnapshot),
+ any(),
+ mGatewayStatusCallbackCaptor.capture());
+
+ // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+ // all Gateways
+ final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+ statusCallback.onEnteredSafemode();
+ mTestLooper.dispatchAll();
+
+ for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
+ verify(gatewayConnection).teardownAsynchronously();
+ }
+ verify(mVcnNetworkProvider).unregisterListener(requestListener);
+ verify(mVcnSafemodeCallback).onEnteredSafemode();
+ }
+}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
index 2140954..04f9bf2 100644
--- a/tools/powerstats/PowerStatsServiceProtoParser.java
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -86,6 +86,12 @@
csvRow += energyConsumerResult.getId() + ","
+ energyConsumerResult.getTimestampMs() + ","
+ energyConsumerResult.getEnergyUws() + ",";
+ for (int k = 0; k < energyConsumerResult.getAttributionCount(); k++) {
+ final EnergyConsumerAttributionProto energyConsumerAttribution =
+ energyConsumerResult.getAttribution(k);
+ csvRow += energyConsumerAttribution.getUid() + ","
+ + energyConsumerAttribution.getEnergyUws() + ",";
+ }
}
System.out.println(csvRow);
}