diff options
470 files changed, 26846 insertions, 10789 deletions
diff --git a/apct-tests/perftests/core/OWNERS b/apct-tests/perftests/core/OWNERS new file mode 100644 index 000000000000..18486af9d12c --- /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 c37f6d97aba7..a2dc1c29f026 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 @@ package android.wm; 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 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase 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 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase 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 1876110c9355..8310ba769f55 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 bde2bec0816b..b3e81b925ddc 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 6eb44a74a3f3..930415fcd8fd 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 @@ public class JobInfo implements Parcelable { } /** - * @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 @@ public class JobInfo implements Parcelable { } /** - * @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 ab8722286efc..283e933e0fa9 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java @@ -74,6 +74,27 @@ public class PowerWhitelistManager { } /** + * 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 444164390550..e045b0fa3a6b 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 @@ public interface DeviceIdleInternal { * 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 c9427e923bba..8f7f705163ba 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.ActivityManager; 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.Message; 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 @@ public class DeviceIdleController extends SystemService * @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 6ab10518b557..18856f782b7d 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 @@ class JobConcurrencyManager { 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 @@ class JobConcurrencyManager { } } + 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 @@ class JobConcurrencyManager { 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 @@ class JobConcurrencyManager { } } + 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 ba78bda0d2fa..7ce867c6c850 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 @@ public class JobSchedulerService extends com.android.server.SystemService // 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService } 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 247b4211fdf9..d15bae0274ac 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 @@ public final class JobServiceContext implements ServiceConnection { 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 @@ public final class JobServiceContext implements ServiceConnection { } } - 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 @@ public final class JobServiceContext implements ServiceConnection { if (mWakeLock != null) { mWakeLock.release(); } + final int workType = mRunningJobWorkType; mContext.unbindService(JobServiceContext.this); mWakeLock = null; mRunningJob = null; @@ -847,6 +850,7 @@ public final class JobServiceContext implements ServiceConnection { mAvailable = true; removeOpTimeOutLocked(); mCompletedListener.onJobCompletedLocked(completedJob, reschedule); + mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType); } private void applyStoppedReasonLocked(String reason) { diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 5f13a5ce3aae..f85e30d38f86 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 @@ public final class Telecom extends BaseCommand { 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 @@ public final class Telecom extends BaseCommand { 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 @@ public final class Telecom extends BaseCommand { 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 5a242517ae8f..a9458897dc4f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -657,7 +657,7 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android.app.admin { 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 @@ package android.app.admin { 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 @@ package android.app.job { 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 @@ package android.app.job { 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 @@ package android.bluetooth.le { 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 @@ package android.content.pm { 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 @@ package android.content.pm { 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; @@ -19453,7 +19463,7 @@ package android.location { 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 +19549,8 @@ package android.location { 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 +19608,6 @@ package android.location { 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 +20181,7 @@ package android.media { } 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 +20242,7 @@ package android.media { 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; @@ -34977,6 +34975,55 @@ package android.provider { 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(); } @@ -45742,6 +45789,8 @@ package android.util { 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 +49666,7 @@ package android.view { 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 +49757,7 @@ package android.view { 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; diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index be3c246d452e..bf70803fbc74 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -182,6 +182,10 @@ package android.net { 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 9bff793ea0e6..1977babe0bfb 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -34,6 +34,7 @@ package android { 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 @@ package android { 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 @@ package android { 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"; @@ -629,8 +632,6 @@ package android.app { 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 +2103,7 @@ package android.content { 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 +2147,13 @@ package android.content { 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 +2486,8 @@ package android.content.pm { 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 +2511,9 @@ package android.content.pm { 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 +2523,7 @@ package android.content.pm { 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 +2581,13 @@ package android.content.pm { 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 +2715,52 @@ package android.content.pm.permission { } +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 +2910,9 @@ package android.hardware.display { 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 +4718,6 @@ package android.location { 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 +4784,7 @@ package android.location.provider { 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 +4987,7 @@ package android.media { } 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 +8511,8 @@ package android.os { 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 +9363,24 @@ package android.provider { 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 @@ -13727,11 +13796,13 @@ package android.telephony.ims.stub { 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 e0391eee5077..e88b6b92581e 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -23,6 +23,7 @@ package android { 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"; @@ -389,7 +390,8 @@ package android.app.admin { 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_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"; @@ -456,6 +458,7 @@ package android.app.admin { 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 +515,7 @@ package android.app.admin { } 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 74026909be8b..9b6f4b43581b 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -30,6 +30,7 @@ import android.content.pm.UserInfo; 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 @@ public abstract class ActivityManagerInternal { * @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 @@ public abstract class ActivityManagerInternal { * @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 @@ public abstract class ActivityManagerInternal { * @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/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1f9cb6430a5d..e5a04c98b9e7 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -95,7 +95,6 @@ import android.media.MediaFrameworkInitializer; 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 @@ public final class ActivityThread extends ClientTransactionHandler { // 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 @@ public final class ActivityThread extends ClientTransactionHandler { 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 final class ActivityThread extends ClientTransactionHandler { } 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/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index a16f6a84f18f..445fdd83f34a 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 @@ public class BroadcastOptions { "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. + * @hide + * @deprecated Use {@link android.os.PowerWhitelistManager# + * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead. */ - public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; - + @Deprecated + public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = + PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; /** - * The list of temp allowlist types. * @hide + * @deprecated Use {@link android.os.PowerWhitelistManager# + * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_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_NOT_ALLOWED = + PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED; public static BroadcastOptions makeBasic() { BroadcastOptions opts = new BroadcastOptions(); @@ -125,7 +119,8 @@ public class BroadcastOptions { 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 9e1c505e7d4d..a9e28bb635be 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -145,18 +145,6 @@ oneway interface ITaskStackListener { 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 f8c33b58b689..7404e53bd8b3 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -69,6 +69,9 @@ import android.content.pm.IShortcutService; 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 @@ public final class SystemServiceRegistry { } }); + // 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 d1b544d77951..517ae24b75a6 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -21,7 +21,6 @@ import android.compat.annotation.UnsupportedAppUsage; 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 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e84d4a5c1e5e..642bce4eea08 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2905,6 +2905,35 @@ public class DevicePolicyManager { 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) { @@ -13098,10 +13127,11 @@ public class DevicePolicyManager { */ @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 +13228,8 @@ public class DevicePolicyManager { 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 +13260,7 @@ public class DevicePolicyManager { 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) { diff --git a/core/java/android/app/admin/DevicePolicySafetyChecker.java b/core/java/android/app/admin/DevicePolicySafetyChecker.java index b1a80c55cf1e..6c6f2aa15ab7 100644 --- a/core/java/android/app/admin/DevicePolicySafetyChecker.java +++ b/core/java/android/app/admin/DevicePolicySafetyChecker.java @@ -17,6 +17,7 @@ package android.app.admin; 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 @@ public interface DevicePolicySafetyChecker { /** * 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 3765a67b9c8a..f9ee15393e93 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -491,11 +491,11 @@ interface IDevicePolicyManager { 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); } diff --git a/core/java/android/app/admin/UnsafeStateException.java b/core/java/android/app/admin/UnsafeStateException.java index 9dcaae42e96e..56eeb06e8cc0 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 @@ import android.os.Parcelable; 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 @@ public final class UnsafeStateException extends IllegalStateException implements 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 @@ public final class UnsafeStateException extends IllegalStateException implements @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mOperation); + dest.writeInt(mReason); } @NonNull @@ -63,7 +93,7 @@ public final class UnsafeStateException extends IllegalStateException implements @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/smartspace/SmartspaceAction.java b/core/java/android/app/smartspace/SmartspaceAction.java index 033cda1ba845..439d851acea6 100644 --- a/core/java/android/app/smartspace/SmartspaceAction.java +++ b/core/java/android/app/smartspace/SmartspaceAction.java @@ -18,6 +18,7 @@ package android.app.smartspace; 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 @@ public final class SmartspaceAction implements Parcelable { /** * Returns the extra bundle for this object. */ + @SuppressLint("NullableCollection") public @Nullable Bundle getExtras() { return mExtras; } @@ -334,7 +336,7 @@ public final class SmartspaceAction implements Parcelable { * 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 1f9cbb5ca33b..07d7bf0e33af 100644 --- a/core/java/android/app/smartspace/SmartspaceConfig.java +++ b/core/java/android/app/smartspace/SmartspaceConfig.java @@ -17,6 +17,7 @@ package android.app.smartspace; 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 @@ public final class SmartspaceConfig implements Parcelable { } @Nullable + @SuppressLint("NullableCollection") public Bundle getExtras() { return mExtras; } @@ -186,7 +188,7 @@ public final class SmartspaceConfig implements Parcelable { * 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 397326c7fd40..cec658049ca5 100644 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ b/core/java/android/bluetooth/le/AdvertiseData.java @@ -44,7 +44,7 @@ public final class AdvertiseData implements Parcelable { @Nullable private final List<ParcelUuid> mServiceUuids; - @Nullable + @NonNull private final List<ParcelUuid> mServiceSolicitationUuids; private final SparseArray<byte[]> mManufacturerSpecificData; @@ -77,7 +77,7 @@ public final class AdvertiseData implements Parcelable { /** * 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 final class AdvertiseData implements Parcelable { 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 083ce968d66f..102c98ff9329 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -55,6 +55,8 @@ import java.util.Objects; genBuilder = false) public final class AssociationRequest implements Parcelable { + private static final String LOG_TAG = AssociationRequest.class.getSimpleName(); + /** * Device profile: watch. * @@ -115,13 +117,6 @@ public final class AssociationRequest implements Parcelable { 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 @@ public final class AssociationRequest implements Parcelable { this.mCallingPackage = callingPackage; this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; - onConstructed(); + // onConstructed(); // You can define this method to get a callback } /** @@ -386,7 +381,7 @@ public final class AssociationRequest implements Parcelable { this.mCallingPackage = callingPackage; this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; - onConstructed(); + // onConstructed(); // You can define this method to get a callback } @DataClass.Generated.Member @@ -404,10 +399,10 @@ public final class AssociationRequest implements Parcelable { }; @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 80a7b16ee761..0581ed5cbf22 100644 --- a/core/java/android/content/AutofillOptions.java +++ b/core/java/android/content/AutofillOptions.java @@ -17,6 +17,7 @@ package android.content; 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 @@ public final class AutofillOptions implements Parcelable { * List of allowlisted activities. */ @Nullable + @SuppressLint("NullableCollection") public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill; /** @@ -73,6 +75,7 @@ public final class AutofillOptions implements Parcelable { * 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 ef49e029db13..c296bb52e73d 100644 --- a/core/java/android/content/ContentCaptureOptions.java +++ b/core/java/android/content/ContentCaptureOptions.java @@ -17,6 +17,7 @@ package android.content; 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 @@ public final class ContentCaptureOptions implements Parcelable { * for all acitivites in the package). */ @Nullable + @SuppressLint("NullableCollection") public final ArraySet<ComponentName> whitelistedComponents; /** @@ -96,6 +98,7 @@ public final class ContentCaptureOptions implements Parcelable { */ 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 4dc41b2d5114..987de3fca6b1 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5451,6 +5451,15 @@ public abstract class Context { 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. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 1752b480c06b..30b24044a624 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -37,6 +37,7 @@ import android.content.pm.PackageManager; 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 @@ public class Intent implements Parcelable, Cloneable { * </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 e32068fe4b39..6ec11693d69b 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -38,6 +38,8 @@ import android.util.SparseArray; 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 @@ import java.util.UUID; * <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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { hiddenUntilInstalled = orig.hiddenUntilInstalled; zygotePreloadName = orig.zygotePreloadName; gwpAsanMode = orig.gwpAsanMode; + memtagMode = orig.memtagMode; + nativeHeapZeroInit = orig.nativeHeapZeroInit; } public String toString() { @@ -1774,6 +1837,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { hiddenUntilInstalled = source.readInt() != 0; zygotePreloadName = source.readString8(); gwpAsanMode = source.readInt(); + memtagMode = source.readInt(); + nativeHeapZeroInit = sForBoolean.unparcel(source); } /** @@ -2237,6 +2304,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** {@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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** {@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/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 34d100354051..b8829bbf1ca5 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -627,9 +627,13 @@ interface IPackageManager { 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); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 9f79dd060b95..d09d83f0cd1d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -47,6 +47,7 @@ import android.content.Context; 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.content.res.XmlResourceParser; 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.incremental.IncrementalManager; 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.List; 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 @@ public abstract class PackageManager { * {@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 @@ public abstract class PackageManager { * {@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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 abstract class PackageManager { 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * * 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 @@ public abstract class PackageManager { * * 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 @@ public abstract class PackageManager { * 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"; @@ -5514,7 +5576,7 @@ public abstract class PackageManager { * * @hide */ - @SuppressWarnings("HiddenAbstractMethod") + @SuppressWarnings({"HiddenAbstractMethod", "NullableCollection"}) @TestApi public abstract @Nullable String[] getNamesForUids(int[] uids); @@ -6918,8 +6980,10 @@ public abstract class PackageManager { * @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 +7008,10 @@ public abstract class PackageManager { * {@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 +7036,18 @@ public abstract class PackageManager { * * @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 +7064,10 @@ public abstract class PackageManager { * * @return a list of IntentFilterVerificationInfo for a specific package. * + * @deprecated Use {@link DomainVerificationManager} instead. * @hide */ + @Deprecated @SuppressWarnings("HiddenAbstractMethod") @NonNull @SystemApi diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 99258712030c..5cc74c0a1c8e 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -77,8 +77,6 @@ public class PackageUserState { 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 @@ public class PackageUserState { 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 @@ public class PackageUserState { 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 @@ public class PackageUserState { && !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 @@ public class PackageUserState { 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 d45ff98d58e4..3dd5ee102090 100644 --- a/core/java/android/content/pm/ProcessInfo.java +++ b/core/java/android/content/pm/ProcessInfo.java @@ -53,16 +53,30 @@ public class ProcessInfo implements Parcelable { */ 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 @@ public class ProcessInfo implements Parcelable { * 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 @@ public class ProcessInfo implements Parcelable { 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 @@ public class ProcessInfo implements Parcelable { 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 @@ public class ProcessInfo implements Parcelable { 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 @@ public class ProcessInfo implements Parcelable { 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 @@ public class ProcessInfo implements Parcelable { }; @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 814759956772..7a01392a24e8 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -252,6 +252,10 @@ public interface ParsingPackage extends ParsingPackageRead { 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 51ec297df9e4..c1a93d8c2428 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -380,6 +380,11 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @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 a102e828744e..ff4cebdd1533 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -874,6 +874,19 @@ public interface ParsingPackageRead extends Parcelable { */ 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 8fbf2879bc27..66bdb9bb864a 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -1984,6 +1984,11 @@ public class ParsingPackageUtils { } 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 @@ public class ParsingPackageUtils { /** * 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/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java index e0ae81b2e30b..89fef9d8e0dd 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 static java.util.Collections.emptySet; 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 @@ public class ParsedProcess implements Parcelable { @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 @@ public class ParsedProcess implements Parcelable { - // 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 class ParsedProcess implements Parcelable { 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 @@ public class ParsedProcess implements Parcelable { 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 @@ public class ParsedProcess implements Parcelable { } @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 @@ public class ParsedProcess implements Parcelable { // 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 @@ public class ParsedProcess implements Parcelable { // 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 @@ public class ParsedProcess implements Parcelable { 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 @@ public class ParsedProcess implements Parcelable { }; @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 9bff719e45dd..257977467312 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 @@ public class ParsedProcessUtils { } 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/content/pm/verify/domain/DomainVerificationInfo.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl new file mode 100644 index 000000000000..c143cc517486 --- /dev/null +++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +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 000000000000..7afbe1fcb69f --- /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 000000000000..af12536fff99 --- /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 000000000000..5938def5c83c --- /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 000000000000..473abce26d81 --- /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 000000000000..17593ef2aeb1 --- /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/content/pm/verify/domain/DomainVerificationUserSelection.aidl b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl new file mode 100644 index 000000000000..ddb5ef85382a --- /dev/null +++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +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 000000000000..8d16f75bf1b4 --- /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 000000000000..21dd623b46bc --- /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 000000000000..c6c979107e75 --- /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 f0b218ce5343..abb4f9fa7eef 100644 --- a/core/java/android/graphics/fonts/FontManager.java +++ b/core/java/android/graphics/fonts/FontManager.java @@ -261,7 +261,7 @@ public class FontManager { @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 index 19b20f268486..f6cb373b7ed2 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 000000000000..db047f8ba696 --- /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 e2d836c59099..a6c6b46d5b81 100644 --- a/core/java/android/hardware/display/BrightnessChangeEvent.java +++ b/core/java/android/hardware/display/BrightnessChangeEvent.java @@ -65,6 +65,23 @@ public final class BrightnessChangeEvent implements Parcelable { /** 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 @@ public final class BrightnessChangeEvent implements Parcelable { 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 @@ public final class BrightnessChangeEvent implements Parcelable { 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 @@ public final class BrightnessChangeEvent implements Parcelable { 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 @@ public final class BrightnessChangeEvent implements Parcelable { 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 @@ public final class BrightnessChangeEvent implements Parcelable { 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 @@ public final class BrightnessChangeEvent implements Parcelable { 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 @@ public final class BrightnessChangeEvent implements Parcelable { 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 final class BrightnessChangeEvent implements Parcelable { 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 efeb89ed96ac..e247df320115 100644 --- a/core/java/android/hardware/display/ColorDisplayManager.java +++ b/core/java/android/hardware/display/ColorDisplayManager.java @@ -438,6 +438,56 @@ public final class ColorDisplayManager { } /** + * 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 @@ public final class ColorDisplayManager { } /** + * 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 @@ public final class ColorDisplayManager { } } + 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 7b1033df8b62..ef4f5f9b05e9 100644 --- a/core/java/android/hardware/display/IColorDisplayManager.aidl +++ b/core/java/android/hardware/display/IColorDisplayManager.aidl @@ -45,4 +45,10 @@ interface IColorDisplayManager { 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/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS index e6a04dad25c2..d7db7c741364 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/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index 4cd62eb859f8..f2b466d7fa5d 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -70,17 +70,6 @@ public abstract class BatteryConsumer { 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 @@ public abstract class BatteryConsumer { 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 b846142e873d..cc86a604c194 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1556,8 +1556,7 @@ public abstract class BatteryStats implements Parcelable { 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 @@ public abstract class BatteryStats implements Parcelable { out.writeInt(statIrqTime); out.writeInt(statSoftIrqTime); out.writeInt(statIdlTime); - out.writeString(statPlatformIdleState); out.writeString(statSubsystemPowerState); } @@ -1611,7 +1609,6 @@ public abstract class BatteryStats implements Parcelable { statIrqTime = in.readInt(); statSoftIrqTime = in.readInt(); statIdlTime = in.readInt(); - statPlatformIdleState = in.readString(); statSubsystemPowerState = in.readString(); } } @@ -1656,6 +1653,7 @@ public abstract class BatteryStats implements Parcelable { public byte batteryPlugType; public short batteryTemperature; + // Battery voltage in millivolts (mV). @UnsupportedAppUsage public char batteryVoltage; @@ -6599,9 +6597,6 @@ public abstract class BatteryStats implements Parcelable { 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 @@ public abstract class BatteryStats implements Parcelable { 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 f21a8125f0a5..04e529e80bff 100644 --- a/core/java/android/os/BatteryStatsManager.java +++ b/core/java/android/os/BatteryStatsManager.java @@ -32,6 +32,7 @@ import com.android.internal.app.IBatteryStats; 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 @@ public final class BatteryStatsManager { @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 a6df87d54dc0..af8e8de85d9f 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -114,7 +114,6 @@ public final class BatteryUsageStats implements Parcelable { 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 @@ public final class BatteryUsageStats implements Parcelable { 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 @@ public final class BatteryUsageStats implements Parcelable { 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 @@ public final class BatteryUsageStats implements Parcelable { 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 9a0067902514..48e7389e5ff2 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -38,18 +38,19 @@ public final class BatteryUsageStatsQuery implements Parcelable { * @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 @@ public final class BatteryUsageStatsQuery implements Parcelable { } /** - * 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 0185ba444ca4..16d041ac60f2 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -547,7 +547,8 @@ public final class BinderProxy implements IBinder { } 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 @@ public final class BinderProxy implements IBinder { * 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 fc650901cb2d..5f5a910a74ac 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -3729,4 +3729,9 @@ public final class Parcel { 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 f681c4ff0ce4..1337d558e439 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -33,11 +33,9 @@ class PowerComponents { 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 @@ class PowerComponents { 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 @@ class PowerComponents { 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 @@ class PowerComponents { 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 @@ class PowerComponents { 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 49bf08461016..fc4aa93b97c7 100644 --- a/core/java/android/os/SystemBatteryConsumer.java +++ b/core/java/android/os/SystemBatteryConsumer.java @@ -123,8 +123,8 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable 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 31617668be08..3ffaa9e4b1f0 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -98,8 +98,8 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela 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/provider/OWNERS b/core/java/android/provider/OWNERS index cb1509af66ac..cb06515910bf 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 000000000000..2efc21229422 --- /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/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 507dc7a1d59a..82e0b4a1aecc 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -880,8 +880,8 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { 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 b22921233f05..790773fd83c5 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -65,7 +65,7 @@ public class FeatureFlagUtils { 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 86120d1e650c..6718e93f908c 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 com.android.internal.util.GrowingArrayUtils; 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 @@ public class SparseArray<E> implements Cloneable { 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/FrameMetrics.java b/core/java/android/view/FrameMetrics.java index c2566cc78bb0..5937499744b8 100644 --- a/core/java/android/view/FrameMetrics.java +++ b/core/java/android/view/FrameMetrics.java @@ -149,7 +149,7 @@ public final class FrameMetrics { * <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/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 7b15f5243d08..990b7bdfa987 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -47,11 +47,11 @@ import java.util.List; 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 bc03222f390d..59e493191711 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -455,7 +455,6 @@ public final class InputDevice implements Parcelable { * 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/SurfaceView.java b/core/java/android/view/SurfaceView.java index f603ef7901c7..70ec2d42b59b 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -877,6 +877,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall synchronized (mSurfaceControlLock) { mSurface.release(); + if (mBlastBufferQueue != null) { + mBlastBufferQueue.destroy(); + mBlastBufferQueue = null; + } if (mRtHandlingPositionUpdates) { mRtReleaseSurfaces = true; @@ -901,10 +905,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall 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 5e3599d40dff..18ef80ce9772 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -138,6 +138,7 @@ import android.os.Trace; 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.AttachInfo; 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; @@ -926,6 +928,33 @@ public final class ViewRootImpl implements ViewParent, } } + // 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, } 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 @@ public final class ViewRootImpl implements ViewParent, /* 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, } // 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 63f1eedf1c50..9e87c95a8a75 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1511,9 +1511,7 @@ public interface WindowManager extends ViewManager { * 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 interface WindowManager extends ViewManager { 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 @@ public interface WindowManager extends ViewManager { 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 @@ public interface WindowManager extends ViewManager { 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 @@ public interface WindowManager extends ViewManager { 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 @@ public interface WindowManager extends ViewManager { 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/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 5ae66e3076f8..b85f10799210 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -135,7 +135,7 @@ public class WindowlessWindowManager implements IWindowSession { */ @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 @@ public class WindowlessWindowManager implements IWindowSession { @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 73962d7f65e0..10fd0e036814 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java +++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java @@ -82,6 +82,7 @@ public final class InlineSuggestionInfo implements Parcelable { 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 e6a04dad25c2..d7db7c741364 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 fa4614628102..b49d3c004f44 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -101,15 +101,17 @@ public class UiTranslationController { } 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 @@ public class UiTranslationController { */ 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 @@ public class UiTranslationController { } } - 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/com/android/internal/annotations/CompositeRWLock.java b/core/java/com/android/internal/annotations/CompositeRWLock.java new file mode 100644 index 000000000000..b6ddfc4d2cc4 --- /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 0e63214b4f1a..c05c4abd10e4 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 @@ import java.lang.annotation.Target; * 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 55f8c400022f..c1952c7d52cf 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -52,7 +52,7 @@ interface IBatteryStats { // 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 cafe0de58b78..dfcc91421b9e 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 @@ import android.graphics.fonts.SystemFontState; 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 9d6210e5dcc8..1a6870c138f4 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; +public interface ListenerTransport<TListener> { - 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; - } + /** + * 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. + */ + @Nullable TListener getListener(); /** - * Prevents any listener invocations that happen-after this call. + * Must be implemented so that {@link #getListener()} returns null after this is invoked. */ - public final void unregister() { - mListener = null; - } + 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 @@ public class ListenerTransport<TListener> { 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 000000000000..0d5d1b7b53ff --- /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 fc1d69f570ad..000000000000 --- 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 178de0647fd1..000000000000 --- 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 6609ebe49bf6..8fe17fb1565c 100644 --- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -54,6 +54,7 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { .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 class AmbientDisplayPowerCalculator extends PowerCalculator { 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 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { 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 c8805ddd3c43..af61f91841fa 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -35,6 +35,8 @@ public class BatterySipper implements Comparable<BatterySipper> { /** * 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 fcf8bb4e748b..aa5015a61f63 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.ServiceManager; 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 2b034b0667d6..1f7a7aa42669 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.net.NetworkCapabilities.TRANSPORT_WIFI; 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.KernelCpuUidClusterTimeRea 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 @@ public class BatteryStatsImpl extends BatteryStats { 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 class BatteryStatsImpl extends BatteryStats { public interface PlatformIdleStateCallback { public void fillLowPowerStats(RpmStats rpmStats); - public String getPlatformLowPowerStats(); public String getSubsystemLowPowerStats(); } @@ -3482,11 +3479,6 @@ public class BatteryStatsImpl extends BatteryStats { } 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 @@ public class BatteryStatsImpl extends BatteryStats { 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 @@ public class BatteryStatsImpl extends BatteryStats { 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 @@ public class BatteryStatsImpl extends BatteryStats { 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 @@ public class BatteryStatsImpl extends BatteryStats { @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 @@ public class BatteryStatsImpl extends BatteryStats { return; } - final @EnergyBucket int energyBucket = + final @StandardEnergyBucket int energyBucket = MeasuredEnergyStats.getDisplayEnergyBucket(mScreenStateAtLastEnergyMeasurement); mScreenStateAtLastEnergyMeasurement = screenState; @@ -12425,7 +12413,7 @@ public class BatteryStatsImpl extends BatteryStats { 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 @@ public class BatteryStatsImpl extends BatteryStats { 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 @@ public class BatteryStatsImpl extends BatteryStats { /** * 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 964568c3307b..e76e34f840ba 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -83,7 +83,7 @@ public class BatteryUsageStatsProvider { /** * 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 @@ public class BatteryUsageStatsProvider { 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 7972924549e5..11c87618762e 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -122,11 +122,5 @@ public class CpuPowerCalculator extends PowerCalculator { .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 974894f33f30..05fcc705e734 100644 --- a/core/java/com/android/internal/os/PowerCalculator.java +++ b/core/java/com/android/internal/os/PowerCalculator.java @@ -102,7 +102,7 @@ public abstract class PowerCalculator { // 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 @@ public abstract class PowerCalculator { // 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 9c4a26724ba8..c86c795c965a 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.SystemBatteryConsumer; 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 @@ public class ScreenPowerCalculator extends PowerCalculator { .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 @@ public class ScreenPowerCalculator extends PowerCalculator { @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { + + final long energyUJ = batteryStats.getScreenOnEnergy(); + final boolean isMeasuredDataAvailable = energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE; + 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 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 @@ public class ScreenPowerCalculator extends PowerCalculator { 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 @@ public class ScreenPowerCalculator extends PowerCalculator { 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 @@ public class ScreenPowerCalculator extends PowerCalculator { } 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 4b343af61f0c..d196d4af2e42 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -117,6 +117,8 @@ public final class Zygote { 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 @@ public final class Zygote { * 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 141dc79f4c93..5df175e8aee5 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -2540,8 +2540,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - 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 b744a5d57e98..e310f8d9b36e 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -20,8 +20,10 @@ package com.android.internal.power; 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.Retention; 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 @@ import java.lang.annotation.RetentionPolicy; 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 @@ public class MeasuredEnergyStats { 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 @@ public class MeasuredEnergyStats { * 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 @@ public class MeasuredEnergyStats { /** * 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 @@ public class MeasuredEnergyStats { * 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 @@ public class MeasuredEnergyStats { /** * 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 @@ public class MeasuredEnergyStats { /** * 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 @@ public class MeasuredEnergyStats { * 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 class MeasuredEnergyStats { */ 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 @@ public class MeasuredEnergyStats { /** 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 @@ public class MeasuredEnergyStats { 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 @@ public class MeasuredEnergyStats { 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 3cf00aed3880..c6fd6eec2f91 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.Collections; 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 @@ public class ArrayUtils { 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 dd64c402fdbf..1ab316d07e42 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.ArrayList; 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 @@ public interface Parcelling<T> { 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 ae1a815910ed..4b9a1606975b 100644 --- a/core/java/com/android/internal/view/ScrollCaptureInternal.java +++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java @@ -59,6 +59,11 @@ public class ScrollCaptureInternal { 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 @@ public class ScrollCaptureInternal { } 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/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index e5bc47097751..cb586d660634 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -178,8 +178,9 @@ public class SystemConfig { // 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 787d34822ef7..3acbd1ecd16e 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 jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jclass clazz, j 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_freeBuffer(JNIEnv* env, jclass clazz, jlong native 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 675648a59423..f7b3f309ed12 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 bool should_time_binder_calls() { } 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 @@ static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, 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 @@ static const JNINativeMethod gBinderProxyMethods[] = { {"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/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index c5a955976867..3d1c38d4b5ca 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -339,6 +339,7 @@ enum RuntimeFlags : uint32_t { 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 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, } 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 748b4b4f5743..99fd21592411 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -16,6 +16,7 @@ ogunwale@google.com 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 e6833066f17e..bb39ea810add 100644 --- a/core/proto/android/content/package_item_info.proto +++ b/core/proto/android/content/package_item_info.proto @@ -110,6 +110,8 @@ message ApplicationInfoProto { 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 000000000000..d341c4b2f0a8 --- /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 0c5a36049a29..4b1ee02a6c30 100644 --- a/core/proto/android/server/powerstatsservice.proto +++ b/core/proto/android/server/powerstatsservice.proto @@ -191,6 +191,14 @@ message EnergyConsumerProto { 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 @@ message EnergyConsumerResultProto { /** 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 5442c0355590..269ccbbbfaec 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" /> @@ -2583,6 +2584,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 +4707,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/values/attrs.xml b/core/res/res/values/attrs.xml index a8ef2cf8efca..eb867090f8ba 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}. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index c16588c4bc7b..b4e580aac959 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 4f920dac1bcf..20a5d379cbcc 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 6e9eb8207922..c8cccc45f634 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 416bc8447869..3790aa4fb072 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 2381fc6fb457..a7e8f2adab9c 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,8 @@ <public name="sspAdvancedPattern" /> <public name="fontProviderSystemFontFamily" /> <public name="hand_second" /> + <public name="memtagMode" /> + <public name="nativeHeapZeroInit" /> </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 672aec62f6a6..80163b16ada0 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 dff6e874c9b0..ee17f6f7ce21 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -219,6 +219,9 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 e0d159b63227..9e6827c19cb2 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 @@ public class BatteryConsumerData { 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 @@ public class BatteryConsumerData { } BatteryConsumer requestedBatteryConsumer = null; - double totalModeledCpuPowerMah = 0; for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { if (batteryConsumerId(consumer).equals(batteryConsumerId)) { requestedBatteryConsumer = consumer; } + } + + double totalModeledCpuPowerMah = 0; + BatteryConsumer requestedBatteryConsumerPowerProfileModeled = null; + for (BatteryConsumer consumer : powerProfileModeledUsageStats.getUidBatteryConsumers()) { + if (batteryConsumerId(consumer).equals(batteryConsumerId)) { + requestedBatteryConsumerPowerProfileModeled = consumer; + } - totalModeledCpuPowerMah += consumer.getConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID - + BatteryConsumer.POWER_COMPONENT_CPU); + totalModeledCpuPowerMah += consumer.getConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU); } if (requestedBatterySipper == null) { @@ -196,11 +204,10 @@ public class BatteryConsumerData { 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 87a175af41cb..4ead8eef5684 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 @@ public class BatteryStatsViewerActivity extends ComponentActivity { 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 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } } - 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 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } @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 @@ public class BatteryStatsViewerActivity extends ComponentActivity { 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 151a320494b4..bb826deb4eff 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" /> @@ -1456,7 +1455,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 +1462,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 000000000000..b0e0b9deaddb --- /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 000000000000..b0e0b9deaddb --- /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 000000000000..b0e0b9deaddb --- /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/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java index d79c2fe19ce2..0f81896692c0 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 @@ public class BroadcastTest extends ActivityTestsBase { "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 class BroadcastTest extends ActivityTestsBase { } 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 @@ public class BroadcastTest extends ActivityTestsBase { 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 class BroadcastTest extends ActivityTestsBase { 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 @@ public class BroadcastTest extends ActivityTestsBase { }; 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 @@ public class BroadcastTest extends ActivityTestsBase { }; 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 class BroadcastTest extends ActivityTestsBase { 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 @@ public class BroadcastTest extends ActivityTestsBase { }; 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 @@ public class BroadcastTest extends ActivityTestsBase { }; 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 class BroadcastTest extends ActivityTestsBase { 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 0b21fa90c1c4..766245600d13 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 @@ public class LaunchpadActivity extends Activity { 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 @@ public class LaunchpadActivity extends Activity { 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 @@ public class LaunchpadActivity extends Activity { } 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 16ea73f30937..2120a1db463c 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.RemoteException; 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 5c1ded93e1c3..7c89346e820d 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.RemoteException; 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 000000000000..be3826007aa3 --- /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 127ecfb9dc39..000000000000 --- 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 59534e4a84ef..2c71287fac4a 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 @@ public class BatteryUsageStatsRule implements TestRule { } 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 7b1ca8f36224..018a810772be 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 @@ public class BatteryUsageStatsTest { 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 @@ public class BatteryUsageStatsTest { 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 @@ public class BatteryUsageStatsTest { 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 @@ public class BatteryUsageStatsTest { 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 @@ public class BatteryUsageStatsTest { 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 bf74c8d82fa2..1687a78c9875 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 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { 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 c9c81ac51944..b9908f46c81c 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 android.os.BatteryStats.ENERGY_DATA_UNAVAILABLE; 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 @@ public class MeasuredEnergyStatsTest { @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 MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); - - for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - if (supportedEnergyBuckets[i]) { - assertTrue(stats.isEnergyBucketSupported(i)); - assertEquals(0L, stats.getAccumulatedBucketEnergy(i)); + 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); + + 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 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 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 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 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 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(); stats.writeToParcel(parcel); @@ -108,94 +126,127 @@ public class MeasuredEnergyStatsTest { 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 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 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); - 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 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 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.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 @@ public class MeasuredEnergyStatsTest { 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 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); - - assertEquals(15, stats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); + 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_DOZE, 30, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + + 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 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 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); 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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index ae8e3ce854e3..cdc61a1c5752 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -468,6 +468,7 @@ applications that come with the platform <!-- 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/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index b1b6306e0cf6..16bf5469296f 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -23,7 +23,6 @@ import android.security.KeyStore; 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 @@ public class AndroidKeyStoreProvider extends Provider { 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 @@ public class AndroidKeyStoreProvider extends Provider { * @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 @@ public class AndroidKeyStoreProvider extends Provider { // 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/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json index 2cfb13e7dea6..9c3d84e72f8c 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/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index ffeabd876b81..40fdb97b0094 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.graphics.drawable.Icon; 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 @@ public class Bubble implements BubbleViewProvider { 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 @@ public class Bubble implements BubbleViewProvider { mMetadataShortcutId = shortcutInfo.getId(); mShortcutInfo = shortcutInfo; mKey = key; + mGroupKey = null; mFlags = 0; mUser = shortcutInfo.getUserHandle(); mPackageName = shortcutInfo.getPackage(); @@ -182,6 +186,7 @@ public class Bubble implements BubbleViewProvider { final Bubbles.PendingIntentCanceledListener intentCancelListener, Executor mainExecutor) { mKey = entry.getKey(); + mGroupKey = entry.getGroupKey(); mSuppressionListener = listener; mIntentCancelListener = intent -> { if (mIntent != null) { @@ -200,6 +205,14 @@ public class Bubble implements BubbleViewProvider { 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 7538c8b7ffad..d73fc6dca4c6 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 @@ public class BubbleController { // 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 @@ public class BubbleController { 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 @@ public class BubbleController { 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 9d196ba32f8a..53b75373a647 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 @@ public class BubbleData { 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 3361c4ce11da..c88a58be1461 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 @@ public class BubbleLogger { 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 @@ public class BubbleLogger { 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 af421facd72a..39510e55139a 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 @@ -2733,9 +2733,13 @@ public class BubbleStackView extends FrameLayout * @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 6102147e4b53..6a1026bb24fe 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 @@ public interface Bubbles { 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 @@ public interface Bubbles { 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 e1f831eae699..73371e7eff20 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 @@ public class StackAnimationController extends 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 @@ public class StackAnimationController extends 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 @@ public class StackAnimationController extends @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 @@ public class StackAnimationController extends * 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 @@ public class StackAnimationController extends 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 00bd9e507335..59374a6069c8 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 @@ package com.android.wm.shell.common; 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 @@ public interface TaskStackListenerCallback { 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 db34248d491b..e94080aa8db7 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.IActivityTaskManager; 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 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. 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 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. } @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 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. } 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 90992fb92324..45aa3870ecb6 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.Animator; 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 @@ public class PipAnimationController { @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 @@ public class PipAnimationController { * 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 @@ public class PipAnimationController { } 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 @@ public class PipAnimationController { /** * 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 class PipAnimationController { 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 @@ public class PipAnimationController { 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 @@ public class PipAnimationController { mCurrentValue = mStartValue; onStartTransaction(mLeash, newSurfaceControlTransaction()); if (mPipAnimationCallback != null) { - mPipAnimationCallback.onPipAnimationStart(this); + mPipAnimationCallback.onPipAnimationStart(mTaskInfo, this); } } @@ -250,14 +255,14 @@ public class PipAnimationController { 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 @@ public class PipAnimationController { 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 @@ public class PipAnimationController { }; } - 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 @@ public class PipAnimationController { 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 a8961ea3d8a8..ac5d14c802d8 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 @@ package com.android.wm.shell.pip; 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.graphics.Rect; 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 @@ public class PipBoundsAlgorithm { 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 b112c51455d2..cb39b4e63655 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.Point; 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 @@ public final class PipBoundsState { } } + /** + * 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 b7958b7a7077..fb83006e8522 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.Animator; 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.IBinder; 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.WindowContainerTransaction; 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.ShellExecutor; 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } @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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } @Override - public void onPipAnimationCancel(PipAnimationController.PipTransitionAnimator animator) { + public void onPipAnimationCancel(TaskInfo taskInfo, + PipAnimationController.PipTransitionAnimator animator) { sendOnPipTransitionCancelled(animator.getTransitionDirection()); } }; @@ -202,7 +202,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** - * 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 class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } - /** - * 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 000000000000..91e8c9939244 --- /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 000000000000..d801c918973a --- /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 c06f9d03cdf7..c3970e33d736 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.PipBoundsState; 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 @@ import java.util.function.Consumer; /** * 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 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { 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 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { } } - /** * Instantiates {@link PipController}, returns {@code null} if the feature not supported. */ @@ -223,7 +224,8 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { 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 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { 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 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, + PipTransitionController pipTransitionController, WindowManagerShellWrapper windowManagerShellWrapper, TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor @@ -266,9 +270,10 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { 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 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { } @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 @@ public class PipController implements PipTaskOrganizer.PipTransitionCallback { } @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 fd4ea61713ef..b19dcae2def8 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 static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; 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.common.magnetictarget.MagnetizedObject; 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 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ 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 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } @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 8fb358ad74d1..b91ba34c4464 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 @@ public class PipResizeGestureHandler { 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 @@ public class PipResizeGestureHandler { // 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 3cb3ae89b5f5..e69c6f2f47bc 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.PipAnimationController; 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 @@ public class PipTouchHandler { PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, + PipTransitionController pipTransitionController, FloatingContentCoordinator floatingContentCoordinator, PipUiEventLogger pipUiEventLogger, ShellExecutor mainExecutor) { @@ -168,7 +170,7 @@ public class PipTouchHandler { 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 75fc9f5a4ecf..56f183fd7303 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.PipBoundsAlgorithm; 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 @@ import java.lang.annotation.RetentionPolicy; /** * 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 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipTaskOrganizer pipTaskOrganizer, + PipTransitionController pipTransitionController, TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, TvPipNotificationController pipNotificationController, @@ -116,6 +118,7 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, pipBoundsState, pipBoundsAlgorithm, pipTaskOrganizer, + pipTransitionController, tvPipMenuController, pipMediaController, pipNotificationController, @@ -129,6 +132,7 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipTaskOrganizer pipTaskOrganizer, + PipTransitionController pipTransitionController, TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, TvPipNotificationController pipNotificationController, @@ -152,7 +156,7 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, mTvPipMenuController.setDelegate(this); mPipTaskOrganizer = pipTaskOrganizer; - mPipTaskOrganizer.registerPipTransitionCallback(this); + pipTransitionController.registerPipTransitionCallback(this); loadConfigurations(); @@ -302,17 +306,17 @@ public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, } @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 000000000000..b7caf72641a3 --- /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 8cb16c87ce6f..286c3b6a051e 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 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang } } - // 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 319872556365..b5e18960ff5c 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 @@ public class TaskSnapshotWindow { 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 8271b0689053..ac93a17b4014 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.TransitionInfo; 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 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { @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 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { 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 2ab4e0bdd76f..d8687bd899de 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 @@ public class Transitions { 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 @@ public class Transitions { 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 5374bd9f40de..2c29220bf20e 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 @@ package com.android.wm.shell.flicker.legacysplitscreen 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 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +// @FlakyTest(bugId = 179116910) class EnterSplitScreenDockActivity( testSpec: FlickerTestRunnerFactory.TestSpec ) : FlickerTestRunner(testSpec) { @@ -92,7 +94,9 @@ class EnterSplitScreenDockActivity( } 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 d750403d66c6..903971ea084f 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 @@ package com.android.wm.shell.flicker.legacysplitscreen 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 @@ class EnterSplitScreenLaunchToSide( } 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 7782364636f1..493366553623 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 @@ package com.android.wm.shell.flicker.legacysplitscreen 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 @@ class ExitLegacySplitScreenFromBottom( } 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 59f6aaf7dd6a..ff3a979717f2 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 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen( } 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 c802ffef204f..7edef9314941 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 @@ package com.android.wm.shell.flicker.legacysplitscreen 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 @@ class OpenAppToLegacySplitScreen( } 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/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java index 495be4146f9d..21bc32c6563c 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.ActivityManager; 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 @@ public class TaskStackListenerImplTest { 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/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index c565a4cc2e28..0087d917f007 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.any; 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 @@ public class PipAnimationControllerTest extends ShellTestCase { private SurfaceControl mLeash; @Mock + private TaskInfo mTaskInfo; + + @Mock private PipAnimationController.PipAnimationCallback mPipAnimationCallback; @Before @@ -70,7 +74,7 @@ public class PipAnimationControllerTest extends ShellTestCase { @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 @@ public class PipAnimationControllerTest extends ShellTestCase { @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 @@ public class PipAnimationControllerTest extends ShellTestCase { 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 @@ public class PipAnimationControllerTest extends ShellTestCase { @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 @@ public class PipAnimationControllerTest extends ShellTestCase { 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 @@ public class PipAnimationControllerTest extends ShellTestCase { 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 @@ public class PipAnimationControllerTest extends ShellTestCase { // 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 7a810a1742d7..9430af946899 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 @@ package com.android.wm.shell.pip; 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.ShellTestCase; 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 @@ public class PipTaskOrganizerTest extends ShellTestCase { 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 @@ public class PipTaskOrganizerTest extends ShellTestCase { 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 @@ public class PipTaskOrganizerTest extends ShellTestCase { @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 class PipTaskOrganizerTest extends ShellTestCase { 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 @@ public class PipTaskOrganizerTest extends ShellTestCase { 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 62ffac4fbd3f..cfe84639d24e 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.PipBoundsAlgorithm; 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 @@ public class PipControllerTest extends ShellTestCase { @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 @@ public class PipControllerTest extends ShellTestCase { 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 @@ public class PipControllerTest extends ShellTestCase { @Test public void instantiatePipController_registersPipTransitionCallback() { - verify(mMockPipTaskOrganizer).registerPipTransitionCallback(any()); + verify(mMockPipTransitionController).registerPipTransitionCallback(any()); } @Test @@ -113,8 +115,8 @@ public class PipControllerTest extends ShellTestCase { 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 b4cfbc281d61..449ad88f6532 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.PipBoundsAlgorithm; 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 @@ public class PipTouchHandlerTest extends ShellTestCase { private PipTaskOrganizer mPipTaskOrganizer; @Mock + private PipTransitionController mMockPipTransitionController; + + @Mock private FloatingContentCoordinator mFloatingContentCoordinator; @Mock @@ -98,7 +102,8 @@ public class PipTouchHandlerTest extends ShellTestCase { 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 65f4e8c8ecec..e798f2a2bc69 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -129,8 +129,9 @@ bool Properties::load() { 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/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 633f21ceba07..37a6ee71c4a6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -157,12 +157,14 @@ static void setBufferCount(ANativeWindow* window) { 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 @@ bool CanvasContext::surfaceRequiresRedraw() { } 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 ce92661cb87c..40d8615b95f7 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 @@ import android.os.IRemoteCallback; */ 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 523117b39762..35a40910e373 100644 --- a/location/java/android/location/LocationListener.java +++ b/location/java/android/location/LocationListener.java @@ -19,6 +19,7 @@ package android.location; import android.annotation.NonNull; import android.os.Bundle; +import java.util.List; import java.util.concurrent.Executor; /** @@ -48,15 +49,18 @@ public interface LocationListener { /** * 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 fdb044d0dcf6..d56948222797 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_COARSE_LOCATION; 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.app.PropertyInvalidatedCache; 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.IRemoteCallback; 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 @@ public class LocationManager { /** * 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 @@ public class LocationManager { 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; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link " - + "LocationManager}") - final ILocationManager mService; + // 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(); + } - private final Object mLock = new Object(); + private final Context mContext; + private final ILocationManager mService; - @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 @@ public class LocationManager { } }; - @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 @@ public class LocationManager { */ @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 @@ public class LocationManager { @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 @@ public class LocationManager { "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 @@ public class LocationManager { "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 @@ public class LocationManager { "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } - getGnssStatusTransportMultiplexer().removeListener(listener); + GnssLazyLoader.sGnssStatusListeners.removeListener(listener); } /** @@ -2382,7 +2353,8 @@ public class LocationManager { 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 @@ public class LocationManager { * @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 class LocationManager { 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 @@ public class LocationManager { * @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 @@ public class LocationManager { @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 @@ public class LocationManager { @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 class LocationManager { */ public void unregisterGnssMeasurementsCallback( @NonNull GnssMeasurementsEvent.Callback callback) { - getGnssMeasurementsTransportMultiplexer().removeListener(callback); + GnssLazyLoader.sGnssMeasurementsListeners.removeListener(callback); } /** @@ -2681,7 +2652,8 @@ public class LocationManager { 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 @@ public class LocationManager { */ @Deprecated public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) { - getGnssAntennaInfoTransportMultiplexer().removeListener(listener); + GnssLazyLoader.sGnssAntennaInfoListeners.removeListener(listener); } /** @@ -2784,7 +2756,8 @@ public class LocationManager { 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 class LocationManager { */ public void unregisterGnssNavigationMessageCallback( @NonNull GnssNavigationMessage.Callback callback) { - getGnssNavigationTransportMultiplexer().removeListener(callback); + GnssLazyLoader.sGnssNavigationListeners.removeListener(callback); } /** @@ -2904,6 +2877,89 @@ public class LocationManager { } } + 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 @@ public class LocationManager { } @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 @@ public class LocationManager { @Override public void onStarted() { - mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED); + mGpsListener.onGpsStatusChanged(GPS_EVENT_STARTED); } @Override @@ -3037,273 +3093,269 @@ public class LocationManager { } } - 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() {} + 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 GnssStatus getGnssStatus() { - return mGnssStatus; + public String getPackage() { + return mPackageName; } - public int getTtff() { - return mTtff; + public String getAttributionTag() { + return mAttributionTag; } - public void addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor) { - addListener(listener, null, new GpsAdapter(listener), executor); + @Override + public void unregister() { + mListener = null; } @Override - protected void registerWithServer(Void ignored) throws RemoteException { - IGnssStatusListener transport = mListenerTransport; - if (transport == null) { - transport = new GnssStatusListener(); - } + public @Nullable GnssStatus.Callback getListener() { + return mListener; + } - // if a remote exception is thrown the transport should not be set - mListenerTransport = null; - mService.registerGnssStatusCallback(transport, mContext.getPackageName(), - mContext.getAttributionTag()); - mListenerTransport = transport; + @Override + public void onGnssStarted() { + execute(mExecutor, GnssStatus.Callback::onStarted); } @Override - protected void unregisterWithServer() throws RemoteException { - if (mListenerTransport != null) { - IGnssStatusListener transport = mListenerTransport; - mListenerTransport = null; - mService.unregisterGnssStatusCallback(transport); - } + public void onGnssStopped() { + execute(mExecutor, GnssStatus.Callback::onStopped); } - private class GnssStatusListener extends IGnssStatusListener.Stub { + @Override + public void onFirstFix(int ttff) { + execute(mExecutor, listener -> listener.onFirstFix(ttff)); - GnssStatusListener() {} + } - @Override - public void onGnssStarted() { - deliverToListeners(GnssStatus.Callback::onStarted); - } + @Override + public void onSvStatusChanged(GnssStatus gnssStatus) { + execute(mExecutor, listener -> listener.onSatelliteStatusChanged(gnssStatus)); + } + } - @Override - public void onGnssStopped() { - deliverToListeners(GnssStatus.Callback::onStopped); - } + private static class GpsStatusTransport extends GnssStatusTransport { - @Override - public void onFirstFix(int ttff) { - mTtff = ttff; - deliverToListeners(callback -> callback.onFirstFix(ttff)); - } + static volatile int sTtff; + static volatile GnssStatus sGnssStatus; - @Override - public void onSvStatusChanged(GnssStatus gnssStatus) { - mGnssStatus = gnssStatus; - deliverToListeners(callback -> callback.onSatelliteStatusChanged(gnssStatus)); - } + 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 class GnssNmeaTransportMultiplexer extends - ListenerTransportMultiplexer<Void, OnNmeaMessageListener> { + private static class GnssNmeaTransport extends IGnssNmeaListener.Stub implements + ListenerTransport<OnNmeaMessageListener> { - private @Nullable IGnssNmeaListener mListenerTransport; + private final Executor mExecutor; + private final String mPackageName; + private final String mAttributionTag; - GnssNmeaTransportMultiplexer() {} + private volatile @Nullable OnNmeaMessageListener mListener; - public void addListener(@NonNull OnNmeaMessageListener listener, - @NonNull Executor executor) { - addListener(listener, null, listener, executor); + 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 - protected void registerWithServer(Void ignored) throws RemoteException { - IGnssNmeaListener transport = mListenerTransport; - if (transport == null) { - transport = new GnssNmeaListener(); - } + public void unregister() { + mListener = null; + } - // if a remote exception is thrown the transport should not be set - mListenerTransport = null; - mService.registerGnssNmeaCallback(transport, mContext.getPackageName(), - mContext.getAttributionTag()); - mListenerTransport = transport; + @Override + public @Nullable OnNmeaMessageListener getListener() { + return mListener; } @Override - protected void unregisterWithServer() throws RemoteException { - if (mListenerTransport != null) { - IGnssNmeaListener transport = mListenerTransport; - mListenerTransport = null; - mService.unregisterGnssNmeaCallback(transport); - } + public void onNmeaReceived(long timestamp, String nmea) { + execute(mExecutor, callback -> callback.onNmeaMessage(nmea, timestamp)); } + } - private class GnssNmeaListener extends IGnssNmeaListener.Stub { + private static class GnssMeasurementsTransport extends IGnssMeasurementsListener.Stub implements + ListenerTransport<GnssMeasurementsEvent.Callback> { - GnssNmeaListener() {} + private final Executor mExecutor; + private final String mPackageName; + private final String mAttributionTag; + private final GnssMeasurementRequest mRequest; - @Override - public void onNmeaReceived(long timestamp, String nmea) { - deliverToListeners(callback -> callback.onNmeaMessage(nmea, timestamp)); - } + 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; } - } - private class GnssMeasurementsTransportMultiplexer extends - ListenerTransportMultiplexer<GnssMeasurementRequest, GnssMeasurementsEvent.Callback> { + public String getPackage() { + return mPackageName; + } - private @Nullable IGnssMeasurementsListener mListenerTransport; + public String getAttributionTag() { + return mAttributionTag; + } - GnssMeasurementsTransportMultiplexer() {} + public GnssMeasurementRequest getRequest() { + return mRequest; + } @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 void unregister() { + mListener = null; } @Override - protected void unregisterWithServer() throws RemoteException { - if (mListenerTransport != null) { - IGnssMeasurementsListener transport = mListenerTransport; - mListenerTransport = null; - mService.removeGnssMeasurementsListener(transport); - } + public @Nullable GnssMeasurementsEvent.Callback getListener() { + return mListener; } @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); - } - } + public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) { + execute(mExecutor, callback -> callback.onGnssMeasurementsReceived(event)); + } - return builder.build(); + @Override + public void onStatusChanged(int status) { + execute(mExecutor, callback -> callback.onStatusChanged(status)); } + } - private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub { + private static class GnssAntennaInfoTransport extends BroadcastReceiver implements + ListenerTransport<GnssAntennaInfo.Listener> { - GnssMeasurementsListener() {} + private final Executor mExecutor; + private final Context mContext; - @Override - public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) { - deliverToListeners(callback -> callback.onGnssMeasurementsReceived(event)); - } + private volatile @Nullable GnssAntennaInfo.Listener mListener; - @Override - public void onStatusChanged(int status) { - deliverToListeners(callback -> callback.onStatusChanged(status)); - } + 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; } - } - - private class GnssNavigationTransportMultiplexer extends - ListenerTransportMultiplexer<Void, GnssNavigationMessage.Callback> { - - @Nullable - private IGnssNavigationMessageListener mListenerTransport; - GnssNavigationTransportMultiplexer() {} + public Context getContext() { + return mContext; + } @Override - protected void registerWithServer(Void ignored) throws RemoteException { - IGnssNavigationMessageListener transport = mListenerTransport; - if (transport == null) { - transport = new GnssNavigationMessageListener(); - } + public void unregister() { + mListener = null; + } - // if a remote exception is thrown the transport should not be set - mListenerTransport = null; - mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(), - mContext.getAttributionTag()); - mListenerTransport = transport; + @Override + public @Nullable GnssAntennaInfo.Listener getListener() { + return mListener; } @Override - protected void unregisterWithServer() throws RemoteException { - if (mListenerTransport != null) { - IGnssNavigationMessageListener transport = mListenerTransport; - mListenerTransport = null; - mService.removeGnssNavigationMessageListener(transport); + 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 GnssNavigationMessageListener extends IGnssNavigationMessageListener.Stub { + private static class GnssNavigationTransport extends IGnssNavigationMessageListener.Stub + implements ListenerTransport<GnssNavigationMessage.Callback> { - GnssNavigationMessageListener() {} + private final Executor mExecutor; + private final String mPackageName; + private final String mAttributionTag; - @Override - public void onGnssNavigationMessageReceived(GnssNavigationMessage event) { - deliverToListeners(listener -> listener.onGnssNavigationMessageReceived(event)); - } + private volatile @Nullable GnssNavigationMessage.Callback mListener; - @Override - public void onStatusChanged(int status) { - deliverToListeners(listener -> listener.onStatusChanged(status)); - } + 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; } - } - private class GnssAntennaInfoTransportMultiplexer extends - ListenerTransportMultiplexer<Void, GnssAntennaInfo.Listener> { - - private @Nullable BroadcastReceiver mListenerTransport; + public String getPackage() { + return mPackageName; + } - GnssAntennaInfoTransportMultiplexer() {} + public String getAttributionTag() { + return mAttributionTag; + } @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; - } + public void unregister() { + mListener = null; } @Override - protected void unregisterWithServer() { - if (mListenerTransport != null) { - BroadcastReceiver transport = mListenerTransport; - mListenerTransport = null; - mContext.unregisterReceiver(transport); - } + public @Nullable GnssNavigationMessage.Callback getListener() { + return mListener; } - private class GnssAntennaInfoReceiver extends BroadcastReceiver { - - GnssAntennaInfoReceiver() {} + @Override + public void onGnssNavigationMessageReceived(GnssNavigationMessage event) { + execute(mExecutor, listener -> listener.onGnssNavigationMessageReceived(event)); + } - @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)); - } - } + @Override + public void onStatusChanged(int status) { + execute(mExecutor, listener -> listener.onStatusChanged(status)); } } @@ -3321,8 +3373,8 @@ public class LocationManager { } @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 @@ public class LocationManager { /** * @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 @@ public class LocationManager { * @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 60b251ea990d..26cf0188f046 100644 --- a/location/java/android/location/LocationProvider.java +++ b/location/java/android/location/LocationProvider.java @@ -23,7 +23,9 @@ import android.location.provider.ProviderProperties; * 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 323e740ee8e3..cb56ee5318ee 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -605,7 +605,7 @@ public final class LocationRequest implements Parcelable { * 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 79a000c23484..8423000b6276 100644 --- a/location/java/android/location/LocationResult.java +++ b/location/java/android/location/LocationResult.java @@ -19,7 +19,6 @@ package android.location; 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,25 +33,31 @@ import java.util.function.Predicate; /** * 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. + * 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 Location location) { - ArrayList<Location> locations = new ArrayList<>(1); - locations.add(new Location(Objects.requireNonNull(location))); - return new LocationResult(locations); + public static @NonNull LocationResult create(@NonNull List<Location> locations) { + Preconditions.checkArgument(!locations.isEmpty()); + ArrayList<Location> locationsCopy = new ArrayList<>(locations.size()); + for (Location location : locations) { + locationsCopy.add(new Location(Objects.requireNonNull(location))); + } + return new LocationResult(locationsCopy); } /** - * 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()); - ArrayList<Location> locationsCopy = new ArrayList<>(locations.size()); + 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))); } @@ -60,16 +65,27 @@ public final class LocationResult implements Parcelable { } /** - * 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 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). */ - @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 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 @@ public final class LocationResult implements Parcelable { } /** - * 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 @@ public final class LocationResult implements Parcelable { * @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 @@ public final class LocationResult implements Parcelable { * 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 e3f51d9a23e1..50ed046f09cd 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 @@ interface ILocationProviderManager { 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 8f455cd7df07..ae6395d5d12d 100644 --- a/location/java/android/location/provider/LocationProviderBase.java +++ b/location/java/android/location/provider/LocationProviderBase.java @@ -25,12 +25,13 @@ import android.annotation.SystemApi; 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 @@ public abstract class LocationProviderBase { * 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 @@ public abstract class LocationProviderBase { /** * 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 @@ public abstract class LocationProviderBase { @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 338d7ccd6c91..0d81f36f540b 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -21,8 +21,8 @@ package com.android.location.provider { 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 aea93ce80e9b..7f1cf6dc3459 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 androidx.annotation.RequiresApi; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; /** @@ -94,10 +95,6 @@ public abstract class LocationProviderBase { */ 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 @@ public abstract class LocationProviderBase { /** * 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 @@ public abstract class LocationProviderBase { } /** - * 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 @@ public abstract class LocationProviderBase { /** * 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 @@ public abstract class LocationProviderBase { 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 c2ce7d384972..b19643761fd7 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -21,6 +21,7 @@ import android.annotation.FloatRange; 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 @@ import java.util.concurrent.Executor; * 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 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * 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 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * @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 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * 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/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp index 0540aac3672d..08573a61c623 100644 --- a/media/jni/tuner/FrontendClient.cpp +++ b/media/jni/tuner/FrontendClient.cpp @@ -21,8 +21,8 @@ #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; @@ -43,6 +43,7 @@ using ::android::hardware::tv::tuner::V1_1::Constant; using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation; using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation; using ::android::hardware::tv::tuner::V1_1::FrontendModulation; +using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion; namespace android { @@ -86,14 +87,13 @@ void FrontendClient::setHidlFrontend(sp<IFrontend> frontend) { 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 +123,13 @@ Result FrontendClient::stopTune() { 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; } @@ -293,6 +292,8 @@ Result FrontendClient::close() { return Result::INVALID_STATE; } +/////////////// TunerFrontend Helper Methods /////////////////////// + shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() { return mTunerFrontend; } @@ -302,52 +303,59 @@ int FrontendClient::getId() { } 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 +364,192 @@ TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSett 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) diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h index 17fd583cbaae..b0107ff174d4 100644 --- a/media/jni/tuner/FrontendClient.h +++ b/media/jni/tuner/FrontendClient.h @@ -31,6 +31,16 @@ using Status = ::ndk::ScopedAStatus; 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; @@ -172,8 +182,24 @@ public: int getId(); private: - TunerFrontendSettings getAidlFrontendSettings(const FrontendSettings& settings, - const FrontendSettingsExt1_1& settingsExt1_1); + 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/native/android/OWNERS b/native/android/OWNERS index ac5a89527ef0..d414ed4cd5e2 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 620c7ae96ebf..72589e39d555 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java @@ -18,6 +18,7 @@ package com.android.companiondevicemanager; 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 @@ public class DeviceChooserActivity extends Activity { 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 @@ public class DeviceChooserActivity extends Activity { 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 @@ public class DeviceChooserActivity extends Activity { 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 2a0a74ed0604..2716e092c6c3 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -4609,7 +4609,7 @@ public class ConnectivityManager { // 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/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java index d22d82d1f4d0..27aa15d1e1e4 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.IntDef; 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 @@ public abstract class NetworkAgent { * @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 8bfa77acd36d..55b2c3c9e11f 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -1786,6 +1786,15 @@ public final class NetworkCapabilities implements Parcelable { 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 @@ public final class NetworkCapabilities implements Parcelable { 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 @@ public final class NetworkCapabilities implements Parcelable { 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 final class NetworkCapabilities implements Parcelable { 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 03b07e080add..03cfbbb4a22d 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 @@ public final class Proxy { /** @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 @@ public final class Proxy { 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 d47231147a38..7cc599499ca1 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.Criteria; 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 org.junit.runner.RunWith; 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 @@ public class FusedLocationServiceTest { } @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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 1af7781dff96..eab0990bb868 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 000000000000..1a9144bd7efb --- /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 108591beb05a..d097472471b0 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 b5def5ebf539..41140a7a8c85 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 59dad0e9ee15..b8ea622fb385 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 2a055fc8252f..8f3345f9d85c 100644 --- a/packages/SystemUI/res/layout/long_screenshot.xml +++ b/packages/SystemUI/res/layout/long_screenshot.xml @@ -22,35 +22,82 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + <Button + android:id="@+id/save" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + 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" + app:layout_constraintBottom_toTopOf="@id/guideline" /> + + <Button + android:id="@+id/cancel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/cancel" + app:layout_constraintEnd_toStartOf="@id/edit" + app:layout_constraintStart_toEndOf="@id/save" + app:layout_constraintTop_toTopOf="parent" + 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.1" /> + <ImageView android:id="@+id/preview" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginVertical="8dp" + android:layout_marginBottom="24dp" android:layout_marginHorizontal="48dp" android:adjustViewBounds="true" app:layout_constrainedHeight="true" app:layout_constrainedWidth="true" - app:layout_constraintBottom_toBottomOf="@id/guideline" + app:layout_constraintTop_toBottomOf="@id/guideline" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="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:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginVertical="8dp" + android:layout_marginBottom="24dp" app:layout_constrainedHeight="true" app:layout_constrainedWidth="true" - app:layout_constraintBottom_toBottomOf="@id/guideline" + app:layout_constraintTop_toBottomOf="@id/guideline" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" app:handleThickness="3dp" app:handleColor="@*android:color/accent_device_default" app:scrimColor="#9444" @@ -58,46 +105,5 @@ tools:minHeight="100dp" tools:minWidth="100dp" /> - <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" /> - - <Button - android:id="@+id/close" - 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" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" - app:layout_constraintStart_toEndOf="@+id/edit" - app:layout_constraintTop_toTopOf="@+id/guideline" /> - </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 75f76b431da8..d6385ffbcc0c 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-television/config.xml b/packages/SystemUI/res/values-television/config.xml index dec83d95f0be..722f1480463d 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 e2fe223f591b..6c55fb62e638 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 09710d7e3fad..44eeba1146a4 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> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1fac96bb181d..d92f4ea65390 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 81b7a7b4aaff..d93239531038 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 a74b56494b27..4b04eebfddf0 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/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl new file mode 100644 index 000000000000..e5ced3ec3dc2 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl @@ -0,0 +1,25 @@ +/* + * 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.shared.recents; + +/** + * 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 388eeb6cd706..e38cf237d36b 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.Bitmap; 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 @@ interface ISystemUiProxy { /** 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/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index 8d010c743d81..6c77af7bbddd 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 @@ package com.android.systemui.shared.system; 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 abstract class TaskStackChangeListener { 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 a907e6676b69..8f08f5a51143 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 @@ package com.android.systemui.shared.system; 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 @@ public class TaskStackChangeListeners { 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 @@ public class TaskStackChangeListeners { } @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 @@ public class TaskStackChangeListeners { } 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/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index a7ed67287fc7..c182fd178202 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -297,7 +297,7 @@ public class KeyguardSecurityContainer extends FrameLayout { } private void updateSecurityViewGravity() { - View securityView = getChildAt(0); + View securityView = findKeyguardSecurityView(); if (securityView == null) { return; @@ -320,7 +320,7 @@ public class KeyguardSecurityContainer extends FrameLayout { * by the security view . */ private void updateSecurityViewLocation(boolean animate) { - View securityView = getChildAt(0); + View securityView = findKeyguardSecurityView(); if (securityView == null) { return; @@ -355,6 +355,23 @@ public class KeyguardSecurityContainer extends FrameLayout { } } + @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 @@ public class KeyguardSecurityContainer extends FrameLayout { @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 7a52d270f92c..000000000000 --- 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 68f14148ffc7..e07c84034b31 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 android.util.Log; 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 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { @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 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { } } mFingerprintDrawable.draw(canvas); - - canvas.restore(); } @Override @@ -98,13 +92,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { @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 fded737ce9fc..4e3419e1fab3 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.util.AttributeSet; 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 @@ public class UdfpsAnimationView extends View implements DozeReceiver, 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 @@ public class UdfpsAnimationView extends View implements DozeReceiver, } @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 @@ public class UdfpsAnimationView extends View implements DozeReceiver, ((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 ac4b93a684e1..baa597398188 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.dagger.qualifiers.Main; 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 @@ public class UdfpsController implements DozeReceiver, HbmCallback { 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 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mView.setSensorProperties(mSensorProps); mView.setHbmCallback(this); - scrimController.addScrimChangedListener(mView); + statusBar.addExpansionChangedListener(mView); statusBarStateController.addCallback(mView); mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController()); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index d448ed8d550c..7e378d3c568e 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 android.widget.FrameLayout; 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 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin } @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 55359ea70873..e5c9d104ea93 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 @@ package com.android.systemui.dagger; 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 @@ public abstract class SystemUIBinder { @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/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 4384610b2027..dab4d0bb00c5 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 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } private void initSecondaryHomeHandleForRotation() { - if (!canShowSecondaryHandle()) { + if (mNavBarMode != NAV_BAR_MODE_GESTURAL) { return; } @@ -1524,7 +1524,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } 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/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index e9207f1feff3..d248ab544656 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.QSPanel.QSTileLayout; 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 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha 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 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mHost = qsTileHost; mExecutor = executor; mTunerService = tunerService; + mFeatureFlags = featureFlags; mHost.addCallback(this); mQsPanelController.addOnAttachStateChangeListener(this); qs.getView().addOnLayoutChangeListener(this); @@ -228,6 +233,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha // 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 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha 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 d0601f00b937..91ae571d1cfb 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 @@ public class QSPanel extends LinearLayout implements Tunable { 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 30774be0491b..f56a890c54d4 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 @@ package com.android.systemui.qs; 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 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { 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 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { 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 e2d7d201a5de..a0db2000cb4d 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 @@ import javax.inject.Named; @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 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> @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 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> 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 ad4c8bb41021..9ab2d7370ed8 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 @@ package com.android.systemui.qs.dagger; 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 @@ import dagger.Provides; @Module public interface QSFlagsModule { String QS_LABELS_FLAG = "qs_labels_flag"; - String QS_SIDE_LABELS = "qs_side_labels"; @Provides @SysUISingleton @@ -36,12 +34,4 @@ public interface QSFlagsModule { 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 9e582dd2fd81..11e6330d37f3 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 @@ public class QSFactoryImpl implements QSFactory { @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 2dbd2cfe9c10..a699e2ec7cfc 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 @@ import java.util.Objects; /** 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 @@ public class QSTileView extends QSTileBaseView { // 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 2ef78c249246..dc81b702021f 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.PaintDrawable 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 @@ class QSTileViewHorizontal( 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 @@ class QSTileViewHorizontal( 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 760ebadf1b99..a9f76f61c537 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 static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_T 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.recents.OverviewProxyService.OverviewProxyListener; 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.legacysplitscreen.LegacySplitScreen; 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 @@ public class OverviewProxyService extends CurrentUserTracker implements 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 @@ public class OverviewProxyService extends CurrentUserTracker implements } final long token = Binder.clearCallingIdentity(); try { - return mSplitScreenOptional.map(splitScreen -> + return mLegacySplitScreenOptional.map(splitScreen -> splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds()) .orElse(null); } finally { @@ -401,7 +405,7 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void setSplitScreenMinimized(boolean minimized) { - mSplitScreenOptional.ifPresent( + mLegacySplitScreenOptional.ifPresent( splitScreen -> splitScreen.setMinimized(minimized)); } @@ -559,6 +563,105 @@ public class OverviewProxyService extends CurrentUserTracker implements } } + @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 @@ public class OverviewProxyService extends CurrentUserTracker implements 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 @@ public class OverviewProxyService extends CurrentUserTracker implements 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 @@ public class OverviewProxyService extends CurrentUserTracker implements }); 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 @@ public class OverviewProxyService extends CurrentUserTracker implements 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 8ff66f548172..20f845103723 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 @@ class ImageTileSet { } 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 @@ class ImageTileSet { 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 @@ class ImageTileSet { 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 3bc5ebf8c64e..5c650ad3982e 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 @@ public class ScreenshotView extends FrameLayout implements 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 db295332bbdd..18c379a4650f 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.annotation.UiThread; 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 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener // TODO: Support saving without additional action. private enum PendingAction { SHARE, - EDIT + EDIT, + SAVE } public static final int MAX_PAGES = 5; @@ -74,9 +76,11 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener 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 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener .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 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener } void disableButtons() { - mClose.setEnabled(false); + mSave.setEnabled(false); + mCancel.setEnabled(false); mEdit.setEnabled(false); mShare.setEnabled(false); } @@ -139,19 +147,17 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener 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 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener } 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 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener } 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 8dcc8b46f024..3c7d78c928fa 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 @@ package com.android.systemui.settings.brightness; 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 @@ public class BrightnessControllerSettings { 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 53ff1dfd277b..a6aec3b7b1b7 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.RestrictedLockUtils; 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 @@ public class BrightnessSlider 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/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 175e046141f6..a51674280c1c 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.MotionEvent; 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 @@ public class NotificationStackScrollLayoutController { 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/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index cf4e9bea3982..a30f193c50a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -756,19 +756,16 @@ public class NotificationPanelViewController extends PanelViewController { 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); } } 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 27c94d277cc4..b36740620d08 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.view.WindowInsets; 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 @@ import java.util.Comparator; /** * 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 @@ public class NotificationsQuickSettingsContainer extends FrameLayout @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 c0a5ffa1474e..31a432e2c451 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.plugins.DarkIconDispatcher.DarkReceiver; 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 @@ public class PhoneStatusBarView extends PanelBar { @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 @@ public class PhoneStatusBarView extends PanelBar { mBar = bar; } + public void setExpansionChangedListeners( + @Nullable List<StatusBar.ExpansionChangedListener> listeners) { + mExpansionChangedListeners = listeners; + } + public void setScrimController(ScrimController scrimController) { mScrimController = scrimController; } @@ -277,6 +285,12 @@ public class PhoneStatusBarView extends PanelBar { 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 e39065b6cc04..7b2330bdcd6e 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.ViewTreeObserver; 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.FileDescriptor; 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mScrimVisibleListener = listener; } - public void addScrimChangedListener(@NonNull ScrimChangedListener listener) { - mScrimChangedListeners.add(listener); - } - public void transitionTo(ScrimState state) { transitionTo(state, null); } @@ -580,10 +562,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump 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 4a3d8d67e85d..7095afd45959 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 @@ public class StatusBar extends SystemUI implements DemoMode, ONLY_CORE_APPS = onlyCoreApps; } + public interface ExpansionChangedListener { + void onExpansionChanged(float expansion, boolean expanded); + } + /** * The {@link StatusBarState} of the status bar. */ @@ -439,6 +443,8 @@ public class StatusBar extends SystemUI implements DemoMode, 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 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationIconAreaController = notificationIconAreaController; mBrightnessSliderFactory = brightnessSliderFactory; + mExpansionChangedListeners = new ArrayList<>(); + mBubbleExpandListener = (isExpanding, key) -> { mContext.getMainExecutor().execute(() -> { @@ -1077,6 +1085,7 @@ public class StatusBar extends SystemUI implements DemoMode, 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 class StatusBar extends SystemUI implements DemoMode, 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 000000000000..1af2c9f46373 --- /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 416de0465f1a..0795d89eb0bc 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.TaskStackListenerImpl; 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 @@ public abstract class TvPipModule { PipTaskOrganizer pipTaskOrganizer, TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, + PipTransitionController pipTransitionController, TvPipNotificationController tvPipNotificationController, TaskStackListenerImpl taskStackListener, WindowManagerShellWrapper windowManagerShellWrapper, @@ -68,6 +73,7 @@ public abstract class TvPipModule { pipBoundsState, pipBoundsAlgorithm, pipTaskOrganizer, + pipTransitionController, tvPipMenuController, pipMediaController, tvPipNotificationController, @@ -92,6 +98,16 @@ public abstract class TvPipModule { // 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 @@ public abstract class TvPipModule { @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 12a3b5d66d55..2aaa0951d9d9 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.common.annotations.ShellMainThread; 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 @@ public class WMShellModule { 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 @@ public class WMShellModule { 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 @@ public class WMShellModule { 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 1e473cdd52ef..3e873d1985fb 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 androidx.test.filters.SmallTest; 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 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private StatusBarStateController mStatusBarStateController; @Mock - private ScrimController mScrimController; + private StatusBar mStatusBar; private FakeExecutor mFgExecutor; @@ -126,7 +126,7 @@ public class UdfpsControllerTest extends SysuiTestCase { mWindowManager, mStatusBarStateController, mFgExecutor, - mScrimController); + mStatusBar); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); @@ -241,6 +241,6 @@ public class UdfpsControllerTest extends SysuiTestCase { @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/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java index c6f97fa01dee..4381158c3415 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 @@ public class QSPanelControllerTest extends SysuiTestCase { 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 c490c4c3a368..107160f47f02 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 @@ class QuickQSPanelControllerTest : SysuiTestCase() { 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 b63274ba246a..451c78fd1202 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.NotificationShadeWindowController; 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 @@ public class OverviewProxyServiceTest extends SysuiTestCase { @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 @@ public class OverviewProxyServiceTest extends SysuiTestCase { 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/services/backup/OWNERS b/services/backup/OWNERS index 3c5268c5a2a9..ba2a63abb62d 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 04e08aeaf78e..55e3ef2e427f 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 @@ public class CompanionDeviceManagerService extends SystemService implements Bind 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 6de227e7ee7a..e01c4df42ff5 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -158,9 +158,9 @@ java_genrule { 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/power/PowerStatsInternal.java b/services/core/java/android/power/PowerStatsInternal.java index f7417daf036d..40107a5d1578 100644 --- a/services/core/java/android/power/PowerStatsInternal.java +++ b/services/core/java/android/power/PowerStatsInternal.java @@ -18,8 +18,12 @@ package android.power; 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 @@ public abstract class PowerStatsInternal { @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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index f2e192065ed1..08390b4e52e4 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.FileDescriptor; 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub ", 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 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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); @@ -1366,7 +1376,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private NetworkState getUnfilteredActiveNetworkState(int uid) { - NetworkAgentInfo nai = getFallbackNetwork(); + NetworkAgentInfo nai = getDefaultNetwork(); final Network[] networks = getVpnUnderlyingNetworks(uid); if (networks != null) { @@ -1499,7 +1509,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - NetworkAgentInfo nai = getFallbackNetwork(); + NetworkAgentInfo nai = getDefaultNetwork(); if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid, ignoreBlocked)) { return null; @@ -1638,7 +1648,7 @@ public class ConnectivityService extends IConnectivityManager.Stub HashMap<Network, NetworkCapabilities> result = new HashMap<>(); - final NetworkAgentInfo nai = getFallbackNetwork(); + final NetworkAgentInfo nai = getDefaultNetwork(); NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai); if (nc != null) { result.put( @@ -2025,7 +2035,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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); } } @@ -2582,12 +2592,12 @@ public class ConnectivityService extends IConnectivityManager.Stub 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(); @@ -2970,7 +2980,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 +2989,7 @@ public class ConnectivityService extends IConnectivityManager.Stub log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg); } if (valid != nai.lastValidated) { - if (wasFallback) { + if (wasDefault) { mMetricsLog.logDefaultNetworkValidity(valid); } final int oldScore = nai.getCurrentScore(); @@ -3355,13 +3365,13 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 +3410,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ -3444,9 +3454,9 @@ public class ConnectivityService extends IConnectivityManager.Stub nri.setSatisfier(null, null); 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( @@ -3458,9 +3468,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } nai.clearLingerState(); // 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 +3478,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // (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); } @@ -4260,7 +4269,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest getDefaultRequest() { - return mFallbackRequest.mRequests.get(0); + return mDefaultRequest.mRequests.get(0); } private class InternalHandler extends Handler { @@ -4506,7 +4515,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 +4534,7 @@ public class ConnectivityService extends IConnectivityManager.Stub Network network, int uid, boolean hasConnectivity) { final NetworkAgentInfo nai; if (network == null) { - nai = getFallbackNetwork(); + nai = getDefaultNetwork(); } else { nai = getNetworkAgentInfoForNetwork(network); } @@ -4891,7 +4900,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret // the underlyingNetworks list. if (underlyingNetworks == null) { - final NetworkAgentInfo defaultNai = getFallbackNetwork(); + final NetworkAgentInfo defaultNai = getDefaultNetwork(); if (defaultNai != null) { underlyingNetworks = new Network[] { defaultNai.network }; } @@ -4943,7 +4952,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) { - final Network defaultNetwork = getNetwork(getFallbackNetwork()); + final Network defaultNetwork = getNetwork(getDefaultNetwork()); if (underlyingNetworks == null && defaultNetwork != null) { // null underlying networks means to track the default. underlyingNetworks = new Network[] { defaultNetwork }; @@ -6050,7 +6059,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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<>(); @@ -6067,9 +6076,9 @@ public class ConnectivityService extends IConnectivityManager.Stub 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; + // The NetworkAgentInfo currently satisfying the default request, if any. + private NetworkAgentInfo getDefaultNetwork() { + return mDefaultRequest.mSatisfier; } @Nullable @@ -6086,8 +6095,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } @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 +6165,8 @@ public class ConnectivityService extends IConnectivityManager.Stub LinkProperties lp = new LinkProperties(linkProperties); - // TODO: Instead of passing mFallbackRequest, provide an API to determine whether a Network - // satisfies mFallbackRequest. + // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network + // satisfies mDefaultRequest. final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); final NetworkAgentInfo nai = new NetworkAgentInfo(na, new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, @@ -6234,7 +6243,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // for (LinkProperties lp : newLp.getStackedLinks()) { // updateMtu(lp, null); // } - if (isFallbackNetwork(networkAgent)) { + if (isDefaultNetwork(networkAgent)) { updateTcpBufferSizes(newLp.getTcpBufferSizes()); } @@ -6246,7 +6255,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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); @@ -7226,7 +7235,7 @@ public class ConnectivityService extends IConnectivityManager.Stub try { // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes. - if (mFallbackRequest != nri) { + if (mDefaultRequest != nri) { return; } @@ -7624,34 +7633,34 @@ public class ConnectivityService extends IConnectivityManager.Stub 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; - - if (oldFallbackNetwork != newFallbackNetwork) { + final NetworkReassignment.RequestReassignment reassignmentOfDefault = + changes.getReassignment(mDefaultRequest); + final NetworkAgentInfo oldDefaultNetwork = + null != reassignmentOfDefault ? reassignmentOfDefault.mOldNetwork : null; + final NetworkAgentInfo newDefaultNetwork = + null != reassignmentOfDefault ? reassignmentOfDefault.mNewNetwork : null; + + 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 +7695,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // 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 +7706,9 @@ public class ConnectivityService extends IConnectivityManager.Stub 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. @@ -7967,8 +7976,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 = getDefaultNetwork(); if (newDefaultAgent != null) { intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, newDefaultAgent.networkInfo); @@ -8016,9 +8025,9 @@ public class ConnectivityService extends IConnectivityManager.Stub private Network[] getDefaultNetworks() { ensureRunningOnConnectivityServiceThread(); final ArrayList<Network> defaultNetworks = new ArrayList<>(); - final NetworkAgentInfo fallbackNetwork = getFallbackNetwork(); + final NetworkAgentInfo defaultNetwork = getDefaultNetwork(); for (NetworkAgentInfo nai : mNetworkAgentInfos) { - if (nai.everConnected && (nai == fallbackNetwork || nai.isVPN())) { + if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) { defaultNetworks.add(nai.network); } } @@ -8350,7 +8359,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 5ce16c49a945..b894f34ffd9f 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.ArrayMap; 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 class LockGuard { 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 @@ public class LockGuard { 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 6a72010738db..916bec27af39 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -290,8 +290,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { public Vcn newVcn( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, - @NonNull VcnConfig config) { - return new Vcn(vcnContext, subscriptionGroup, config); + @NonNull VcnConfig config, + @NonNull TelephonySubscriptionSnapshot snapshot) { + return new Vcn(vcnContext, subscriptionGroup, config, snapshot); } /** Gets the subId indicated by the given {@link WifiInfo}. */ @@ -382,6 +383,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { // 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 +397,13 @@ public class VcnManagementService extends IVcnManagementService.Stub { // 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 +411,39 @@ public class VcnManagementService extends IVcnManagementService.Stub { } @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 Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot); mVcns.put(subscriptionGroup, newInstance); + + // Now that a new VCN has started, notify all registered listeners to refresh their + // UnderlyingNetworkPolicy. + notifyAllPolicyListenersLocked(); } @GuardedBy("mLock") @@ -476,9 +506,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { synchronized (mLock) { mConfigs.remove(subscriptionGroup); - if (mVcns.containsKey(subscriptionGroup)) { - mVcns.remove(subscriptionGroup).teardownAsynchronously(); - } + stopVcnLocked(subscriptionGroup); writeConfigsToDiskLocked(); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7da4d64fdb4a..a4ff230b0678 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 @@ 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_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.ActivityManagerInternal; 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { // 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } // 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { "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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { } private boolean appIsTopLocked(int uid) { - return mAm.getUidState(uid) <= ActivityManager.PROCESS_STATE_TOP; + return mAm.getUidStateLocked(uid) <= PROCESS_STATE_TOP; } /** @@ -1682,13 +1684,13 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { + 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 @@ public final class ActiveServices { + 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { } } - 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { } } } - 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 @@ public final class ActiveServices { + " 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 @@ public final class ActiveServices { "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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { // 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 @@ public final class ActiveServices { } long now = SystemClock.uptimeMillis(); + ProcessServiceRecord psr; if (r.executeNesting == 0) { r.executeFg = fg; ServiceState stracker = r.getTracker(); @@ -3043,16 +3061,20 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { * 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { // Cleanup. if (newService) { - app.stopService(r); - r.setProcess(null); + psr.stopService(r); + r.setProcess(null, null, 0, null); } // Retry. @@ -3518,15 +3549,15 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { // 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { } // 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { // 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } } - app.executingServices.clear(); + psr.stopAllExecutingServices(); } ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) { @@ -4578,7 +4605,7 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { // 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { if (proc == null) { return; } - final IApplicationThread thread = proc.thread; + final IApplicationThread thread = proc.getThread(); if (thread == null) { return; } @@ -5320,20 +5348,21 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 27be53a5faf4..31db95b7d48c 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.os.UserHandle; 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 final boolean mPostChangesToAtm; - private 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 @@ final class ActiveUids { } } + @GuardedBy({"mService", "mProcLock"}) void remove(int uid) { mActiveUids.remove(uid); if (mPostChangesToAtm) { @@ -50,38 +59,45 @@ final class ActiveUids { } } + @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 @@ final class ActiveUids { } 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 @@ final class ActiveUids { 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/services/core/java/com/android/server/am/ActivityManagerGlobalLock.java b/services/core/java/com/android/server/am/ActivityManagerGlobalLock.java new file mode 100644 index 000000000000..7823038fa37e --- /dev/null +++ b/services/core/java/com/android/server/am/ActivityManagerGlobalLock.java @@ -0,0 +1,24 @@ +/* + * 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.am; + +/** + * 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/services/core/java/com/android/server/am/ActivityManagerProcLock.java b/services/core/java/com/android/server/am/ActivityManagerProcLock.java new file mode 100644 index 000000000000..3ef19ad8290e --- /dev/null +++ b/services/core/java/com/android/server/am/ActivityManagerProcLock.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +/** + * 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 cdf5c9851f48..ecb915f49bc3 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_HIDDEN_API_CHECKS; 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_NETWORK; 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.DropBoxManager; 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.ParcelFileDescriptor; 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.View; 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.Executor; 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub /** * 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub /** * 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub * <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 @@ public class ActivityManagerService extends IActivityManager.Stub * <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 @@ public class ActivityManagerService extends IActivityManager.Stub * <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 @@ public class ActivityManagerService extends IActivityManager.Stub proto.end(pToken); } } + @GuardedBy("this") final SparseArray<ImportanceToken> mImportantProcesses = new SparseArray<ImportanceToken>(); /** @@ -872,12 +947,14 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub /** * 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub /** * Last time (in uptime) at which we checked for power usage. */ + @GuardedBy("mProcLock") long mLastPowerCheckUptime; /** @@ -1162,10 +1243,14 @@ public class ActivityManagerService extends IActivityManager.Stub /** * 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub /** * 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 @@ public class ActivityManagerService extends IActivityManager.Stub /** * The last time when the binder heavy hitter auto sampler started. */ + @GuardedBy("mProcLock") private long mLastBinderHeavyHitterAutoSamplerStart = 0L; final AppProfiler mAppProfiler; @@ -1405,19 +1509,19 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub "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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub "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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub }); } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } + boolean didSomething; if (doit) { if (packageName != null) { Slog.i(TAG, "Force stopping " + packageName + " appid=" + appId @@ -3914,20 +4036,22 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub + " 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } - EventLogTags.writeAmProcBound(app.userId, app.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; + EventLogTags.writeAmProcBound(app.userId, pid, app.processName); - - // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub oldToken.token.unlinkToDeath(oldToken, 0); mImportantProcesses.remove(pid); if (pr != null) { - pr.forcingToImportant = null; + pr.mState.setForcingToImportant(null); } changed = true; } @@ -4984,7 +5120,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } // 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub /** * @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 @@ public class ActivityManagerService extends IActivityManager.Stub || 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 @@ public class ActivityManagerService extends IActivityManager.Stub * @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 @@ public class ActivityManagerService extends IActivityManager.Stub final int modeFlags, int userId) { enforceNotIsolatedCaller("grantUriPermission"); GrantUri grantUri = new GrantUri(userId, uri, modeFlags); - synchronized(this) { - final ProcessRecord r = getRecordForAppLocked(caller); + synchronized (mProcLock) { + final ProcessRecord r = getRecordForAppLOSP(caller); if (r == null) { throw new SecurityException("Unable to find app for caller " + caller @@ -5517,8 +5666,8 @@ public class ActivityManagerService extends IActivityManager.Stub public void revokeUriPermission(IApplicationThread caller, String targetPackage, Uri uri, final int modeFlags, int userId) { enforceNotIsolatedCaller("revokeUriPermission"); - synchronized(this) { - final ProcessRecord r = getRecordForAppLocked(caller); + synchronized (mProcLock) { + final ProcessRecord r = getRecordForAppLOSP(caller); if (r == null) { throw new SecurityException("Unable to find app for caller " + caller @@ -5549,9 +5698,8 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub "getUidProcessState"); } - synchronized (this) { - return mProcessList.getUidProcStateLocked(uid); + synchronized (mProcLock) { + return mProcessList.getUidProcStateLOSP(uid); } } @@ -6471,17 +6622,18 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void setRenderThread(int tid) { - synchronized (this) { + synchronized (mProcLock) { ProcessRecord proc; int pid = Binder.getCallingPid(); if (pid == Process.myPid()) { @@ -6548,33 +6700,32 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 { - if (DEBUG_OOM_ADJ) { - Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? " + - "PID: " + pid + ", TID: " + tid + " FIFO: " + - mUseFifoUiScheduling); + } 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); + } } } } @@ -6630,11 +6781,11 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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; - - final int adj = proc.setAdj; - if (adj > belowAdj && !proc.killedByAm) { - proc.kill(reason, ApplicationExitInfo.REASON_PERMISSION_CHANGE, true); - killed = true; + 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.mState.getSetAdj(); + if (adj > belowAdj && !proc.isKilledByAm()) { + proc.killLocked(reason, ApplicationExitInfo.REASON_PERMISSION_CHANGE, + true); + killed = true; + } + } } } } @@ -7025,16 +7189,16 @@ public class ActivityManagerService extends IActivityManager.Stub + 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub (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 @@ public class ActivityManagerService extends IActivityManager.Stub return null; } - synchronized (this) { - return mProcessList.findAppProcessLocked(app, reason); + synchronized (mProcLock) { + return mProcessList.findAppProcessLOSP(app, reason); } } @@ -7765,7 +7935,7 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized(this) { mConstants.dump(pw); - mOomAdjuster.dumpCachedAppOptimizerSettings(pw); + synchronized (mProcLock) { + mOomAdjuster.dumpCachedAppOptimizerSettings(pw); + } mOomAdjuster.dumpCacheOomRankerSettings(pw); pw.println(); if (dumpAll) { @@ -8328,7 +8500,9 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } else if ("settings".equals(cmd)) { synchronized (this) { mConstants.dump(pw); + } + synchronized (mProcLock) { mOomAdjuster.dumpCachedAppOptimizerSettings(pw); mOomAdjuster.dumpCacheOomRankerSettings(pw); } @@ -8863,8 +9045,8 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub "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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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); - } - - 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); + boolean restart; + synchronized (mProcLock) { + if (index >= 0) { + removeLruProcessLocked(app); + ProcessList.remove(pid); } - 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(); + restart = app.onCleanupApplicationRecordLSP(mProcessStats, allowRestart); } - + mAppProfiler.onCleanupApplicationRecordLocked(app); 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 @@ public class ActivityManagerService extends IActivityManager.Stub }); } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub + " 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } if (rl.app != null) { - rl.app.receivers.remove(rl); + rl.app.mReceivers.removeReceiver(rl); } removeReceiverLocked(rl); if (rl.linkedToDeath) { @@ -12371,7 +12489,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } try { - pr.thread.instrumentWithoutRestart( + pr.getThread().instrumentWithoutRestart( activeInstr.mClass, activeInstr.mArguments, activeInstr.mWatcher, @@ -13459,7 +13581,7 @@ public class ActivityManagerService extends IActivityManager.Stub } synchronized(this) { - ProcessRecord app = getRecordForAppLocked(target); + ProcessRecord app = getRecordForAppLOSP(target); if (app == null) { Slog.w(TAG, "addInstrumentationResults: no app for " + target); return; @@ -13481,35 +13603,37 @@ public class ActivityManagerService extends IActivityManager.Stub 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(); + // 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.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, @@ -13546,7 +13670,7 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub /** * 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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; - } + updatePhantomProcessCpuTimeLPr( + uptimeSince, doCpuKills, checkDur, cpuLimit, app); + } + }); + } + } + + @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); } - r.mLastCputime = r.mCurrentCputime; - return true; }); + profile.reportExcessiveCpu(); } } + + profile.mLastCpuTime.set(curCpuTime); } } - private boolean checkExcessivePowerUsageLocked(final long uptimeSince, boolean doCpuKills, + @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - 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 @@ public class ActivityManagerService extends IActivityManager.Stub } finally { Binder.restoreCallingIdentity(identity); } - } } return r; @@ -14140,17 +14280,20 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub + 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 @@ public class ActivityManagerService extends IActivityManager.Stub */ @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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } /** 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 @@ public class ActivityManagerService extends IActivityManager.Stub + 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } }, 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 @@ public class ActivityManagerService extends IActivityManager.Stub } void onCoreSettingsChange(Bundle settings) { - synchronized (this) { - mProcessList.updateCoreSettingsLocked(settings); + synchronized (mProcLock) { + mProcessList.updateCoreSettingsLOSP(settings); } } @@ -14700,8 +14850,9 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub + 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub + 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 class ActivityManagerService extends IActivityManager.Stub 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; - } - if (pr.getWindowProcessController().hasRunningActivity(packageName)) { - return true; + synchronized (mProcLock) { + return mProcessList.searchEachLruProcessesLOSP(true, app -> { + if (app.uid == uid + && app.getWindowProcessController().hasRunningActivity(packageName)) { + return Boolean.TRUE; } - } + return null; + }) != null; } - return false; } @Override @@ -15564,7 +15725,7 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub * 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 class ActivityManagerService extends IActivityManager.Stub */ 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } - proc.thread.attachAgent(path); + thread.attachAgent(path); } } catch (RemoteException e) { throw new IllegalStateException("Process disappeared"); @@ -16167,7 +16330,7 @@ public class ActivityManagerService extends IActivityManager.Stub } // 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 @@ public class ActivityManagerService extends IActivityManager.Stub && 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 a7119d18117a..34d9a60d78aa 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 @@ class AnrHelper { } 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 779d37858ce2..48222cb075cd 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 @@ import android.widget.TextView; 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 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen 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 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen 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 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen 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 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen } 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 7be54c29a218..e5a5cff409b3 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 @@ class AppErrors { 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 @@ class AppErrors { AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog) { context.assertRuntimeOverlayThemable(); mService = service; + mProcLock = service.mProcLock; mContext = context; mPackageWatchdog = watchdog; } @@ -154,17 +156,48 @@ class AppErrors { } } - 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); - 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); + } + } + + synchronized (mBadProcessLock) { if (!mProcessCrashTimes.getMap().isEmpty()) { final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); final int procCount = pmap.size(); @@ -177,7 +210,7 @@ class AppErrors { 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 @@ class AppErrors { } 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { } } + @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 @@ class AppErrors { if (uid >= 0 && p.uid != uid) { continue; } - if (p.pid == initialPid) { + if (p.getPid() == initialPid) { proc = p; break; } @@ -508,7 +517,7 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { } } + @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 @@ class AppErrors { 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 @@ class AppErrors { }); } + @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 @@ class AppErrors { 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 @@ class AppErrors { 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; - - synchronized (mBadProcessLock) { - if (!app.isolated) { - crashTime = mProcessCrashTimes.get(app.processName, app.uid); - crashTimePersistent = mProcessCrashTimesPersistent.get(app.processName, app.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; - } - } + 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; + + if (!app.isolated) { + crashTime = mProcessCrashTimes.get(processName, uid); + crashTimePersistent = mProcessCrashTimesPersistent.get(processName, uid); + } else { + crashTime = crashTimePersistent = null; + } - 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; - } + // 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 " + 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 (data != null && crashTimePersistent != null + && now < crashTimePersistent + ActivityManagerConstants.MIN_CRASH_INTERVAL) { + data.repeating = true; } + } - 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 && 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 (app.crashHandler != null) mService.mHandler.post(app.crashHandler); + if (errState.getCrashHandler() != null) { + mService.mHandler.post(errState.getCrashHandler()); + } return true; } @@ -951,15 +959,16 @@ class AppErrors { 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 @@ class AppErrors { } 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 @@ class AppErrors { 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 @@ class AppErrors { 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 @@ class AppErrors { 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 48aa8be1e2d5..17be2100414f 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 @@ public final class AppExitInfoTracker { 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 96e6f6ea4476..77d2898842c6 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 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli ? 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 class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli 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 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli // 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 3df058c8ed82..8f11a5ab900b 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 @@ public class AppProfiler { */ private volatile boolean mTestPssMode = false; - @GuardedBy("mService") private final LowMemDetector mLowMemDetector; /** @@ -239,13 +238,13 @@ public class AppProfiler { /** * 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 @@ public class AppProfiler { */ final Object mProfilerLock = new Object(); + final ActivityManagerGlobalLock mProcLock; + /** * Observe DeviceConfig changes to the PSS calculation interval */ @@ -852,8 +853,8 @@ public class AppProfiler { /** * 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 @@ public class AppProfiler { 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 @@ public class AppProfiler { 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 @@ public class AppProfiler { } 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 @@ public class AppProfiler { 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 @@ public class AppProfiler { 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 @@ public class AppProfiler { 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 @@ public class AppProfiler { } 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 @@ public class AppProfiler { 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 @@ public class AppProfiler { if (mLowRamStartTime == 0) { mLowRamStartTime = now; } - int step = 0; int fgTrimLevel; switch (memFactor) { case ADJ_MEM_FACTOR_CRITICAL: @@ -1022,126 +1020,129 @@ public class AppProfiler { 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 @@ public class AppProfiler { // 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 @@ public class AppProfiler { 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 @@ public class AppProfiler { // 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 @@ public class AppProfiler { addProcessToGcListLPf(rec); } } - } + }); if (doReport) { Message msg = mService.mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos); mService.mHandler.sendMessage(msg); @@ -1554,7 +1555,9 @@ public class AppProfiler { 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 @@ public class AppProfiler { } } - @GuardedBy("mService") + @GuardedBy("mProfilerLock") private void stopProfilerLPf(ProcessRecord proc, int profileType) { if (proc == null || proc == mProfileData.getProfileProc()) { proc = mProfileData.getProfileProc(); @@ -1644,7 +1647,7 @@ public class AppProfiler { } 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 @@ public class AppProfiler { 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 @@ public class AppProfiler { 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 170c34d2e859..c6947c2d9525 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 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { 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 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { /** * 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 c2872405ae18..773e3137c247 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.ContentResolver; 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.connectivity.WifiBatteryStats; 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.CodingErrorAction; 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub } }; + 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override - public String getPlatformLowPowerStats() { - if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats"); + public String getSubsystemLowPowerStats() { + final StateResidencyResult[] results; try { - mUtf8BufferStat.clear(); - mUtf16BufferStat.clear(); - mDecoderStat.reset(); - int bytesWritten = getPlatformLowPowerStats(mUtf8BufferStat); - if (bytesWritten < 0) { - return null; - } else if (bytesWritten == 0) { - return "Empty"; + 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); } - 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"); - try { - mUtf8BufferStat.clear(); - mUtf16BufferStat.clear(); - mDecoderStat.reset(); - int bytesWritten = getSubsystemLowPowerStats(mUtf8BufferStat); - if (bytesWritten < 0) { - return null; - } else if (bytesWritten == 0) { - return "Empty"; + if (subsystemBuilder.length() <= charsLeft) { + charsLeft -= subsystemBuilder.length(); + builder.append(subsystemBuilder); + } else { + Slog.e(TAG, "getSubsystemLowPowerStats: buffer not enough"); + break; } - mUtf8BufferStat.limit(bytesWritten); - mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true); - mUtf16BufferStat.flip(); - return mUtf16BufferStat.toString(); - } finally { - if (DBG) Slog.d(TAG, "end getSubsystemLowPowerStats"); } + + return builder.toString(); } BatteryStatsService(Context context, File systemDir, Handler handler) { @@ -274,6 +322,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub } 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub * 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 4de12183e403..7e65434b8189 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.ActivityManager; 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.Handler; 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 final class BroadcastQueue { } 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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { + ": " + 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 @@ public final class BroadcastQueue { "Process cur broadcast " + r + ": NOT STARTED!"); r.receiver = null; r.curApp = null; - app.curReceivers.remove(r); + prr.removeCurReceiver(r); } } } @@ -335,7 +339,7 @@ public final class BroadcastQueue { 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 final class BroadcastQueue { 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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { // 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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { // 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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { } 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 @@ public final class BroadcastQueue { } 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 @@ public final class BroadcastQueue { + 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 @@ public final class BroadcastQueue { + " (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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { } // 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 @@ public final class BroadcastQueue { 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 45ce4c5021bb..50278fd81bb2 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 @@ public class CacheOomRanker { private final Object mPhenotypeFlagLock = new Object(); private final ActivityManagerService mService; + private final ActivityManagerGlobalLock mProcLock; private final Object mProfilerLock; @GuardedBy("mPhenotypeFlagLock") @@ -106,6 +107,7 @@ public class CacheOomRanker { CacheOomRanker(final ActivityManagerService service) { mService = service; + mProcLock = service.mProcLock; mProfilerLock = service.mAppProfiler.mProfilerLock; } @@ -179,15 +181,14 @@ public class CacheOomRanker { * 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 @@ public class CacheOomRanker { // 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 @@ public class CacheOomRanker { 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 @@ public class CacheOomRanker { 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 c558b3d00400..c18031fd6de6 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.annotations.VisibleForTesting; 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 @@ public final class CachedAppOptimizer { */ 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 @@ public final class CachedAppOptimizer { @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 @@ public final class CachedAppOptimizer { // 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 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { } } - @GuardedBy("mAm") + @GuardedBy("mProcLock") void dump(PrintWriter pw) { pw.println("CachedAppOptimizer settings"); synchronized (mPhenotypeFlagLock) { @@ -350,58 +356,57 @@ public final class CachedAppOptimizer { } } - @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 @@ public final class CachedAppOptimizer { // 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); - - for (int i = 0; i < processCount; i++) { - ProcessRecord process = mAm.mProcessList.mLruProcesses.get(i); - - if (process == null) { - continue; - } + mAm.mProcessList.forEachLruProcessesLOSP(true, process -> { + if (process == null) { + return; + } - if (enable && process.freezerOverride) { - freezeAppAsync(process); - process.freezerOverride = false; - } + final ProcessCachedOptimizerRecord opt = process.mOptRecord; + if (enable && opt.hasFreezerOverride()) { + freezeAppAsyncLSP(process); + opt.setFreezerOverride(false); + } - if (!enable && process.frozen) { - unfreezeAppLocked(process); + if (!enable && opt.isFrozen()) { + unfreezeAppLSP(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 @@ public final class CachedAppOptimizer { } // 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 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { case COMPACT_PROCESS_MSG: { long start = SystemClock.uptimeMillis(); ProcessRecord proc; + final ProcessCachedOptimizerRecord opt; int pid; String action; final String name; @@ -877,18 +884,19 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { return; } - lastCompactAction = proc.lastCompactAction; - lastCompactTime = proc.lastCompactTime; + lastCompactAction = opt.getLastCompactAction(); + lastCompactTime = opt.getLastCompactTime(); lastCompactionStats = mLastCompactionStats.get(pid); } @@ -1076,9 +1084,9 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { } } - 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 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { } 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 478c512338f0..f43c7f6278c9 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.RemoteException; 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { // 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { // 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { } 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 @@ public class ContentProviderHelper { // 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 @@ public class ContentProviderHelper { } 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 @@ public class ContentProviderHelper { 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 class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { } - 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { 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 @@ public class ContentProviderHelper { } 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 @@ public class ContentProviderHelper { } } 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 @@ public class ContentProviderHelper { // 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 59a19398420d..b217cae61d4c 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 @@ package com.android.server.am; 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 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { + " 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 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { && 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 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { 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 000000000000..ef135d55e43c --- /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 6e42aee3ba2a..94eb07611037 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.StructTimeval; 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 @@ final class NativeCrashListener extends Thread { // 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 5e146e16deb0..d79fb8a265e8 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.setProcessGroup; 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.Pair; 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { ServiceThread adjusterThread) { mService = service; mProcessList = processList; + mProcLock = service.mProcLock; mActiveUids = activeUids; mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class); @@ -388,24 +393,28 @@ public final class OomAdjuster { @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; - } - } - if (!includeWarmPkg) { - continue; - } - for (int j = app.numberOfRunningServices() - 1; j >= 0; j--) { - app.getRunningServiceAt(j).updateKeepWarmLocked(); + 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) { + return; + } + final ProcessServiceRecord psr = app.mServices; + for (int j = psr.numberOfRunningServices() - 1; j >= 0; j--) { + psr.getRunningServiceAt(j).updateKeepWarmLocked(); + } } /** @@ -419,11 +428,20 @@ public final class OomAdjuster { @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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { */ @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 @@ public final class OomAdjuster { */ @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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } // 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 @@ public final class OomAdjuster { } 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { * 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } 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 @@ public final class OomAdjuster { } } + @GuardedBy({"mService", "mProcLock"}) private void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) { final int numLru = lruList.size(); @@ -899,23 +936,27 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { inGroup = true; } else { lastCachedGroupUid = app.uid; - lastCachedGroup = app.connectionGroup; - lastCachedGroupImportance = app.connectionImportance; + lastCachedGroup = connectionGroup; + lastCachedGroupImportance = connectionImportance; } } if (!inGroup && curCachedAdj != nextCachedAdj) { @@ -943,11 +984,12 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { } } - 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { // 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; + @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); + } + } + } + + @GuardedBy({"mService", "mProcLock"}) + private void updateAppUidRecLSP(ProcessRecord app) { + final UidRecord uidRec = app.getUidRecord(); if (uidRec != null) { - uidRec.ephemeral = app.info.isInstantApp(); - if (uidRec.getCurProcState() > app.getCurProcState()) { - uidRec.setCurProcState(app.getCurProcState()); + final ProcessStateRecord state = app.mState; + uidRec.setEphemeral(app.info.isInstantApp()); + if (uidRec.getCurProcState() > state.getCurProcState()) { + uidRec.setCurProcState(state.getCurProcState()); } - if (app.hasForegroundServices()) { - uidRec.foregroundServices = true; + if (app.mServices.hasForegroundServices()) { + uidRec.setForegroundServices(true); } - uidRec.curCapability |= app.curCapability; + uidRec.setCurCapability(uidRec.getCurCapability() | state.getCurCapability()); } } - private void updateUidsLocked(ActiveUids activeUids, final long nowElapsed) { + @GuardedBy({"mService", "mProcLock"}) + private void updateUidsLSP(ActiveUids activeUids, final long nowElapsed) { ArrayList<UidRecord> becameIdle = mTmpBecameIdle; becameIdle.clear(); @@ -1110,22 +1171,22 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { this.appUid = appUid; this.logUid = logUid; this.processStateCurTop = processStateCurTop; + this.mState = app.mState; } @Override @@ -1215,14 +1280,14 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { 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 final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 final class OomAdjuster { 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 @@ public final class OomAdjuster { } } - 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { } // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } } - 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 @@ public final class OomAdjuster { } 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { if (adj > clientAdj) { adjType = "cch-bound-ui-services"; } - app.setCached(false); + state.setCached(false); clientAdj = adj; clientProcState = procState; } else { @@ -1843,7 +1912,7 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { if (procState > clientProcState) { procState = clientProcState; - app.setCurRawProcState(procState); + state.setCurRawProcState(procState); if (adjType == null) { adjType = "service"; } @@ -1979,12 +2048,12 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } } - 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } 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 @@ public final class OomAdjuster { } } - 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 @@ public final class OomAdjuster { } 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 @@ public final class OomAdjuster { } 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } // 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { * 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 @@ public final class OomAdjuster { } /** 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 @@ public final class OomAdjuster { } /** 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { && 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } } 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 @@ public final class OomAdjuster { 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 @@ public final class OomAdjuster { } } 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 @@ public final class OomAdjuster { } } - 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 @@ public final class OomAdjuster { // 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 @@ public final class OomAdjuster { } } } - 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 @@ public final class OomAdjuster { } 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 @@ public final class OomAdjuster { } } - @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 @@ public final class OomAdjuster { @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 @@ public final class OomAdjuster { 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 978bcb7afee4..aa10d52eb48c 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 @@ final class PackageList { } } - 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 @@ final class PackageList { } } - 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 @@ final class PackageList { } } - <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 @@ final class PackageList { 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 631b6327e7ad..0eb48f6da60d 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 static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA 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 @@ public final class PendingIntentRecord extends IIntentSender.Stub { 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 37b17411dac5..4f3438fe8706 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 @@ public final class PhantomProcessList { @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 @@ public final class PhantomProcessList { 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 @@ public final class PhantomProcessList { 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 @@ public final class PhantomProcessList { } } 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 @@ public final class PhantomProcessList { } @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 @@ public final class PhantomProcessList { 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 @@ public final class PhantomProcessList { * 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.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); + 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(); } - mTempPhantomProcesses.clear(); } } } @@ -498,7 +502,7 @@ public final class PhantomProcessList { } } // 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 @@ public final class PhantomProcessList { 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 000000000000..464361076dc8 --- /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 000000000000..64e307a5f182 --- /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 8f994591f96c..0576345686fc 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_PROCESSES; 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.TimeUtils; 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.HashMap; 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { * 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 @@ public final class ProcessList { /** * 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 @@ public final class ProcessList { */ private final int[] mZygoteSigChldMessage = new int[3]; + ActivityManagerGlobalLock mProcLock; + final class IsolatedUidRange { @VisibleForTesting public final int mFirstUid; @@ -640,6 +661,7 @@ public final class ProcessList { * 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 @@ public final class ProcessList { * 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 @@ public final class ProcessList { /** * 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 @@ public final class ProcessList { */ 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { */ 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 @@ public final class ProcessList { } pidPackageMap.put(pid, record.info.packageName); } - return pidPackageMap; } + return pidPackageMap; } private void updateOomLevels(int displayWidth, int displayHeight, boolean write) { @@ -1534,6 +1559,7 @@ public final class ProcessList { } } + @GuardedBy("mService") final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) { if (uid == SYSTEM_UID) { @@ -1554,21 +1580,19 @@ public final class ProcessList { } 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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; + if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) { + return Zygote.MEMORY_TAG_LEVEL_TBI; } - return true; + return Zygote.MEMORY_TAG_LEVEL_NONE; } 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)) { - return Zygote.MEMORY_TAG_LEVEL_TBI; + // 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 0; + return level; } private int decideGwpAsanLevel(ProcessRecord app) { @@ -1745,7 +1790,7 @@ public final class ProcessList { ? 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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) { + app.setPendingStart(true); + app.setRemoved(false); + synchronized (mProcLock) { + app.setKilledByAm(false); + app.setKilled(false); + } + if (app.getStartSeq() != 0) { Slog.wtf(TAG, "startProcessLocked processName:" + app.processName - + " with non-zero startSeq:" + app.startSeq); + + " with non-zero startSeq:" + app.getStartSeq()); } - if (app.pid != 0) { + if (app.getPid() != 0) { Slog.wtf(TAG, "startProcessLocked processName:" + app.processName - + " with non-zero pid:" + app.pid); + + " with non-zero pid:" + app.getPid()); } - app.mDisabledCompatChanges = null; + 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 @@ public final class ProcessList { } 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { // 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } 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 @@ public final class ProcessList { // 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { // 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } @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 @@ public final class ProcessList { info.processName); mService.mAppErrors.clearBadProcess(processName, info.uid); if (app != null) { - app.bad = false; + app.mErrorState.setBad(false); } } } @@ -2416,11 +2486,11 @@ public final class ProcessList { // 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 @@ public final class ProcessList { // 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { + 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 @@ public final class ProcessList { @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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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.getStartUid()); + synchronized (mProcLock) { + app.setPid(pid); + app.setUsingWrapper(usingWrapper); + app.setPendingStart(false); } - 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"); + checkSlow(app.getStartTime(), "startProcess: starting to update pids map"); ProcessRecord oldApp; synchronized (mService.mPidsSelfLocked) { oldApp = mService.mPidsSelfLocked.get(pid); @@ -2612,11 +2684,11 @@ public final class ProcessList { 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 @@ public final class ProcessList { ? 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 @@ public final class ProcessList { } } - @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 @@ public final class ProcessList { // we don't kill persistent processes continue; } - if (app.removed) { + if (app.isRemoved()) { if (doit) { procs.add(app); } @@ -2725,7 +2800,7 @@ public final class ProcessList { } // 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 @@ public final class ProcessList { // 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 @@ public final class ProcessList { return true; } if (setRemoved) { - app.removed = true; + app.setRemoved(true); } procs.add(app); } @@ -2806,12 +2881,12 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } @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); - } - } - 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; - } - 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); - - // 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); + 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, 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()); + } + proc.setUidRecord(uidRec); + uidRec.addProcess(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 @@ public final class ProcessList { } @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 @@ public final class ProcessList { 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); - } - record.uidRecord = null; + 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); + } + 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 @@ public final class ProcessList { } /** 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 @@ public final class ProcessList { * @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 @@ public final class ProcessList { 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 @@ public final class ProcessList { * 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } } - @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 @@ public final class ProcessList { } } - @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 @@ public final class ProcessList { 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 @@ public final class ProcessList { * @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 @@ public final class ProcessList { + " from position " + i + " to end of group @ " + endIndex); endIndex--; - endImportance = subProc.connectionImportance; + endImportance = subConnectionImportance; } } } @@ -3238,6 +3333,8 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { "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 @@ public final class ProcessList { } 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 @@ public final class ProcessList { } } - 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { // 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { pw.println(); } + @GuardedBy("mService") boolean dumpLruLocked(PrintWriter pw, String dumpPackage, String prefix) { final int lruSize = mLruProcesses.size(); final String innerPrefix; @@ -3786,8 +4007,8 @@ public final class ProcessList { 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 @@ public final class ProcessList { needSep = true; } - if (getLruSizeLocked() > 0) { + if (getLruSizeLOSP() > 0) { if (needSep) { pw.println(); } @@ -3871,12 +4092,12 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { @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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } /** 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { * {@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 @@ public final class ProcessList { * {@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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } 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 9fd2bd798c04..e533cc3512d3 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.os.SystemClock; 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 @@ final class ProcessProfileRecord { /** * 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 @@ final class ProcessProfileRecord { /** * When we last told the app that memory is low. */ - @GuardedBy("mService") + @CompositeRWLock({"mService", "mProfilerLock"}) private long mLastLowMemory; /** @@ -193,9 +194,12 @@ final class ProcessProfileRecord { @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 @@ final class ProcessProfileRecord { 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 @@ final class ProcessProfileRecord { 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 @@ final class ProcessProfileRecord { } @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 000000000000..751e8a821ab1 --- /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 000000000000..8d3e9669ea00 --- /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 a1adeaac52ca..da8aeb52f3c2 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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 . - - // Controller for error dialogs - private final ErrorDialogController mDialogController = new ErrorDialogController(); - // 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; + /** + * Additional packages we have a dependency on. + */ + @CompositeRWLock({"mService", "mProcLock"}) + private ArraySet<String> mPkgDeps; + + /** + * 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; - // 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; + /** + * 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; /** * 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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); - } - 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 (lastProviderTime > 0) { - pw.print(prefix); pw.print("lastProviderTime="); - TimeUtils.formatDuration(lastProviderTime, nowUptime, pw); - pw.println(); - } - if (lastTopTime > 0) { - pw.print(prefix); pw.print("lastTopTime="); - TimeUtils.formatDuration(lastTopTime, nowUptime, pw); - pw.println(); + pw.print(" removed="); pw.println(mRemoved); } - if (hasStartedServices) { - pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices); + if (mDebugging) { + pw.print(prefix); pw.print("mDebugging="); pw.println(mDebugging); } - if (pendingStart) { - pw.print(prefix); pw.print("pendingStart="); pw.println(pendingStart); + if (mPendingStart) { + pw.print(prefix); pw.print("pendingStart="); pw.println(mPendingStart); } - 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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; - } - boolean added = mServices.add(record); - if (added && record.serviceInfo != null) { - mWindowProcessController.onServiceStarted(record.serviceInfo); - } - return added; + @GuardedBy("mService") + int[] getGids() { + return mGids; } - /** - * 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 setGids(int[] gids) { + mGids = gids; } - /** - * The same as calling {@link #stopService(ServiceRecord)} on all current running services. - */ - void stopAllServices() { - mServices.clear(); + @GuardedBy("mService") + String getRequiredAbi() { + return mRequiredAbi; } - /** - * 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("mService") + void setRequiredAbi(String requiredAbi) { + mRequiredAbi = requiredAbi; + mWindowProcessController.setRequiredAbi(requiredAbi); } - /** - * Returns the service at the specified {@code index}. - * - * @see #numberOfRunningServices() - */ - ServiceRecord getRunningServiceAt(int index) { - return mServices.valueAt(index); + @GuardedBy("mService") + String getInstructionSet() { + return mInstructionSet; } - void setCached(boolean cached) { - if (mCached != cached) { - mCached = cached; - if (cached) { - ++mCacheOomRankerUseCount; - } + @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); } + mDeathRecipient = null; + } + + @GuardedBy("mService") + void setDeathRecipient(IBinder.DeathRecipient deathRecipient) { + mDeathRecipient = deathRecipient; + } + + @GuardedBy({"mService", "mProcLock"}) + void setActiveInstrumentation(ActiveInstrumentation instr) { + mInstr = instr; + boolean isInstrumenting = instr != null; + mWindowProcessController.setInstrumenting( + isInstrumenting, + isInstrumenting ? instr.mSourceUid : -1, + isInstrumenting && instr.mHasBackgroundActivityStartsPermission); + } + + @GuardedBy(anyOf = {"mService", "mProcLock"}) + ActiveInstrumentation getActiveInstrumentation() { + return mInstr; + } + + @GuardedBy(anyOf = {"mService", "mProcLock"}) + boolean isKilledByAm() { + return mKilledByAm; + } + + @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 isCached() { - return mCached; + 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); } - int getCacheOomRankerUseCount() { - return mCacheOomRankerUseCount; + @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 mState.isCached(); } boolean hasActivities() { @@ -738,6 +894,22 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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; - } - - 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; - } - } + return mServices.hasForegroundServices(); } - 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 @@ class ProcessRecord implements WindowProcessListener { } } - 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { } 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 @@ class ProcessRecord implements WindowProcessListener { } public String toString() { + final String stringName = mStringName; if (stringName != null) { return stringName; } @@ -920,33 +1052,7 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { @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 @@ class ProcessRecord implements WindowProcessListener { public void setPendingUiCleanAndForceProcessStateUpTo(int newState) { synchronized (mService) { setPendingUiClean(true); - forceProcessStateUpTo(newState); + mState.forceProcessStateUpTo(newState); } } @@ -1362,40 +1188,35 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { // 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 @@ class ProcessRecord implements WindowProcessListener { @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 @@ class ProcessRecord implements WindowProcessListener { } public int getProcessClassEnum() { - if (pid == MY_PID) { + if (mPid == MY_PID) { return ServerProtoEnums.SYSTEM_SERVER; } if (info == null) { @@ -1455,687 +1268,9 @@ class ProcessRecord implements WindowProcessListener { 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 000000000000..5c3bf605f71a --- /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 000000000000..e1a153dfa920 --- /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 c10a07862123..34a0c1beec09 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 @@ public final class ProcessStatsService extends IProcessStats.Stub { 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 29c165733808..072eba5d1205 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.RemoteException; 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 @@ public final class ProviderMap { */ 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 @@ public final class ProviderMap { 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 @@ public final class ProviderMap { 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 @@ public final class ProviderMap { // 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 @@ public final class ProviderMap { * 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 8a1b4e3e832e..485087d29422 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_AM; 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } // 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 = _proc; - if (pendingConnectionGroup > 0 && _proc != null) { - _proc.connectionService = this; - _proc.connectionGroup = pendingConnectionGroup; - _proc.connectionImportance = pendingConnectionImportance; + app.mServices.updateBoundClientUids(); + } + 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } } - if (_proc != null) { - _proc.updateBoundClientUids(); + if (proc != null) { + proc.mServices.updateBoundClientUids(); } } @@ -658,7 +661,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return; } if (mIsAllowedBgFgsStartsByBinding) { - app.addAllowBackgroundFgsStartsToken(this); + app.mState.addAllowBackgroundFgsStartsToken(this); } else { - app.removeAllowBackgroundFgsStartsToken(this); + app.mState.removeAllowBackgroundFgsStartsToken(this); } } @@ -937,7 +940,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN 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 class ServiceRecord extends Binder implements ComponentName.WithComponentN 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 22e7faa4bad9..4cc1fc1b7d09 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 @@ final class StrictModeViolationDialog extends BaseErrorDialog { 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 @@ final class StrictModeViolationDialog extends BaseErrorDialog { 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 @@ final class StrictModeViolationDialog extends BaseErrorDialog { 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 b3150d1489bb..df65ee6e3a28 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 @@ public class UidObserverController { } 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 36d22bf0f5da..2fb662f5ee69 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.TimeUtils; 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 @@ public final class UidRecord { // 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 @@ public final class UidRecord { 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 @@ public final class UidRecord { 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 @@ public final class UidRecord { } } 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 fded85cd9126..e97f0b47380a 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 @@ package com.android.server.apphibernation; 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.Intent; 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.os.UserManager; 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 @@ import java.util.Set; */ 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 @@ public final class AppHibernationService extends SystemService { 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 @@ public final class AppHibernationService extends SystemService { * @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 @@ public final class AppHibernationService extends SystemService { @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 @@ public final class AppHibernationService extends SystemService { */ 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 @@ public final class AppHibernationService extends SystemService { */ 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 @@ public final class AppHibernationService extends SystemService { */ 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 @@ public final class AppHibernationService extends SystemService { } 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 @@ public final class AppHibernationService extends SystemService { * @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 @@ public final class AppHibernationService extends SystemService { } /** - * 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 @@ public final class AppHibernationService extends SystemService { /** * 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) { + /** + * 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); + } + } + } + + @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) { - addUserPackageStatesL(userId); + initializeUserHibernationStates(userId, storedStates); } } - private void onUserRemoved(int userId) { + @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 @@ public final class AppHibernationService extends SystemService { private void onPackageRemovedForAllUsers(@NonNull String packageName) { synchronized (mLock) { - mGloballyHibernatedPackages.remove(packageName); + mGlobalHibernationStates.remove(packageName); } } @@ -395,7 +499,7 @@ public final class AppHibernationService extends SystemService { } } - // 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 @@ public final class AppHibernationService extends SystemService { } 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 @@ public final class AppHibernationService extends SystemService { } /** - * 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 000000000000..79e995b038fa --- /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/services/core/java/com/android/server/apphibernation/GlobalLevelState.java b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java new file mode 100644 index 000000000000..4f756756c2ab --- /dev/null +++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * 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 000000000000..c83659d2ff56 --- /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 000000000000..0cbc09a7a99d --- /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 000000000000..a24c4c575975 --- /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/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java new file mode 100644 index 000000000000..c66dad87c891 --- /dev/null +++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * 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 f07da8f0236b..10fe1e1d0684 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { } cbs.add(cb); } - if (packageName != null) { + if (mayWatchPackageName) { ArraySet<ModeCallback> cbs = mPackageModeWatchers.get(packageName); if (cbs == null) { cbs = new ArraySet<>(); @@ -3008,13 +3010,27 @@ public class AppOpsService extends IAppOpsService.Stub { 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 d5759633bf2d..a43f0c6647e1 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1076,7 +1076,6 @@ public class AudioService extends IAudioService.Stub if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) { synchronized (mHdmiClientLock) { - mHdmiCecSink = false; mHdmiManager = mContext.getSystemService(HdmiControlManager.class); if (mHdmiManager != null) { mHdmiManager.addHdmiControlStatusChangeListener( @@ -1508,7 +1507,8 @@ public class AudioService extends IAudioService.Stub if (isPlatformTelevision()) { synchronized (mHdmiClientLock) { if (mHdmiManager != null && mHdmiPlaybackClient != null) { - updateHdmiCecSinkLocked(mHdmiCecSink | false); + updateHdmiCecSinkLocked( + mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI)); } } } @@ -1518,7 +1518,8 @@ public class AudioService extends IAudioService.Stub if (isPlatformTelevision()) { synchronized (mHdmiClientLock) { if (mHdmiManager != null) { - updateHdmiCecSinkLocked(mHdmiCecSink | false); + updateHdmiCecSinkLocked( + mFullVolumeDevices.contains(AudioSystem.DEVICE_OUT_HDMI)); } } } @@ -2670,8 +2671,7 @@ public class AudioService extends IAudioService.Stub 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 @@ -7825,9 +7825,8 @@ public class AudioService extends IAudioService.Stub @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 +7884,6 @@ public class AudioService extends IAudioService.Stub // 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 +8138,6 @@ public class AudioService extends IAudioService.Stub 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); 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 9869f779cc20..1135126d86fb 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 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @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 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } 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 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider 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 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider 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/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java new file mode 100644 index 000000000000..802472fcfba8 --- /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 d7dcbde5692d..375ec3a0f95f 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 @@ package com.android.server.devicestate; 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.IBinder; 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 com.android.server.policy.DeviceStatePolicyImpl; 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 @@ public final class DeviceStateManagerService extends SystemService { @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 @@ public final class DeviceStateManagerService extends SystemService { * * @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 @@ public final class DeviceStateManagerService extends SystemService { * 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 @@ public final class DeviceStateManagerService extends SystemService { return false; } - mRequestedOverrideState = overrideState; + mRequestedOverrideState = getStateLocked(overrideState); updatePendingStateLocked(); } @@ -181,20 +190,24 @@ public final class DeviceStateManagerService extends SystemService { } /** - * 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 @@ public final class DeviceStateManagerService extends SystemService { 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 @@ public final class DeviceStateManagerService extends SystemService { /** * 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 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. */ - private boolean isSupportedStateLocked(int state) { - return mSupportedDeviceStates.binarySearch(state) >= 0; + @Nullable + private Optional<DeviceState> getStateLocked(int identifier) { + return Optional.ofNullable(mDeviceStates.get(identifier)); } /** @@ -242,10 +266,11 @@ public final class DeviceStateManagerService extends SystemService { * * @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 @@ public final class DeviceStateManagerService extends SystemService { * 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 @@ public final class DeviceStateManagerService extends SystemService { return; } - mPendingState = stateToConfigure; + mPendingState = Optional.of(stateToConfigure); mIsPolicyWaitingForState = true; } @@ -302,7 +327,7 @@ public final class DeviceStateManagerService extends SystemService { return; } mIsPolicyWaitingForState = false; - state = mPendingState; + state = mPendingState.get().getIdentifier(); } if (DEBUG) { @@ -333,9 +358,9 @@ public final class DeviceStateManagerService extends SystemService { 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 @@ public final class DeviceStateManagerService extends SystemService { } mCallbacks.put(callingPid, record); - currentState = mCommittedState; + currentState = mCommittedState.getIdentifier(); } // Notify the callback of the state at registration. @@ -406,10 +431,10 @@ public final class DeviceStateManagerService extends SystemService { 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 @@ public final class DeviceStateManagerService extends SystemService { } } - 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 cf3b297545dc..7914531f9910 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 @@ public class DeviceStateManagerShellCommand extends ShellCommand { } private void printState(PrintWriter pw) { - int committedState = mInternal.getCommittedState(); - int requestedState = mInternal.getRequestedState(); - int requestedOverrideState = mInternal.getOverrideState(); - - if (committedState == INVALID_DEVICE_STATE) { - pw.println("Device state: (invalid)"); - } else { - pw.println("Device state: " + committedState); - } + DeviceState committedState = mInternal.getCommittedState(); + Optional<DeviceState> requestedState = mInternal.getRequestedState(); + Optional<DeviceState> requestedOverrideState = mInternal.getOverrideState(); - 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 @@ public class DeviceStateManagerShellCommand extends ShellCommand { } 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 0e8bf9bda4aa..2d4377f820fd 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 @@ public interface DeviceStateProvider { /** * 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 @@ public interface DeviceStateProvider { /** 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 b8e579d58794..4832e46be8a8 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 @@ public abstract class BrightnessMappingStrategy { /** @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 @@ public abstract class BrightnessMappingStrategy { } @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 @@ public abstract class BrightnessMappingStrategy { // 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 @@ public abstract class BrightnessMappingStrategy { // 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 @@ public abstract class BrightnessMappingStrategy { 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 @@ public abstract class BrightnessMappingStrategy { } @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 @@ public abstract class BrightnessMappingStrategy { 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 2375f746c57d..a186e334c5c0 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 android.view.Display; 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 libcore.io.IoUtils; 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.IOException; 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 @@ public class BrightnessTracker { 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 @@ public class BrightnessTracker { 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 @@ public class BrightnessTracker { 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 @@ public class BrightnessTracker { 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 @@ public class BrightnessTracker { 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 e0baee70b819..e5151d84c33e 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { 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 @@ public final class DisplayManagerService extends SystemService { mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS); mSettingsObserver = new SettingsObserver(); + mBrightnessSynchronizer.startSynchronizing(); } @VisibleForTesting @@ -2761,22 +2763,8 @@ public final class DisplayManagerService extends SystemService { 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 13dc0b9be21f..2df336528939 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.Trace; 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.MetricsLogger; 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final DisplayPowerCallbacks mCallbacks; // Battery stats. + @Nullable private final IBatteryStats mBatteryStats; // The sensor manager. @@ -170,6 +174,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final int mDisplayId; // Tracker for brightness changes. + @Nullable private final BrightnessTracker mBrightnessTracker; // Tracker for brightness settings changes. @@ -346,6 +351,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call * @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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call * 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 8ee01bef8f54..16c4b2641433 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 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { 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 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { 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 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } 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 4706edc151f8..88b26680631e 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 @@ public final class ColorDisplayService extends SystemService { private ContentObserver mContentObserver; private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener; + private ReduceBrightColorsListener mReduceBrightColorsListener; private NightDisplayAutoMode mNightDisplayAutoMode; @@ -617,18 +618,24 @@ public final class ColorDisplayService extends SystemService { 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 @@ public final class ColorDisplayService extends SystemService { 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 @@ public final class ColorDisplayService extends SystemService { } /** + * 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 @@ public final class ColorDisplayService extends SystemService { 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 @@ public final class ColorDisplayService extends SystemService { } @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 7e120c91ce9d..cb93cc8fc3a7 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 @@ import java.util.Arrays; 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 class ReduceBrightColorsTintController extends TintController { 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 @@ public class ReduceBrightColorsTintController extends TintController { 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 class ReduceBrightColorsTintController extends TintController { 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 90368129e78e..3e2b5ab795be 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.Typeface; 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.NioUtils; 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 @@ public final class FontManagerService extends IFontManager.Stub { } @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 @@ public final class FontManagerService extends IFontManager.Stub { 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 @@ public final class FontManagerService extends IFontManager.Stub { } } - /* 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 @@ public final class FontManagerService extends IFontManager.Stub { "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 2029f395a352..cf9a79f69f19 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.content.Context; 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.LocalDateTime; 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 @@ public class FontManagerShellCommand extends ShellCommand { "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 @@ public class FontManagerShellCommand extends ShellCommand { } 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 f0d14ba80383..017f11ceaf06 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 @@ package com.android.server.graphics.fonts; 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 org.xmlpull.v1.XmlPullParserException; 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 @@ import java.io.OutputStream; */ 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 @@ import java.io.OutputStream; } 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 @@ import java.io.OutputStream; 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 @@ import java.io.OutputStream; } } + @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 8fac0865dd15..afc97c7fd803 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -21,6 +21,7 @@ import static com.android.server.graphics.fonts.FontManagerService.SystemFontExc 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; @@ -72,15 +73,6 @@ final class UpdatableFontDir { 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 @@ final class UpdatableFontDir { private final File mConfigFile; private final File mTmpConfigFile; - private final PersistentSystemFontConfig.Config mConfig = - new PersistentSystemFontConfig.Config(); - + private long mLastModifiedDate; private int mConfigVersion = 1; /** @@ -145,22 +135,36 @@ final class UpdatableFontDir { } /* 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 @@ final class UpdatableFontDir { // 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 @@ final class UpdatableFontDir { 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 @@ final class UpdatableFontDir { } /** + * 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. + HashMap<String, FontFileInfo> backupMap = new HashMap<>(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 @@ final class UpdatableFontDir { * @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 @@ final class UpdatableFontDir { "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,6 +454,15 @@ final class UpdatableFontDir { } } + 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<>(); for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) { @@ -448,11 +472,7 @@ final class UpdatableFontDir { } /* 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/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index d16267f0582e..9216a6b245a6 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 com.android.server.location.provider.AbstractLocationProvider; 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } 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/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 06ca9ec387a9..221d4fb40ef9 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.app.compat.CompatChanges.isChangeEnabled; 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 @@ public class LocationProviderManager extends @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 @@ public class LocationProviderManager extends // 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 dce7b081de6e..0d8f64377db0 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 @@ public class MockLocationProvider extends AbstractLocationProvider { 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 c274c2848ab6..32d637ffa730 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 static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; 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 com.android.server.location.provider.AbstractLocationProvider; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -260,13 +262,25 @@ public class ProxyLocationProvider extends AbstractLocationProvider { // 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/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 504eefe1fa26..ca21640feb8f 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // 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, diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java index 4f986bd5276b..2a1fc87fc29f 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 final class DumpState { 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 6e5bd947c456..f444ed6df2a3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -377,6 +377,11 @@ import com.android.server.pm.dex.DexManager; 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 +404,7 @@ import com.android.server.utils.Watchable; 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 +466,7 @@ import java.util.concurrent.atomic.AtomicInteger; 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 +1070,9 @@ public class PackageManagerService extends IPackageManager.Stub 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 +1101,9 @@ public class PackageManagerService extends IPackageManager.Stub instantAppResolverConnectionProducer, Producer<ModuleInfoProvider> moduleInfoProviderProducer, Producer<LegacyPermissionManagerInternal> legacyPermissionManagerInternalProducer, + Producer<DomainVerificationManagerInternal> + domainVerificationManagerInternalProducer, + Producer<Handler> handlerProducer, SystemWrapper systemWrapper, ServiceProducer getLocalServiceProducer, ServiceProducer getSystemServiceProducer) { @@ -1128,6 +1141,9 @@ public class PackageManagerService extends IPackageManager.Stub mSystemWrapper = systemWrapper; mGetLocalServiceProducer = getLocalServiceProducer; mGetSystemServiceProducer = getSystemServiceProducer; + mDomainVerificationManagerInternalProducer = + new Singleton<>(domainVerificationManagerInternalProducer); + mHandlerProducer = new Singleton<>(handlerProducer); } /** @@ -1273,6 +1289,14 @@ public class PackageManagerService extends IPackageManager.Stub 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 +1351,6 @@ public class PackageManagerService extends IPackageManager.Stub 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 +1473,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 +1490,6 @@ public class PackageManagerService extends IPackageManager.Stub 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 +1509,6 @@ public class PackageManagerService extends IPackageManager.Stub 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 +1583,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 +1593,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 +1687,74 @@ public class PackageManagerService extends IPackageManager.Stub 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 +1975,6 @@ public class PackageManagerService extends IPackageManager.Stub 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 +2028,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 +2074,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 +2586,6 @@ public class PackageManagerService extends IPackageManager.Stub 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 +2601,15 @@ public class PackageManagerService extends IPackageManager.Stub 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 +2623,12 @@ public class PackageManagerService extends IPackageManager.Stub // 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 +2679,6 @@ public class PackageManagerService extends IPackageManager.Stub // 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 +2772,16 @@ public class PackageManagerService extends IPackageManager.Stub 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 +3024,20 @@ public class PackageManagerService extends IPackageManager.Stub 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); + Slog.v(TAG, "Instant app approvd for intent; pkg: " + + packageName); } - blockResolution = true; - break; + localInstantApp = info; } 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 not approved for intent; pkg: " + + packageName); } - localInstantApp = info; - break; + blockResolution = true; } + break; } } } @@ -3846,6 +3622,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 +3953,10 @@ public class PackageManagerService extends IPackageManager.Stub 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 +4249,6 @@ public class PackageManagerService extends IPackageManager.Stub 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 +5098,6 @@ public class PackageManagerService extends IPackageManager.Stub 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 +5158,12 @@ public class PackageManagerService extends IPackageManager.Stub } break; } + case DOMAIN_VERIFICATION: { + int messageCode = msg.arg1; + Object object = msg.obj; + mDomainVerificationManager.runMessage(messageCode, object); + break; + } } } } @@ -6014,7 +5730,8 @@ public class PackageManagerService extends IPackageManager.Stub } 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 +5754,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 +5788,13 @@ public class PackageManagerService extends IPackageManager.Stub 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 +5966,9 @@ public class PackageManagerService extends IPackageManager.Stub 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 +5978,11 @@ public class PackageManagerService extends IPackageManager.Stub 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 +6188,9 @@ public class PackageManagerService extends IPackageManager.Stub 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 +6213,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 +6698,6 @@ public class PackageManagerService extends IPackageManager.Stub if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) { for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) { mSettings.applyDefaultPreferredAppsLPw(user.id); - primeDomainVerificationsLPw(user.id); } } @@ -7080,13 +6804,18 @@ public class PackageManagerService extends IPackageManager.Stub 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 +6824,6 @@ public class PackageManagerService extends IPackageManager.Stub mRequiredVerifierPackage = null; mRequiredInstallerPackage = null; mRequiredUninstallerPackage = null; - mIntentFilterVerifierComponent = null; - mIntentFilterVerifier = null; mServicesExtensionPackageName = null; mSharedSystemSharedLibraryPackageName = null; } @@ -7631,6 +7358,40 @@ public class PackageManagerService extends IPackageManager.Stub 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 +7525,6 @@ public class PackageManagerService extends IPackageManager.Stub 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); @@ -9663,9 +9370,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 +9863,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 +9949,6 @@ public class PackageManagerService extends IPackageManager.Stub 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 +13198,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 +13455,9 @@ public class PackageManagerService extends IPackageManager.Stub 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 +13481,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 +13501,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 +14346,8 @@ public class PackageManagerService extends IPackageManager.Stub * 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 +14470,12 @@ public class PackageManagerService extends IPackageManager.Stub 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 +16148,31 @@ public class PackageManagerService extends IPackageManager.Stub 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 +19855,6 @@ public class PackageManagerService extends IPackageManager.Stub "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 +20188,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - 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 +20857,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 +21384,6 @@ public class PackageManagerService extends IPackageManager.Stub null /*lastDisableAppCaller*/, null /*enabledComponents*/, null /*disabledComponents*/, - ps.readUserState(nextUserId).domainVerificationStatus, - 0 /*linkGeneration*/, PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.UNINSTALL_REASON_UNKNOWN, null /*harmfulAppWarning*/); @@ -22464,40 +21933,6 @@ public class PackageManagerService extends IPackageManager.Stub 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 +21988,7 @@ public class PackageManagerService extends IPackageManager.Stub } 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 +22250,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 +22259,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 +23512,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 +23695,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 +23790,16 @@ public class PackageManagerService extends IPackageManager.Stub } } - 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 +23807,14 @@ public class PackageManagerService extends IPackageManager.Stub 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 +23931,20 @@ public class PackageManagerService extends IPackageManager.Stub } } - 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 +24133,10 @@ public class PackageManagerService extends IPackageManager.Stub 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 +24261,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - 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 +25456,6 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); scheduleWritePackageListLocked(userId); - primeDomainVerificationsLPw(userId); mAppsFilter.onUsersChanged(); } } @@ -28426,6 +27755,11 @@ public class PackageManagerService extends IPackageManager.Stub 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 212edf6d0daa..b5765b50e746 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.FgThread; 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 ade087be30c9..69e84b536004 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.Collections; 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 @@ public class PackageSetting extends PackageSettingBase { @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 @@ public class PackageSetting extends PackageSettingBase { 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 @@ public class PackageSetting extends PackageSettingBase { 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 class PackageSetting extends PackageSettingBase { 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 67bd82b42983..8aa553d68b98 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.SparseArray; 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 @@ public abstract class PackageSettingBase extends SettingBase { /** Whether or not an update is available. Ostensibly only for instant apps. */ boolean updateAvailable; - IntentFilterVerificationInfo verificationInfo; - boolean forceQueryableOverride; @NonNull @@ -258,7 +258,6 @@ public abstract class PackageSettingBase extends SettingBase { 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 @@ public abstract class PackageSettingBase extends SettingBase { 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 @@ public abstract class PackageSettingBase extends SettingBase { 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 @@ public abstract class PackageSettingBase extends SettingBase { 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 @@ public abstract class PackageSettingBase extends SettingBase { otherState.instantApp, otherState.virtualPreload, otherState.lastDisableAppCaller, otherState.enabledComponents, otherState.disabledComponents, - otherState.domainVerificationStatus, otherState.appLinkGeneration, otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning); } @@ -644,40 +642,6 @@ public abstract class PackageSettingBase extends SettingBase { 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 @@ public abstract class PackageSettingBase extends SettingBase { 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/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 349d556eac87..fb033e6594b8 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_DISABLED 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.permission.persistence.RuntimePermissionsState; 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 libcore.io.IoUtils; 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.text.SimpleDateFormat; 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; 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 @@ public final class Settings implements Watchable, Snappable { "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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { @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 @@ public final class Settings implements Watchable, Snappable { private final LegacyPermissionDataProvider mPermissionDataProvider; + private final DomainVerificationManagerInternal mDomainVerificationManager; + /** * The observer that watches for changes from array members */ @@ -538,13 +538,12 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { mOtherAppIds.registerObserver(mObserver); mRenamedPackages.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); - mNextAppLinkGeneration.registerObserver(mObserver); Watchable.verifyWatchedAttributes(this, mObserver); } @@ -629,11 +629,12 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { @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 @@ public final class Settings implements Watchable, Snappable { pkgSetting.usesStaticLibrariesVersions = null; } pkgSetting.updateMimeGroups(mimeGroupNames); + pkgSetting.setDomainSetId(domainSetId); } /** @@ -1168,15 +1170,6 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { } } - 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { return; } - int maxAppLinkGeneration = 0; - int outerDepth = parser.getDepth(); PackageSetting ps = null; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT @@ -1817,11 +1650,6 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { } 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 @@ public final class Settings implements Watchable, Snappable { } str.close(); - - mNextAppLinkGeneration.put(userId, maxAppLinkGeneration + 1); - } catch (XmlPullParserException e) { mReadMessages.append("Error reading: " + e.toString()); PackageManagerService.reportSettingsProblem(Log.ERROR, @@ -2038,77 +1864,7 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { } } - 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 @@ public final class Settings implements Watchable, Snappable { 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 000000000000..9588a279ecec --- /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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index f43240bd810a..a8077774d62a 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3880,6 +3880,7 @@ public class UserManagerService extends IUserManager.Stub { */ @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); 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 9dde7dfd5978..629f12035b39 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 @@ public class PackageInfoUtils { 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 000000000000..36efb39909a6 --- /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 000000000000..af9978b91e48 --- /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 000000000000..c521f828ade9 --- /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 000000000000..c787356f342c --- /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 000000000000..7ad275a6f351 --- /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 000000000000..8aa63372b826 --- /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 000000000000..7af78c6a98ca --- /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 000000000000..679f948bb3de --- /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 000000000000..53540c8e0d4f --- /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 000000000000..073967e00134 --- /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 000000000000..7f9e75aa2926 --- /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 000000000000..474f822d6a73 --- /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 000000000000..c6c979107e75 --- /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 000000000000..48099aa5382b --- /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 000000000000..88ccd83899c6 --- /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 000000000000..8e8260899a48 --- /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 000000000000..715d8fb0fc2d --- /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 000000000000..8571c08699bd --- /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/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java new file mode 100644 index 000000000000..bd77983256c5 --- /dev/null +++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyUnavailable.java @@ -0,0 +1,21 @@ +/* + * 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; + +/** 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 000000000000..eab89e987885 --- /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 000000000000..9fcbce2ad055 --- /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 e901f66fd272..ac358db51939 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 android.util.SparseArray; 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.io.InputStream; 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 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, 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 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, @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 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, 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 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, 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 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, /** 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 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, 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 ff51237a900a..c10e828d8c3d 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 @@ class DisplayFoldController { * 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/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java index eb9df756dd0e..9a91848475fe 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 @@ public final class PowerStatsHALWrapper { * @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 @@ public final class PowerStatsHALWrapper { } @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 @@ public final class PowerStatsHALWrapper { } @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 78a227ee170e..37fc5a0f68fa 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 @@ import java.io.IOException; 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 @@ public final class PowerStatsLogger extends Handler { @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 @@ public final class PowerStatsLogger extends Handler { // 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 5fe5db69bc8e..ea41980c02b1 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.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.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 @@ public class PowerStatsService extends SystemService { 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 766cf9c1b678..bd003d3ac2dd 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 @@ package com.android.server.powerstats; 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 @@ public class ProtoStreamUtils { } 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 @@ public class ProtoStreamUtils { } } + 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 @@ public class ProtoStreamUtils { 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 @@ public class ProtoStreamUtils { 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 f8b9601bfd62..7c6999acc666 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 @@ public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCall } 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 7cba00f9a669..f8a4135b9d66 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 @@ public final class TimerTrigger extends PowerStatsLogTrigger { 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 @@ public final class TimerTrigger extends PowerStatsLogTrigger { 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 000000000000..7b9ad0f531aa --- /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 3340792fe5e0..f52b9b1b1c58 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.database.ContentObserver; 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 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir @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 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir 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 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir // 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 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir } 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 cac1a6d29829..c464b74ca726 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 android.util.Slog; 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 final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat */ 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 83b33ee75de9..a54288f73018 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 @@ package com.android.server.timezonedetector.location; 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 @@ import java.util.Objects; */ 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 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir @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 220810f137ad..364eaf8dac04 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.DumpUtils; 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 @@ public class LocationTimeZoneManagerService extends Binder { 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 fd12c2d2ebb8..b6ddd93af3b8 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.NetworkRequest; 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 @@ public class UnderlyingNetworkTracker { @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 class UnderlyingNetworkTracker { 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 @@ public class UnderlyingNetworkTracker { 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 @@ public class UnderlyingNetworkTracker { mHandler = new Handler(mVcnContext.getLooper()); mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class); - mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class); registerNetworkRequests(); } @@ -149,36 +150,49 @@ public class UnderlyingNetworkTracker { private void updateSubIdsAndCellularRequests() { mVcnContext.ensureRunningOnLooperThread(); - Set<Integer> prevSubIds = new ArraySet<>(mSubIds); - mSubIds.clear(); + // Don't bother re-filing NetworkRequests if this Tracker has been torn down. + if (!mIsRunning) { + return; + } - // Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup - // STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony - List<SubscriptionInfo> subInfos = - mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup); + final Set<Integer> subIdsInSubGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup); - for (SubscriptionInfo subInfo : subInfos) { - final int subId = subInfo.getSubscriptionId(); - mSubIds.add(subId); + // new subIds to track = (updated list of subIds) - (currently tracked subIds) + final Set<Integer> subIdsToRegister = new ArraySet<>(subIdsInSubGroup); + subIdsToRegister.removeAll(mCellBringupCallbacks.keySet()); - if (!mCellBringupCallbacks.contains(subId)) { - final NetworkBringupCallback cb = new NetworkBringupCallback(); - mCellBringupCallbacks.put(subId, cb); + // subIds to stop tracking = (currently tracked subIds) - (updated list of subIds) + final Set<Integer> subIdsToUnregister = new ArraySet<>(mCellBringupCallbacks.keySet()); + subIdsToUnregister.removeAll(subIdsInSubGroup); - mConnectivityManager.requestBackgroundNetwork( - getCellNetworkRequestForSubId(subId), mHandler, cb); - } + for (final int subId : subIdsToRegister) { + final NetworkBringupCallback cb = new NetworkBringupCallback(); + mCellBringupCallbacks.put(subId, cb); + + mConnectivityManager.requestBackgroundNetwork( + getCellNetworkRequestForSubId(subId), mHandler, cb); } - // unregister all NetworkCallbacks for outdated subIds - for (final int subId : prevSubIds) { - if (!mSubIds.contains(subId)) { - final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId); - mConnectivityManager.unregisterNetworkCallback(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. */ public void teardown() { mVcnContext.ensureRunningOnLooperThread(); @@ -186,11 +200,12 @@ public class UnderlyingNetworkTracker { 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 132883e4a041..a82f239948ff 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -27,9 +27,16 @@ import android.os.Message; 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.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; /** * Represents an single instance of a VCN. @@ -63,6 +70,15 @@ public class Vcn extends Handler { */ 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; @@ -76,20 +92,24 @@ public class Vcn extends Handler { new HashMap<>(); @NonNull private VcnConfig mConfig; + @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; private boolean mIsRunning = true; public Vcn( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, - @NonNull VcnConfig config) { - this(vcnContext, subscriptionGroup, config, new Dependencies()); + @NonNull VcnConfig config, + @NonNull TelephonySubscriptionSnapshot snapshot) { + this(vcnContext, subscriptionGroup, config, snapshot, new Dependencies()); } - private Vcn( + @VisibleForTesting(visibility = Visibility.PRIVATE) + public Vcn( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, + @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull Dependencies deps) { super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); mVcnContext = vcnContext; @@ -98,6 +118,7 @@ public class Vcn extends Handler { 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 +131,24 @@ public class Vcn extends Handler { 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)); } + /** 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) { @@ -137,6 +171,9 @@ public class Vcn extends Handler { 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; @@ -192,13 +229,26 @@ public class Vcn extends Handler { "Bringing up new VcnGatewayConnection for request " + request.requestId); final VcnGatewayConnection vcnGatewayConnection = - new VcnGatewayConnection( - mVcnContext, mSubscriptionGroup, gatewayConnectionConfig); + mDeps.newVcnGatewayConnection( + mVcnContext, + mSubscriptionGroup, + mLastSnapshot, + gatewayConnectionConfig); mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection); } } } + private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) { + mLastSnapshot = snapshot; + + if (mIsRunning) { + 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 +264,24 @@ public class Vcn extends Handler { } /** 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 {} + /** 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) { + return new VcnGatewayConnection( + vcnContext, subscriptionGroup, snapshot, connectionConfig); + } + } } diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 39c96069f9c6..853bb4324f90 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.LinkAddress; 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,16 +52,21 @@ import android.net.ipsec.ike.IkeSessionParams; 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.GuardedBy; 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; @@ -64,6 +74,7 @@ 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 @@ import java.util.concurrent.TimeUnit; 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 @@ public class VcnGatewayConnection extends StateMachine { */ 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,6 +403,11 @@ public class VcnGatewayConnection extends StateMachine { @NonNull final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState(); + @NonNull private final Object mLock = new Object(); + + @GuardedBy("mLock") + @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; + @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker; @@ -457,14 +486,16 @@ public class VcnGatewayConnection extends StateMachine { public VcnGatewayConnection( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnGatewayConnectionConfig connectionConfig) { - this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies()); + this(vcnContext, subscriptionGroup, snapshot, connectionConfig, new Dependencies()); } @VisibleForTesting(visibility = Visibility.PRIVATE) VcnGatewayConnection( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull Dependencies deps) { super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); @@ -473,12 +504,17 @@ public class VcnGatewayConnection extends StateMachine { mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); mDeps = Objects.requireNonNull(deps, "Missing deps"); + synchronized (mLock) { + 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 +569,31 @@ public class VcnGatewayConnection extends StateMachine { 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"); + + // Vcn is the only user of this method and runs on the same Thread, but lock around + // mLastSnapshot to be technically correct. + synchronized (mLock) { + 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 +725,8 @@ public class VcnGatewayConnection extends StateMachine { 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 +979,8 @@ public class VcnGatewayConnection extends StateMachine { 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 +1001,108 @@ public class VcnGatewayConnection extends StateMachine { } } - 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 +1112,89 @@ public class VcnGatewayConnection extends StateMachine { */ 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 +1209,8 @@ public class VcnGatewayConnection extends StateMachine { @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 +1222,52 @@ public class VcnGatewayConnection extends StateMachine { 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 +1428,15 @@ public class VcnGatewayConnection extends StateMachine { 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 b9babae4c6b7..fe4ea303610f 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.ArrayMap; 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 @@ public class VcnNetworkProvider extends NetworkProvider { 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 @@ public class VcnNetworkProvider extends NetworkProvider { } } - // 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 5fe853a38dd7..ce951b85ffe6 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.content.res.Configuration; 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 @@ class ActivityClientController extends IActivityClientController.Stub { } @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 { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index c6ed16ce1f4d..d846c3a6d36c 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2098,13 +2098,6 @@ class ActivityStarter { 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, diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a97eb7fb2235..2d6e9b2c68c5 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4849,13 +4849,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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; } /** diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 1b20c4462333..02e281f512a2 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -37,6 +37,7 @@ 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_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.Message; 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.InsetsState.InternalInsetsType; 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; @@ -1395,29 +1393,15 @@ public class DisplayPolicy { * * @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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index e1fdefd026cc..e02cce4b946a 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 @@ public class RecentsAnimationController implements DeathRecipient { 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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index bbf6c7616d46..61fec0d0ead9 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.FLAG_KEEP_SCREEN_ON; 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; @@ -846,6 +847,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Slog.i(TAG, ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces"); } + // Send any pending task-info changes that were queued-up during a layout deferment + mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges"); mWmService.openSurfaceTransaction(); try { @@ -862,8 +865,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - // Send any pending task-info changes that were queued-up during a layout deferment - mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); @@ -2180,6 +2181,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // 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/Session.java b/services/core/java/com/android/server/wm/Session.java index fe7ec1659c9e..1f8daf6f361f 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 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @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 57ba915d4651..30af0eda17fc 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2319,12 +2319,9 @@ class Task extends WindowContainer<WindowContainer> { 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 +2331,10 @@ class Task extends WindowContainer<WindowContainer> { mDisplayContent.rotateBounds( newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation, newBounds); - hasNewOverrideBounds = true; - } - } - - 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 diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 3bd11ba688f2..e18219ef4f46 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.app.TaskInfo; 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 @@ class TaskChangeNotificationController { 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 @@ class TaskChangeNotificationController { 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 @@ class TaskChangeNotificationController { 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 @@ class TaskChangeNotificationController { } /** - * 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/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index b3e010872d3b..9fac3f003f70 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_FREEFORM; 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.ActivityInfo; 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 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { 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 09df71c21dac..07610ab6d546 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 @@ class TaskSnapshotSurface implements StartingSurface { } 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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 09fbce034639..673c6a5fb470 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1488,7 +1488,7 @@ public class WindowManagerService extends IWindowManager.Stub } 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 +1830,7 @@ public class WindowManagerService extends IWindowManager.Stub 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; } @@ -2595,11 +2595,12 @@ public class WindowManagerService extends IWindowManager.Stub } 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. @@ -8555,8 +8556,8 @@ public class WindowManagerService extends IWindowManager.Stub + "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 1b81914bbe7c..63a083261614 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.PixelFormat; 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } @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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub 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 c6985915de68..661118ff1990 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_SUB_WINDOW; 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; @@ -5269,7 +5270,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP 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 +5279,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // 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/jni/OWNERS b/services/core/jni/OWNERS index 995cfe9fc2de..9a8942b47428 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -12,6 +12,9 @@ per-file com_android_server_input_InputManagerService.cpp = michaelwr@google.com 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 c3e7c7a6d070..16eaa77e0822 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::hidl_vec; 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 std::vector<std::string> mWakeupReasons; 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 @@ static bool checkPowerStatsHalResultLocked(const Return<void>& ret, const char* 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 @@ static bool initializePowerStatsLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHal return false; } - return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty()); + return true; } static bool getPowerStatsHalLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMutex) { @@ -333,196 +258,6 @@ static bool getPowerStatsHalLocked() EXCLUSIVE_LOCKS_REQUIRED(gPowerStatsHalMute 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 getPowerStatsHalRailEnergyDataLocked(JNIEnv* env, jobject jrailStats } } -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 void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jr 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 @@ static const JNINativeMethod method_table[] = { 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 1194099c1145..11e4db503ec5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -120,11 +120,11 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { @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) { } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 4b1bae4d903e..3d2e5de0c19b 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_REMOVE_N 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.AccountManager; 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; 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @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 @@ -1067,30 +1085,35 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * @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); } /** @@ -15927,11 +15950,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @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 +15970,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { "Provisioning preconditions failed with result: " + result); } + final long startTime = SystemClock.elapsedRealtime(); final Set<String> nonRequiredApps = provisioningParams.isLeaveAllSystemAppsEnabled() ? Collections.emptySet() : mOverlayPackagesProvider.getNonRequiredApps( @@ -15962,8 +15987,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { "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,10 +16002,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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); @@ -15994,7 +16023,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 +16132,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 +16153,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 +16164,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - 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 +16177,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 +16195,35 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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, @@ -16208,7 +16269,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void provisionFullyManagedDevice( - FullyManagedDeviceProvisioningParams provisioningParams) { + FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) { ComponentName deviceAdmin = provisioningParams.getDeviceAdminComponentName(); Objects.requireNonNull(deviceAdmin, "admin is null."); @@ -16261,6 +16322,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .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 +16411,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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(); + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index 22866b45911d..fc1d83158801 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 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { 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 f7a826156d68..883f95d930a0 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 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { 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 b2efc71860fb..42360d82afe9 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -88,7 +88,6 @@ BinderIncrementalService* BinderIncrementalService::start(JNIEnv* env) { } 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 636be4a359a1..50cb00f1887f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -160,6 +160,7 @@ import com.android.server.pm.PackageManagerService; 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; @@ -1060,11 +1061,18 @@ public final class SystemServer implements Dumpable { 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"); } 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 21c863dde3f6..6c5c1d4b59eb 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.same 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 @@ class PackageManagerComponentLabelIconOverrideTest { .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 000000000000..4aa8abc84392 --- /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 000000000000..2ef7a1f56e76 --- /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 000000000000..78dd1c5f6990 --- /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 000000000000..e99b07144853 --- /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 000000000000..deb314764404 --- /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 000000000000..d863194d6889 --- /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 000000000000..9a3bd994eac0 --- /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 000000000000..a76d8cee582c --- /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 000000000000..a76152c9df7d --- /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 000000000000..db541f672954 --- /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 a18632b8d1b2..961fc1884cfd 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 @@ public class AppChildProcessTest { 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 9441ecf74e83..22b2f7e04069 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 @@ public class ApplicationExitInfoTest { 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 @@ public class ApplicationExitInfoTest { 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 c82db7305bd8..022fadcc6dd0 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 @@ public class CacheOomRankerTest { /* 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 @@ public class CacheOomRankerTest { 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 @@ public class CacheOomRankerTest { /* 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 @@ public class CacheOomRankerTest { 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 @@ public class CacheOomRankerTest { /* 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 @@ public class CacheOomRankerTest { 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 @@ public class CacheOomRankerTest { /* 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 @@ public class CacheOomRankerTest { 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 @@ public class CacheOomRankerTest { /* 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 @@ public class CacheOomRankerTest { 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 @@ public class CacheOomRankerTest { 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 96a44a46bbaf..d86032622e43 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 @@ public final class CachedAppOptimizerTest { 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 @@ public final class CachedAppOptimizerTest { 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 @@ public final class CachedAppOptimizerTest { 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 7daf357705bc..27825a461b40 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 @@ public class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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)); + } + for (int i = 0; i < numOfCurReceivers; i++) { + receivers.addCurReceiver(mock(BroadcastRecord.class)); } - app.isolatedEntryPoint = isolatedEntryPoint; - app.execServicesFg = execServicesFg; + providers.setLastProviderTime(lastProviderTime); return app; } @@ -1829,7 +1822,7 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 84bfc9be3d41..3b5cc887c798 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.FileDescriptor; 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { .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 @@ public class LocationProviderManagerTest { 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 99846c517b7a..07170dacb4da 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 @@ public class MockableLocationProviderTest { @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 c522541b166f..6e27b3a8166c 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.AndroidPackage 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 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { } 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 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { 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 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { 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 3b4699e70aec..8c21a39c20d1 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 @@ public class ActivityManagerInternalTest { 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 e119d52daa6c..f31400853060 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 @@ public class ActivityManagerServiceTest { } 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 @@ public class ActivityManagerServiceTest { 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 @@ public class ActivityManagerServiceTest { 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 @@ public class ActivityManagerServiceTest { @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 @@ public class ActivityManagerServiceTest { 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 @@ public class ActivityManagerServiceTest { @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 @@ public class ActivityManagerServiceTest { @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 @@ public class ActivityManagerServiceTest { 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 52c824a020ed..432f6d6c85a5 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 com.android.server.wm.WindowProcessController; 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 @@ public class AnrHelperTest { @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 @@ public class AnrHelperTest { 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 e44b0836bd18..fdf509504837 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.assertNotEquals; 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 @@ public class BatteryExternalStatsWorkerTest { 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 7355b80940a4..638b1b4c57c4 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 @@ public class OomAdjusterTests { 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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 class OomAdjusterTests { 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 class OomAdjusterTests { 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 class OomAdjusterTests { 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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { @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 @@ public class OomAdjusterTests { 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 263efa670a67..6538a17cf46a 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 org.junit.Test; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.Collections; /** * Build/Install/Run: @@ -57,6 +56,7 @@ public class ProcessRecordTests { private static ActivityManagerService sService; private ProcessRecord mProcessRecord; + private ProcessErrorStateRecord mProcessErrorState; @BeforeClass public static void setUpOnce() throws Exception { @@ -72,6 +72,8 @@ public class ProcessRecordTests { 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 class ProcessRecordTests { 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 @@ public class ProcessRecordTests { */ @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 @@ public class ProcessRecordTests { */ @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 @@ public class ProcessRecordTests { */ @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 @@ public class ProcessRecordTests { */ @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 @@ public class ProcessRecordTests { */ @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 @@ public class ProcessRecordTests { @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 45bca6829553..1328b91d03f9 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.junit.Test; 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 @@ public final class AppHibernationServiceTest { 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 @@ public final class AppHibernationServiceTest { 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 @@ public final class AppHibernationServiceTest { } @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 @@ public final class AppHibernationServiceTest { } @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 @@ public final class AppHibernationServiceTest { } @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 @@ public final class AppHibernationServiceTest { } /** - * 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 @@ public final class AppHibernationServiceTest { 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 000000000000..59f3c35f2137 --- /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/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index 95aac60f65ff..54da6436ad89 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.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Optional; + import javax.annotation.Nullable; /** @@ -43,9 +45,9 @@ import javax.annotation.Nullable; @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 @@ public final class DeviceStateManagerServiceTest { @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 @@ public final class DeviceStateManagerServiceTest { @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 @@ public final class DeviceStateManagerServiceTest { 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 @@ public final class DeviceStateManagerServiceTest { 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 @@ public final class DeviceStateManagerServiceTest { } 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 @@ public final class DeviceStateManagerServiceTest { 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 ae966aaf2b58..f0b4f1bec77b 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 @@ public class BrightnessMappingStrategyTest { } @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 e02a46af3849..d36279109254 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { } @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 000000000000..35014dcb7492 --- /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 c10cee9b4c3d..86054e48fe6d 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 @@ public final class PersistentSystemFontConfigTest { 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 @@ public final class PersistentSystemFontConfigTest { 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 833103142ccf..cb83b0f7f2f7 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 static org.junit.Assert.fail; 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.Test; 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { } @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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 @@ public final class UpdatableFontDirTest { 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 62088015cbd5..263cf48a8a18 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 @@ public class WorkCountTrackerTest { 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 @@ public class WorkCountTrackerTest { } 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 @@ public class WorkCountTrackerTest { 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 @@ public class WorkCountTrackerTest { 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 @@ public class WorkCountTrackerTest { /* 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/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java index 90edaef4294f..709b009c2feb 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 @@ public class KeySetManagerServiceTest extends AndroidTestCase { 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 4ce1bbc0d017..558fb309ad98 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 @@ public class PackageManagerServiceTest { // 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 333ec9295b93..59458e8df118 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.assertSame; 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 androidx.test.runner.AndroidJUnit4; 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.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.UUID; @RunWith(AndroidJUnit4.class) @SmallTest @@ -94,10 +96,14 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 class PackageManagerSettingsTests { 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 class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { @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 @@ public class PackageManagerSettingsTests { @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 @@ public class PackageManagerSettingsTests { @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 @@ public class PackageManagerSettingsTests { @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 @@ public class PackageManagerSettingsTests { @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 class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 0, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); final PackageSetting testPkgSetting01 = new PackageSetting(origPkgSetting01); verifySettingCopy(origPkgSetting01, testPkgSetting01); } @@ -606,7 +584,8 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 0, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); testPkgSetting01.copyFrom(origPkgSetting01); verifySettingCopy(origPkgSetting01, testPkgSetting01); } @@ -648,7 +628,8 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { /** 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 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); fail("Expected a PackageManagerException"); } catch (PackageManagerException expected) { } @@ -752,7 +732,8 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { /** 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { 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 @@ public class PackageManagerSettingsTests { sharedUserId, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); } private PackageSetting createPackageSetting(String packageName) { @@ -1024,7 +1005,8 @@ public class PackageManagerSettingsTests { 0, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, - null /*mimeGroups*/); + null /*mimeGroups*/, + UUID.randomUUID()); } private @NonNull List<UserInfo> createFakeUsers() { @@ -1212,6 +1194,12 @@ public class PackageManagerSettingsTests { 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 90c29824409f..e6a238a775c6 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 @@ public class PackageParserTest { } 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 84551c51052c..f75751bf54ae 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.ArraySet; 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 @@ public class PackageSettingBuilder { 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 @@ public class PackageSettingBuilder { 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 90658055ad6f..27f3eec655e3 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 @@ public class PackageSignaturesTest { 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 1cfbad93c2e5..938e4cc84e62 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 @@ public class PackageUserStateTest { 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 d8c3979c9cf9..b5add849c2dc 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.platform.test.annotations.Presubmit; 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 @@ public class ScanTests { 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/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java index 0bea5845d293..4f36c8ae99ae 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 android.hardware.input.InputManagerInternal; 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 @@ import java.util.List; * 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 @@ public final class DeviceStateProviderImplTest { 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 @@ public final class DeviceStateProviderImplTest { 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 @@ public final class DeviceStateProviderImplTest { + " </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 @@ public final class DeviceStateProviderImplTest { 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 @@ public final class DeviceStateProviderImplTest { 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 @@ public final class DeviceStateProviderImplTest { 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 @@ public final class DeviceStateProviderImplTest { + " </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 @@ public final class DeviceStateProviderImplTest { + " </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 @@ public final class DeviceStateProviderImplTest { 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 @@ public final class DeviceStateProviderImplTest { 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 @@ public final class DeviceStateProviderImplTest { 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 @@ public final class DeviceStateProviderImplTest { 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 84b690f01b02..03e60afe4489 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 static org.junit.Assert.fail; 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 @@ public class PowerStatsServiceTest { 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 @@ public class PowerStatsServiceTest { 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 @@ public class PowerStatsServiceTest { } @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 @@ public class PowerStatsServiceTest { 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 @@ public class PowerStatsServiceTest { 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 @@ public class PowerStatsServiceTest { 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 4c82818f71e4..c6e35cf84355 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 @@ import org.mockito.stubbing.Stubber 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 @@ object MockitoUtils { } } } + + // 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> spyThrowOnUnmocked(value: T?, block: T.() -> Unit = {}): 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/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index de2cc761c2c5..42b080e69211 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1715,9 +1715,8 @@ public class ActivityRecordTests extends WindowTestsBase { 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/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 0afdc589ad4e..b1ea4a58d0f1 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.doNothing; 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 class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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, - 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, + 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))); 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 409bad416cf8..c6be987802b5 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 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { 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 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { 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/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index d49956a55d21..9372530f0e80 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 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { 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/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index b73ef9b794e4..be5fae488d5e 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -18,6 +18,7 @@ package android.telecom; 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 @@ public final class ConnectionRequest implements Parcelable { * 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 5c75a2f05195..3b06fd3d4ea1 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -2459,7 +2459,7 @@ public abstract class ConnectionService extends Service { } 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 6dc096daf4ea..88ef1b09f6b1 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -333,6 +333,8 @@ interface ITelecomService { void cleanupStuckCalls(); + void resetCarMode(); + void setTestDefaultCallRedirectionApp(String packageName); void setTestPhoneAcctSuggestionComponent(String flattenedComponentName); diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java index fb659490d546..6bf992e64480 100644 --- a/telephony/java/android/telephony/ims/DelegateStateCallback.java +++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java @@ -18,6 +18,7 @@ package android.telephony.ims; 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 @@ public interface DelegateStateCallback { * 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 f444c628cee3..31ba8530e623 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant; 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { } /** - * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 2e9eb94605a5..04421c9a2449 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; 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 @@ import java.util.concurrent.Executor; * 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 @@ public class SipDelegateManager { * {@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 481e7f8b37b9..b99d8a7d6d38 100644 --- a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl @@ -26,4 +26,5 @@ import java.util.List; 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 a14199365b07..8cc8020df29a 100644 --- a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl @@ -30,6 +30,7 @@ import java.util.Map; 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 22985d0cf85c..65415ea441b5 100644 --- a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java @@ -34,10 +34,11 @@ public class RcsPublishResponseAidlWrapper implements PublishResponseCallback { } @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 @@ public class RcsPublishResponseAidlWrapper implements PublishResponseCallback { 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 1fb339c0cf89..11118c0617c2 100644 --- a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java @@ -40,10 +40,11 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac } @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 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac 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 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac try { mResponseBinder.onNotifyCapabilitiesUpdate(pidfXmls); } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } @@ -69,6 +83,7 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac try { mResponseBinder.onResourceTerminated(getTerminatedReasonList(uriTerminatedReason)); } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } @@ -90,6 +105,7 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac 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/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index 7eba709a11da..ec98be6e5062 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -140,6 +140,9 @@ public class RcsCapabilityExchangeImplBase { * 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 @@ public class RcsCapabilityExchangeImplBase { */ 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 @@ public class RcsCapabilityExchangeImplBase { * {@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 @@ public class RcsCapabilityExchangeImplBase { @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 000000000000..34ac813f02e0 --- /dev/null +++ b/tests/UpdatableSystemFontTest/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 24939 + +include /graphics/java/android/graphics/fonts/OWNERS diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index eac8c292bed4..c7554f6b79b6 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -8355,13 +8355,14 @@ public class ConnectivityServiceTest { 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 +8411,7 @@ public class ConnectivityServiceTest { 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 +8421,7 @@ public class ConnectivityServiceTest { 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 +8432,7 @@ public class ConnectivityServiceTest { 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 e590fb76f2ec..a10a3c81bc86 100644 --- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt +++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt @@ -55,7 +55,7 @@ class LegacyTypeTrackerTest { 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 @@ class LegacyTypeTrackerTest { 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/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index e7d334ebd490..e32e1e831f83 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -184,7 +184,7 @@ public class VcnManagementServiceTest { 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()); final PersistableBundle bundle = PersistableBundleUtils.fromMap( @@ -304,14 +304,17 @@ public class VcnManagementServiceTest { @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)); } @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 +322,7 @@ public class VcnManagementServiceTest { mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); mTestLooper.dispatchAll(); verify(vcn).teardownAsynchronously(); + verify(mMockPolicyListener).onPolicyChanged(); } @Test @@ -389,6 +393,7 @@ public class VcnManagementServiceTest { 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 +405,7 @@ public class VcnManagementServiceTest { 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 +415,7 @@ public class VcnManagementServiceTest { 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 +480,12 @@ public class VcnManagementServiceTest { 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)); // Verify Vcn is updated if it was previously started mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); @@ -520,7 +532,7 @@ public class VcnManagementServiceTest { 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 +541,7 @@ public class VcnManagementServiceTest { } @Test - public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception { + public void testGetUnderlyingNetworkPolicyCellular() throws Exception { setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); NetworkCapabilities nc = @@ -542,12 +554,12 @@ public class VcnManagementServiceTest { 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 +575,7 @@ public class VcnManagementServiceTest { mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); assertFalse(policy.isTeardownRequested()); - verifyMergedNetworkCapabilities( + verifyMergedNetworkCapabilitiesIsVcnManaged( policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI); } @@ -591,4 +603,35 @@ public class VcnManagementServiceTest { 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(); + } } diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java index 48e068d14182..1d459a347526 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.net.TelephonyNetworkSpecifier; 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 org.mockito.MockitoAnnotations; 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 @@ public class UnderlyingNetworkTrackerTest { @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 @@ public class UnderlyingNetworkTrackerTest { 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 @@ public class UnderlyingNetworkTrackerTest { 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)); + } + + 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)); + } + } - verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP)); + @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 000000000000..e20070ee4f07 --- /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 4ecd21503165..fbaae6f534a9 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java @@ -44,7 +44,8 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect @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, 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 d741e5cf4b35..bc6bee28d14f 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 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 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.annotation.NonNull; -import android.content.Context; +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 @@ import java.util.UUID; /** 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 @@ public class VcnGatewayConnectionTest { 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); } - @Test - public void testBuildNetworkCapabilities() throws Exception { - final NetworkCapabilities caps = + 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()); + VcnGatewayConnectionConfigTest.buildTestConfig(), record); - for (int exposedCapability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) { - assertTrue(caps.hasCapability(exposedCapability)); + 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 testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception { + verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI); + } + + @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 4d92fb9c42f2..df1341cce20f 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 static org.mockito.Mockito.verify; 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,21 @@ import android.os.ParcelUuid; import android.os.test.TestLooper; import com.android.server.IpSecService; +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; 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,6 +71,10 @@ public class VcnGatewayConnectionTestBase { 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; @@ -94,7 +106,7 @@ public class VcnGatewayConnectionTestBase { doReturn(mUnderlyingNetworkTracker) .when(mDeps) - .newUnderlyingNetworkTracker(any(), any(), any(), any()); + .newUnderlyingNetworkTracker(any(), any(), any(), any(), any()); } @Before @@ -109,7 +121,13 @@ public class VcnGatewayConnectionTestBase { 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, 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 000000000000..0c1df763a08e --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java @@ -0,0 +1,129 @@ +/* + * 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.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.verify; + +import android.content.Context; +import android.net.NetworkRequest; +import android.net.vcn.VcnConfig; +import android.net.vcn.VcnGatewayConnectionConfigTest; +import android.os.ParcelUuid; +import android.os.test.TestLooper; + +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +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 Vcn.Dependencies mDeps; + + private TestLooper mTestLooper; + 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); + 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()); + + mConfig = + new VcnConfig.Builder(mContext) + .addGatewayConnectionConfig( + VcnGatewayConnectionConfigTest.buildTestConfig()) + .build(); + + mVcn = new Vcn(mVcnContext, TEST_SUB_GROUP, mConfig, mSubscriptionSnapshot, mDeps); + } + + private NetworkRequestListener verifyAndGetRequestListener() { + ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor = + ArgumentCaptor.forClass(NetworkRequestListener.class); + verify(mVcnNetworkProvider).registerListener(mNetworkRequestListenerCaptor.capture()); + + return mNetworkRequestListenerCaptor.getValue(); + } + + private NetworkRequest getNetworkRequestWithCapabilities(int[] networkCapabilities) { + final NetworkRequest.Builder builder = new NetworkRequest.Builder(); + for (final int netCapability : networkCapabilities) { + builder.addCapability(netCapability); + } + return builder.build(); + } + + @Test + public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() { + final NetworkRequestListener requestListener = verifyAndGetRequestListener(); + + requestListener.onNetworkRequested( + getNetworkRequestWithCapabilities(VcnGatewayConnectionConfigTest.EXPOSED_CAPS), + NETWORK_SCORE, + PROVIDER_ID); + mTestLooper.dispatchAll(); + + 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)); + } + } +} diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java index 21409542714d..04f9bf26b43c 100644 --- a/tools/powerstats/PowerStatsServiceProtoParser.java +++ b/tools/powerstats/PowerStatsServiceProtoParser.java @@ -86,6 +86,12 @@ public class PowerStatsServiceProtoParser { 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); } |