Merge "Update FakedFont to use shared pointer" into sc-dev
diff --git a/Android.bp b/Android.bp
index 202e548..2ccadd3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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/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/core/api/current.txt b/core/api/current.txt
index 5a24251..bfdfb40 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
@@ -7312,6 +7316,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 +7470,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 +7749,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 +7781,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 +9284,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 +11643,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 +11694,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 +12298,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 +12318,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 +12503,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 +15028,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 +19467,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 +19553,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 +19612,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);
}
@@ -20183,7 +20185,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();
@@ -20244,7 +20246,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;
@@ -31446,6 +31448,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();
@@ -34977,6 +34980,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();
}
@@ -40343,6 +40395,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";
@@ -45742,6 +45795,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);
@@ -49617,7 +49672,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
@@ -49708,6 +49763,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;
@@ -53293,7 +53349,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);
@@ -53359,6 +53415,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);
@@ -54399,14 +54456,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);
@@ -54528,6 +54585,7 @@
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);
@@ -54552,6 +54610,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);
@@ -54918,7 +54977,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);
@@ -54949,12 +55008,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 9bff793..3111b7c 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 {
@@ -2102,6 +2104,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 +2148,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 +2487,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 +2512,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 +2524,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 +2582,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 +2716,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 +2911,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 +4719,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 +4785,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";
@@ -4938,7 +4988,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 +8512,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 {
@@ -9312,6 +9364,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
@@ -10262,7 +10332,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
@@ -11635,6 +11705,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);
@@ -11642,6 +11713,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);
@@ -13727,11 +13799,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;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e0391ee..79917d0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -23,10 +23,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 +120,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 +392,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,6 +459,7 @@
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 {
@@ -512,6 +516,7 @@
}
public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
+ ctor public UnsafeStateException(int, int);
method public int getOperation();
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7402690..9b6f4b4 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,
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/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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e84d4a5..06fe9d7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1730,8 +1730,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 +2909,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 +5473,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;
@@ -13098,10 +13111,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 +13212,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 +13244,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 +13252,23 @@
}
}
}
+
+ /**
+ * 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();
+ }
+ }
+ }
}
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/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3765a67..cf0b31e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -491,11 +491,13 @@
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);
}
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/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..5d28216 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;
@@ -5451,6 +5452,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 +6111,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 +6131,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 +6179,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/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/graphics/fonts/SystemFontState.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl
similarity index 80%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl
index 19b20f2..c143cc5 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package android.content.pm.verify.domain;
-/** @hide */
-parcelable SystemFontState;
\ 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/graphics/fonts/SystemFontState.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
similarity index 79%
copy from core/java/android/graphics/fonts/SystemFontState.aidl
copy to core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
index 19b20f2..ddb5ef8 100644
--- a/core/java/android/graphics/fonts/SystemFontState.aidl
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-package android.graphics.fonts;
+package android.content.pm.verify.domain;
-/** @hide */
-parcelable SystemFontState;
\ 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/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/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/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/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 4cd62eb..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);
}
/**
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 f681c4f..1337d55 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -33,11 +33,9 @@
private final double[] mPowerComponents;
private final long[] mTimeComponents;
private final int mCustomPowerComponentCount;
- private final int mModeledPowerComponentOffset;
PowerComponents(@NonNull Builder builder) {
mCustomPowerComponentCount = builder.mCustomPowerComponentCount;
- mModeledPowerComponentOffset = builder.mModeledPowerComponentOffset;
mPowerComponents = builder.mPowerComponents;
mTimeComponents = builder.mTimeComponents;
double totalPower = 0;
@@ -50,9 +48,6 @@
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();
}
@@ -106,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);
@@ -165,20 +152,12 @@
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];
}
@@ -222,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/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/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/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/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/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/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b229212..790773f 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@
DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
- DEFAULT_FLAGS.put("settings_silky_home", "false");
+ DEFAULT_FLAGS.put("settings_silky_home", "true");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
}
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 4168064..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);
}
}
@@ -1213,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) {
@@ -1250,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).
@@ -1265,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) {
@@ -1286,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/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8a44504..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;
@@ -616,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 7843411..ae8afca 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -117,7 +117,7 @@
// 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.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7b15f52..990b7bd 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -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);
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/InsetsState.java b/core/java/android/view/InsetsState.java
index fe6b6e4..219190f 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -604,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;
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/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/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..170124e 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;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 5ae66e3..b85f1079 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -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
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/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/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/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 b47a0ac..dfef7ca 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -132,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 {
@@ -185,6 +192,8 @@
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 = {
@@ -2552,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.
@@ -2766,6 +2856,10 @@
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");
}
@@ -3846,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/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/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/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 964568c..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,17 +100,21 @@
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());
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 4b343af..d196d4a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -117,6 +117,8 @@
public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
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.
@@ -160,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/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/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/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/OWNERS b/core/java/com/android/internal/widget/OWNERS
index 3fc3933..d284d51 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -11,6 +11,7 @@
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
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_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 5442c03..dc81bc9 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"
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 a8ef2cf..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}.
@@ -8494,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>
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 416bc84..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. -->
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/symbols.xml b/core/res/res/values/symbols.xml
index 672aec6..80163b1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3193,8 +3193,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" />
@@ -3731,7 +3732,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" />
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..da7304e 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,133 @@
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());
+
+ 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/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/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 7b1ca8f..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,7 +66,7 @@
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);
@@ -77,9 +77,6 @@
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);
@@ -92,9 +89,6 @@
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(
@@ -118,9 +112,6 @@
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(
@@ -141,9 +132,6 @@
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(
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/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/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/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/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/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/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/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/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 7782364..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
@@ -86,7 +87,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/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/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/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/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/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index 37e5276..a07723f 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(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/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/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/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/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..fa3213d 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);
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 1b4d2e4..db8b7ed 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -156,9 +156,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);
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/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java
index 03b07e0..03cfbbb 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;
@@ -245,7 +247,19 @@
/** @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 +270,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/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/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/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/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/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/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 2a055fc..8f3345f 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -22,82 +22,88 @@
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="24dp"
+ 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="24dp"
+ 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="3dp"
+ app:handleColor="@*android:color/accent_device_default"
+ app:scrimColor="#9444"
+ tools:background="?android:colorBackground"
+ tools:minHeight="100dp"
+ tools:minWidth="100dp" />
</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/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..6c55fb6 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -177,5 +177,9 @@
<attr name="handleColor" format="color" />
<attr name="scrimColor" format="color" />
</declare-styleable>
+
+ <declare-styleable name="RoundedCornerProgressDrawable">
+ <attr name="android:drawable" />
+ </declare-styleable>
</resources>
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..d92f4ea 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1333,4 +1333,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 a74b564..4b04eeb 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">
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/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 a7ed672..c182fd1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -297,7 +297,7 @@
}
private void updateSecurityViewGravity() {
- View securityView = getChildAt(0);
+ View securityView = findKeyguardSecurityView();
if (securityView == null) {
return;
@@ -320,7 +320,7 @@
* by the security view .
*/
private void updateSecurityViewLocation(boolean animate) {
- View securityView = getChildAt(0);
+ View securityView = findKeyguardSecurityView();
if (securityView == null) {
return;
@@ -355,6 +355,23 @@
}
}
+ @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() {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
@@ -640,39 +657,30 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // This is a little hacky, but this element only ever has one wrap_content child, and is
- // itself set to match_parent, so we can take a couple of shortcuts compared to
- // FrameLayout#onMeasure
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
- int count = getChildCount();
-
int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec) / 2,
MeasureSpec.getMode(widthMeasureSpec));
- if (count > 1) {
- throw new IllegalStateException("KeyguardSecurityContainer should only have one child");
- }
-
- if (count > 0) {
- final View securityView = getChildAt(0);
- if (securityView.getVisibility() != GONE) {
- if (mOneHandedMode) {
- measureChildWithMargins(securityView, halfWidthMeasureSpec, 0,
+ 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(securityView, widthMeasureSpec, 0,
+ measureChildWithMargins(view, widthMeasureSpec, 0,
heightMeasureSpec, 0);
}
- final LayoutParams lp = (LayoutParams) securityView.getLayoutParams();
+ final LayoutParams lp = (LayoutParams) view.getLayoutParams();
maxWidth = Math.max(maxWidth,
- securityView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
- securityView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
- childState = combineMeasuredStates(childState, securityView.getMeasuredState());
+ view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ childState = combineMeasuredStates(childState, view.getMeasuredState());
}
}
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/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index 68f1414..e07c8403 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;
@@ -70,9 +69,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,8 +77,6 @@
}
}
mFingerprintDrawable.draw(canvas);
-
- canvas.restore();
}
@Override
@@ -98,13 +92,7 @@
@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
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index fded737..4e3419e 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,8 +98,8 @@
}
@Override
- public void onAlphaChanged(float alpha) {
- mNotificationPanelAlpha = (int) (alpha * 255);
+ public void onExpansionChanged(float expansion, boolean expanded) {
+ mAlpha = expansionToAlpha(expansion);
postInvalidate();
}
@@ -103,4 +114,18 @@
((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
}
}
+
+ public int getPaddingX() {
+ if (mUdfpsAnimation == null) {
+ return 0;
+ }
+ return mUdfpsAnimation.getPaddingX();
+ }
+
+ 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..c088400 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;
@@ -155,7 +156,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 +187,7 @@
mView.setSensorProperties(mSensorProps);
mView.setHbmCallback(this);
- scrimController.addScrimChangedListener(mView);
+ statusBar.addExpansionChangedListener(mView);
statusBarStateController.addCallback(mView);
mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
@@ -259,15 +260,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:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index d448ed8..7e378d3 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;
@@ -133,14 +133,18 @@
}
@Override
- public void onAlphaChanged(float alpha) {
- mAnimationView.onAlphaChanged(alpha);
+ public void onExpansionChanged(float expansion, boolean expanded) {
+ mAnimationView.onExpansionChanged(expansion, expanded);
}
@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));
}
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/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/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/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/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/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 db29533..18c379a 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;
@@ -54,7 +55,8 @@
// TODO: Support saving without additional action.
private enum PendingAction {
SHARE,
- EDIT
+ EDIT,
+ SAVE
}
public static final int MAX_PAGES = 5;
@@ -74,9 +76,11 @@
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 CropView mCropView;
public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
@@ -111,11 +115,14 @@
.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);
- mClose.setOnClickListener(this::onClicked);
+ mSave.setOnClickListener(this::onClicked);
+ mCancel.setOnClickListener(this::onClicked);
mEdit.setOnClickListener(this::onClicked);
mShare.setOnClickListener(this::onClicked);
@@ -130,7 +137,8 @@
}
void disableButtons() {
- mClose.setEnabled(false);
+ mSave.setEnabled(false);
+ mCancel.setEnabled(false);
mEdit.setEnabled(false);
mShare.setEnabled(false);
}
@@ -139,19 +147,17 @@
Log.d(TAG, "button clicked!");
int id = v.getId();
- if (id == R.id.close) {
- v.setPressed(true);
- disableButtons();
+ 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();
startExport(PendingAction.EDIT);
} else if (id == R.id.share) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE);
- v.setPressed(true);
- disableButtons();
startExport(PendingAction.SHARE);
}
}
@@ -165,8 +171,13 @@
}
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(), mCaptureTime);
+ mBgExecutor, mRequestId, mImageTileSet.toBitmap(croppedPortion), mCaptureTime);
exportFuture.addListener(() -> {
try {
ImageExporter.Result result = exportFuture.get();
@@ -191,24 +202,21 @@
}
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);
- Intent sharingChooserIntent = Intent.createChooser(intent, null)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ 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(sharingChooserIntent, UserHandle.CURRENT);
+ 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_GRANT_READ_URI_PERMISSION);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ 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)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION);
mContext.startActivityAsUser(sharingChooserIntent, 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/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 175e046..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;
@@ -847,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/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index cf4e9be..3b47594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -95,6 +95,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;
@@ -292,6 +293,7 @@
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final QSDetailDisplayer mQSDetailDisplayer;
+ private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
// 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
@@ -554,7 +556,9 @@
QSDetailDisplayer qsDetailDisplayer,
ScrimController scrimController,
MediaDataManager mediaDataManager,
- AmbientState ambientState) {
+ AmbientState ambientState,
+ FeatureFlags featureFlags
+ ) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
@@ -571,6 +575,7 @@
mNotificationIconAreaController = notificationIconAreaController;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mQSDetailDisplayer = qsDetailDisplayer;
+ mFeatureFlags = featureFlags;
mView.setWillNotDraw(!DEBUG);
mLayoutInflater = layoutInflater;
mFalsingManager = falsingManager;
@@ -756,21 +761,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() {
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/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/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/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 8d4470b..fa78ca6 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
@@ -68,6 +68,7 @@
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;
@@ -204,6 +205,10 @@
@Mock
private MediaDataManager mMediaDataManager;
@Mock
+ private FeatureFlags mFeatureFlags;
+ @Mock
+ private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ @Mock
private AmbientState mAmbientState;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -219,6 +224,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);
@@ -237,6 +244,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);
@@ -264,6 +273,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,
@@ -285,7 +299,8 @@
new QSDetailDisplayer(),
mScrimController,
mMediaDataManager,
- mAmbientState);
+ mAmbientState,
+ mFeatureFlags);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);
@@ -400,6 +415,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/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..e01c4df 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -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..9ccb0c7 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1137,7 +1137,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 f2e1920..bf9d564 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -222,6 +222,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;
@@ -739,11 +740,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 +763,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 +795,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 +811,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 +991,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 +1032,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);
@@ -1217,6 +1227,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 +1384,7 @@
}
private NetworkState getUnfilteredActiveNetworkState(int uid) {
- NetworkAgentInfo nai = getFallbackNetwork();
+ NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
final Network[] networks = getVpnUnderlyingNetworks(uid);
if (networks != null) {
@@ -1499,7 +1517,7 @@
}
}
- NetworkAgentInfo nai = getFallbackNetwork();
+ NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
ignoreBlocked)) {
return null;
@@ -1638,21 +1656,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 +1699,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
*/
@@ -2025,7 +2048,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 +2142,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 +2605,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 +2726,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 +2993,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 +3002,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 +3323,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();
+ // one lingered request, set inactive.
+ nai.updateInactivityTimer();
if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
- if (DBG) log("Unlingering " + nai.toShortString());
- nai.unlinger();
+ 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 +3357,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 +3377,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 +3422,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 +3458,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 +3481,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 +3493,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,8 +3580,8 @@
}
}
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;
}
@@ -3700,7 +3724,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())) {
@@ -3799,7 +3823,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 +4284,7 @@
@Override
public NetworkRequest getDefaultRequest() {
- return mFallbackRequest.mRequests.get(0);
+ return mDefaultRequest.mRequests.get(0);
}
private class InternalHandler extends Handler {
@@ -4506,7 +4530,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 +4549,7 @@
Network network, int uid, boolean hasConnectivity) {
final NetworkAgentInfo nai;
if (network == null) {
- nai = getFallbackNetwork();
+ nai = getDefaultNetwork();
} else {
nai = getNetworkAgentInfoForNetwork(network);
}
@@ -4891,7 +4915,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 +4967,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 +4983,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 +5442,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 +5467,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 +5515,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 REQUEST-type requests can have a satisfier.
@Nullable
private NetworkAgentInfo mSatisfier;
NetworkAgentInfo getSatisfier() {
@@ -5537,6 +5539,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 +5584,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 +5616,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 +5974,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 +5987,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 +6006,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 +6056,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 +6092,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 +6139,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 +6209,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 +6285,7 @@
// for (LinkProperties lp : newLp.getStackedLinks()) {
// updateMtu(lp, null);
// }
- if (isFallbackNetwork(networkAgent)) {
+ if (isDefaultNetwork(networkAgent)) {
updateTcpBufferSizes(newLp.getTcpBufferSizes());
}
@@ -6246,7 +6297,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 +6631,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;
@@ -7186,7 +7238,7 @@
// 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.
@@ -7224,21 +7276,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 +7300,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 +7323,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 +7487,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());
}
@@ -7413,7 +7503,7 @@
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 +7554,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));
@@ -7557,7 +7651,7 @@
// 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)) {
+ if (updateInactivityState(nai, now)) {
lingeredNetworks.add(nai);
}
}
@@ -7584,7 +7678,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 +7686,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 +7718,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 +7780,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 +7791,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.
@@ -7869,7 +7963,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 +8061,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 +8110,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);
}
}
@@ -8350,7 +8449,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. */
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/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 7da4d64..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
@@ -727,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 "
@@ -752,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);
}
}
@@ -825,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
@@ -853,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.
@@ -862,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);
@@ -1159,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
@@ -1195,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) {
@@ -1625,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;
@@ -1650,7 +1652,7 @@
}
private boolean appIsTopLocked(int uid) {
- return mAm.getUidState(uid) <= ActivityManager.PROCESS_STATE_TOP;
+ return mAm.getUidStateLocked(uid) <= PROCESS_STATE_TOP;
}
/**
@@ -1682,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();
@@ -1729,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(
@@ -1758,7 +1761,7 @@
+ 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;
}
@@ -1771,7 +1774,7 @@
+ r.shortInstanceName;
Slog.w(TAG, msg);
showFgsBgRestrictedNotificationLocked(r);
- updateServiceForegroundLocked(r.app, true);
+ updateServiceForegroundLocked(psr, true);
ignoreForeground = true;
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
r.appInfo.uid)) {
@@ -1802,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();
@@ -1837,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,
@@ -1889,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
@@ -2127,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) {
@@ -2225,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<>();
@@ -2262,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()) {
@@ -2277,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;
}
@@ -2295,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;
}
@@ -2314,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
@@ -2386,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;
@@ -2440,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
@@ -2458,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;
@@ -2475,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) {
@@ -2497,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);
@@ -2627,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) {
@@ -2672,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);
}
@@ -2714,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;
@@ -3028,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());
@@ -3036,6 +3053,7 @@
}
long now = SystemClock.uptimeMillis();
+ ProcessServiceRecord psr;
if (r.executeNesting == 0) {
r.executeFg = fg;
ServiceState stracker = r.getTracker();
@@ -3043,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;
@@ -3062,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;
}
@@ -3071,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;
}
@@ -3296,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;
}
@@ -3354,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
@@ -3391,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 "
@@ -3448,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 {
@@ -3477,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;
@@ -3488,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) {
@@ -3506,8 +3537,8 @@
// Cleanup.
if (newService) {
- app.stopService(r);
- r.setProcess(null);
+ psr.stopService(r);
+ r.setProcess(null, null, 0, null);
}
// Retry.
@@ -3518,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
@@ -3620,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);
@@ -3712,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
@@ -3723,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 "
@@ -3770,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);
@@ -3832,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);
@@ -3907,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.
@@ -3927,7 +3954,7 @@
s.updateIsAllowedBgFgsStartsByBinding();
}
if (s.app != null) {
- updateServiceClientActivitiesLocked(s.app, c, true);
+ updateServiceClientActivitiesLocked(s.app.mServices, c, true);
}
}
clist = mServiceConnections.get(binder);
@@ -3948,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.
@@ -3969,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);
@@ -4100,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;
}
}
@@ -4142,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);
}
}
}
@@ -4167,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
@@ -4246,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<>();
@@ -4352,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 {
@@ -4370,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);
@@ -4398,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();
@@ -4438,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;
@@ -4446,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) {
@@ -4458,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);
}
+ */
}
}
}
@@ -4472,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
@@ -4501,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)) {
@@ -4530,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--) {
@@ -4570,7 +4597,7 @@
}
}
- app.executingServices.clear();
+ psr.stopAllExecutingServices();
}
ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) {
@@ -4578,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;
@@ -4594,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()) {
@@ -4693,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;
@@ -4711,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);
@@ -4726,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));
}
}
@@ -4780,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(
@@ -4994,7 +5022,7 @@
if (proc == null) {
return;
}
- final IApplicationThread thread = proc.thread;
+ final IApplicationThread thread = proc.getThread();
if (thread == null) {
return;
}
@@ -5320,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 {
@@ -5395,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;
}
}
@@ -5439,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;
}
}
@@ -5501,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;
}
}
@@ -5559,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;
@@ -5635,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";
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 cdf5c98..6ae0644 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;
@@ -236,7 +236,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 +248,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 +294,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 +397,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 +417,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 +527,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 +591,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 +615,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 +690,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 +729,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 +823,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 +851,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 +872,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 +891,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 +939,7 @@
proto.end(pToken);
}
}
+ @GuardedBy("this")
final SparseArray<ImportanceToken> mImportantProcesses = new SparseArray<ImportanceToken>();
/**
@@ -872,12 +947,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 +1002,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 +1055,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 +1095,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 +1147,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 +1185,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 +1207,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 +1227,7 @@
/**
* Last time (in uptime) at which we checked for power usage.
*/
+ @GuardedBy("mProcLock")
long mLastPowerCheckUptime;
/**
@@ -1162,10 +1243,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 +1268,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 +1444,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 +1471,7 @@
/**
* The last time when the binder heavy hitter auto sampler started.
*/
+ @GuardedBy("mProcLock")
private long mLastBinderHeavyHitterAutoSamplerStart = 0L;
final AppProfiler mAppProfiler;
@@ -1405,19 +1509,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 +1531,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 +1590,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 +1661,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 +1670,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 +1706,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 +1747,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 +2205,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 +2394,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 +2457,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 +2553,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 +2654,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 +2728,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 +2749,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 +2762,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 +2926,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 +2948,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 +2980,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 +2997,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 +3029,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 +3040,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 +3495,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 +3524,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 +3561,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 +3638,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 +3705,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 +3807,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 +3859,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 +4024,7 @@
}
}
+ boolean didSomething;
if (doit) {
if (packageName != null) {
Slog.i(TAG, "Force stopping " + packageName + " appid=" + appId
@@ -3914,20 +4036,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 +4110,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 +4170,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 +4180,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 +4198,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 +4227,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 +4241,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 +4250,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 +4308,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 +4333,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 +4377,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 +4391,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 +4413,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 +4429,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 +4494,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 +4508,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 +4608,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 +4644,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 +5080,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 +5106,7 @@
oldToken.token.unlinkToDeath(oldToken, 0);
mImportantProcesses.remove(pid);
if (pr != null) {
- pr.forcingToImportant = null;
+ pr.mState.setForcingToImportant(null);
}
changed = true;
}
@@ -4984,7 +5120,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 +5136,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 +5152,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 +5210,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 +5404,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 +5416,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 +5446,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 +5464,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 +5476,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 +5485,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 +5494,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 +5528,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 +5546,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 +5563,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 +5576,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 +5586,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 +5632,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 +5666,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 +5698,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 +5998,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 +6043,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;
+ }
}
}
@@ -5924,8 +6075,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);
}
@@ -5941,9 +6092,9 @@
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),
@@ -6014,7 +6165,7 @@
}
void onWakefulnessChanged(int wakefulness) {
- synchronized(this) {
+ synchronized (this) {
boolean wasAwake = mWakefulness.getAndSet(wakefulness)
== PowerManagerInternal.WAKEFULNESS_AWAKE;
boolean isAwake = wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -6141,13 +6292,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;
}
}
@@ -6204,7 +6355,7 @@
@Override
public void setUserIsMonkey(boolean userIsMonkey) {
- synchronized (this) {
+ synchronized (mProcLock) {
synchronized (mPidsSelfLocked) {
final int callingPid = Binder.getCallingPid();
ProcessRecord proc = mPidsSelfLocked.get(callingPid);
@@ -6223,7 +6374,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();
}
@@ -6444,8 +6595,8 @@
"getUidProcessState");
}
- synchronized (this) {
- return mProcessList.getUidProcStateLocked(uid);
+ synchronized (mProcLock) {
+ return mProcessList.getUidProcStateLOSP(uid);
}
}
@@ -6471,17 +6622,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
@@ -6539,7 +6691,7 @@
@Override
public void setRenderThread(int tid) {
- synchronized (this) {
+ synchronized (mProcLock) {
ProcessRecord proc;
int pid = Binder.getCallingPid();
if (pid == Process.myPid()) {
@@ -6548,32 +6700,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);
}
}
}
@@ -6630,11 +6781,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;
}
}
@@ -6814,42 +6965,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;
+ }
+ }
}
}
}
@@ -6862,13 +7017,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);
}
@@ -6881,13 +7038,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);
}
@@ -6909,17 +7068,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;
+ }
+ }
}
}
}
@@ -7025,16 +7189,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 ");
@@ -7051,13 +7215,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();
@@ -7065,39 +7229,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);
}
}
- }
+ });
}
}
@@ -7208,7 +7376,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,
@@ -7435,16 +7603,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();
+ }
}
}
}
@@ -7509,7 +7679,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
@@ -7755,8 +7925,8 @@
return null;
}
- synchronized (this) {
- return mProcessList.findAppProcessLocked(app, reason);
+ synchronized (mProcLock) {
+ return mProcessList.findAppProcessLOSP(app, reason);
}
}
@@ -7765,7 +7935,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
@@ -7779,17 +7949,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) {
@@ -7808,7 +7979,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";
@@ -7870,8 +8041,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");
}
}
@@ -7879,7 +8050,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)) {
@@ -7975,47 +8146,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
@@ -8031,9 +8201,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);
}
}
@@ -8144,13 +8314,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);
}
}
}
@@ -8196,7 +8366,9 @@
synchronized(this) {
mConstants.dump(pw);
- mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
+ synchronized (mProcLock) {
+ mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
+ }
mOomAdjuster.dumpCacheOomRankerSettings(pw);
pw.println();
if (dumpAll) {
@@ -8328,7 +8500,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("-------------------------------------------------------------------------------");
@@ -8432,7 +8606,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
@@ -8451,7 +8627,9 @@
proto.end(serviceToken);
long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
- mProcessList.writeProcessesToProtoLocked(proto, dumpPackage);
+ synchronized (mProcLock) {
+ mProcessList.writeProcessesToProtoLSP(proto, dumpPackage);
+ }
proto.end(processToken);
}
}
@@ -8525,8 +8703,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) {
@@ -8614,6 +8794,8 @@
} else if ("settings".equals(cmd)) {
synchronized (this) {
mConstants.dump(pw);
+ }
+ synchronized (mProcLock) {
mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
mOomAdjuster.dumpCacheOomRankerSettings(pw);
}
@@ -8863,8 +9045,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<>();
@@ -8872,7 +9054,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;
}
@@ -8883,7 +9065,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));
}
}
@@ -8923,8 +9105,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;
@@ -8965,7 +9146,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());
@@ -9060,7 +9241,7 @@
TimeUtils.formatDuration(now, mLastIdleTime, pw);
pw.print(" mLowRamSinceLastIdle=");
TimeUtils.formatDuration(
- mAppProfiler.getLowRamTimeSinceIdleLocked(now), pw);
+ mAppProfiler.getLowRamTimeSinceIdleLPr(now), pw);
pw.println();
pw.println();
@@ -9077,8 +9258,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);
@@ -9095,7 +9276,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;
@@ -9151,7 +9332,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());
@@ -9224,7 +9405,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));
}
}
@@ -9571,14 +9752,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;
}
@@ -9594,8 +9774,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);
}
}
@@ -9614,13 +9794,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();
@@ -9647,13 +9829,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();
@@ -9680,13 +9864,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();
@@ -10149,10 +10335,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) {
@@ -10223,8 +10409,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);
@@ -10734,10 +10920,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) {
@@ -10803,8 +10989,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);
@@ -11143,96 +11329,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() {
@@ -11249,9 +11365,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) {
@@ -11259,13 +11375,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();
@@ -11285,7 +11401,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.
@@ -11305,7 +11421,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
@@ -11313,14 +11429,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) {
@@ -11659,12 +11774,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
@@ -11672,10 +11787,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) {
@@ -11785,14 +11901,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:");
@@ -11887,7 +12004,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
@@ -11901,7 +12018,7 @@
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
- callingPid = callerApp.pid;
+ callingPid = callerApp.getPid();
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
@@ -11970,8 +12087,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;
}
@@ -11980,13 +12098,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);
@@ -12072,7 +12190,7 @@
}
if (rl.app != null) {
- rl.app.receivers.remove(rl);
+ rl.app.mReceivers.removeReceiver(rl);
}
removeReceiverLocked(rl);
if (rl.linkedToDeath) {
@@ -12371,7 +12489,7 @@
}
}
if (brOptions.isDontSendToRestrictedApps()
- && !isUidActiveLocked(callingUid)
+ && !isUidActiveLOSP(callingUid)
&& isBackgroundRestrictedNoCheck(callingUid, callerPackage)) {
Slog.i(TAG, "Not sending broadcast " + action + " - app " + callerPackage
+ " has background restrictions");
@@ -12578,12 +12696,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(
@@ -12727,7 +12847,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();
}
@@ -13112,7 +13232,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();
@@ -13352,27 +13472,29 @@
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, 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) {
@@ -13399,7 +13521,7 @@
}
try {
- pr.thread.instrumentWithoutRestart(
+ pr.getThread().instrumentWithoutRestart(
activeInstr.mClass,
activeInstr.mArguments,
activeInstr.mWatcher,
@@ -13459,7 +13581,7 @@
}
synchronized(this) {
- ProcessRecord app = getRecordForAppLocked(target);
+ ProcessRecord app = getRecordForAppLOSP(target);
if (app == null) {
Slog.w(TAG, "addInstrumentationResults: no app for " + target);
return;
@@ -13481,36 +13603,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,
@@ -13546,7 +13670,7 @@
}
synchronized(this) {
- ProcessRecord app = getRecordForAppLocked(target);
+ ProcessRecord app = getRecordForAppLOSP(target);
if (app == null) {
Slog.w(TAG, "finishInstrumentation: no app for " + target);
return;
@@ -13652,10 +13776,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;
}
@@ -13774,6 +13899,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()) {
@@ -13786,77 +13912,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) {
@@ -13901,19 +14041,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;
@@ -13922,7 +14063,7 @@
uidRec == null ? null : uidRec.pendingChange,
uid, change, procState, procStateSeq, capability, ephemeral);
if (uidRec != null) {
- uidRec.lastReportedChange = enqueuedChange;
+ uidRec.setLastReportedChange(enqueuedChange);
uidRec.updateLastDispatchedProcStateSeq(enqueuedChange);
}
@@ -13945,21 +14086,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) {
@@ -13985,9 +14126,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;
}
@@ -14030,7 +14171,6 @@
} finally {
Binder.restoreCallingIdentity(identity);
}
-
}
}
return r;
@@ -14140,17 +14280,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);
}
}
}
@@ -14175,11 +14318,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".
@@ -14233,7 +14376,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)
@@ -14257,13 +14400,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);
+ }
}
}
@@ -14273,7 +14418,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++) {
@@ -14293,25 +14438,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) {
@@ -14328,26 +14475,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()) {
@@ -14368,7 +14517,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");
}
@@ -14379,13 +14528,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);
}
- }
+ });
}
}
@@ -14404,21 +14552,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;
@@ -14431,8 +14581,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);
@@ -14454,7 +14604,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).
@@ -14468,9 +14617,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);
}
@@ -14492,7 +14642,7 @@
}
}, null);
- proc.thread.dumpHeap(managed, mallocInfo, runGc, path, fd, intermediateCallback);
+ thread.dumpHeap(managed, mallocInfo, runGc, path, fd, intermediateCallback);
fd = null;
return true;
}
@@ -14547,8 +14697,8 @@
}
void onCoreSettingsChange(Bundle settings) {
- synchronized (this) {
- mProcessList.updateCoreSettingsLocked(settings);
+ synchronized (mProcLock) {
+ mProcessList.updateCoreSettingsLOSP(settings);
}
}
@@ -14700,8 +14850,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;
}
@@ -14723,34 +14874,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");
}
@@ -14758,9 +14911,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);
@@ -14768,7 +14922,7 @@
try {
TransferPipe tp = new TransferPipe();
try {
- process.thread.stopBinderTrackingAndDump(tp.getWriteFd());
+ thread.stopBinderTrackingAndDump(tp.getWriteFd());
tp.go(fd.getFileDescriptor());
} finally {
tp.kill();
@@ -14782,12 +14936,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) {
@@ -14832,12 +14986,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);
@@ -14845,19 +14999,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");
+ }
}
}
}
@@ -14903,22 +15061,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);
}
}
@@ -14956,10 +15118,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);
}
@@ -14977,8 +15139,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
@@ -15041,19 +15203,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;
@@ -15093,14 +15257,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");
}
}
}
@@ -15122,18 +15286,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
@@ -15564,7 +15725,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();
}
}
@@ -15595,19 +15756,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
@@ -15622,7 +15781,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
@@ -15774,8 +15933,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;
}
@@ -15891,11 +16050,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);
@@ -15912,10 +16073,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);
}
@@ -15934,12 +16095,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
@@ -15968,7 +16130,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);
@@ -16005,7 +16167,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;
@@ -16039,7 +16201,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;
@@ -16085,10 +16247,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);
}
@@ -16098,7 +16261,7 @@
}
}
- proc.thread.attachAgent(path);
+ thread.attachAgent(path);
}
} catch (RemoteException e) {
throw new IllegalStateException("Process disappeared");
@@ -16167,7 +16330,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)) {
@@ -16213,7 +16376,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();
}
@@ -16335,7 +16498,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/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..64e307a
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -0,0 +1,555 @@
+/*
+ * 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;
+ }
+
+ 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.sendMessage(msg);
+ }
+ }
+ }
+
+ @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 8f99459..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,24 +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, String abiOverride) {
- if (app.pendingStart) {
+ 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(
@@ -1834,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;
@@ -1950,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)
@@ -1968,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",
@@ -2003,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);
@@ -2043,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;
}
}
@@ -2060,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();
@@ -2072,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);
@@ -2109,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");
@@ -2135,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 */);
@@ -2164,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) {
@@ -2173,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.
@@ -2186,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);
@@ -2233,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,
@@ -2256,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;
@@ -2306,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;
}
}
@@ -2323,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);
@@ -2334,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;
@@ -2359,14 +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 */, 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,
@@ -2399,7 +2469,7 @@
info.processName);
mService.mAppErrors.clearBadProcess(processName, info.uid);
if (app != null) {
- app.bad = false;
+ app.mErrorState.setBad(false);
}
}
}
@@ -2416,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);
@@ -2434,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
@@ -2448,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;
}
@@ -2461,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 {
@@ -2499,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;");
}
@@ -2507,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);
@@ -2536,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
}
@@ -2555,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 ");
@@ -2587,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);
@@ -2612,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);
@@ -2628,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);
@@ -2697,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) {
@@ -2717,7 +2792,7 @@
// we don't kill persistent processes
continue;
}
- if (app.removed) {
+ if (app.isRemoved()) {
if (doit) {
procs.add(app);
}
@@ -2725,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
@@ -2747,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;
}
@@ -2765,7 +2840,7 @@
return true;
}
if (setRemoved) {
- app.removed = true;
+ app.setRemoved(true);
}
procs.add(app);
}
@@ -2806,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) {
@@ -2827,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 */,
@@ -2842,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);
}
@@ -2902,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);
@@ -2936,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);
@@ -3000,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 */
@@ -3020,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++) {
@@ -3029,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);
}
}
@@ -3047,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);
@@ -3064,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);
@@ -3083,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);
}
@@ -3097,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);
@@ -3112,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.
@@ -3147,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;
}
@@ -3167,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,
@@ -3226,7 +3321,7 @@
+ " from position " + i + " to end of group @ "
+ endIndex);
endIndex--;
- endImportance = subProc.connectionImportance;
+ endImportance = subConnectionImportance;
}
}
}
@@ -3238,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) {
@@ -3260,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) {
@@ -3270,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) {
@@ -3281,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--;
@@ -3316,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;
}
}
@@ -3336,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
@@ -3349,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.
@@ -3445,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);
@@ -3472,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);
@@ -3509,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;
}
@@ -3556,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;
}
}
@@ -3587,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;
@@ -3603,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);
@@ -3658,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());
@@ -3673,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) {
@@ -3681,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()) {
@@ -3703,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("|");
}
@@ -3720,6 +3940,7 @@
pw.println();
}
+ @GuardedBy("mService")
boolean dumpLruLocked(PrintWriter pw, String dumpPackage, String prefix) {
final int lruSize = mLruProcesses.size();
final String innerPrefix;
@@ -3786,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;
@@ -3861,7 +4082,7 @@
needSep = true;
}
- if (getLruSizeLocked() > 0) {
+ if (getLruSizeLOSP() > 0) {
if (needSep) {
pw.println();
}
@@ -3871,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();
@@ -3910,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);
@@ -3924,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(
@@ -3944,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;
}
@@ -3971,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;
@@ -3994,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;
@@ -4073,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;
@@ -4096,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(" #");
@@ -4119,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(' ');
@@ -4127,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}");
}
@@ -4153,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=");
@@ -4172,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;
@@ -4212,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:");
@@ -4235,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;
}
@@ -4396,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) != '-') {
@@ -4409,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])) {
@@ -4427,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;
}
@@ -4446,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));
}
}
});
@@ -4465,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) {
}
}
@@ -4487,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);
}
@@ -4512,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);
}
}
@@ -4529,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.
@@ -4557,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;
}
@@ -4567,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);
@@ -4589,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"
@@ -4612,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) {
}
@@ -4692,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);
}
@@ -4814,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);
@@ -4909,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;
@@ -4926,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/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/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d575963..e7e9832 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -300,6 +300,7 @@
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;
// 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 +700,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;
@@ -1076,7 +1078,6 @@
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
synchronized (mHdmiClientLock) {
- mHdmiCecSink = false;
mHdmiManager = mContext.getSystemService(HdmiControlManager.class);
if (mHdmiManager != null) {
mHdmiManager.addHdmiControlStatusChangeListener(
@@ -1508,7 +1509,8 @@
if (isPlatformTelevision()) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null && mHdmiPlaybackClient != null) {
- updateHdmiCecSinkLocked(mHdmiCecSink | false);
+ updateHdmiCecSinkLocked(
+ mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI));
}
}
}
@@ -1518,7 +1520,8 @@
if (isPlatformTelevision()) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
- updateHdmiCecSinkLocked(mHdmiCecSink | false);
+ updateHdmiCecSinkLocked(
+ mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI));
}
}
}
@@ -2310,11 +2313,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 +2442,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 +2670,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 +2965,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() {
@@ -3639,8 +3636,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;
@@ -4383,13 +4379,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 +4400,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 +4414,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 +5381,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 +5391,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.
@@ -7082,6 +7084,10 @@
case MSG_REINIT_VOLUMES:
onReinitVolumes((String) msg.obj);
break;
+ case MSG_UPDATE_A11Y_SERVICE_UIDS:
+ onUpdateAccessibilityServiceUids();
+ break;
+
}
}
}
@@ -7825,9 +7831,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 +7890,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;
@@ -8142,7 +8144,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 +8154,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 +8190,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 +8506,8 @@
mAccessibilityServiceUids = uids.toArray();
}
}
- AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
+ sendMsg(mAudioHandler, MSG_UPDATE_A11Y_SERVICE_UIDS, SENDMSG_REPLACE,
+ 0, 0, null, 0);
}
}
@@ -8507,6 +8525,14 @@
}
}
+ private void onUpdateAccessibilityServiceUids() {
+ int[] accessibilityServiceUids;
+ synchronized (mAccessibilityServiceUidsLock) {
+ accessibilityServiceUids = mAccessibilityServiceUids;
+ }
+ AudioSystem.setA11yServicesUids(accessibilityServiceUids);
+ }
+
//==========================================================================================
// Audio policy management
//==========================================================================================
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/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 9869f77..1135126 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
@@ -113,6 +113,7 @@
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
private int mCurrentUserId = UserHandle.USER_NULL;
+ private boolean mIsUdfps = false;
private final int mSensorId;
private final class BiometricTaskStackListener extends TaskStackListener {
@@ -335,22 +336,22 @@
}
final IBiometricsFingerprint daemon = getDaemon();
- boolean isUdfps = false;
+ mIsUdfps = 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);
+ mIsUdfps = extension.isUdfps(sensorId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception while quering udfps", e);
- isUdfps = false;
+ mIsUdfps = false;
}
}
final @FingerprintSensorProperties.SensorType int sensorType =
- isUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
- : FingerprintSensorProperties.TYPE_REAR;
+ 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()
@@ -414,7 +415,7 @@
Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+ mScheduler.getCurrentClient());
try {
- mDaemon = IBiometricsFingerprint.getService();
+ mDaemon = IBiometricsFingerprint.getService(true /* retry */);
} catch (java.util.NoSuchElementException e) {
// Service doesn't exist or cannot be opened.
Slog.w(TAG, "NoSuchElementException", e);
@@ -799,6 +800,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/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1a4f20c7..a9a705f 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -210,23 +210,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 +269,31 @@
*/
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.
// 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;
@@ -898,17 +899,17 @@
* ConnectivityService when the request is moved to another network with a higher score.
*/
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 +917,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 +943,52 @@
// 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 isLingering() {
- return mLingering;
+ return mInactive;
}
- public void clearLingerState() {
- if (mLingerMessage != null) {
- mLingerMessage.cancel();
- mLingerMessage = null;
+ 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);
+ }
}
/**
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index d956ba3..e8062ae 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -203,6 +203,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 +278,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 {
@@ -405,6 +410,7 @@
mLooper = looper;
mSystemServices = systemServices;
mIkev2SessionCreator = ikev2SessionCreator;
+ mUserManager = mContext.getSystemService(UserManager.class);
mPackage = VpnConfig.LEGACY_VPN;
mOwnerUID = getAppUid(mPackage, mUserId);
@@ -1431,7 +1437,7 @@
final long token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
- users = UserManager.get(mContext).getAliveUsers();
+ users = mUserManager.getAliveUsers();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1515,7 +1521,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 +1549,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 +1774,7 @@
private void prepareStatusIntent() {
final long token = Binder.clearCallingIdentity();
try {
- mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+ mStatusIntent = mDeps.getIntentForStatusPanel(mContext);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1968,8 +1974,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");
@@ -2004,9 +2009,8 @@
*/
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
@Nullable Network underlying, @NonNull LinkProperties egress) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(mUserId);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ 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");
}
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..802472f
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/DeviceState.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 com.android.server.devicestate;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
+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 = INVALID_DEVICE_STATE)
+ private final int mIdentifier;
+
+ /** String description of the device state. */
+ @NonNull
+ private final String mName;
+
+ public DeviceState(@IntRange(from = INVALID_DEVICE_STATE) int identifier,
+ @NonNull String name) {
+ if (identifier != INVALID_DEVICE_STATE && 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 = INVALID_DEVICE_STATE)
+ 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..375ec3a 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,7 +19,9 @@
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+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.IDeviceStateManager;
@@ -29,7 +31,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
-import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -42,7 +43,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
@@ -74,26 +75,31 @@
@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 INVALID_DEVICE_STATE 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(INVALID_DEVICE_STATE, "INVALID");
// 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.
+ @NonNull
+ private Optional<DeviceState> mRequestedState = Optional.empty();
+ // The most recently requested override state, or empty if no override is requested.
@GuardedBy("mLock")
- private int mRequestedOverrideState = INVALID_DEVICE_STATE;
+ @NonNull
+ private Optional<DeviceState> mRequestedOverrideState = Optional.empty();
// List of registered callbacks indexed by process id.
@GuardedBy("mLock")
@@ -122,18 +128,20 @@
*
* @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;
}
@@ -143,7 +151,8 @@
* Returns the requested state. The service will configure the device to match the requested
* state when possible.
*/
- int getRequestedState() {
+ @NonNull
+ Optional<DeviceState> getRequestedState() {
synchronized (mLock) {
return mRequestedState;
}
@@ -165,7 +174,7 @@
return false;
}
- mRequestedOverrideState = overrideState;
+ mRequestedOverrideState = getStateLocked(overrideState);
updatePendingStateLocked();
}
@@ -181,20 +190,24 @@
}
/**
- * Returns the current requested override state, or {@link #INVALID_DEVICE_STATE} is no override
+ * Returns the current requested override state, or {@link Optional#empty()} if no override
* state is requested.
*/
- int getOverrideState() {
+ @NonNull
+ Optional<DeviceState> getOverrideState() {
synchronized (mLock) {
return mRequestedOverrideState;
}
}
/** 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;
}
}
@@ -203,24 +216,26 @@
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)) {
+ if (mRequestedState.isPresent()
+ && !isSupportedStateLocked(mRequestedState.get().getIdentifier())) {
// The current requested 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;
+ mRequestedState = Optional.empty();
}
- if (mRequestedOverrideState != INVALID_DEVICE_STATE
- && !isSupportedStateLocked(mRequestedOverrideState)) {
+ if (mRequestedOverrideState.isPresent()
+ && !isSupportedStateLocked(mRequestedOverrideState.get().getIdentifier())) {
// The current override state is no longer valid. We'll clear it here and update
// the committed state if necessary.
- mRequestedOverrideState = INVALID_DEVICE_STATE;
+ mRequestedOverrideState = Optional.empty();
}
updatePendingStateLocked();
}
@@ -230,10 +245,19 @@
/**
* 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);
+ }
+
+ /**
+ * 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));
}
/**
@@ -242,10 +266,11 @@
*
* @see #isSupportedStateLocked(int)
*/
- private void requestState(int state) {
+ private void requestState(int identifier) {
synchronized (mLock) {
- if (isSupportedStateLocked(state)) {
- mRequestedState = state;
+ final Optional<DeviceState> requestedState = getStateLocked(identifier);
+ if (requestedState.isPresent()) {
+ mRequestedState = requestedState;
}
updatePendingStateLocked();
}
@@ -259,19 +284,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 (mRequestedOverrideState.isPresent()) {
+ stateToConfigure = mRequestedOverrideState.get();
} else {
- stateToConfigure = mRequestedState;
+ stateToConfigure = mRequestedState.orElse(null);
}
- if (stateToConfigure == INVALID_DEVICE_STATE) {
+ if (stateToConfigure == null) {
// No currently requested state.
return;
}
@@ -281,7 +306,7 @@
return;
}
- mPendingState = stateToConfigure;
+ mPendingState = Optional.of(stateToConfigure);
mIsPolicyWaitingForState = true;
}
@@ -302,7 +327,7 @@
return;
}
mIsPolicyWaitingForState = false;
- state = mPendingState;
+ state = mPendingState.get().getIdentifier();
}
if (DEBUG) {
@@ -333,9 +358,9 @@
if (DEBUG) {
Slog.d(TAG, "Committing state: " + mPendingState);
}
- mCommittedState = mPendingState;
- newState = mCommittedState;
- mPendingState = INVALID_DEVICE_STATE;
+ mCommittedState = mPendingState.get();
+ newState = mCommittedState.getIdentifier();
+ mPendingState = Optional.empty();
updatePendingStateLocked();
}
@@ -389,7 +414,7 @@
}
mCallbacks.put(callingPid, record);
- currentState = mCommittedState;
+ currentState = mCommittedState.getIdentifier();
}
// Notify the callback of the state at registration.
@@ -406,10 +431,10 @@
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(" mRequestedState=" + mRequestedState);
+ pw.println(" mRequestedOverrideState=" + mRequestedOverrideState);
final int callbackCount = mCallbacks.size();
pw.println();
@@ -421,30 +446,28 @@
}
}
- 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) {
+ public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
+ if (newDeviceStates.length == 0) {
+ throw new IllegalArgumentException("Supported device states must not be empty");
+ }
for (int i = 0; i < newDeviceStates.length; i++) {
- if (newDeviceStates[i] < 0) {
- throw new IllegalArgumentException("Supported device states includes invalid"
- + " value: " + newDeviceStates[i]);
+ if (newDeviceStates[i].getIdentifier() == INVALID_DEVICE_STATE) {
+ throw new IllegalArgumentException(
+ "Supported device states includes INVALID_DEVICE_STATE identifier");
}
}
-
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);
+ requestState(identifier);
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index cf3b297..7914531 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,11 +16,10 @@
package com.android.server.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-
import android.os.ShellCommand;
import java.io.PrintWriter;
+import java.util.Optional;
/**
* ShellCommands for {@link DeviceStateManagerService}.
@@ -52,24 +51,15 @@
}
private void printState(PrintWriter pw) {
- int committedState = mInternal.getCommittedState();
- int requestedState = mInternal.getRequestedState();
- int requestedOverrideState = mInternal.getOverrideState();
+ DeviceState committedState = mInternal.getCommittedState();
+ Optional<DeviceState> requestedState = mInternal.getRequestedState();
+ Optional<DeviceState> requestedOverrideState = mInternal.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 (requestedOverrideState.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: " + requestedState.orElse(null));
+ pw.println("Override state: " + requestedOverrideState.get());
}
}
@@ -102,15 +92,12 @@
}
private int runPrintStates(PrintWriter pw) {
- int[] states = mInternal.getSupportedStates();
- pw.print("Supported states: [ ");
+ DeviceState[] states = mInternal.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/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e0baee7..e5151d8 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);
@@ -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/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 9036812..3e2b5ab 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -24,8 +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.ResultReceiver;
import android.os.SharedMemory;
import android.os.ShellCallback;
@@ -54,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;
@@ -71,14 +72,15 @@
}
@Override
- public int updateFont(ParcelFileDescriptor fd, byte[] signature, int baseVersion) {
- 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);
@@ -219,7 +221,13 @@
private void initialize() {
synchronized (mUpdatableFontDirLock) {
if (mUpdatableFontDir == null) {
- updateSerializedFontMap();
+ synchronized (mSerializedFontMapLock) {
+ try {
+ mSerializedFontMap = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+ } catch (IOException | ErrnoException e) {
+ mSerializedFontMap = null;
+ }
+ }
return;
}
if (mFontCrashDetector.hasCrashed()) {
@@ -249,7 +257,7 @@
}
}
- /* 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(
@@ -265,7 +273,7 @@
"The base config version is older than current.");
}
try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
- mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
+ mUpdatableFontDir.update(requests);
updateSerializedFontMap();
}
}
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 8fac086..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,11 +21,13 @@
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;
@@ -38,7 +40,6 @@
import java.io.IOException;
import java.security.SecureRandom;
import java.time.Instant;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -72,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;
@@ -116,9 +108,7 @@
private final File mConfigFile;
private final File mTmpConfigFile;
- private final PersistentSystemFontConfig.Config mConfig =
- new PersistentSystemFontConfig.Config();
-
+ private long mLastModifiedDate;
private int mConfigVersion = 1;
/**
@@ -126,7 +116,7 @@
* FontFileInfo}. All files in this map are validated, and have higher revision numbers than
* corresponding font files in {@link #mPreinstalledFontDirs}.
*/
- 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,22 +135,36 @@
}
/* package */ void loadFontFileMap() {
- boolean success = false;
-
- try (FileInputStream fis = new FileInputStream(mConfigFile)) {
- PersistentSystemFontConfig.loadFromXml(fis, mConfig);
- } catch (IOException | XmlPullParserException e) {
- mConfig.reset();
- }
-
mFontFileInfoMap.clear();
+ mLastModifiedDate = 0;
+ boolean success = false;
try {
+ PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+ try (FileInputStream fis = new FileInputStream(mConfigFile)) {
+ PersistentSystemFontConfig.loadFromXml(fis, config);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Failed to load config xml file", e);
+ return;
+ }
+ mLastModifiedDate = config.lastModifiedDate;
+
File[] dirs = mFilesDir.listFiles();
if (dirs == null) return;
for (File dir : dirs) {
- if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
+ if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) {
+ Slog.e(TAG, "Unexpected dir found: " + dir);
+ return;
+ }
+ 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) return;
+ if (files == null || files.length != 1) {
+ Slog.e(TAG, "Unexpected files in dir: " + dir);
+ return;
+ }
FontFileInfo fontFileInfo = validateFontFile(files[0]);
addFileToMapIfNewer(fontFileInfo, true /* deleteOldFile */);
}
@@ -173,6 +177,7 @@
// Delete all files just in case if we find a problematic file.
if (!success) {
mFontFileInfoMap.clear();
+ mLastModifiedDate = 0;
FileUtils.deleteContents(mFilesDir);
}
}
@@ -182,10 +187,9 @@
mFontFileInfoMap.clear();
FileUtils.deleteContents(mFilesDir);
- mConfig.reset();
- mConfig.lastModifiedDate = Instant.now().getEpochSecond();
+ mLastModifiedDate = Instant.now().getEpochSecond();
try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
- PersistentSystemFontConfig.writeToXml(fos, mConfig);
+ PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig());
} catch (Exception e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
@@ -195,6 +199,47 @@
}
/**
+ * 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;
+ }
+ }
+ }
+
+ /**
* Installs a new font file, or updates an existing font file.
*
* <p>The new font will be immediately available for new Zygote-forked processes through
@@ -205,7 +250,8 @@
* @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 {
+ private void installFontFile(FileDescriptor fd, byte[] pkcs7Signature)
+ throws SystemFontException {
File newDir = getRandomDir(mFilesDir);
if (!newDir.mkdir()) {
throw new SystemFontException(
@@ -268,42 +314,11 @@
"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 (!addFileToMapIfNewer(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) {
@@ -439,8 +454,17 @@
}
}
+ 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<>();
+ Map<String, File> map = new ArrayMap<>();
for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
map.put(entry.getKey(), entry.getValue().getFile());
}
@@ -448,11 +472,7 @@
}
/* package */ FontConfig getSystemFontConfig() {
- return SystemFonts.getSystemFontConfig(
- getFontFileMap(),
- mConfig.lastModifiedDate,
- mConfigVersion
- );
+ return SystemFonts.getSystemFontConfig(getFontFileMap(), mLastModifiedDate, mConfigVersion);
}
/* package */ int getConfigVersion() {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e5b5350..d8e124a0 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;
@@ -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;
@@ -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/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/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 504eefe..b99a552 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1254,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,
@@ -5402,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;
@@ -5410,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/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/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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6e5bd94..9a84b36 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -177,6 +177,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;
@@ -377,6 +378,11 @@
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.dex.ViewCompiler;
+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.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageParser2;
@@ -399,6 +405,7 @@
import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedSparseBooleanArray;
+import com.android.server.utils.WatchedSparseIntArray;
import com.android.server.utils.Watcher;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -460,6 +467,7 @@
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.function.Supplier;
/**
* Keep track of all those APKs everywhere.
@@ -1063,6 +1071,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 +1102,9 @@
instantAppResolverConnectionProducer,
Producer<ModuleInfoProvider> moduleInfoProviderProducer,
Producer<LegacyPermissionManagerInternal> legacyPermissionManagerInternalProducer,
+ Producer<DomainVerificationManagerInternal>
+ domainVerificationManagerInternalProducer,
+ Producer<Handler> handlerProducer,
SystemWrapper systemWrapper,
ServiceProducer getLocalServiceProducer,
ServiceProducer getSystemServiceProducer) {
@@ -1128,6 +1142,9 @@
mSystemWrapper = systemWrapper;
mGetLocalServiceProducer = getLocalServiceProducer;
mGetSystemServiceProducer = getSystemServiceProducer;
+ mDomainVerificationManagerInternalProducer =
+ new Singleton<>(domainVerificationManagerInternalProducer);
+ mHandlerProducer = new Singleton<>(handlerProducer);
}
/**
@@ -1273,6 +1290,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 +1352,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 +1474,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 +1491,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 +1510,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 +1584,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 +1594,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 +1688,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 +1976,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 +2029,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 +2075,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 +2587,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 +2602,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 +2624,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 +2680,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 +2773,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 +3025,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;
}
}
}
@@ -3846,6 +3623,8 @@
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
+ enforceCrossUserPermission(callingUid, userId,
+ /* requireFullPermission */ false, /* checkShell */ false, "getPackagesForUid");
return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
}
@@ -4175,14 +3954,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 +4250,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 +5099,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 +5159,12 @@
}
break;
}
+ case DOMAIN_VERIFICATION: {
+ int messageCode = msg.arg1;
+ Object object = msg.obj;
+ mDomainVerificationManager.runMessage(messageCode, object);
+ break;
+ }
}
}
}
@@ -5807,19 +5524,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 +5572,7 @@
() -> mInjector.getIncrementalManager(),
() -> mPmInternal);
ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
- trustedCerts, statusReceiver, injector);
+ trustedCerts, onChecksumsReadyListener, injector);
});
}
@@ -6014,7 +5731,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 +5755,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(),
@@ -6070,6 +5789,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 +5967,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 +5979,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 +6189,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 +6214,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 +6699,6 @@
if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(user.id);
- primeDomainVerificationsLPw(user.id);
}
}
@@ -7080,13 +6805,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 +6825,6 @@
mRequiredVerifierPackage = null;
mRequiredInstallerPackage = null;
mRequiredUninstallerPackage = null;
- mIntentFilterVerifierComponent = null;
- mIntentFilterVerifier = null;
mServicesExtensionPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
}
@@ -7631,6 +7359,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 +7526,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 +8949,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 +9372,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 +9865,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 +9951,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 +13200,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 +13457,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 +13483,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 +13503,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 +14348,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 +14472,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 +16150,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
@@ -20193,13 +19857,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 +20190,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 +20859,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 +21386,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 +21935,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 +21990,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 +22252,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 +22261,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 +23514,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 +23697,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 +23792,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 +23809,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 +23933,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 +24135,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 +24263,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 +25458,6 @@
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
scheduleWritePackageListLocked(userId);
- primeDomainVerificationsLPw(userId);
mAppsFilter.onUsersChanged();
}
}
@@ -27781,12 +27112,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 +27759,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..69e84b5 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) {
@@ -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..8aa553d 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -42,6 +42,8 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.File;
@@ -131,8 +133,6 @@
/** Whether or not an update is available. Ostensibly only for instant apps. */
boolean updateAvailable;
- IntentFilterVerificationInfo verificationInfo;
-
boolean forceQueryableOverride;
@NonNull
@@ -258,7 +258,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 +349,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 +498,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 +512,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 +527,6 @@
otherState.instantApp,
otherState.virtualPreload, otherState.lastDisableAppCaller,
otherState.enabledComponents, otherState.disabledComponents,
- otherState.domainVerificationStatus, otherState.appLinkGeneration,
otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning);
}
@@ -644,40 +642,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++) {
@@ -845,7 +809,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/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..c10e828 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -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..c77e266 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1158,7 +1158,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/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/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 3340792..f52b9b1 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -33,15 +33,19 @@
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.Environment}.
@@ -55,21 +59,26 @@
@NonNull private final Handler mHandler;
@NonNull private final ContentResolver mCr;
@NonNull private final UserManager mUserManager;
+ @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;
EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
- boolean geoDetectionSupported) {
+ @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);
+ 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);
}
@@ -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/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index cac1a6d..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;
@@ -203,10 +204,11 @@
*/
public static TimeZoneDetectorStrategyImpl create(
@NonNull Context context, @NonNull Handler handler,
- boolean geolocationTimeZoneDetectionSupported) {
+ boolean geoDetectionSupported) {
+ DeviceConfig deviceConfig = new DeviceConfig();
EnvironmentImpl environment = new EnvironmentImpl(
- context, handler, geolocationTimeZoneDetectionSupported);
+ context, handler, deviceConfig, geoDetectionSupported);
return new TimeZoneDetectorStrategyImpl(environment);
}
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 220810f..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;
@@ -227,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/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/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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 36c5037..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,7 +201,6 @@
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;
@@ -273,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;
@@ -682,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
@@ -1365,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);
@@ -1627,6 +1623,8 @@
if (rotationAnimation >= 0) {
mRotationAnimationHint = rotationAnimation;
}
+
+ mOverrideTaskTransition = options.getOverrideTaskTransition();
}
ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
@@ -3997,7 +3995,8 @@
pendingOptions.getCustomEnterResId(),
pendingOptions.getCustomExitResId(),
pendingOptions.getAnimationStartedListener(),
- pendingOptions.getAnimationFinishedListener());
+ pendingOptions.getAnimationFinishedListener(),
+ pendingOptions.getOverrideTaskTransition());
break;
case ANIM_CLIP_REVEAL:
displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
@@ -6775,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());
- }
}
/**
@@ -6972,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 a97eb7f..2d6e9b2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4849,15 +4849,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..4575cf7 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,6 +714,13 @@
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) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1b20c44..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;
@@ -1076,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:
@@ -1103,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;
});
@@ -1395,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) {
@@ -1558,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
@@ -1620,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/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 2274a4a..99c9e79 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -340,7 +340,7 @@
}
public void applySurfaceChanges(SurfaceControl.Transaction t) {
- if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
+ if (!needsApplySurfaceChanges()) {
// Nothing changed.
return;
}
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/Session.java b/services/core/java/com/android/server/wm/Session.java
index fe7ec16..1f8daf6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -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
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 57ba915..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;
@@ -2319,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.
@@ -2334,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
@@ -2474,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;
@@ -2882,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..63732d8 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;
@@ -905,6 +906,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/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 52ed278..1a0e16b 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -220,8 +220,6 @@
mRemoveReplacedWindows = false;
}
- mService.destroyPreservedSurfaceLocked();
-
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
executeAfterPrepareSurfacesRunnables();
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a3a9eb7..eed3299 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -26,9 +26,11 @@
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;
@@ -154,6 +156,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 {
@@ -372,6 +389,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 09fbce0..8965cab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -83,7 +83,6 @@
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;
@@ -405,6 +404,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.
@@ -413,7 +414,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
@@ -421,6 +421,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";
@@ -626,13 +639,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.
*/
@@ -770,7 +776,8 @@
final AnrController mAnrController;
private final ScreenshotHashController mScreenshotHashController;
- private final WindowContextListenerController mWindowContextListenerController =
+ @VisibleForTesting
+ final WindowContextListenerController mWindowContextListenerController =
new WindowContextListenerController();
@VisibleForTesting
@@ -1488,7 +1495,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);
@@ -1830,7 +1837,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;
}
@@ -2328,7 +2335,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;
}
@@ -2595,11 +2601,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.
@@ -2793,6 +2800,17 @@
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) {
@@ -2848,6 +2866,7 @@
}
}
+ /** Returns {@code true} if this binder is a registered window token. */
@Override
public boolean isWindowToken(IBinder binder) {
synchronized (mGlobalLock) {
@@ -5490,14 +5509,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
// -------------------------------------------------------------
@@ -7749,6 +7760,15 @@
}
@Override
+ public void registerKeyguardExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener) {
+ synchronized (mGlobalLock) {
+ getDefaultDisplayContentLocked().mAppTransition
+ .registerKeygaurdExitAnimationStartListener(listener);
+ }
+ }
+
+ @Override
public void reportPasswordChanged(int userId) {
mKeyguardDisableHandler.updateKeyguardEnabled(userId);
}
@@ -8555,8 +8575,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);
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/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c698591..89cb163 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;
@@ -2242,7 +2242,6 @@
disposeInputChannel();
- mWinAnimator.destroyDeferredSurfaceLocked(mTmpTransaction);
mWinAnimator.destroySurfaceLocked(mTmpTransaction);
mTmpTransaction.apply();
mSession.windowRemovedLocked();
@@ -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"
@@ -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..d164f30 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;
}
@@ -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..cd18311 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,7 +17,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 +25,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 +39,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 +55,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,17 +108,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 boolean mFromClientToken;
private final int mOwnerUid;
@@ -188,30 +177,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.
@@ -266,17 +231,6 @@
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 +368,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 +379,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
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/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 1194099..160033e 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,14 @@
@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) {
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4b1bae4..717e77b 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;
@@ -558,6 +561,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 +910,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 +933,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 +1099,35 @@
* @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);
}
/**
@@ -1598,7 +1635,8 @@
mSetupContentObserver = new SetupContentObserver(mHandler);
- mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
+ mUserManagerInternal.addUserRestrictionsListener(
+ new RestrictionsListener(mContext, mUserManagerInternal, this));
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
loadOwners();
@@ -15927,11 +15965,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 +15985,7 @@
"Provisioning preconditions failed with result: " + result);
}
+ final long startTime = SystemClock.elapsedRealtime();
final Set<String> nonRequiredApps = provisioningParams.isLeaveAllSystemAppsEnabled()
? Collections.emptySet()
: mOverlayPackagesProvider.getNonRequiredApps(
@@ -15962,8 +16002,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 +16017,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 +16137,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 +16158,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 +16169,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 +16182,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 +16200,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 +16255,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.");
@@ -16252,15 +16317,12 @@
}
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);
+ } catch (Exception e) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
+ .setStrings(callerPackage)
+ .write();
+ throw e;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -16344,4 +16406,54 @@
}
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.
+ }
+ });
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 22866b4..fc1d831 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -73,25 +73,28 @@
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");
}
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;
}
}
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/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/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..54da643 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -33,6 +33,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Optional;
+
import javax.annotation.Nullable;
/**
@@ -43,9 +45,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;
@@ -61,47 +63,53 @@
@Test
public void requestStateChange() {
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.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(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.getRequestedState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
}
@Test
public void requestStateChange_pendingState() {
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(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.getRequestedState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
+ mProvider.notifyRequestState(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.getRequestedState().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.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
public void requestStateChange_unsupportedState() {
- mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE);
+ mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
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.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
@@ -113,85 +121,83 @@
@Test
public void requestOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE);
+ mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
// 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);
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
// 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);
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
public void requestOverrideState_unsupportedState() {
- mService.setOverrideState(UNSUPPORTED_DEVICE_STATE);
+ mService.setOverrideState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
// 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.getRequestedState().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.getRequestedState().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);
- }
-
- @Test
- public void supportedStatesChanged_invalidState() {
- assertThrows(IllegalArgumentException.class, () -> {
- mProvider.notifySupportedDeviceStates(new int []{ INVALID_DEVICE_STATE });
- });
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
}
@Test
public void supportedStatesChanged_unsupportedRequestedState() {
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.getRequestedState().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.getRequestedState(), Optional.empty());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
- assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
}
@Test
public void supportedStatesChanged_unsupportedOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE);
+ mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
// 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);
+ assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ 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.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
@@ -199,23 +205,27 @@
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(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.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedValue().intValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE);
+ mProvider.notifyRequestState(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 +233,8 @@
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());
}
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
@@ -275,9 +286,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,17 +298,16 @@
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 notifyRequestState(int identifier) {
+ mListener.onStateChanged(identifier);
}
}
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/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/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/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/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/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/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 0afdc58..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;
@@ -673,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)));
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/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index fa3e3ae..7714a6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -183,6 +183,10 @@
mColor = Color.GREEN;
assertTrue(mLetterbox.needsApplySurfaceChanges());
+
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
}
@Test
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/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/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/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/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/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/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/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/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/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index eac8c292..0674138 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1262,22 +1262,28 @@
}
}
- private void updateUidNetworkingBlocked() {
- doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked(
- i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */,
- mRestrictBackground)
+ 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);
}
@@ -6809,6 +6815,7 @@
.addTransportType(TRANSPORT_CELLULAR)
.build();
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+ mockUidNetworkingBlocked();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
@@ -6891,6 +6898,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);
@@ -7160,6 +7168,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(
@@ -7254,22 +7269,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.
@@ -7302,16 +7325,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
@@ -8355,13 +8382,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)
@@ -8410,8 +8438,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
@@ -8421,8 +8448,7 @@
mServiceContext.setPermission(
android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
@Test
@@ -8433,8 +8459,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) {
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 f478282..32c6a75 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;
@@ -213,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))
@@ -253,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)
@@ -267,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);
@@ -290,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]};
@@ -316,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.
@@ -341,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;
@@ -369,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;
@@ -444,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)};
@@ -477,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)
@@ -954,7 +953,14 @@
}
private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
- setMockedUsers(primaryUser);
+ // TODO(b/175883995): once these tests have been updated for the changes to the UserManager
+ // API, remove this ad-hoc setup code and use setMockedUsers(primaryUser) again.
+ // setMockedUsers(primaryUser);
+ final ArrayList<UserInfo> users = new ArrayList<>();
+ users.add(primaryUser);
+ when(mUserManager.getAliveUsers()).thenReturn(users);
+ when(mUserManager.getUserInfo(primaryUser.id)).thenReturn(primaryUser);
+ when(mUserManager.canHaveRestrictedProfile()).thenReturn(false);
// Dummy egress interface
final LinkProperties lp = new LinkProperties();
@@ -997,14 +1003,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 {
@@ -1020,14 +1024,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();
@@ -1083,6 +1093,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 {
@@ -1144,6 +1159,10 @@
doReturn(UserHandle.of(userId)).when(asUserContext).getUser();
when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
.thenReturn(asUserContext);
+ when(asUserContext.getSystemServiceName(UserManager.class))
+ .thenReturn(Context.USER_SERVICE);
+ when(asUserContext.getSystemService(UserManager.class))
+ .thenReturn(mUserManager);
final TestLooper testLooper = new TestLooper();
final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
@@ -1179,11 +1198,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/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);
}