diff options
246 files changed, 9206 insertions, 5399 deletions
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index ed55f0056ea0..3bbc945c8b87 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -64,7 +64,7 @@ public class AppSearchManagerService extends SystemService { public void onStart() { publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); - mImplInstanceManager = new ImplInstanceManager(getContext()); + mImplInstanceManager = ImplInstanceManager.getInstance(getContext()); } private class Stub extends IAppSearchManager.Stub { @@ -102,7 +102,8 @@ public class AppSearchManagerService extends SystemService { } schemasPackageAccessible.put(entry.getKey(), packageIdentifiers); } - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.setSchema( packageName, databaseName, @@ -133,7 +134,8 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyCallingPackage(callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName); List<Bundle> schemaBundles = new ArrayList<>(schemas.size()); for (int i = 0; i < schemas.size(); i++) { @@ -166,7 +168,8 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); for (int i = 0; i < documentBundles.size(); i++) { GenericDocument document = new GenericDocument(documentBundles.get(i)); try { @@ -207,12 +210,18 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder<String, Bundle> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { - GenericDocument document = impl.getDocument(packageName, databaseName, - namespace, uri, typePropertyPaths); + GenericDocument document = + impl.getDocument( + packageName, + databaseName, + namespace, + uri, + typePropertyPaths); resultBuilder.setSuccess(uri, document.getBundle()); } catch (Throwable t) { resultBuilder.setResult(uri, throwableToFailedResult(t)); @@ -245,7 +254,8 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyCallingPackage(callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); SearchResultPage searchResultPage = impl.query( packageName, @@ -278,12 +288,14 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyCallingPackage(callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); - SearchResultPage searchResultPage = impl.globalQuery( - queryExpression, - new SearchSpec(searchSpecBundle), - packageName, - callingUid); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + SearchResultPage searchResultPage = + impl.globalQuery( + queryExpression, + new SearchSpec(searchSpecBundle), + packageName, + callingUid); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); @@ -306,7 +318,8 @@ public class AppSearchManagerService extends SystemService { // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally // opened it try { - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); SearchResultPage searchResultPage = impl.getNextPage(nextPageToken); invokeCallbackOnResult( callback, @@ -324,7 +337,8 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.invalidateNextPageToken(nextPageToken); } catch (Throwable t) { Log.e(TAG, "Unable to invalidate the query page token", t); @@ -350,15 +364,11 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); - impl.reportUsage( - packageName, - databaseName, - namespace, - uri, - usageTimeMillis); - invokeCallbackOnResult(callback, - AppSearchResult.newSuccessfulResult(/*result=*/ null)); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis); + invokeCallbackOnResult( + callback, AppSearchResult.newSuccessfulResult(/*result=*/ null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); } finally { @@ -385,7 +395,8 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { @@ -421,7 +432,8 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyCallingPackage(callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.removeByQuery( packageName, databaseName, @@ -441,7 +453,8 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.persistToDisk(); } catch (Throwable t) { Log.e(TAG, "Unable to persist the data to disk", t); @@ -457,7 +470,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - mImplInstanceManager.getInstance(callingUserId); + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java index fe3c2e1d1604..97b1a8cd6d50 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java @@ -41,14 +41,33 @@ import java.io.File; public final class ImplInstanceManager { private static final String APP_SEARCH_DIR = "appSearch"; - private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>(); + private static ImplInstanceManager sImplInstanceManager; - private final Context mContext; + private final SparseArray<AppSearchImpl> mInstances = new SparseArray<>(); private final String mGlobalQuerierPackage; - public ImplInstanceManager(@NonNull Context context) { - mContext = context; - mGlobalQuerierPackage = getGlobalAppSearchDataQuerierPackageName(mContext); + private ImplInstanceManager(@NonNull String globalQuerierPackage) { + mGlobalQuerierPackage = globalQuerierPackage; + } + + /** + * Gets an instance of ImplInstanceManager to be used. + * + * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the + * existing instance will be returned. + */ + @NonNull + public static ImplInstanceManager getInstance(@NonNull Context context) { + if (sImplInstanceManager == null) { + synchronized (ImplInstanceManager.class) { + if (sImplInstanceManager == null) { + sImplInstanceManager = + new ImplInstanceManager( + getGlobalAppSearchDataQuerierPackageName(context)); + } + } + } + return sImplInstanceManager; } /** @@ -57,30 +76,30 @@ public final class ImplInstanceManager { * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will * be created. * + * @param context The context * @param userId The multi-user userId of the device user calling AppSearch * @return An initialized {@link AppSearchImpl} for this user */ @NonNull - public AppSearchImpl getInstance(@UserIdInt int userId) + public AppSearchImpl getAppSearchImpl(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { - AppSearchImpl instance = sInstances.get(userId); + AppSearchImpl instance = mInstances.get(userId); if (instance == null) { synchronized (ImplInstanceManager.class) { - instance = sInstances.get(userId); + instance = mInstances.get(userId); if (instance == null) { - instance = createImpl(userId); - sInstances.put(userId, instance); + instance = createImpl(context, userId); + mInstances.put(userId, instance); } } } return instance; } - private AppSearchImpl createImpl(@UserIdInt int userId) + private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { - File appSearchDir = getAppSearchDir(mContext, userId); - return AppSearchImpl.create( - appSearchDir, mContext, userId, mGlobalQuerierPackage); + File appSearchDir = getAppSearchDir(context, userId); + return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage); } private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) { @@ -96,7 +115,8 @@ public final class ImplInstanceManager { * * @param context Context of the system service. */ - private static String getGlobalAppSearchDataQuerierPackageName(Context context) { + @NonNull + private static String getGlobalAppSearchDataQuerierPackageName(@NonNull Context context) { String globalAppSearchDataQuerierPackage = context.getString(R.string.config_globalAppSearchDataQuerierPackage); try { diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index d4ee9bb813c9..f6a1b8af9e49 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -151,7 +151,8 @@ public class AlarmManagerService extends SystemService { static final boolean DEBUG_BG_LIMIT = localLOGV || false; static final boolean DEBUG_STANDBY = localLOGV || false; static final boolean RECORD_ALARMS_IN_HISTORY = true; - static final boolean RECORD_DEVICE_IDLE_ALARMS = false; + // TODO(b/178484639) : Turn off once alarms and reminders work is complete. + static final boolean RECORD_DEVICE_IDLE_ALARMS = true; static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; static final int TICK_HISTORY_DEPTH = 10; @@ -1731,8 +1732,8 @@ public class AlarmManagerService extends SystemService { if (RECORD_DEVICE_IDLE_ALARMS) { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = a.uid; - ent.pkg = a.operation.getCreatorPackage(); - ent.tag = a.operation.getTag(""); + ent.pkg = a.sourcePackage; + ent.tag = a.statsTag; ent.op = "START IDLE"; ent.elapsedRealtime = mInjector.getElapsedRealtime(); ent.argRealtime = a.getWhenElapsed(); @@ -3151,8 +3152,8 @@ public class AlarmManagerService extends SystemService { if (RECORD_DEVICE_IDLE_ALARMS) { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = alarm.uid; - ent.pkg = alarm.operation.getCreatorPackage(); - ent.tag = alarm.operation.getTag(""); + ent.pkg = alarm.sourcePackage; + ent.tag = alarm.statsTag; ent.op = "END IDLE"; ent.elapsedRealtime = mInjector.getElapsedRealtime(); ent.argRealtime = alarm.getWhenElapsed(); 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 7cad4ab0183d..e05f0b062dbe 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -16,6 +16,8 @@ package com.android.server.job; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.job.JobInfo; import android.content.BroadcastReceiver; @@ -26,8 +28,11 @@ import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; import android.provider.DeviceConfig; +import android.util.ArraySet; import android.util.IndentingPrintWriter; +import android.util.Pair; import android.util.Slog; +import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -36,16 +41,17 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.util.StatLogger; import com.android.server.JobSchedulerBackgroundThread; -import com.android.server.job.JobSchedulerService.MaxJobCountsPerMemoryTrimLevel; import com.android.server.job.controllers.JobStatus; import com.android.server.job.controllers.StateController; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Iterator; import java.util.List; /** - * This class decides, given the various configuration and the system status, how many more jobs - * can start. + * This class decides, given the various configuration and the system status, which jobs can start + * and which {@link JobServiceContext} to run each job on. */ class JobConcurrencyManager { private static final String TAG = JobSchedulerService.TAG; @@ -56,9 +62,22 @@ class JobConcurrencyManager { CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms"; private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000; + static final int WORK_TYPE_NONE = 0; + static final int WORK_TYPE_TOP = 1 << 0; + static final int WORK_TYPE_BG = 1 << 1; + private static final int NUM_WORK_TYPES = 2; + + @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = { + WORK_TYPE_NONE, + WORK_TYPE_TOP, + WORK_TYPE_BG + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WorkType { + } + private final Object mLock; private final JobSchedulerService mService; - private final JobSchedulerService.Constants mConstants; private final Context mContext; private final Handler mHandler; @@ -72,6 +91,53 @@ class JobConcurrencyManager { private static final int MAX_JOB_CONTEXTS_COUNT = JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; + private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON = + new WorkConfigLimitsPerMemoryTrimLevel( + new WorkTypeConfig("screen_on_normal", 8, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, 6))), + new WorkTypeConfig("screen_on_moderate", 8, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, 4))), + new WorkTypeConfig("screen_on_low", 5, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, 1))), + new WorkTypeConfig("screen_on_critical", 5, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, 1))) + ); + private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF = + new WorkConfigLimitsPerMemoryTrimLevel( + new WorkTypeConfig("screen_off_normal", 10, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, 6))), + new WorkTypeConfig("screen_off_moderate", 10, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, 4))), + new WorkTypeConfig("screen_off_low", 5, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, 1))), + new WorkTypeConfig("screen_off_critical", 5, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, 1))) + ); + /** * This array essentially stores the state of mActiveServices array. * The ith index stores the job present on the ith JobServiceContext. @@ -84,10 +150,11 @@ class JobConcurrencyManager { int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; - /** Max job counts according to the current system state. */ - private JobSchedulerService.MaxJobCounts mMaxJobCounts; + int[] mRecycledWorkTypeForContext = new int[MAX_JOB_CONTEXTS_COUNT]; + + private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>(); - private final JobCountTracker mJobCountTracker = new JobCountTracker(); + private final WorkCountTracker mWorkCountTracker = new WorkCountTracker(); /** Wait for this long after screen off before adjusting the job concurrency. */ private long mScreenOffAdjustmentDelayMs = DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS; @@ -114,7 +181,6 @@ class JobConcurrencyManager { JobConcurrencyManager(JobSchedulerService service) { mService = service; mLock = mService.mLock; - mConstants = service.mConstants; mContext = service.getContext(); mHandler = JobSchedulerBackgroundThread.getHandler(); @@ -209,12 +275,6 @@ class JobConcurrencyManager { } } - private boolean isFgJob(JobStatus job) { - // (It's super confusing PRIORITY_BOUND_FOREGROUND_SERVICE isn't FG here) - return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP - || job.shouldTreatAsExpeditedJob(); - } - @GuardedBy("mLock") private void refreshSystemStateLocked() { final long nowUptime = JobSchedulerService.sUptimeMillisClock.millis(); @@ -237,28 +297,29 @@ class JobConcurrencyManager { } @GuardedBy("mLock") - private void updateMaxCountsLocked() { + private void updateCounterConfigLocked() { refreshSystemStateLocked(); - final MaxJobCountsPerMemoryTrimLevel jobCounts = mEffectiveInteractiveState - ? mConstants.MAX_JOB_COUNTS_SCREEN_ON - : mConstants.MAX_JOB_COUNTS_SCREEN_OFF; - + final WorkConfigLimitsPerMemoryTrimLevel workConfigs = mEffectiveInteractiveState + ? CONFIG_LIMITS_SCREEN_ON : CONFIG_LIMITS_SCREEN_OFF; + WorkTypeConfig workTypeConfig; switch (mLastMemoryTrimLevel) { case ProcessStats.ADJ_MEM_FACTOR_MODERATE: - mMaxJobCounts = jobCounts.moderate; + workTypeConfig = workConfigs.moderate; break; case ProcessStats.ADJ_MEM_FACTOR_LOW: - mMaxJobCounts = jobCounts.low; + workTypeConfig = workConfigs.low; break; case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: - mMaxJobCounts = jobCounts.critical; + workTypeConfig = workConfigs.critical; break; default: - mMaxJobCounts = jobCounts.normal; + workTypeConfig = workConfigs.normal; break; } + + mWorkCountTracker.setConfig(workTypeConfig); } /** @@ -282,31 +343,26 @@ class JobConcurrencyManager { Slog.d(TAG, printPendingQueueLocked()); } - final JobPackageTracker tracker = mService.mJobPackageTracker; final List<JobStatus> pendingJobs = mService.mPendingJobs; final List<JobServiceContext> activeServices = mService.mActiveServices; - final List<StateController> controllers = mService.mControllers; - - updateMaxCountsLocked(); // To avoid GC churn, we recycle the arrays. JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap; boolean[] slotChanged = mRecycledSlotChanged; int[] preferredUidForContext = mRecycledPreferredUidForContext; + int[] workTypeForContext = mRecycledWorkTypeForContext; + updateCounterConfigLocked(); + // Reset everything since we'll re-evaluate the current state. + mWorkCountTracker.resetCounts(); - // Initialize the work variables and also count running jobs. - mJobCountTracker.reset( - mMaxJobCounts.getMaxTotal(), - mMaxJobCounts.getMaxBg(), - mMaxJobCounts.getMinBg()); - - for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { + for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { final JobServiceContext js = mService.mActiveServices.get(i); final JobStatus status = js.getRunningJobLocked(); if ((contextIdToJobMap[i] = status) != null) { - mJobCountTracker.incrementRunningJobCount(isFgJob(status)); + mWorkCountTracker.incrementRunningJobCount(js.getRunningJobWorkType()); + workTypeForContext[i] = js.getRunningJobWorkType(); } slotChanged[i] = false; @@ -317,41 +373,25 @@ class JobConcurrencyManager { } // Next, update the job priorities, and also count the pending FG / BG jobs. - for (int i = 0; i < pendingJobs.size(); i++) { - final JobStatus pending = pendingJobs.get(i); + updateNonRunningPriorities(pendingJobs, true); - // If job is already running, go to next job. - int jobRunningContext = findJobContextIdFromMap(pending, contextIdToJobMap); - if (jobRunningContext != -1) { - continue; - } - - final int priority = mService.evaluateJobPriorityLocked(pending); - pending.lastEvaluatedPriority = priority; - - mJobCountTracker.incrementPendingJobCount(isFgJob(pending)); - } - - mJobCountTracker.onCountDone(); + mWorkCountTracker.onCountDone(); for (int i = 0; i < pendingJobs.size(); i++) { final JobStatus nextPending = pendingJobs.get(i); - // Unfortunately we need to repeat this relatively expensive check. - int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); - if (jobRunningContext != -1) { + if (mRunningJobs.contains(nextPending)) { continue; } // TODO(171305774): make sure HPJs aren't pre-empted and add dedicated contexts for them - final boolean isPendingFg = isFgJob(nextPending); - // Find an available slot for nextPending. The context should be available OR // it should have lowest priority among all running jobs // (sharing the same Uid as nextPending) int minPriorityForPreemption = Integer.MAX_VALUE; int selectedContextId = -1; + int workType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending)); boolean startingJob = false; for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { JobStatus job = contextIdToJobMap[j]; @@ -360,7 +400,7 @@ class JobConcurrencyManager { final boolean preferredUidOkay = (preferredUid == nextPending.getUid()) || (preferredUid == JobServiceContext.NO_PREFERRED_UID); - if (preferredUidOkay && mJobCountTracker.canJobStart(isPendingFg)) { + if (preferredUidOkay && workType != WORK_TYPE_NONE) { // This slot is free, and we haven't yet hit the limit on // concurrent jobs... we can just throw the job in to here. selectedContextId = j; @@ -396,19 +436,19 @@ class JobConcurrencyManager { } if (startingJob) { // Increase the counters when we're going to start a job. - mJobCountTracker.onStartingNewJob(isPendingFg); + workTypeForContext[selectedContextId] = workType; + mWorkCountTracker.stageJob(workType); } } if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); } - mJobCountTracker.logStatus(); - - tracker.noteConcurrency(mJobCountTracker.getTotalRunningJobCountToNote(), - mJobCountTracker.getFgRunningJobCountToNote()); + if (DEBUG) { + Slog.d(TAG, "assignJobsToContexts: " + mWorkCountTracker.toString()); + } - for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { + for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { boolean preservePreferredUid = false; if (slotChanged[i]) { JobStatus js = activeServices.get(i).getRunningJobLocked(); @@ -426,30 +466,58 @@ class JobConcurrencyManager { Slog.d(TAG, "About to run job on context " + i + ", job: " + pendingJob); } - for (int ic=0; ic<controllers.size(); ic++) { - controllers.get(ic).prepareForExecutionLocked(pendingJob); - } - if (!activeServices.get(i).executeRunnableJob(pendingJob)) { - Slog.d(TAG, "Error executing " + pendingJob); - } - if (pendingJobs.remove(pendingJob)) { - tracker.noteNonpending(pendingJob); - } + startJobLocked(activeServices.get(i), pendingJob, workTypeForContext[i]); } } if (!preservePreferredUid) { activeServices.get(i).clearPreferredUid(); } } + mWorkCountTracker.resetStagingCount(); + noteConcurrency(); } - private static int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) { - for (int i=0; i<map.length; i++) { - if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) { - return i; + private void noteConcurrency() { + mService.mJobPackageTracker.noteConcurrency(mRunningJobs.size(), + // TODO: log per type instead of only TOP + mWorkCountTracker.getRunningJobCount(WORK_TYPE_TOP)); + } + + private void updateNonRunningPriorities(@NonNull final List<JobStatus> pendingJobs, + boolean updateCounter) { + for (int i = 0; i < pendingJobs.size(); i++) { + final JobStatus pending = pendingJobs.get(i); + + // If job is already running, go to next job. + if (mRunningJobs.contains(pending)) { + continue; + } + + pending.lastEvaluatedPriority = mService.evaluateJobPriorityLocked(pending); + + if (updateCounter) { + mWorkCountTracker.incrementPendingJobCount(getJobWorkTypes(pending)); } } - return -1; + } + + private void startJobLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus, + @WorkType final int workType) { + final List<StateController> controllers = mService.mControllers; + for (int ic = 0; ic < controllers.size(); ic++) { + controllers.get(ic).prepareForExecutionLocked(jobStatus); + } + if (!worker.executeRunnableJob(jobStatus, workType)) { + Slog.e(TAG, "Error executing " + jobStatus); + mWorkCountTracker.onStagedJobFailed(workType); + } else { + mRunningJobs.add(jobStatus); + mWorkCountTracker.onJobStarted(workType); + } + final List<JobStatus> pendingJobs = mService.mPendingJobs; + if (pendingJobs.remove(jobStatus)) { + mService.mJobPackageTracker.noteNonpending(jobStatus); + } } @GuardedBy("mLock") @@ -484,6 +552,16 @@ class JobConcurrencyManager { mScreenOffAdjustmentDelayMs = properties.getLong( KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS); + + CONFIG_LIMITS_SCREEN_ON.normal.update(properties); + CONFIG_LIMITS_SCREEN_ON.moderate.update(properties); + CONFIG_LIMITS_SCREEN_ON.low.update(properties); + CONFIG_LIMITS_SCREEN_ON.critical.update(properties); + + CONFIG_LIMITS_SCREEN_OFF.normal.update(properties); + CONFIG_LIMITS_SCREEN_OFF.moderate.update(properties); + CONFIG_LIMITS_SCREEN_OFF.low.update(properties); + CONFIG_LIMITS_SCREEN_OFF.critical.update(properties); } public void dumpLocked(IndentingPrintWriter pw, long now, long nowRealtime) { @@ -494,6 +572,14 @@ class JobConcurrencyManager { pw.print("Configuration:"); pw.increaseIndent(); pw.print(KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, mScreenOffAdjustmentDelayMs).println(); + CONFIG_LIMITS_SCREEN_ON.normal.dump(pw); + CONFIG_LIMITS_SCREEN_ON.moderate.dump(pw); + CONFIG_LIMITS_SCREEN_ON.low.dump(pw); + CONFIG_LIMITS_SCREEN_ON.critical.dump(pw); + CONFIG_LIMITS_SCREEN_OFF.normal.dump(pw); + CONFIG_LIMITS_SCREEN_OFF.moderate.dump(pw); + CONFIG_LIMITS_SCREEN_OFF.low.dump(pw); + CONFIG_LIMITS_SCREEN_OFF.critical.dump(pw); pw.decreaseIndent(); pw.print("Screen state: current "); @@ -514,7 +600,7 @@ class JobConcurrencyManager { pw.println("Current max jobs:"); pw.println(" "); - pw.println(mJobCountTracker); + pw.println(mWorkCountTracker); pw.println(); @@ -540,8 +626,6 @@ class JobConcurrencyManager { proto.write(JobConcurrencyManagerProto.TIME_SINCE_LAST_SCREEN_OFF_MS, nowRealtime - mLastScreenOffRealtime); - mJobCountTracker.dumpProto(proto, JobConcurrencyManagerProto.JOB_COUNT_TRACKER); - proto.write(JobConcurrencyManagerProto.MEMORY_TRIM_LEVEL, mLastMemoryTrimLevel); mStatLogger.dumpProto(proto, JobConcurrencyManagerProto.STATS); @@ -549,199 +633,312 @@ class JobConcurrencyManager { proto.end(token); } - /** - * This class decides, taking into account {@link #mMaxJobCounts} and how mny jos are running / - * pending, how many more job can start. - * - * Extracted for testing and logging. - */ + int getJobWorkTypes(@NonNull JobStatus js) { + int classification = 0; + // TODO(171305774): create dedicated work type for EJ and FGS + if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP + || js.shouldTreatAsExpeditedJob()) { + classification |= WORK_TYPE_TOP; + } else { + classification |= WORK_TYPE_BG; + } + return classification; + } + @VisibleForTesting - static class JobCountTracker { - private int mConfigNumMaxTotalJobs; - private int mConfigNumMaxBgJobs; - private int mConfigNumMinBgJobs; + static class WorkTypeConfig { + private static final String KEY_PREFIX_MAX_TOTAL = + CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_"; + private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_"; + private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_"; + private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_"; + private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_"; + private final String mConfigIdentifier; + + private int mMaxTotal; + private final SparseIntArray mMinReservedSlots = new SparseIntArray(NUM_WORK_TYPES); + private final SparseIntArray mMaxAllowedSlots = new SparseIntArray(NUM_WORK_TYPES); + private final int mDefaultMaxTotal; + private final SparseIntArray mDefaultMinReservedSlots = new SparseIntArray(NUM_WORK_TYPES); + private final SparseIntArray mDefaultMaxAllowedSlots = new SparseIntArray(NUM_WORK_TYPES); + + WorkTypeConfig(@NonNull String configIdentifier, int defaultMaxTotal, + List<Pair<Integer, Integer>> defaultMin, List<Pair<Integer, Integer>> defaultMax) { + mConfigIdentifier = configIdentifier; + mDefaultMaxTotal = mMaxTotal = defaultMaxTotal; + for (int i = defaultMin.size() - 1; i >= 0; --i) { + mDefaultMinReservedSlots.put(defaultMin.get(i).first, defaultMin.get(i).second); + } + for (int i = defaultMax.size() - 1; i >= 0; --i) { + mDefaultMaxAllowedSlots.put(defaultMax.get(i).first, defaultMax.get(i).second); + } + update(new DeviceConfig.Properties.Builder( + DeviceConfig.NAMESPACE_JOB_SCHEDULER).build()); + } - private int mNumRunningFgJobs; - private int mNumRunningBgJobs; + void update(@NonNull DeviceConfig.Properties properties) { + // Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT]. + mMaxTotal = Math.max(1, Math.min(MAX_JOB_CONTEXTS_COUNT, + properties.getInt(KEY_PREFIX_MAX_TOTAL + mConfigIdentifier, mDefaultMaxTotal))); + + mMaxAllowedSlots.clear(); + // Ensure they're in the range [1, total]. + final int maxTop = Math.max(1, Math.min(mMaxTotal, + properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier, + mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal)))); + mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop); + final int maxBg = Math.max(1, Math.min(mMaxTotal, + properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier, + mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal)))); + mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg); + + int remaining = mMaxTotal; + mMinReservedSlots.clear(); + // Ensure top is in the range [1, min(maxTop, total)] + final int minTop = Math.max(1, Math.min(Math.min(maxTop, mMaxTotal), + properties.getInt(KEY_PREFIX_MIN_TOP + mConfigIdentifier, + mDefaultMinReservedSlots.get(WORK_TYPE_TOP)))); + mMinReservedSlots.put(WORK_TYPE_TOP, minTop); + remaining -= minTop; + // Ensure bg is in the range [0, min(maxBg, remaining)] + final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining), + properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier, + mDefaultMinReservedSlots.get(WORK_TYPE_BG)))); + mMinReservedSlots.put(WORK_TYPE_BG, minBg); + } - private int mNumPendingFgJobs; - private int mNumPendingBgJobs; + int getMaxTotal() { + return mMaxTotal; + } - private int mNumStartingFgJobs; - private int mNumStartingBgJobs; + int getMax(@WorkType int workType) { + return mMaxAllowedSlots.get(workType, mMaxTotal); + } - private int mNumReservedForBg; - private int mNumActualMaxFgJobs; - private int mNumActualMaxBgJobs; + int getMinReserved(@WorkType int workType) { + return mMinReservedSlots.get(workType); + } - void reset(int numTotalMaxJobs, int numMaxBgJobs, int numMinBgJobs) { - mConfigNumMaxTotalJobs = numTotalMaxJobs; - mConfigNumMaxBgJobs = numMaxBgJobs; - mConfigNumMinBgJobs = numMinBgJobs; + void dump(IndentingPrintWriter pw) { + pw.print(KEY_PREFIX_MAX_TOTAL + mConfigIdentifier, mMaxTotal).println(); + pw.print(KEY_PREFIX_MIN_TOP + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_TOP)) + .println(); + pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP)) + .println(); + pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG)) + .println(); + pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG)) + .println(); + } + } - mNumRunningFgJobs = 0; - mNumRunningBgJobs = 0; + /** {@link WorkTypeConfig} for each memory trim level. */ + static class WorkConfigLimitsPerMemoryTrimLevel { + public final WorkTypeConfig normal; + public final WorkTypeConfig moderate; + public final WorkTypeConfig low; + public final WorkTypeConfig critical; + + WorkConfigLimitsPerMemoryTrimLevel(WorkTypeConfig normal, WorkTypeConfig moderate, + WorkTypeConfig low, WorkTypeConfig critical) { + this.normal = normal; + this.moderate = moderate; + this.low = low; + this.critical = critical; + } + } - mNumPendingFgJobs = 0; - mNumPendingBgJobs = 0; + /** + * This class decides, taking into account the current {@link WorkTypeConfig} and how many jobs + * are running/pending, how many more job can start. + * + * Extracted for testing and logging. + */ + @VisibleForTesting + static class WorkCountTracker { + private int mConfigMaxTotal; + private final SparseIntArray mConfigNumReservedSlots = new SparseIntArray(NUM_WORK_TYPES); + private final SparseIntArray mConfigAbsoluteMaxSlots = new SparseIntArray(NUM_WORK_TYPES); + + /** + * Numbers may be lower in this than in {@link #mConfigNumReservedSlots} if there aren't + * enough ready jobs of a type to take up all of the desired reserved slots. + */ + private final SparseIntArray mNumActuallyReservedSlots = new SparseIntArray(NUM_WORK_TYPES); + private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES); + private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES); + private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES); + private int mNumUnspecialized = 0; + private int mNumUnspecializedRemaining = 0; + + void setConfig(@NonNull WorkTypeConfig workTypeConfig) { + mConfigMaxTotal = workTypeConfig.getMaxTotal(); + mConfigNumReservedSlots.put(WORK_TYPE_TOP, + workTypeConfig.getMinReserved(WORK_TYPE_TOP)); + mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG)); + mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP)); + mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG)); + + mNumUnspecialized = mConfigMaxTotal; + mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP); + mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG); + mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP); + mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG); + calculateUnspecializedRemaining(); + } - mNumStartingFgJobs = 0; - mNumStartingBgJobs = 0; + private void calculateUnspecializedRemaining() { + mNumUnspecializedRemaining = mNumUnspecialized; + for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) { + mNumUnspecializedRemaining -= mNumRunningJobs.valueAt(i); + } + } - mNumReservedForBg = 0; - mNumActualMaxFgJobs = 0; - mNumActualMaxBgJobs = 0; + void resetCounts() { + mNumActuallyReservedSlots.clear(); + mNumPendingJobs.clear(); + mNumRunningJobs.clear(); + resetStagingCount(); } - void incrementRunningJobCount(boolean isFg) { - if (isFg) { - mNumRunningFgJobs++; - } else { - mNumRunningBgJobs++; - } + void resetStagingCount() { + mNumStartingJobs.clear(); } - void incrementPendingJobCount(boolean isFg) { - if (isFg) { - mNumPendingFgJobs++; - } else { - mNumPendingBgJobs++; - } + void incrementRunningJobCount(@WorkType int workType) { + mNumRunningJobs.put(workType, mNumRunningJobs.get(workType) + 1); } - void onStartingNewJob(boolean isFg) { - if (isFg) { - mNumStartingFgJobs++; - } else { - mNumStartingBgJobs++; + void incrementPendingJobCount(int workTypes) { + // We don't know which type we'll classify the job as when we run it yet, so make sure + // we have space in all applicable slots. + if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) { + mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1); + } + if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) { + mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1); } } - void onCountDone() { - // Note some variables are used only here but are made class members in order to have - // them on logcat / dumpsys. - - // How many slots should we allocate to BG jobs at least? - // That's basically "getMinBg()", but if there are less jobs, decrease it. - // (e.g. even if min-bg is 2, if there's only 1 running+pending job, this has to be 1.) - final int reservedForBg = Math.min( - mConfigNumMinBgJobs, - mNumRunningBgJobs + mNumPendingBgJobs); - - // However, if there are FG jobs already running, we have to adjust it. - mNumReservedForBg = Math.min(reservedForBg, - mConfigNumMaxTotalJobs - mNumRunningFgJobs); - - // Max FG is [total - [number needed for BG jobs]] - // [number needed for BG jobs] is the bigger one of [running BG] or [reserved BG] - final int maxFg = - mConfigNumMaxTotalJobs - Math.max(mNumRunningBgJobs, mNumReservedForBg); - - // The above maxFg is the theoretical max. If there are less FG jobs, the actual - // max FG will be lower accordingly. - mNumActualMaxFgJobs = Math.min( - maxFg, - mNumRunningFgJobs + mNumPendingFgJobs); - - // Max BG is [total - actual max FG], but cap at [config max BG]. - final int maxBg = Math.min( - mConfigNumMaxBgJobs, - mConfigNumMaxTotalJobs - mNumActualMaxFgJobs); - - // If there are less BG jobs than maxBg, then reduce the actual max BG accordingly. - // This isn't needed for the logic to work, but this will give consistent output - // on logcat and dumpsys. - mNumActualMaxBgJobs = Math.min( - maxBg, - mNumRunningBgJobs + mNumPendingBgJobs); - } - - boolean canJobStart(boolean isFg) { - if (isFg) { - return mNumRunningFgJobs + mNumStartingFgJobs < mNumActualMaxFgJobs; - } else { - return mNumRunningBgJobs + mNumStartingBgJobs < mNumActualMaxBgJobs; + void stageJob(@WorkType int workType) { + final int newNumStartingJobs = mNumStartingJobs.get(workType) + 1; + mNumStartingJobs.put(workType, newNumStartingJobs); + mNumPendingJobs.put(workType, Math.max(0, mNumPendingJobs.get(workType) - 1)); + if (newNumStartingJobs + mNumRunningJobs.get(workType) + > mNumActuallyReservedSlots.get(workType)) { + mNumUnspecializedRemaining--; } } - public int getNumStartingFgJobs() { - return mNumStartingFgJobs; + void onStagedJobFailed(@WorkType int workType) { + final int oldNumStartingJobs = mNumStartingJobs.get(workType); + if (oldNumStartingJobs == 0) { + Slog.e(TAG, "# staged jobs for " + workType + " went negative."); + // We are in a bad state. We will eventually recover when the pending list is + // regenerated. + return; + } + mNumStartingJobs.put(workType, oldNumStartingJobs - 1); + maybeAdjustReservations(workType); } - public int getNumStartingBgJobs() { - return mNumStartingBgJobs; + private void maybeAdjustReservations(@WorkType int workType) { + // Always make sure we reserve the minimum number of slots in case new jobs become ready + // soon. + final int numRemainingForType = Math.max(mConfigNumReservedSlots.get(workType), + mNumRunningJobs.get(workType) + mNumStartingJobs.get(workType) + + mNumPendingJobs.get(workType)); + if (numRemainingForType < mNumActuallyReservedSlots.get(workType)) { + // We've run all jobs for this type. Let another type use it now. + mNumActuallyReservedSlots.put(workType, numRemainingForType); + mNumUnspecializedRemaining++; + } } - int getTotalRunningJobCountToNote() { - return mNumRunningFgJobs + mNumRunningBgJobs - + mNumStartingFgJobs + mNumStartingBgJobs; + void onJobStarted(@WorkType int workType) { + mNumRunningJobs.put(workType, mNumRunningJobs.get(workType) + 1); + final int oldNumStartingJobs = mNumStartingJobs.get(workType); + if (oldNumStartingJobs == 0) { + Slog.e(TAG, "# stated jobs for " + workType + " went negative."); + // We are in a bad state. We will eventually recover when the pending list is + // regenerated. For now, only modify the running count. + } else { + mNumStartingJobs.put(workType, oldNumStartingJobs - 1); + } } - int getFgRunningJobCountToNote() { - return mNumRunningFgJobs + mNumStartingFgJobs; + 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). + mNumUnspecialized = mConfigMaxTotal; + final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP) + + mNumPendingJobs.get(WORK_TYPE_TOP); + final int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop); + mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop); + mNumUnspecialized -= resTop; + final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG); + final int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg); + mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg); + mNumUnspecialized -= resBg; + calculateUnspecializedRemaining(); + + // Assign remaining unspecialized based on ranking. + int unspecializedAssigned = Math.max(0, + Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), + Math.min(mNumUnspecializedRemaining, numTop - resTop))); + mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned); + mNumUnspecializedRemaining -= unspecializedAssigned; + unspecializedAssigned = Math.max(0, + Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), + Math.min(mNumUnspecializedRemaining, numBg - resBg))); + mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned); + mNumUnspecializedRemaining -= unspecializedAssigned; } - void logStatus() { - if (DEBUG) { - Slog.d(TAG, "assignJobsToContexts: " + this); + int canJobStart(int workTypes) { + if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) { + final int maxAllowed = Math.min( + mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), + mNumActuallyReservedSlots.get(WORK_TYPE_TOP) + mNumUnspecializedRemaining); + if (mNumRunningJobs.get(WORK_TYPE_TOP) + mNumStartingJobs.get(WORK_TYPE_TOP) + < maxAllowed) { + return WORK_TYPE_TOP; + } + } + if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) { + final int maxAllowed = Math.min( + mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), + mNumActuallyReservedSlots.get(WORK_TYPE_BG) + mNumUnspecializedRemaining); + if (mNumRunningJobs.get(WORK_TYPE_BG) + mNumStartingJobs.get(WORK_TYPE_BG) + < maxAllowed) { + return WORK_TYPE_BG; + } } + return WORK_TYPE_NONE; } - public String toString() { - final int totalFg = mNumRunningFgJobs + mNumStartingFgJobs; - final int totalBg = mNumRunningBgJobs + mNumStartingBgJobs; - return String.format( - "Config={tot=%d bg min/max=%d/%d}" - + " Running[FG/BG (total)]: %d / %d (%d)" - + " Pending: %d / %d (%d)" - + " Actual max: %d%s / %d%s (%d%s)" - + " Res BG: %d" - + " Starting: %d / %d (%d)" - + " Total: %d%s / %d%s (%d%s)", - mConfigNumMaxTotalJobs, mConfigNumMinBgJobs, mConfigNumMaxBgJobs, - - mNumRunningFgJobs, mNumRunningBgJobs, mNumRunningFgJobs + mNumRunningBgJobs, - - mNumPendingFgJobs, mNumPendingBgJobs, mNumPendingFgJobs + mNumPendingBgJobs, - - mNumActualMaxFgJobs, (totalFg <= mConfigNumMaxTotalJobs) ? "" : "*", - mNumActualMaxBgJobs, (totalBg <= mConfigNumMaxBgJobs) ? "" : "*", - mNumActualMaxFgJobs + mNumActualMaxBgJobs, - (mNumActualMaxFgJobs + mNumActualMaxBgJobs <= mConfigNumMaxTotalJobs) - ? "" : "*", - - mNumReservedForBg, - - mNumStartingFgJobs, mNumStartingBgJobs, mNumStartingFgJobs + mNumStartingBgJobs, - - totalFg, (totalFg <= mNumActualMaxFgJobs) ? "" : "*", - totalBg, (totalBg <= mNumActualMaxBgJobs) ? "" : "*", - totalFg + totalBg, (totalFg + totalBg <= mConfigNumMaxTotalJobs) ? "" : "*" - ); + int getRunningJobCount(@WorkType final int workType) { + return mNumRunningJobs.get(workType, 0); } - public void dumpProto(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - - proto.write(JobCountTrackerProto.CONFIG_NUM_MAX_TOTAL_JOBS, mConfigNumMaxTotalJobs); - proto.write(JobCountTrackerProto.CONFIG_NUM_MAX_BG_JOBS, mConfigNumMaxBgJobs); - proto.write(JobCountTrackerProto.CONFIG_NUM_MIN_BG_JOBS, mConfigNumMinBgJobs); - - proto.write(JobCountTrackerProto.NUM_RUNNING_FG_JOBS, mNumRunningFgJobs); - proto.write(JobCountTrackerProto.NUM_RUNNING_BG_JOBS, mNumRunningBgJobs); - - proto.write(JobCountTrackerProto.NUM_PENDING_FG_JOBS, mNumPendingFgJobs); - proto.write(JobCountTrackerProto.NUM_PENDING_BG_JOBS, mNumPendingBgJobs); - - proto.write(JobCountTrackerProto.NUM_ACTUAL_MAX_FG_JOBS, mNumActualMaxFgJobs); - proto.write(JobCountTrackerProto.NUM_ACTUAL_MAX_BG_JOBS, mNumActualMaxBgJobs); - - proto.write(JobCountTrackerProto.NUM_RESERVED_FOR_BG, mNumReservedForBg); - - proto.write(JobCountTrackerProto.NUM_STARTING_FG_JOBS, mNumStartingFgJobs); - proto.write(JobCountTrackerProto.NUM_STARTING_BG_JOBS, mNumStartingBgJobs); - - proto.end(token); + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("Config={"); + sb.append("tot=").append(mConfigMaxTotal); + sb.append(" mins="); + sb.append(mConfigNumReservedSlots); + sb.append(" maxs="); + sb.append(mConfigAbsoluteMaxSlots); + sb.append("}"); + + sb.append(", act res=").append(mNumActuallyReservedSlots); + sb.append(", Pending=").append(mNumPendingJobs); + sb.append(", Running=").append(mNumRunningJobs); + sb.append(", Staged=").append(mNumStartingJobs); + sb.append(", # unspecialized remaining=").append(mNumUnspecializedRemaining); + + return sb.toString(); } } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 885662c24147..ba78bda0d2fa 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -380,7 +380,6 @@ public class JobSchedulerService extends com.android.server.SystemService default: if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY) && !concurrencyUpdated) { - mConstants.updateConcurrencyConstantsLocked(); mConcurrencyManager.updateConfigLocked(); concurrencyUpdated = true; } else { @@ -408,119 +407,6 @@ public class JobSchedulerService extends com.android.server.SystemService mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); } - static class MaxJobCounts { - private final int mTotalDefault; - private final String mTotalKey; - private final int mMaxBgDefault; - private final String mMaxBgKey; - private final int mMinBgDefault; - private final String mMinBgKey; - private int mTotal; - private int mMaxBg; - private int mMinBg; - - MaxJobCounts(int totalDefault, String totalKey, - int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) { - mTotalKey = totalKey; - mTotal = mTotalDefault = totalDefault; - mMaxBgKey = maxBgKey; - mMaxBg = mMaxBgDefault = maxBgDefault; - mMinBgKey = minBgKey; - mMinBg = mMinBgDefault = minBgDefault; - } - - public void update() { - mTotal = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, - mTotalKey, mTotalDefault); - mMaxBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, - mMaxBgKey, mMaxBgDefault); - mMinBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, - mMinBgKey, mMinBgDefault); - - // Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT]. - mTotal = Math.min(Math.max(1, mTotal), MAX_JOB_CONTEXTS_COUNT); - - // Ensure maxBg in the range [1, total]. - mMaxBg = Math.min(Math.max(1, mMaxBg), mTotal); - - // Ensure minBg in the range [0, min(maxBg, total - 1)] - mMinBg = Math.min(Math.max(0, mMinBg), Math.min(mMaxBg, mTotal - 1)); - } - - /** Total number of jobs to run simultaneously. */ - public int getMaxTotal() { - return mTotal; - } - - /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */ - public int getMaxBg() { - return mMaxBg; - } - - /** - * We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any - * pending, rather than always running the TOTAL number of FG jobs. - */ - public int getMinBg() { - return mMinBg; - } - - public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); - pw.print(mTotalKey); - pw.print("="); - pw.print(mTotal); - pw.println(); - - pw.print(prefix); - pw.print(mMaxBgKey); - pw.print("="); - pw.print(mMaxBg); - pw.println(); - - pw.print(prefix); - pw.print(mMinBgKey); - pw.print("="); - pw.print(mMinBg); - pw.println(); - } - - public void dumpProto(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - proto.write(MaxJobCountsProto.TOTAL_JOBS, mTotal); - proto.write(MaxJobCountsProto.MAX_BG, mMaxBg); - proto.write(MaxJobCountsProto.MIN_BG, mMinBg); - proto.end(token); - } - } - - /** {@link MaxJobCounts} for each memory trim level. */ - static class MaxJobCountsPerMemoryTrimLevel { - public final MaxJobCounts normal; - public final MaxJobCounts moderate; - public final MaxJobCounts low; - public final MaxJobCounts critical; - - MaxJobCountsPerMemoryTrimLevel( - MaxJobCounts normal, - MaxJobCounts moderate, MaxJobCounts low, - MaxJobCounts critical) { - this.normal = normal; - this.moderate = moderate; - this.low = low; - this.critical = critical; - } - - public void dumpProto(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - normal.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.NORMAL); - moderate.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.MODERATE); - low.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.LOW); - critical.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.CRITICAL); - proto.end(token); - } - } - /** * All times are in milliseconds. Any access to this class or its fields should be done while * holding the JobSchedulerService.mLock lock. @@ -580,49 +466,6 @@ public class JobSchedulerService extends com.android.server.SystemService */ float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; - /** Prefix for all of the max_job constants. */ - private static final String KEY_PREFIX_MAX_JOB = - JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY + "max_job_"; - - // Max job counts for screen on / off, for each memory trim level. - final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON = - new MaxJobCountsPerMemoryTrimLevel( - new MaxJobCounts( - 8, KEY_PREFIX_MAX_JOB + "total_on_normal", - 6, KEY_PREFIX_MAX_JOB + "max_bg_on_normal", - 2, KEY_PREFIX_MAX_JOB + "min_bg_on_normal"), - new MaxJobCounts( - 8, KEY_PREFIX_MAX_JOB + "total_on_moderate", - 4, KEY_PREFIX_MAX_JOB + "max_bg_on_moderate", - 2, KEY_PREFIX_MAX_JOB + "min_bg_on_moderate"), - new MaxJobCounts( - 5, KEY_PREFIX_MAX_JOB + "total_on_low", - 1, KEY_PREFIX_MAX_JOB + "max_bg_on_low", - 1, KEY_PREFIX_MAX_JOB + "min_bg_on_low"), - new MaxJobCounts( - 5, KEY_PREFIX_MAX_JOB + "total_on_critical", - 1, KEY_PREFIX_MAX_JOB + "max_bg_on_critical", - 1, KEY_PREFIX_MAX_JOB + "min_bg_on_critical")); - - final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF = - new MaxJobCountsPerMemoryTrimLevel( - new MaxJobCounts( - 10, KEY_PREFIX_MAX_JOB + "total_off_normal", - 6, KEY_PREFIX_MAX_JOB + "max_bg_off_normal", - 2, KEY_PREFIX_MAX_JOB + "min_bg_off_normal"), - new MaxJobCounts( - 10, KEY_PREFIX_MAX_JOB + "total_off_moderate", - 4, KEY_PREFIX_MAX_JOB + "max_bg_off_moderate", - 2, KEY_PREFIX_MAX_JOB + "min_bg_off_moderate"), - new MaxJobCounts( - 5, KEY_PREFIX_MAX_JOB + "total_off_low", - 1, KEY_PREFIX_MAX_JOB + "max_bg_off_low", - 1, KEY_PREFIX_MAX_JOB + "min_bg_off_low"), - new MaxJobCounts( - 5, KEY_PREFIX_MAX_JOB + "total_off_critical", - 1, KEY_PREFIX_MAX_JOB + "max_bg_off_critical", - 1, KEY_PREFIX_MAX_JOB + "min_bg_off_critical")); - /** * The minimum backoff time to allow for linear backoff. */ @@ -686,18 +529,6 @@ public class JobSchedulerService extends com.android.server.SystemService DEFAULT_MODERATE_USE_FACTOR); } - void updateConcurrencyConstantsLocked() { - MAX_JOB_COUNTS_SCREEN_ON.normal.update(); - MAX_JOB_COUNTS_SCREEN_ON.moderate.update(); - MAX_JOB_COUNTS_SCREEN_ON.low.update(); - MAX_JOB_COUNTS_SCREEN_ON.critical.update(); - - MAX_JOB_COUNTS_SCREEN_OFF.normal.update(); - MAX_JOB_COUNTS_SCREEN_OFF.moderate.update(); - MAX_JOB_COUNTS_SCREEN_OFF.low.update(); - MAX_JOB_COUNTS_SCREEN_OFF.critical.update(); - } - private void updateBackoffConstantsLocked() { MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_LINEAR_BACKOFF_TIME_MS, @@ -747,16 +578,6 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println(); pw.print(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println(); - MAX_JOB_COUNTS_SCREEN_ON.normal.dump(pw, ""); - MAX_JOB_COUNTS_SCREEN_ON.moderate.dump(pw, ""); - MAX_JOB_COUNTS_SCREEN_ON.low.dump(pw, ""); - MAX_JOB_COUNTS_SCREEN_ON.critical.dump(pw, ""); - - MAX_JOB_COUNTS_SCREEN_OFF.normal.dump(pw, ""); - MAX_JOB_COUNTS_SCREEN_OFF.moderate.dump(pw, ""); - MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, ""); - MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, ""); - pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); @@ -781,9 +602,6 @@ public class JobSchedulerService extends com.android.server.SystemService proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR); proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR); - MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON); - MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF); - proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS); proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS); proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 26b5abed745c..247b4211fdf9 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -16,6 +16,7 @@ package com.android.server.job; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; @@ -124,9 +125,12 @@ public final class JobServiceContext implements ServiceConnection { * * Any reads (dereferences) not done from the handler thread must be synchronized on * {@link #mLock}. - * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}. + * Writes can only be done from the handler thread, + * or {@link #executeRunnableJob(JobStatus, int)}. */ private JobStatus mRunningJob; + @JobConcurrencyManager.WorkType + private int mRunningJobWorkType; private JobCallback mRunningCallback; /** Used to store next job to run when current job is to be preempted. */ private int mPreferredUid; @@ -181,30 +185,26 @@ public final class JobServiceContext implements ServiceConnection { JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, JobPackageTracker tracker, Looper looper) { - this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper); - } - - @VisibleForTesting - JobServiceContext(Context context, Object lock, IBatteryStats batteryStats, - JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) { - mContext = context; - mLock = lock; + mContext = service.getContext(); + mLock = service.getLock(); mBatteryStats = batteryStats; mJobPackageTracker = tracker; mCallbackHandler = new JobServiceHandler(looper); - mCompletedListener = completedListener; + mCompletedListener = service; mAvailable = true; mVerb = VERB_FINISHED; mPreferredUid = NO_PREFERRED_UID; } /** - * Give a job to this context for execution. Callers must first check {@link #getRunningJobLocked()} + * Give a job to this context for execution. Callers must first check {@link + * #getRunningJobLocked()} * and ensure it is null to make sure this is a valid context. + * * @param job The status of the job that we are going to run. * @return True if the job is valid and is running. False if the job cannot be executed. */ - boolean executeRunnableJob(JobStatus job) { + boolean executeRunnableJob(JobStatus job, @JobConcurrencyManager.WorkType int workType) { synchronized (mLock) { if (!mAvailable) { Slog.e(TAG, "Starting new runnable but context is unavailable > Error."); @@ -214,6 +214,7 @@ public final class JobServiceContext implements ServiceConnection { mPreferredUid = NO_PREFERRED_UID; mRunningJob = job; + mRunningJobWorkType = workType; mRunningCallback = new JobCallback(); final boolean isDeadlineExpired = job.hasDeadlineConstraint() && @@ -282,6 +283,7 @@ public final class JobServiceContext implements ServiceConnection { Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable."); } mRunningJob = null; + mRunningJobWorkType = WORK_TYPE_NONE; mRunningCallback = null; mParams = null; mExecutionStartTimeElapsed = 0L; @@ -326,6 +328,11 @@ public final class JobServiceContext implements ServiceConnection { return mRunningJob; } + @JobConcurrencyManager.WorkType + int getRunningJobWorkType() { + return mRunningJobWorkType; + } + /** * Used only for debugging. Will return <code>"<null>"</code> if there is no job running. */ @@ -831,6 +838,7 @@ public final class JobServiceContext implements ServiceConnection { mContext.unbindService(JobServiceContext.this); mWakeLock = null; mRunningJob = null; + mRunningJobWorkType = WORK_TYPE_NONE; mRunningCallback = null; mParams = null; mVerb = VERB_FINISHED; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 09dc7d281cb4..539c3c960f2e 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -305,6 +305,7 @@ public final class JobStatus { public Network network; public ServiceInfo serviceInfo; + /** The evaluated priority of the job when it started running. */ public int lastEvaluatedPriority; // If non-null, this is work that has been enqueued for the job. diff --git a/core/api/current.txt b/core/api/current.txt index ec712d875323..b49e7247708c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -657,6 +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 = 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 @@ -46182,6 +46183,7 @@ package android.view { method @NonNull public android.graphics.Rect getBoundingRectRight(); method @NonNull public android.graphics.Rect getBoundingRectTop(); method @NonNull public java.util.List<android.graphics.Rect> getBoundingRects(); + method @Nullable public android.graphics.Path getCutoutPath(); method public int getSafeInsetBottom(); method public int getSafeInsetLeft(); method public int getSafeInsetRight(); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 0915d0763604..2b9f1712bd87 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -157,6 +157,7 @@ package android.media.session { package android.net { public class ConnectivityManager { + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); } @@ -164,6 +165,14 @@ package android.net { method public int getResourceId(); } + public final class NetworkAgentConfig implements android.os.Parcelable { + method @Nullable public String getSubscriberId(); + } + + public static final class NetworkAgentConfig.Builder { + method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String); + } + public final class NetworkCapabilities implements android.os.Parcelable { field public static final int TRANSPORT_TEST = 7; // 0x7 } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0a0f77e33ed4..214b995f0b27 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1628,8 +1628,10 @@ package android.app.usage { package android.apphibernation { public final class AppHibernationManager { - method public boolean isHibernating(@NonNull String); - method public void setHibernating(@NonNull String, boolean); + method public boolean isHibernatingForUser(@NonNull String); + method public boolean isHibernatingGlobally(@NonNull String); + method public void setHibernatingForUser(@NonNull String, boolean); + method public void setHibernatingGlobally(@NonNull String, boolean); } } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 09c46c2a6991..db31d08100c0 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -685,6 +685,7 @@ package android.content.pm { method public void holdLock(android.os.IBinder, int); field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage"; field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption"; + field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec"; field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80 field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000 field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services"; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 68d3a9203f12..49f508d83f91 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5255,6 +5255,7 @@ public class Notification implements Parcelable // We still want a time to be set but gone, such that we can show and hide it // on demand in case it's a child notification without anything in the header contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime); + setTextViewColorSecondary(contentView, R.id.time, p); } } diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index afa1560420f7..e6aa7a77357c 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -47,8 +47,19 @@ per-file *Notification* = file:/packages/SystemUI/OWNERS per-file *Zen* = file:/packages/SystemUI/OWNERS per-file *StatusBar* = file:/packages/SystemUI/OWNERS +# PackageManager +per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file InstantAppResolverService.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file LoadedApk.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file PackageDeleteObserver.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file PackageInstallObserver.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file EphemeralResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS +per-file IEphemeralResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS +per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS +per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS + # ResourcesManager -per-file ResourcesManager = rtmitchell@google.com, toddke@google.com +per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com # VoiceInteraction per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index d5f01499793c..1498dae764a9 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -72,6 +72,7 @@ import android.content.res.Resources; import android.content.rollback.RollbackManagerFrameworkInitializer; import android.debug.AdbManager; import android.debug.IAdbManager; +import android.graphics.GameManager; import android.graphics.fonts.FontManager; import android.hardware.ConsumerIrManager; import android.hardware.ISerialManager; @@ -1426,6 +1427,16 @@ public final class SystemServiceRegistry { return new MediaMetricsManager(service, ctx.getUserId()); }}); + registerService(Context.GAME_SERVICE, GameManager.class, + new CachedServiceFetcher<GameManager>() { + @Override + public GameManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new GameManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler()); + } + }); + sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/services/core/jni/stats/PowerStatsPuller.h b/core/java/android/app/people/ConversationChannel.aidl index db07d600d251..78df2f10c337 100644 --- a/services/core/jni/stats/PowerStatsPuller.h +++ b/core/java/android/app/people/ConversationChannel.aidl @@ -1,11 +1,11 @@ -/* - * Copyright (C) 2018 The Android Open Source Project +/** + * Copyright (c) 2021, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,24 +14,6 @@ * limitations under the License. */ -#pragma once - -#include <stats_event.h> -#include <stats_pull_atom_callback.h> - -namespace android { -namespace server { -namespace stats { - -/** - * Reads hal for power.stats - */ -class PowerStatsPuller { -public: - PowerStatsPuller(); - AStatsManager_PullAtomCallbackReturn Pull(int32_t atomTag, AStatsEventList* data); -}; +package android.app.people; -} // namespace stats -} // namespace server -} // namespace android +parcelable ConversationChannel;
\ No newline at end of file diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl index 0d12ed02f610..ebe9f60dc150 100644 --- a/core/java/android/app/people/IPeopleManager.aidl +++ b/core/java/android/app/people/IPeopleManager.aidl @@ -17,6 +17,7 @@ package android.app.people; import android.app.people.ConversationStatus; +import android.app.people.ConversationChannel; import android.content.pm.ParceledListSlice; import android.net.Uri; import android.os.IBinder; @@ -26,6 +27,13 @@ import android.os.IBinder; * {@hide} */ interface IPeopleManager { + + /** + * Returns the specified conversation from the conversations list. If the conversation can't be + * found, returns null. + */ + ConversationChannel getConversation(in String packageName, int userId, in String shortcutId); + /** * Returns the recent conversations. The conversations that have customized notification * settings are excluded from the returned list. diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java index 8f1934c7b77a..7281d50a33a5 100644 --- a/core/java/android/apphibernation/AppHibernationManager.java +++ b/core/java/android/apphibernation/AppHibernationManager.java @@ -49,31 +49,61 @@ public final class AppHibernationManager { } /** - * Returns true if the package is hibernating, false otherwise. + * Returns true if the package is hibernating for this context's user, false otherwise. * * @hide */ @SystemApi - public boolean isHibernating(@NonNull String packageName) { + public boolean isHibernatingForUser(@NonNull String packageName) { try { - return mIAppHibernationService.isHibernating(packageName, mContext.getUserId()); + return mIAppHibernationService.isHibernatingForUser(packageName, mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Set whether the package is hibernating. + * Set whether the package is hibernating for this context's user. * * @hide */ @SystemApi - public void setHibernating(@NonNull String packageName, boolean isHibernating) { + public void setHibernatingForUser(@NonNull String packageName, boolean isHibernating) { try { - mIAppHibernationService.setHibernating(packageName, mContext.getUserId(), + mIAppHibernationService.setHibernatingForUser(packageName, mContext.getUserId(), isHibernating); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + + /** + * Returns true if app is hibernating globally / at the package level. + * + * @hide + */ + @SystemApi + public boolean isHibernatingGlobally(@NonNull String packageName) { + try { + return mIAppHibernationService.isHibernatingGlobally(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether a package should be globally hibernating. This hibernates the package at a + * package level. User-level hibernation (e.g.. {@link #isHibernatingForUser} is independent + * from global hibernation. + * + * @hide + */ + @SystemApi + public void setHibernatingGlobally(@NonNull String packageName, boolean isHibernating) { + try { + mIAppHibernationService.setHibernatingGlobally(packageName, isHibernating); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl index db57ecb73051..6a068ee2b147 100644 --- a/core/java/android/apphibernation/IAppHibernationService.aidl +++ b/core/java/android/apphibernation/IAppHibernationService.aidl @@ -21,6 +21,8 @@ package android.apphibernation; * @hide */ interface IAppHibernationService { - boolean isHibernating(String packageName, int userId); - void setHibernating(String packageName, int userId, boolean isHibernating); + boolean isHibernatingForUser(String packageName, int userId); + void setHibernatingForUser(String packageName, int userId, boolean isHibernating); + boolean isHibernatingGlobally(String packageName); + void setHibernatingGlobally(String packageName, boolean isHibernating); }
\ No newline at end of file diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 0c50446e0a4e..6a2329e5235f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5418,6 +5418,16 @@ public abstract class Context { public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition"; /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.graphics.GameManager}. + * + * @see #getSystemService(String) + * + * @hide + */ + public static final String GAME_SERVICE = "game"; + + /** * 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/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 68792b2f47de..9ae9c25c8c08 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3385,6 +3385,7 @@ public abstract class PackageManager { * {@link #hasSystemFeature}: This device supports HDMI-CEC. * @hide */ + @TestApi @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec"; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index fe8bf9d163c7..e6c0f6a4c2fa 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -6265,6 +6265,55 @@ public class PackageParser { } /** + * Returns whether this {@code SigningDetails} has a signer in common with the provided + * {@code otherDetails} with the specified {@code flags} capabilities provided by this + * signer. + * + * <p>Note this method allows for the signing lineage to diverge, so this should only be + * used for instances where the only requirement is a common signer in the lineage with + * the specified capabilities. If the current signer of this instance is an ancestor of + * {@code otherDetails} then {@code true} is immediately returned since the current signer + * has all capabilities granted. + */ + public boolean hasCommonSignerWithCapability(SigningDetails otherDetails, + @CertCapabilities int flags) { + if (this == UNKNOWN || otherDetails == UNKNOWN) { + return false; + } + // If either is signed with more than one signer then both must be signed by the same + // signers to consider the capabilities granted. + if (signatures.length > 1 || otherDetails.signatures.length > 1) { + return signaturesMatchExactly(otherDetails); + } + // The Signature class does not use the granted capabilities in the hashCode + // computation, so a Set can be used to check for a common signer. + Set<Signature> otherSignatures = new ArraySet<>(); + if (otherDetails.hasPastSigningCertificates()) { + otherSignatures.addAll(Arrays.asList(otherDetails.pastSigningCertificates)); + } else { + otherSignatures.addAll(Arrays.asList(otherDetails.signatures)); + } + // If the current signer of this instance is an ancestor of the other than return true + // since all capabilities are granted to the current signer. + if (otherSignatures.contains(signatures[0])) { + return true; + } + if (hasPastSigningCertificates()) { + // Since the current signer was checked above and the last signature in the + // pastSigningCertificates is the current signer skip checking the last element. + for (int i = 0; i < pastSigningCertificates.length - 1; i++) { + if (otherSignatures.contains(pastSigningCertificates[i])) { + // If the caller specified multiple capabilities ensure all are set. + if ((pastSigningCertificates[i].getFlags() & flags) == flags) { + return true; + } + } + } + } + return false; + } + + /** * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or * not this one grants it the provided capability, represented by the {@code flags} * parameter. In the event of signing certificate rotation, a package may still interact diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java index 14eb11ab8863..ecd240d9e4dc 100644 --- a/core/java/android/content/res/FontResourcesParser.java +++ b/core/java/android/content/res/FontResourcesParser.java @@ -47,14 +47,17 @@ public class FontResourcesParser { private final @NonNull String mProviderAuthority; private final @NonNull String mProviderPackage; private final @NonNull String mQuery; + private final @Nullable String mSystemFontFamilyName; private final @Nullable List<List<String>> mCerts; public ProviderResourceEntry(@NonNull String authority, @NonNull String pkg, - @NonNull String query, @Nullable List<List<String>> certs) { + @NonNull String query, @Nullable List<List<String>> certs, + @Nullable String systemFontFamilyName) { mProviderAuthority = authority; mProviderPackage = pkg; mQuery = query; mCerts = certs; + mSystemFontFamilyName = systemFontFamilyName; } public @NonNull String getAuthority() { @@ -69,6 +72,10 @@ public class FontResourcesParser { return mQuery; } + public @NonNull String getSystemFontFamilyName() { + return mSystemFontFamilyName; + } + public @Nullable List<List<String>> getCerts() { return mCerts; } @@ -166,6 +173,8 @@ public class FontResourcesParser { String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage); String query = array.getString(R.styleable.FontFamily_fontProviderQuery); int certsId = array.getResourceId(R.styleable.FontFamily_fontProviderCerts, 0); + String systemFontFamilyName = array.getString( + R.styleable.FontFamily_fontProviderSystemFontFamily); array.recycle(); if (authority != null && providerPackage != null && query != null) { while (parser.next() != XmlPullParser.END_TAG) { @@ -191,7 +200,13 @@ public class FontResourcesParser { } } } - return new ProviderResourceEntry(authority, providerPackage, query, certs); + return new ProviderResourceEntry( + authority, + providerPackage, + query, + certs, + systemFontFamilyName + ); } List<FontFileResourceEntry> fonts = new ArrayList<>(); while (parser.next() != XmlPullParser.END_TAG) { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 2e45ed8504c9..ac8f9c9e62fd 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -15,7 +15,9 @@ */ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import static android.net.IpSecManager.INVALID_RESOURCE_ID; +import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; import static android.net.NetworkRequest.Type.LISTEN; import static android.net.NetworkRequest.Type.REQUEST; import static android.net.NetworkRequest.Type.TRACK_DEFAULT; @@ -28,6 +30,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.PendingIntent; @@ -4964,4 +4967,92 @@ public class ConnectivityManager { } return null; } + + /** + * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but + * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can + * be used to request that the system provide a network without causing the network to be + * in the foreground. + * + * <p>This method will attempt to find the best network that matches the passed + * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the + * criteria. The platform will evaluate which network is the best at its own discretion. + * Throughput, latency, cost per byte, policy, user preference and other considerations + * may be factored in the decision of what is considered the best network. + * + * <p>As long as this request is outstanding, the platform will try to maintain the best network + * matching this request, while always attempting to match the request to a better network if + * possible. If a better match is found, the platform will switch this request to the now-best + * network and inform the app of the newly best network by invoking + * {@link NetworkCallback#onAvailable(Network)} on the provided callback. Note that the platform + * will not try to maintain any other network than the best one currently matching the request: + * a network not matching any network request may be disconnected at any time. + * + * <p>For example, an application could use this method to obtain a connected cellular network + * even if the device currently has a data connection over Ethernet. This may cause the cellular + * radio to consume additional power. Or, an application could inform the system that it wants + * a network supporting sending MMSes and have the system let it know about the currently best + * MMS-supporting network through the provided {@link NetworkCallback}. + * + * <p>The status of the request can be followed by listening to the various callbacks described + * in {@link NetworkCallback}. The {@link Network} object passed to the callback methods can be + * used to direct traffic to the network (although accessing some networks may be subject to + * holding specific permissions). Callers will learn about the specific characteristics of the + * network through + * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)} and + * {@link NetworkCallback#onLinkPropertiesChanged(Network, LinkProperties)}. The methods of the + * provided {@link NetworkCallback} will only be invoked due to changes in the best network + * matching the request at any given time; therefore when a better network matching the request + * becomes available, the {@link NetworkCallback#onAvailable(Network)} method is called + * with the new network after which no further updates are given about the previously-best + * network, unless it becomes the best again at some later time. All callbacks are invoked + * in order on the same thread, which by default is a thread created by the framework running + * in the app. + * + * <p>This{@link NetworkRequest} will live until released via + * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits, at + * which point the system may let go of the network at any time. + * + * <p>It is presently unsupported to request a network with mutable + * {@link NetworkCapabilities} such as + * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or + * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL} + * as these {@code NetworkCapabilities} represent states that a particular + * network may never attain, and whether a network will attain these states + * is unknown prior to bringing up the network so the framework does not + * know how to go about satisfying a request with these capabilities. + * + * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the + * number of outstanding requests to 100 per app (identified by their UID), shared with + * all variants of this method, of {@link #registerNetworkCallback} as well as + * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}. + * Requesting a network with this method will count toward this limit. If this limit is + * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources, + * make sure to unregister the callbacks with + * {@link #unregisterNetworkCallback(NetworkCallback)}. + * + * @param request {@link NetworkRequest} describing this request. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * If null, the callback is invoked on the default internal Handler. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * @throws IllegalArgumentException if {@code request} contains invalid network capabilities. + * @throws SecurityException if missing the appropriate permissions. + * @throws RuntimeException if the app already has too many callbacks registered. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @SuppressLint("ExecutorRegistration") + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK + }) + public void requestBackgroundNetwork(@NonNull NetworkRequest request, + @Nullable Handler handler, @NonNull NetworkCallback networkCallback) { + final NetworkCapabilities nc = request.networkCapabilities; + sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST, + TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler)); + } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 719783163ab9..1b4d2e413943 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -31,7 +31,6 @@ import android.net.NetworkRequest; import android.net.NetworkState; import android.net.ProxyInfo; import android.net.UidRange; -import android.net.VpnInfo; import android.net.QosSocketInfo; import android.os.Bundle; import android.os.IBinder; diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index d5aede71011f..0baf11e850c7 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -23,7 +23,7 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; -import android.net.VpnInfo; +import android.net.UnderlyingNetworkInfo; import android.net.netstats.provider.INetworkStatsProvider; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.os.IBinder; @@ -70,7 +70,7 @@ interface INetworkStatsService { in Network[] defaultNetworks, in NetworkState[] networkStates, in String activeIface, - in VpnInfo[] vpnInfos); + in UnderlyingNetworkInfo[] underlyingNetworkInfos); /** Force update of statistics. */ @UnsupportedAppUsage void forceUpdate(); diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java index 84985b6584c8..9a11be00663b 100644 --- a/core/java/android/net/MatchAllNetworkSpecifier.java +++ b/core/java/android/net/MatchAllNetworkSpecifier.java @@ -32,17 +32,6 @@ import android.os.Parcelable; */ @SystemApi public final class MatchAllNetworkSpecifier extends NetworkSpecifier implements Parcelable { - /** - * Utility method which verifies that the ns argument is not a MatchAllNetworkSpecifier and - * throws an IllegalArgumentException if it is. - * @hide - */ - public static void checkNotMatchAllNetworkSpecifier(NetworkSpecifier ns) { - if (ns instanceof MatchAllNetworkSpecifier) { - throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted"); - } - } - /** @hide */ @Override public boolean canBeSatisfiedBy(NetworkSpecifier other) { diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java index fe1268d79b89..664c2650ff0c 100644 --- a/core/java/android/net/NetworkAgentConfig.java +++ b/core/java/android/net/NetworkAgentConfig.java @@ -16,6 +16,8 @@ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -125,6 +127,7 @@ public final class NetworkAgentConfig implements Parcelable { * @return the subscriber ID, or null if none. * @hide */ + @SystemApi(client = MODULE_LIBRARIES) @Nullable public String getSubscriberId() { return subscriberId; @@ -275,6 +278,7 @@ public final class NetworkAgentConfig implements Parcelable { * @hide */ @NonNull + @SystemApi(client = MODULE_LIBRARIES) public Builder setSubscriberId(@Nullable String subscriberId) { mConfig.subscriberId = subscriberId; return this; diff --git a/core/java/android/net/VpnInfo.aidl b/core/java/android/net/UnderlyingNetworkInfo.aidl index 8bcaa81f3992..a56f2f40583b 100644 --- a/core/java/android/net/VpnInfo.aidl +++ b/core/java/android/net/UnderlyingNetworkInfo.aidl @@ -16,4 +16,4 @@ package android.net; -parcelable VpnInfo; +parcelable UnderlyingNetworkInfo; diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java new file mode 100644 index 000000000000..8fb4832e06c8 --- /dev/null +++ b/core/java/android/net/UnderlyingNetworkInfo.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A lightweight container used to carry information on the networks that underly a given + * virtual network. + * + * @hide + */ +public final class UnderlyingNetworkInfo implements Parcelable { + /** The owner of this network. */ + public final int ownerUid; + /** The interface name of this network. */ + @NonNull + public final String iface; + /** The names of the interfaces underlying this network. */ + @NonNull + public final List<String> underlyingIfaces; + + public UnderlyingNetworkInfo(int ownerUid, @NonNull String iface, + @NonNull List<String> underlyingIfaces) { + Objects.requireNonNull(iface); + Objects.requireNonNull(underlyingIfaces); + this.ownerUid = ownerUid; + this.iface = iface; + this.underlyingIfaces = underlyingIfaces; + } + + private UnderlyingNetworkInfo(@NonNull Parcel in) { + this.ownerUid = in.readInt(); + this.iface = in.readString(); + this.underlyingIfaces = new ArrayList<>(); + in.readList(this.underlyingIfaces, null /*classLoader*/); + } + + @Override + public String toString() { + return "UnderlyingNetworkInfo{" + + "ownerUid=" + ownerUid + + ", iface='" + iface + '\'' + + ", underlyingIfaces='" + underlyingIfaces.toString() + '\'' + + '}'; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(ownerUid); + dest.writeString(iface); + dest.writeList(underlyingIfaces); + } + + @NonNull + public static final Parcelable.Creator<UnderlyingNetworkInfo> CREATOR = + new Parcelable.Creator<UnderlyingNetworkInfo>() { + @NonNull + @Override + public UnderlyingNetworkInfo createFromParcel(@NonNull Parcel in) { + return new UnderlyingNetworkInfo(in); + } + + @NonNull + @Override + public UnderlyingNetworkInfo[] newArray(int size) { + return new UnderlyingNetworkInfo[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UnderlyingNetworkInfo)) return false; + final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o; + return ownerUid == that.ownerUid + && Objects.equals(iface, that.iface) + && Objects.equals(underlyingIfaces, that.underlyingIfaces); + } + + @Override + public int hashCode() { + return Objects.hash(ownerUid, iface, underlyingIfaces); + } +} diff --git a/core/java/android/net/VpnInfo.java b/core/java/android/net/VpnInfo.java deleted file mode 100644 index cf58c570f21f..000000000000 --- a/core/java/android/net/VpnInfo.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Arrays; - -/** - * A lightweight container used to carry information of the ongoing VPN. - * Internal use only. - * - * @hide - */ -public class VpnInfo implements Parcelable { - public final int ownerUid; - @Nullable - public final String vpnIface; - @Nullable - public final String[] underlyingIfaces; - - public VpnInfo(int ownerUid, @Nullable String vpnIface, @Nullable String[] underlyingIfaces) { - this.ownerUid = ownerUid; - this.vpnIface = vpnIface; - this.underlyingIfaces = underlyingIfaces; - } - - private VpnInfo(@NonNull Parcel in) { - this.ownerUid = in.readInt(); - this.vpnIface = in.readString(); - this.underlyingIfaces = in.createStringArray(); - } - - @Override - public String toString() { - return "VpnInfo{" - + "ownerUid=" + ownerUid - + ", vpnIface='" + vpnIface + '\'' - + ", underlyingIfaces='" + Arrays.toString(underlyingIfaces) + '\'' - + '}'; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(ownerUid); - dest.writeString(vpnIface); - dest.writeStringArray(underlyingIfaces); - } - - @NonNull - public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() { - @NonNull - @Override - public VpnInfo createFromParcel(@NonNull Parcel in) { - return new VpnInfo(in); - } - - @NonNull - @Override - public VpnInfo[] newArray(int size) { - return new VpnInfo[size]; - } - }; -} diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl index 80ac64b87d4d..4f293eeb3c3b 100644 --- a/core/java/android/net/vcn/IVcnManagementService.aidl +++ b/core/java/android/net/vcn/IVcnManagementService.aidl @@ -16,8 +16,11 @@ package android.net.vcn; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; +import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.os.ParcelUuid; /** @@ -29,4 +32,5 @@ interface IVcnManagementService { void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); + VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp); } diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 2d0a6d74cb86..33beb6a9d188 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -21,6 +21,8 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -62,7 +64,7 @@ import java.util.concurrent.Executor; * @hide */ @SystemService(Context.VCN_MANAGEMENT_SERVICE) -public final class VcnManager { +public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); /** @hide */ @@ -223,6 +225,37 @@ public final class VcnManager { } /** + * Queries the underlying network policy for a network with the given parameters. + * + * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy + * may have changed via {@link VcnUnderlyingNetworkPolicyListener#onPolicyChanged()}, a Network + * Provider MUST poll for the updated Network policy based on that Network's capabilities and + * properties. + * + * @param networkCapabilities the NetworkCapabilities to be used in determining the Network + * policy for this Network. + * @param linkProperties the LinkProperties to be used in determining the Network policy for + * this Network. + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY + * @return the VcnUnderlyingNetworkPolicy to be used for this Network. + * @hide + */ + @NonNull + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties) { + requireNonNull(networkCapabilities, "networkCapabilities must not be null"); + requireNonNull(linkProperties, "linkProperties must not be null"); + + try { + return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System * Server. * diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java index 71344f90de75..f853e67f87d0 100644 --- a/core/java/android/os/ServiceManager.java +++ b/core/java/android/os/ServiceManager.java @@ -288,6 +288,20 @@ public final class ServiceManager { } /** + * Get service debug info. + * @return an array of information for each service (like listServices, but with PIDs) + * @hide + */ + public static ServiceDebugInfo[] getServiceDebugInfo() { + try { + return getIServiceManager().getServiceDebugInfo(); + } catch (RemoteException e) { + Log.e(TAG, "error in getServiceDebugInfo", e); + return null; + } + } + + /** * This is only intended to be called when the process is first being brought * up and bound by the activity manager. There is only one thread in the process * at that time, so no locking is done. diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java index b70b6b5d209e..60acc57d0cfe 100644 --- a/core/java/android/os/ServiceManagerNative.java +++ b/core/java/android/os/ServiceManagerNative.java @@ -103,6 +103,10 @@ class ServiceManagerProxy implements IServiceManager { throw new RemoteException(); } + public ServiceDebugInfo[] getServiceDebugInfo() throws RemoteException { + return mServiceManager.getServiceDebugInfo(); + } + /** * Same as mServiceManager but used by apps. * diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl index 7db5a80b6cf9..3fbc28402405 100644 --- a/core/java/android/os/incremental/IIncrementalService.aidl +++ b/core/java/android/os/incremental/IIncrementalService.aidl @@ -38,14 +38,20 @@ interface IIncrementalService { * Opens or creates a storage given a target path and data loader params. Returns the storage ID. */ int openStorage(in @utf8InCpp String path); - int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode, - in IDataLoaderStatusListener statusListener, - in StorageHealthCheckParams healthCheckParams, - in IStorageHealthListener healthListener, - in PerUidReadTimeouts[] perUidReadTimeouts); + int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode); int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode); /** + * Loops DataLoader through bind/create/start with params. + */ + boolean startLoading(int storageId, + in DataLoaderParamsParcel params, + in IDataLoaderStatusListener statusListener, + in StorageHealthCheckParams healthCheckParams, + in IStorageHealthListener healthListener, + in PerUidReadTimeouts[] perUidReadTimeouts); + + /** * Bind-mounts a path under a storage to a full path. Can be permanent or temporary. */ const int BIND_TEMPORARY = 0; @@ -101,6 +107,14 @@ interface IIncrementalService { int isFileFullyLoaded(int storageId, in @utf8InCpp String path); /** + * Checks if all files in the storage are fully loaded. + * 0 - fully loaded + * >0 - certain pages missing + * <0 - -errcode + */ + int isFullyLoaded(int storageId); + + /** * Returns overall loading progress of all the files on a storage, progress value between [0,1]. * Returns a negative value on error. */ @@ -113,11 +127,6 @@ interface IIncrementalService { byte[] getMetadataById(int storageId, in byte[] fileId); /** - * Starts loading data for a storage. - */ - boolean startLoading(int storageId); - - /** * Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader. */ void deleteStorage(int storageId); diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 59292baa110c..f2fe71913bb1 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -37,7 +37,6 @@ import android.content.Context; import android.content.pm.DataLoaderParams; import android.content.pm.IDataLoaderStatusListener; import android.content.pm.InstallationFileParcel; -import android.text.TextUtils; import java.io.File; import java.io.IOException; @@ -53,6 +52,7 @@ public final class IncrementalFileStorages { private @NonNull final IncrementalManager mIncrementalManager; private @NonNull final File mStageDir; + private @Nullable IncrementalStorage mInheritedStorage; private @Nullable IncrementalStorage mDefaultStorage; /** @@ -65,6 +65,7 @@ public final class IncrementalFileStorages { */ public static IncrementalFileStorages initialize(Context context, @NonNull File stageDir, + @Nullable File inheritedDir, @NonNull DataLoaderParams dataLoaderParams, @Nullable IDataLoaderStatusListener statusListener, @Nullable StorageHealthCheckParams healthCheckParams, @@ -79,9 +80,8 @@ public final class IncrementalFileStorages { throw new IOException("Failed to obtain incrementalManager."); } - final IncrementalFileStorages result = new IncrementalFileStorages(stageDir, - incrementalManager, dataLoaderParams, statusListener, healthCheckParams, - healthListener, perUidReadTimeouts); + final IncrementalFileStorages result = new IncrementalFileStorages(stageDir, inheritedDir, + incrementalManager, dataLoaderParams); for (InstallationFileParcel file : addedFiles) { if (file.location == LOCATION_DATA_APP) { try { @@ -95,43 +95,46 @@ public final class IncrementalFileStorages { throw new IOException("Unknown file location: " + file.location); } } - - result.startLoading(); + result.startLoading(dataLoaderParams, statusListener, healthCheckParams, healthListener, + perUidReadTimeouts); return result; } private IncrementalFileStorages(@NonNull File stageDir, + @Nullable File inheritedDir, @NonNull IncrementalManager incrementalManager, - @NonNull DataLoaderParams dataLoaderParams, - @Nullable IDataLoaderStatusListener statusListener, - @Nullable StorageHealthCheckParams healthCheckParams, - @Nullable IStorageHealthListener healthListener, - @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException { + @NonNull DataLoaderParams dataLoaderParams) throws IOException { try { mStageDir = stageDir; mIncrementalManager = incrementalManager; - if (dataLoaderParams.getComponentName().getPackageName().equals("local")) { - final String incrementalPath = dataLoaderParams.getArguments(); - if (TextUtils.isEmpty(incrementalPath)) { - throw new IOException("Failed to create storage: incrementalPath is empty"); - } - mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); - if (mDefaultStorage == null) { - throw new IOException( - "Couldn't open incremental storage at " + incrementalPath); - } - mDefaultStorage.bind(stageDir.getAbsolutePath()); - } else { - mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(), - dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE - | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false, - statusListener, healthCheckParams, healthListener, perUidReadTimeouts); - if (mDefaultStorage == null) { - throw new IOException( - "Couldn't create incremental storage at " + stageDir); + if (inheritedDir != null && IncrementalManager.isIncrementalPath( + inheritedDir.getAbsolutePath())) { + mInheritedStorage = mIncrementalManager.openStorage( + inheritedDir.getAbsolutePath()); + if (mInheritedStorage != null) { + if (!mInheritedStorage.isFullyLoaded()) { + throw new IOException("Inherited storage has missing pages."); + } + + mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(), + mInheritedStorage, IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_TEMPORARY_BIND); + if (mDefaultStorage == null) { + throw new IOException( + "Couldn't create linked incremental storage at " + stageDir); + } + return; } } + + mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(), + dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_TEMPORARY_BIND); + if (mDefaultStorage == null) { + throw new IOException( + "Couldn't create incremental storage at " + stageDir); + } } catch (IOException e) { cleanUp(); throw e; @@ -149,9 +152,16 @@ public final class IncrementalFileStorages { /** * Starts or re-starts loading of data. */ - public void startLoading() throws IOException { - if (!mDefaultStorage.startLoading()) { - throw new IOException("Failed to start loading data for Incremental installation."); + void startLoading( + @NonNull DataLoaderParams dataLoaderParams, + @Nullable IDataLoaderStatusListener statusListener, + @Nullable StorageHealthCheckParams healthCheckParams, + @Nullable IStorageHealthListener healthListener, + @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException { + if (!mDefaultStorage.startLoading(dataLoaderParams, statusListener, healthCheckParams, + healthListener, perUidReadTimeouts)) { + throw new IOException( + "Failed to start or restart loading data for Incremental installation."); } } @@ -163,6 +173,21 @@ public final class IncrementalFileStorages { } /** + * Creates a hardlink from inherited storage to default. + */ + public boolean makeLink(@NonNull String relativePath, @NonNull String fromBase, + @NonNull String toBase) throws IOException { + if (mInheritedStorage == null) { + return false; + } + final File sourcePath = new File(fromBase, relativePath); + final File destPath = new File(toBase, relativePath); + mInheritedStorage.makeLink(sourcePath.getAbsolutePath(), mDefaultStorage, + destPath.getAbsolutePath()); + return true; + } + + /** * Permanently disables readlogs. */ public void disallowReadLogs() { diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 4b9327021cb0..7e7057fe56cb 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.annotation.SystemService; import android.content.Context; import android.content.pm.DataLoaderParams; -import android.content.pm.IDataLoaderStatusListener; import android.content.pm.IPackageLoadingProgressCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -95,32 +94,20 @@ public final class IncrementalManager { * @param params IncrementalDataLoaderParams object to configure data loading. * @param createMode Mode for opening an old Incremental File System mount or creating * a new mount. - * @param autoStartDataLoader Set true to immediately start data loader after creating storage. * @return IncrementalStorage object corresponding to the mounted directory. */ @Nullable public IncrementalStorage createStorage(@NonNull String path, @NonNull DataLoaderParams params, - @CreateMode int createMode, - boolean autoStartDataLoader, - @Nullable IDataLoaderStatusListener statusListener, - @Nullable StorageHealthCheckParams healthCheckParams, - @Nullable IStorageHealthListener healthListener, - @NonNull PerUidReadTimeouts[] perUidReadTimeouts) { + @CreateMode int createMode) { Objects.requireNonNull(path); Objects.requireNonNull(params); - Objects.requireNonNull(perUidReadTimeouts); try { - final int id = mService.createStorage(path, params.getData(), createMode, - statusListener, healthCheckParams, healthListener, perUidReadTimeouts); + final int id = mService.createStorage(path, params.getData(), createMode); if (id < 0) { return null; } - final IncrementalStorage storage = new IncrementalStorage(mService, id); - if (autoStartDataLoader) { - storage.startLoading(); - } - return storage; + return new IncrementalStorage(mService, id); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -281,15 +268,18 @@ public final class IncrementalManager { * Unbinds the target dir and deletes the corresponding storage instance. * Deletes the package name and associated storage id from maps. */ - public void onPackageRemoved(@NonNull String codePath) { + public void onPackageRemoved(@NonNull File codeFile) { try { + final String codePath = codeFile.getAbsolutePath(); final IncrementalStorage storage = openStorage(codePath); if (storage == null) { return; } mLoadingProgressCallbacks.cleanUpCallbacks(storage); unregisterHealthListener(codePath); - mService.deleteStorage(storage.getId()); + + // Parent since we bind-mount a folder one level above. + mService.deleteBindMount(storage.getId(), codeFile.getParent()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java index 5b688bbd0655..e6ce8cd56d28 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -18,11 +18,14 @@ package android.os.incremental; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.DataLoaderParams; +import android.content.pm.IDataLoaderStatusListener; import android.os.RemoteException; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.Objects; import java.util.UUID; /** @@ -323,6 +326,24 @@ public final class IncrementalStorage { } } + + /** + * Checks if all files in the storage are fully loaded. + */ + public boolean isFullyLoaded() throws IOException { + try { + final int res = mService.isFullyLoaded(mId); + if (res < 0) { + throw new IOException( + "isFullyLoaded() failed at querying loading progress, errno " + -res); + } + return res == 0; + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return false; + } + } + /** * Returns the loading progress of a storage * @@ -376,13 +397,21 @@ public final class IncrementalStorage { } /** - * Informs the data loader service associated with the current storage to start data loader - * - * @return True if data loader is successfully started. + * Iinitializes and starts the DataLoader. + * This makes sure all install-time parameters are applied. + * Does not affect persistent DataLoader params. + * @return True if start request was successfully queued. */ - public boolean startLoading() { + public boolean startLoading( + @NonNull DataLoaderParams dataLoaderParams, + @Nullable IDataLoaderStatusListener statusListener, + @Nullable StorageHealthCheckParams healthCheckParams, + @Nullable IStorageHealthListener healthListener, + @NonNull PerUidReadTimeouts[] perUidReadTimeouts) { + Objects.requireNonNull(perUidReadTimeouts); try { - return mService.startLoading(mId); + return mService.startLoading(mId, dataLoaderParams.getData(), statusListener, + healthCheckParams, healthListener, perUidReadTimeouts); } catch (RemoteException e) { e.rethrowFromSystemServer(); return false; diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index b1b29254783c..85e9fdb9a9d1 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -28,7 +28,10 @@ import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; import static android.app.AppOpsManager.opToPermission; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED; +import static android.media.AudioSystem.MODE_IN_COMMUNICATION; +import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; +import android.Manifest; import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.ComponentName; @@ -41,12 +44,14 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.icu.text.ListFormatter; import android.location.LocationManager; +import android.media.AudioManager; import android.os.Process; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.speech.RecognitionService; import android.speech.RecognizerIntent; +import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; import android.view.inputmethod.InputMethodInfo; @@ -75,6 +80,9 @@ public class PermissionUsageHelper { private static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"; + /** Whether to show the Permissions Hub. */ + private static final String PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled"; + /** How long after an access to show it as "recent" */ private static final String RECENT_ACCESS_TIME_MS = "recent_acccess_time_ms"; @@ -84,17 +92,25 @@ public class PermissionUsageHelper { /** The name of the expected voice IME subtype */ private static final String VOICE_IME_SUBTYPE = "voice"; + private static final String SYSTEM_PKG = "android"; + private static final long DEFAULT_RUNNING_TIME_MS = 5000L; - private static final long DEFAULT_RECENT_TIME_MS = 30000L; + private static final long DEFAULT_RECENT_TIME_MS = 15000L; + + private static boolean shouldShowPermissionsHub() { + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_PERMISSIONS_HUB_2_ENABLED, false); + } private static boolean shouldShowIndicators() { return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_CAMERA_MIC_ICONS_ENABLED, true); + PROPERTY_CAMERA_MIC_ICONS_ENABLED, true) || shouldShowPermissionsHub(); } private static boolean shouldShowLocationIndicator() { return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_LOCATION_INDICATORS_ENABLED, false); + PROPERTY_LOCATION_INDICATORS_ENABLED, false) + || shouldShowPermissionsHub(); } private static long getRecentThreshold(Long now) { @@ -113,7 +129,7 @@ public class PermissionUsageHelper { ); private static final List<String> MIC_OPS = List.of( - OPSTR_PHONE_CALL_CAMERA, + OPSTR_PHONE_CALL_MICROPHONE, OPSTR_RECORD_AUDIO ); @@ -163,6 +179,13 @@ public class PermissionUsageHelper { return mUserContexts.get(user); } + // TODO ntmyren: Replace this with better check if this moves beyond teamfood + private boolean isAppPredictor(String packageName, UserHandle user) { + return shouldShowPermissionsHub() && getUserContext(user).getPackageManager() + .checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, packageName) + == PackageManager.PERMISSION_GRANTED; + } + /** * @see PermissionManager.getIndicatorAppOpUsageData */ @@ -186,7 +209,28 @@ public class PermissionUsageHelper { Map<PackageAttribution, CharSequence> packagesWithAttributionLabels = getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains); - List<String> usedPermGroups = new ArrayList<>(rawUsages.keySet()); + ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet()); + + // If we have a phone call, and a carrier privileged app using microphone, hide the + // phone call. + AudioManager audioManager = mContext.getSystemService(AudioManager.class); + boolean hasPhoneCall = usedPermGroups.contains(OPSTR_PHONE_CALL_CAMERA) + || usedPermGroups.contains(OPSTR_PHONE_CALL_MICROPHONE); + if (hasPhoneCall && usedPermGroups.contains(MICROPHONE) && audioManager.getMode() + == MODE_IN_COMMUNICATION) { + TelephonyManager telephonyManager = + mContext.getSystemService(TelephonyManager.class); + List<OpUsage> permUsages = rawUsages.get(MICROPHONE); + for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) { + if (telephonyManager.checkCarrierPrivilegesForPackage( + permUsages.get(usageNum).packageName) + == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + usedPermGroups.remove(OPSTR_PHONE_CALL_CAMERA); + usedPermGroups.remove(OPSTR_PHONE_CALL_MICROPHONE); + } + } + } + for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) { boolean isPhone = false; String permGroup = usedPermGroups.get(permGroupNum); @@ -269,8 +313,11 @@ public class PermissionUsageHelper { if (lastAccessTime < recentThreshold && !attrOpEntry.isRunning()) { continue; } - if (!isUserSensitive(packageName, user, op) - && !isLocationProvider(packageName, user)) { + + if (packageName.equals(SYSTEM_PKG) + || (!isUserSensitive(packageName, user, op) + && !isLocationProvider(packageName, user) + && !isAppPredictor(packageName, user))) { continue; } diff --git a/core/java/android/service/voice/IVoiceInteractionSessionService.aidl b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl index 7f8158f433be..dc0718a13fbc 100644 --- a/core/java/android/service/voice/IVoiceInteractionSessionService.aidl +++ b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl @@ -18,8 +18,6 @@ package android.service.voice; import android.os.Bundle; -import android.service.voice.IVoiceInteractionSession; - /** * @hide */ diff --git a/core/java/android/service/voice/VoiceInteractionSessionService.java b/core/java/android/service/voice/VoiceInteractionSessionService.java index 424ff9def41c..8300343d03b6 100644 --- a/core/java/android/service/voice/VoiceInteractionSessionService.java +++ b/core/java/android/service/voice/VoiceInteractionSessionService.java @@ -21,11 +21,14 @@ import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; +import android.os.DeadObjectException; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.Log; + import com.android.internal.app.IVoiceInteractionManagerService; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; @@ -38,6 +41,8 @@ import java.io.PrintWriter; */ public abstract class VoiceInteractionSessionService extends Service { + private static final String TAG = "VoiceInteractionSession"; + static final int MSG_NEW_SESSION = 1; IVoiceInteractionManagerService mSystemService; @@ -120,10 +125,22 @@ public abstract class VoiceInteractionSessionService extends Service { mSession = null; } mSession = onNewSession(args); - try { - mSystemService.deliverNewSession(token, mSession.mSession, mSession.mInteractor); + if (deliverSession(token)) { mSession.doCreate(mSystemService, token); + } else { + // TODO(b/178777121): Add an onError() method to let the application know what happened. + mSession.doDestroy(); + mSession = null; + } + } + + private boolean deliverSession(IBinder token) { + try { + return mSystemService.deliverNewSession(token, mSession.mSession, mSession.mInteractor); + } catch (DeadObjectException ignored) { } catch (RemoteException e) { + Log.e(TAG, "Failed to deliver session: " + e); } + return false; } } diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java index a44ed59c14d4..698cb77947cf 100644 --- a/core/java/android/util/RotationUtils.java +++ b/core/java/android/util/RotationUtils.java @@ -21,7 +21,9 @@ import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import android.annotation.Dimension; import android.graphics.Insets; +import android.graphics.Matrix; import android.view.Surface.Rotation; /** @@ -69,4 +71,34 @@ public class RotationUtils { } return rotated; } + + /** + * Sets a matrix such that given a rotation, it transforms physical display + * coordinates to that rotation's logical coordinates. + * + * @param rotation the rotation to which the matrix should transform + * @param out the matrix to be set + */ + public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation, + @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) { + switch (rotation) { + case ROTATION_0: + out.reset(); + break; + case ROTATION_90: + out.setRotate(270); + out.postTranslate(0, physicalWidth); + break; + case ROTATION_180: + out.setRotate(180); + out.postTranslate(physicalWidth, physicalHeight); + break; + case ROTATION_270: + out.setRotate(90); + out.postTranslate(physicalHeight, 0); + break; + default: + throw new IllegalArgumentException("Unknown rotation: " + rotation); + } + } } diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 525ac534612d..e1a4402d8964 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -23,6 +23,7 @@ import static android.view.DisplayCutoutProto.BOUND_LEFT; import static android.view.DisplayCutoutProto.BOUND_RIGHT; import static android.view.DisplayCutoutProto.BOUND_TOP; import static android.view.DisplayCutoutProto.INSETS; +import static android.view.Surface.ROTATION_0; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; @@ -31,13 +32,16 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Resources; import android.graphics.Insets; +import android.graphics.Matrix; import android.graphics.Path; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Pair; +import android.util.RotationUtils; import android.util.proto.ProtoOutputStream; +import android.view.Surface.Rotation; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -69,6 +73,9 @@ public final class DisplayCutout { "com.android.internal.display_cutout_emulation"; private static final Rect ZERO_RECT = new Rect(); + private static final CutoutPathParserInfo EMPTY_PARSER_INFO = new CutoutPathParserInfo( + 0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */, + 0 /* rotation */, 0f /* scale */); /** * An instance where {@link #isEmpty()} returns {@code true}. @@ -76,7 +83,7 @@ public final class DisplayCutout { * @hide */ public static final DisplayCutout NO_CUTOUT = new DisplayCutout( - ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, + ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, EMPTY_PARSER_INFO, false /* copyArguments */); @@ -96,11 +103,15 @@ public final class DisplayCutout { @GuardedBy("CACHE_LOCK") private static Insets sCachedWaterfallInsets; + @GuardedBy("CACHE_LOCK") + private static CutoutPathParserInfo sCachedCutoutPathParserInfo; + @GuardedBy("CACHE_LOCK") + private static Path sCachedCutoutPath; + private final Rect mSafeInsets; @NonNull private final Insets mWaterfallInsets; - /** * The bound is at the left of the screen. * @hide @@ -210,6 +221,7 @@ public final class DisplayCutout { } return result; } + @Override public boolean equals(@Nullable Object o) { if (o == this) { @@ -232,6 +244,106 @@ public final class DisplayCutout { private final Bounds mBounds; /** + * Stores all the needed info to create the cutout paths. + * + * @hide + */ + public static class CutoutPathParserInfo { + private final int mDisplayWidth; + private final int mDisplayHeight; + private final float mDensity; + private final String mCutoutSpec; + private final @Rotation int mRotation; + private final float mScale; + + public CutoutPathParserInfo(int displayWidth, int displayHeight, float density, + String cutoutSpec, @Rotation int rotation, float scale) { + mDisplayWidth = displayWidth; + mDisplayHeight = displayHeight; + mDensity = density; + mCutoutSpec = cutoutSpec == null ? "" : cutoutSpec; + mRotation = rotation; + mScale = scale; + } + + public CutoutPathParserInfo(CutoutPathParserInfo cutoutPathParserInfo) { + mDisplayWidth = cutoutPathParserInfo.mDisplayWidth; + mDisplayHeight = cutoutPathParserInfo.mDisplayHeight; + mDensity = cutoutPathParserInfo.mDensity; + mCutoutSpec = cutoutPathParserInfo.mCutoutSpec; + mRotation = cutoutPathParserInfo.mRotation; + mScale = cutoutPathParserInfo.mScale; + } + + public int getDisplayWidth() { + return mDisplayWidth; + } + + public int getDisplayHeight() { + return mDisplayHeight; + } + + public float getDensity() { + return mDensity; + } + + public @NonNull String getCutoutSpec() { + return mCutoutSpec; + } + + public int getRotation() { + return mRotation; + } + + public float getScale() { + return mScale; + } + + private boolean hasCutout() { + return !mCutoutSpec.isEmpty(); + } + + @Override + public int hashCode() { + int result = 0; + result = result * 48271 + Integer.hashCode(mDisplayWidth); + result = result * 48271 + Integer.hashCode(mDisplayHeight); + result = result * 48271 + Float.hashCode(mDensity); + result = result * 48271 + mCutoutSpec.hashCode(); + result = result * 48271 + Integer.hashCode(mRotation); + result = result * 48271 + Float.hashCode(mScale); + return result; + } + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (o instanceof CutoutPathParserInfo) { + CutoutPathParserInfo c = (CutoutPathParserInfo) o; + return mDisplayWidth == c.mDisplayWidth && mDisplayHeight == c.mDisplayHeight + && mDensity == c.mDensity && mCutoutSpec.equals(c.mCutoutSpec) + && mRotation == c.mRotation && mScale == c.mScale; + } + return false; + } + + @Override + public String toString() { + return "CutoutPathParserInfo{displayWidth=" + mDisplayWidth + + " displayHeight=" + mDisplayHeight + + " density={" + mDensity + "}" + + " cutoutSpec={" + mCutoutSpec + "}" + + " rotation={" + mRotation + "}" + + " scale={" + mScale + "}" + + "}"; + } + } + + private final @NonNull CutoutPathParserInfo mCutoutPathParserInfo; + + /** * Creates a DisplayCutout instance. * * <p>Note that this is only useful for tests. For production code, developers should always @@ -251,7 +363,8 @@ public final class DisplayCutout { // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE) public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft, @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom) { - this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, true); + this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, null, + true); } /** @@ -276,7 +389,7 @@ public final class DisplayCutout { @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom, @NonNull Insets waterfallInsets) { this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom, - true); + null, true); } /** @@ -294,7 +407,7 @@ public final class DisplayCutout { // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE) @Deprecated public DisplayCutout(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) { - this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects), + this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects), null, true /* copyArguments */); } @@ -303,28 +416,42 @@ public final class DisplayCutout { * * @param safeInsets the insets from each edge which avoid the display cutout as returned by * {@link #getSafeInsetTop()} etc. + * @param waterfallInsets the insets for the curved areas in waterfall display. + * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed, + * it's treated as an empty rectangle (0,0)-(0,0). + * @param boundTop the top bounding rect of the display cutout in pixels. If null is passed, + * it's treated as an empty rectangle (0,0)-(0,0). + * @param boundRight the right bounding rect of the display cutout in pixels. If null is + * passed, it's treated as an empty rectangle (0,0)-(0,0). + * @param boundBottom the bottom bounding rect of the display cutout in pixels. If null is + * passed, it's treated as an empty rectangle (0,0)-(0,0). + * @param info the cutout path parser info. * @param copyArguments if true, create a copy of the arguments. If false, the passed arguments * are not copied and MUST remain unchanged forever. */ - private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft, - Rect boundTop, Rect boundRight, Rect boundBottom, boolean copyArguments) { + private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft, Rect boundTop, + Rect boundRight, Rect boundBottom, CutoutPathParserInfo info, + boolean copyArguments) { mSafeInsets = getCopyOrRef(safeInsets, copyArguments); mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets; mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments); + mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info; } private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect[] bounds, - boolean copyArguments) { + CutoutPathParserInfo info, boolean copyArguments) { mSafeInsets = getCopyOrRef(safeInsets, copyArguments); mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets; mBounds = new Bounds(bounds, copyArguments); + mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info; } - private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds) { + private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds, + CutoutPathParserInfo info) { mSafeInsets = safeInsets; mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets; mBounds = bounds; - + mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info; } private static Rect getCopyOrRef(Rect r, boolean copyArguments) { @@ -534,10 +661,70 @@ public final class DisplayCutout { return mBounds.getRect(BOUNDS_POSITION_BOTTOM); } + /** + * Returns a {@link Path} that contains the cutout paths of all sides on the display. + * + * To get a cutout path for one specific side, apps can intersect the {@link Path} with the + * {@link Rect} obtained from {@link #getBoundingRectLeft()}, {@link #getBoundingRectTop()}, + * {@link #getBoundingRectRight()} or {@link #getBoundingRectBottom()}. + * + * @return a {@link Path} contains all the cutout paths based on display coordinate. Returns + * null if there is no cutout on the display. + */ + public @Nullable Path getCutoutPath() { + if (!mCutoutPathParserInfo.hasCutout()) { + return null; + } + synchronized (CACHE_LOCK) { + if (mCutoutPathParserInfo.equals(sCachedCutoutPathParserInfo)) { + return sCachedCutoutPath; + } + } + final CutoutSpecification cutoutSpec = new CutoutSpecification.Parser( + mCutoutPathParserInfo.getDensity(), mCutoutPathParserInfo.getDisplayWidth(), + mCutoutPathParserInfo.getDisplayHeight()) + .parse(mCutoutPathParserInfo.getCutoutSpec()); + + final Path cutoutPath = cutoutSpec.getPath(); + if (cutoutPath == null || cutoutPath.isEmpty()) { + return null; + } + final Matrix matrix = new Matrix(); + if (mCutoutPathParserInfo.getRotation() != ROTATION_0) { + RotationUtils.transformPhysicalToLogicalCoordinates( + mCutoutPathParserInfo.getRotation(), + mCutoutPathParserInfo.getDisplayWidth(), + mCutoutPathParserInfo.getDisplayHeight(), + matrix + ); + } + matrix.postScale(mCutoutPathParserInfo.getScale(), mCutoutPathParserInfo.getScale()); + cutoutPath.transform(matrix); + + synchronized (CACHE_LOCK) { + sCachedCutoutPathParserInfo = new CutoutPathParserInfo(mCutoutPathParserInfo); + sCachedCutoutPath = cutoutPath; + } + return cutoutPath; + } + + /** + * @return the {@link CutoutPathParserInfo}; + * + * @hide + */ + public CutoutPathParserInfo getCutoutPathParserInfo() { + return mCutoutPathParserInfo; + } + @Override public int hashCode() { - return (mSafeInsets.hashCode() * 48271 + mBounds.hashCode()) * 48271 - + mWaterfallInsets.hashCode(); + int result = 0; + result = 48271 * result + mSafeInsets.hashCode(); + result = 48271 * result + mBounds.hashCode(); + result = 48271 * result + mWaterfallInsets.hashCode(); + result = 48271 * result + mCutoutPathParserInfo.hashCode(); + return result; } @Override @@ -548,7 +735,8 @@ public final class DisplayCutout { if (o instanceof DisplayCutout) { DisplayCutout c = (DisplayCutout) o; return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds) - && mWaterfallInsets.equals(c.mWaterfallInsets); + && mWaterfallInsets.equals(c.mWaterfallInsets) + && mCutoutPathParserInfo.equals(c.mCutoutPathParserInfo); } return false; } @@ -558,6 +746,7 @@ public final class DisplayCutout { return "DisplayCutout{insets=" + mSafeInsets + " waterfall=" + mWaterfallInsets + " boundingRect={" + mBounds + "}" + + " cutoutPathParserInfo={" + mCutoutPathParserInfo + "}" + "}"; } @@ -607,7 +796,7 @@ public final class DisplayCutout { } return new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, - false /* copyArguments */); + mCutoutPathParserInfo, false /* copyArguments */); } private Rect insetInsets(int insetLeft, int insetTop, int insetRight, int insetBottom, @@ -638,7 +827,8 @@ public final class DisplayCutout { * @hide */ public DisplayCutout replaceSafeInsets(Rect safeInsets) { - return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds); + return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds, + mCutoutPathParserInfo); } private static int atLeastZero(int value) { @@ -658,16 +848,18 @@ public final class DisplayCutout { for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) { bounds[i] = (pos == i) ? new Rect(left, top, right, bottom) : new Rect(); } - return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */); + return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null, false /* copyArguments */); } /** - * Creates an instance from a bounding and waterfall insets. + * Creates an instance from bounds, waterfall insets and CutoutPathParserInfo. * * @hide */ - public static DisplayCutout fromBoundsAndWaterfall(Rect[] bounds, Insets waterfallInsets) { - return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, false /* copyArguments */); + public static DisplayCutout constructDisplayCutout(Rect[] bounds, Insets waterfallInsets, + CutoutPathParserInfo info) { + return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, info, + false /* copyArguments */); } /** @@ -676,7 +868,8 @@ public final class DisplayCutout { * @hide */ public static DisplayCutout fromBounds(Rect[] bounds) { - return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */); + return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null /* cutoutPathParserInfo */, + false /* copyArguments */); } /** @@ -686,10 +879,12 @@ public final class DisplayCutout { * * @hide */ - public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, int displayHeight) { - return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation), + public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, + int displayHeight) { + return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout), + res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation), displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT, - loadWaterfallInset(res)); + loadWaterfallInset(res)).second; } /** @@ -699,7 +894,7 @@ public final class DisplayCutout { */ public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) { return pathAndDisplayCutoutFromSpec( - res.getString(R.string.config_mainBuiltInDisplayCutout), + res.getString(R.string.config_mainBuiltInDisplayCutout), null, displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT, loadWaterfallInset(res)).first; } @@ -710,14 +905,30 @@ public final class DisplayCutout { * @hide */ @VisibleForTesting(visibility = PRIVATE) - public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight, - float density, Insets waterfallInsets) { + public static DisplayCutout fromSpec(String pathSpec, int displayWidth, + int displayHeight, float density, Insets waterfallInsets) { return pathAndDisplayCutoutFromSpec( - spec, displayWidth, displayHeight, density, waterfallInsets).second; + pathSpec, null, displayWidth, displayHeight, density, waterfallInsets) + .second; } - private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(String spec, - int displayWidth, int displayHeight, float density, Insets waterfallInsets) { + /** + * Gets the cutout path and the corresponding DisplayCutout instance from the spec string. + * + * @param pathSpec the spec string read from config_mainBuiltInDisplayCutout. + * @param rectSpec the spec string read from config_mainBuiltInDisplayCutoutRectApproximation. + * @param displayWidth the display width. + * @param displayHeight the display height. + * @param density the display density. + * @param waterfallInsets the waterfall insets of the display. + * @return a Pair contains the cutout path and the corresponding DisplayCutout instance. + */ + private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec( + String pathSpec, String rectSpec, int displayWidth, int displayHeight, float density, + Insets waterfallInsets) { + // Always use the rect approximation spec to create the cutout if it's not null because + // transforming and sending a Region constructed from a path is very costly. + String spec = rectSpec != null ? rectSpec : pathSpec; if (TextUtils.isEmpty(spec) && waterfallInsets.equals(Insets.NONE)) { return NULL_PAIR; } @@ -750,9 +961,12 @@ public final class DisplayCutout { Math.max(waterfallInsets.bottom, safeInset.bottom)); } + final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(displayWidth, + displayHeight, density, pathSpec.trim(), ROTATION_0, 1f /* scale */); + final DisplayCutout cutout = new DisplayCutout( - safeInset, waterfallInsets, boundLeft, boundTop, - boundRight, boundBottom, false /* copyArguments */); + safeInset, waterfallInsets, boundLeft, boundTop, boundRight, boundBottom, + cutoutPathParserInfo , false /* copyArguments */); final Pair<Path, DisplayCutout> result = new Pair<>(cutoutSpec.getPath(), cutout); synchronized (CACHE_LOCK) { sCachedSpec = spec; @@ -817,6 +1031,12 @@ public final class DisplayCutout { out.writeTypedObject(cutout.mSafeInsets, flags); out.writeTypedArray(cutout.mBounds.getRects(), flags); out.writeTypedObject(cutout.mWaterfallInsets, flags); + out.writeInt(cutout.mCutoutPathParserInfo.getDisplayWidth()); + out.writeInt(cutout.mCutoutPathParserInfo.getDisplayHeight()); + out.writeFloat(cutout.mCutoutPathParserInfo.getDensity()); + out.writeString(cutout.mCutoutPathParserInfo.getCutoutSpec()); + out.writeInt(cutout.mCutoutPathParserInfo.getRotation()); + out.writeFloat(cutout.mCutoutPathParserInfo.getScale()); } } @@ -860,9 +1080,17 @@ public final class DisplayCutout { Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH]; in.readTypedArray(bounds, Rect.CREATOR); Insets waterfallInsets = in.readTypedObject(Insets.CREATOR); + int displayWidth = in.readInt(); + int displayHeight = in.readInt(); + float density = in.readFloat(); + String cutoutSpec = in.readString(); + int rotation = in.readInt(); + float scale = in.readFloat(); + final CutoutPathParserInfo info = new CutoutPathParserInfo( + displayWidth, displayHeight, density, cutoutSpec, rotation, scale); return new DisplayCutout( - safeInsets, waterfallInsets, bounds, false /* copyArguments */); + safeInsets, waterfallInsets, bounds, info, false /* copyArguments */); } public DisplayCutout get() { @@ -884,7 +1112,15 @@ public final class DisplayCutout { bounds.scale(scale); final Rect waterfallInsets = mInner.mWaterfallInsets.toRect(); waterfallInsets.scale(scale); - mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds); + final CutoutPathParserInfo info = new CutoutPathParserInfo( + mInner.mCutoutPathParserInfo.getDisplayWidth(), + mInner.mCutoutPathParserInfo.getDisplayHeight(), + mInner.mCutoutPathParserInfo.getDensity(), + mInner.mCutoutPathParserInfo.getCutoutSpec(), + mInner.mCutoutPathParserInfo.getRotation(), + scale); + + mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info); } @Override diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 106e3927656f..0a1a23116941 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -414,6 +414,15 @@ public final class SurfaceControl implements Parcelable { */ public static final int SECURE = 0x00000080; + + /** + * Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is + * set. This blocks the client until all the buffers have been presented. If the buffers + * have presentation timestamps, then we may drop buffers. + * @hide + */ + public static final int ENABLE_BACKPRESSURE = 0x00000100; + /** * Surface creation flag: Creates a surface where color components are interpreted * as "non pre-multiplied" by their alpha channel. Of course this flag is diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java index acbcbfad1a75..b10370aa5d4c 100644 --- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java +++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java @@ -85,13 +85,12 @@ public class SyncRtSurfaceTransactionApplier { for (int i = params.length - 1; i >= 0; i--) { SurfaceParams surfaceParams = params[i]; SurfaceControl surface = surfaceParams.surface; + if (frame > 0) { + t.deferTransactionUntil(surface, mTargetSc, frame); + } applyParams(t, surfaceParams, mTmpFloat9); } - if (mTargetViewRootImpl != null) { - mTargetViewRootImpl.mergeWithNextTransaction(t, frame); - } else { - t.apply(); - } + t.apply(); } public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 52d0062abdf2..844fc267c3cb 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -10109,7 +10109,7 @@ public final class ViewRootImpl implements ViewParent, * Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures * you can add transactions to the upcoming frame. */ - public void mergeWithNextTransaction(Transaction t, long frameNumber) { + void mergeWithNextTransaction(Transaction t, long frameNumber) { if (mBlastBufferQueue != null) { mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber); } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 0e878fcf0e24..4ef63ae93016 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -262,7 +262,7 @@ public: } } - binder::Status onScreenCaptureComplete( + binder::Status onScreenCaptureCompleted( const gui::ScreenCaptureResults& captureResults) override { JNIEnv* env = getenv(); if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) { @@ -270,6 +270,7 @@ public: gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr); return binder::Status::ok(); } + captureResults.fence->waitForever(""); jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( env, captureResults.buffer->toAHardwareBuffer()); const jint namedColorSpace = diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 396f95446bf6..4acdb1663403 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2541,7 +2541,7 @@ <!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo. @hide --> <permission android:name="android.permission.START_TASKS_FROM_RECENTS" - android:protectionLevel="signature|privileged" /> + android:protectionLevel="signature|privileged|recents" /> <!-- @SystemApi @hide Allows an application to call APIs that allow it to do interactions across the users on the device, using singleton services and @@ -2606,7 +2606,7 @@ <!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks --> <permission android:name="android.permission.REMOVE_TASKS" - android:protectionLevel="signature|documenter" /> + android:protectionLevel="signature|documenter|recents" /> <!-- @deprecated Use MANAGE_ACTIVITY_TASKS instead. @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks --> @@ -2615,7 +2615,7 @@ <!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove tasks --> <permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" - android:protectionLevel="signature" /> + android:protectionLevel="signature|recents" /> <!-- @SystemApi @TestApi @hide Allows an application to embed other activities --> <permission android:name="android.permission.ACTIVITY_EMBEDDING" @@ -2697,11 +2697,11 @@ The app can check whether it has this authorization by calling {@link android.provider.Settings#canDrawOverlays Settings.canDrawOverlays()}. - <p>Protection level: signature|appop|preinstalled|pre23|development --> + <p>Protection level: signature|appop|installer|recents|appPredictor|pre23|development --> <permission android:name="android.permission.SYSTEM_ALERT_WINDOW" android:label="@string/permlab_systemAlertWindow" android:description="@string/permdesc_systemAlertWindow" - android:protectionLevel="signature|appop|preinstalled|pre23|development" /> + android:protectionLevel="signature|appop|installer|recents|appPredictor|pre23|development" /> <!-- @SystemApi @hide Allows an application to create windows using the type {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}, @@ -3284,7 +3284,7 @@ and its icons. <p>Not for use by third-party applications. --> <permission android:name="android.permission.STATUS_BAR" - android:protectionLevel="signature|privileged" /> + android:protectionLevel="signature|privileged|recents" /> <!-- Allows an application to trigger bugreport via shell using the bugreport API. <p>Not for use by third-party applications. @@ -3451,7 +3451,7 @@ critical UI such as the home screen. @hide --> <permission android:name="android.permission.STOP_APP_SWITCHES" - android:protectionLevel="signature|privileged" /> + android:protectionLevel="signature|privileged|recents" /> <!-- @SystemApi Allows an application to retrieve private information about the current top activity, such as any assist context it can provide. @@ -3836,7 +3836,7 @@ @hide --> <permission android:name="android.permission.SET_ORIENTATION" - android:protectionLevel="signature" /> + android:protectionLevel="signature|recents" /> <!-- @SystemApi Allows low-level access to setting the pointer speed. <p>Not for use by third-party applications. @@ -4100,7 +4100,7 @@ @hide @removed --> <permission android:name="android.permission.READ_FRAME_BUFFER" - android:protectionLevel="signature" /> + android:protectionLevel="signature|recents" /> <!-- Allows an application to use InputFlinger's low level features. @hide --> @@ -5277,7 +5277,7 @@ <!-- @SystemApi Allows modifying accessibility state. @hide --> <permission android:name="android.permission.MANAGE_ACCESSIBILITY" - android:protectionLevel="signature|setup" /> + android:protectionLevel="signature|setup|recents" /> <!-- @SystemApi Allows an app to grant a profile owner access to device identifiers. <p>Not for use by third-party applications. diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 415a0a2ed595..14f1e0e20ef6 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -9276,6 +9276,12 @@ {@deprecated Use app:fontProviderCerts with Jetpack Core library instead.} --> <attr name="fontProviderCerts" format="reference" /> + <!-- Provides the system font family name to check before downloading the font. For example + if the fontProviderQuery asked for "Sans Serif", it is possible to define + fontProviderSystemFontFamily as "sans-serif" to tell the system to use "sans-serif" font + family if it exists on the system. + --> + <attr name="fontProviderSystemFontFamily" format="string" /> </declare-styleable> <!-- Attributes that are read when parsing a tag. --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index ddf3c5f09e9e..9c1c51cdd48e 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3058,6 +3058,7 @@ <public name="sspSuffix" /> <public name="pathAdvancedPattern" /> <public name="sspAdvancedPattern" /> + <public name="fontProviderSystemFontFamily" /> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp new file mode 100644 index 000000000000..e1787762e067 --- /dev/null +++ b/core/tests/GameManagerTests/Android.bp @@ -0,0 +1,29 @@ +// 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. + +android_test { + name: "FrameworksCoreGameManagerTests", + // Include all test java files + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.test.rules", + "frameworks-base-testutils", + "junit", + "truth-prebuilt", + ], + libs: ["android.test.runner"], + platform_apis: true, + certificate: "platform", + test_suites: ["device-tests"], +} diff --git a/core/tests/GameManagerTests/AndroidManifest.xml b/core/tests/GameManagerTests/AndroidManifest.xml new file mode 100644 index 000000000000..da6636b7b192 --- /dev/null +++ b/core/tests/GameManagerTests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.graphics.gamemanagertests" + android:sharedUserId="android.uid.system" > + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.graphics.gamemanagertests" + android:label="Game Manager Tests"/> + +</manifest> diff --git a/core/tests/GameManagerTests/AndroidTest.xml b/core/tests/GameManagerTests/AndroidTest.xml new file mode 100644 index 000000000000..bfb9802ba1ad --- /dev/null +++ b/core/tests/GameManagerTests/AndroidTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<configuration description="Runs Game Manager Tests."> + <option name="test-suite-tag" value="apct"/> + <option name="test-suite-tag" value="apct-instrumentation"/> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="FrameworksCoreGameManagerTests.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.graphics.gamemanagertests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false" /> + </test> +</configuration> diff --git a/core/tests/GameManagerTests/src/android/graphics/GameManagerTests.java b/core/tests/GameManagerTests/src/android/graphics/GameManagerTests.java new file mode 100644 index 000000000000..d861a894500a --- /dev/null +++ b/core/tests/GameManagerTests/src/android/graphics/GameManagerTests.java @@ -0,0 +1,83 @@ +/* + * 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; + +import static junit.framework.Assert.assertEquals; + +import android.graphics.GameManager.GameMode; +import android.util.ArrayMap; +import android.util.Pair; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Unit tests for {@link GameManager}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class GameManagerTests { + private static final String PACKAGE_NAME_0 = "com.android.app0"; + private static final String PACKAGE_NAME_1 = "com.android.app1"; + + private TestGameManagerService mService; + private GameManager mGameManager; + + @Before + public void setUp() { + mService = new TestGameManagerService(); + mGameManager = new GameManager( + InstrumentationRegistry.getContext(), mService); + } + + @Test + public void testGameModeGetterSetter() { + assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + mGameManager.getGameMode(PACKAGE_NAME_0)); + + mGameManager.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD); + assertEquals(GameManager.GAME_MODE_STANDARD, + mGameManager.getGameMode(PACKAGE_NAME_1)); + + mGameManager.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE); + assertEquals(GameManager.GAME_MODE_PERFORMANCE, + mGameManager.getGameMode(PACKAGE_NAME_1)); + } + + private final class TestGameManagerService extends IGameManagerService.Stub { + private final ArrayMap<Pair<String, Integer>, Integer> mGameModes = new ArrayMap<>(); + + @Override + public @GameMode int getGameMode(String packageName, int userId) { + final Pair key = Pair.create(packageName, userId); + if (mGameModes.containsKey(key)) { + return mGameModes.get(key); + } + return GameManager.GAME_MODE_UNSUPPORTED; + } + + @Override + public void setGameMode(String packageName, @GameMode int gameMode, int userId) { + mGameModes.put(Pair.create(packageName, userId), gameMode); + } + } +} diff --git a/core/tests/coretests/res/font/samplexmldownloadedfont.xml b/core/tests/coretests/res/font/samplexmldownloadedfont.xml index f1bdc473b9d2..9c32ffb0f4c9 100644 --- a/core/tests/coretests/res/font/samplexmldownloadedfont.xml +++ b/core/tests/coretests/res/font/samplexmldownloadedfont.xml @@ -2,5 +2,6 @@ <font-family xmlns:android="http://schemas.android.com/apk/res/android" android:fontProviderAuthority="com.example.test.fontprovider.authority" android:fontProviderPackage="com.example.test.fontprovider.package" - android:fontProviderQuery="MyRequestedFont"> + android:fontProviderQuery="MyRequestedFont" + android:fontProviderSystemFontFamily="my-request-font"> </font-family>
\ No newline at end of file diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java index 24f45a57dabc..bffd1e4a86d6 100644 --- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java +++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.content.pm.PackageParser.SigningDetails; +import android.util.ArraySet; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -35,6 +36,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Set; + @RunWith(AndroidJUnit4.class) @SmallTest public class SigningDetailsTest { @@ -208,8 +211,8 @@ public class SigningDetailsTest { SigningDetails result1 = noLineageDetails.mergeLineageWith(lineageDetails); SigningDetails result2 = lineageDetails.mergeLineageWith(noLineageDetails); - assertTrue(result1 == lineageDetails); - assertTrue(result2 == lineageDetails); + assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE); + assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE); } @Test @@ -271,8 +274,10 @@ public class SigningDetailsTest { SigningDetails result1 = singleSignerDetails.mergeLineageWith(fullLineageDetails); SigningDetails result2 = fullLineageDetails.mergeLineageWith(singleSignerDetails); - assertTrue(result1 == fullLineageDetails); - assertTrue(result2 == fullLineageDetails); + assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE, + THIRD_SIGNATURE); + assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE, + THIRD_SIGNATURE); } @Test @@ -605,6 +610,213 @@ public class SigningDetailsTest { assertTrue(secondLineageDetails.hasCommonAncestor(firstLineageDetails)); } + @Test + public void hasCommonSignerWithCapabilities_singleMatchingSigner_returnsTrue() + throws Exception { + // The hasCommonSignerWithCapabilities method is intended to grant the specified + // capabilities to a requesting package that has a common signer in the lineage (or as the + // current signer) even if their signing identities have diverged. This test verifies if the + // two SigningDetails have the same single signer then the requested capability can be + // granted since the current signer always has all capabilities granted. + SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE); + SigningDetails secondSignerDetails = createSigningDetails(FIRST_SIGNATURE); + + assertTrue(firstDetails.hasCommonSignerWithCapability(secondSignerDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_singleDifferentSigners_returnsFalse() + throws Exception { + // If each package is signed by a single different signer then the method should return + // false since there is no shared signer. + SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE); + SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE); + + assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_oneWithMultipleSigners_returnsFalse() + throws Exception { + // If one of the packages is signed with multiple signers and the other only a single signer + // this method should return false since all signers must match exactly for multiple signer + // cases. + SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); + SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE); + + assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_multipleMatchingSigners_returnsTrue() + throws Exception { + // if both packages are signed by the same multiple signers then this method should return + // true since the current signer is granted all capabilities. + SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); + SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE, FIRST_SIGNATURE); + + assertTrue(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertTrue(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_singleSignerInLineage_returnsTrue() + throws Exception { + // if a single signer is in the lineage and that previous signer has the requested + // capability then this method should return true. + SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, + new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); + SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE); + + assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_singleSignerInLineageWOCapability_returnsFalse() + throws Exception { + // If a single signer is in the lineage and that previous signer does not have the requested + // capability then this method should return false. + SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, + new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES}); + SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE); + + assertFalse(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_singleSignerMatchesCurrentSigner_returnsTrue() + throws Exception { + // If a requesting app is signed by the same current signer as an app with a lineage the + // method should return true since the current signer is granted all capabilities. + SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, + new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES}); + SigningDetails singleSignerDetails = createSigningDetails(SECOND_SIGNATURE); + + assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_divergingSignersWithCommonSigner_returnsTrue() + throws Exception { + // This method is intended to allow granting a capability to another app that has a common + // signer in the lineage with the capability still granted; this test verifies when the + // current signers diverge but a common ancestor has the requested capability this method + // returns true. + SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, FOURTH_SIGNATURE); + + assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_divergingSignersOneGrantsCapability_returnsTrue() + throws Exception { + // If apps have multiple common signers in the lineage with one denying the requested + // capability but the other granting it this method should return true. + SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, FOURTH_SIGNATURE); + + assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_divergingSignersNoneGrantCapability_returnsFalse() + throws Exception { + // If apps have multiple common signers in the lineage with all denying the requested + // capability this method should return false. + SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{SHARED_USER_ID, AUTH, DEFAULT_CAPABILITIES}); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, FOURTH_SIGNATURE); + + assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION)); + } + + @Test + public void + hasCommonSignerWithCapabilities_divergingSignersNoneGrantsAllCapabilities_returnsTrue() + throws Exception { + // If an app has multiple common signers in the lineage, each granting one of the requested + // capabilities but neither granting all this method should return false since a single + // common ancestor must grant all requested capabilities. + SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{SHARED_USER_ID, PERMISSION, DEFAULT_CAPABILITIES}); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, FOURTH_SIGNATURE); + + assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION | SHARED_USER_ID)); + } + + @Test + public void hasCommonSignerWithCapabilities_currentSignerInLineageOfRequestingApp_returnsTrue() + throws Exception { + // If the current signer of an app is in the lineage of the requesting app then this method + // should return true since the current signer is granted all capabilities. + SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + + assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_currentSignerInLineageOfDeclaringApp_returnsTrue() + throws Exception { + // If the current signer of a requesting app with a lineage is in the lineage of the + // declaring app and that previous signature is granted the requested capability the method + // should return true. + SigningDetails declaringDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); + SigningDetails requestingDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE); + + assertTrue(declaringDetails.hasCommonSignerWithCapability(requestingDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_oneSignerNullLineage_returns() throws Exception { + // While the pastSigningCertificates should only be null in the case of multiple current + // signers there are instances where this can be null with a single signer; verify that a + // null pastSigningCertificates array in either SigningDetails does not result in a + // NullPointerException. + SigningDetails firstDetails = createSigningDetails(true, FIRST_SIGNATURE); + SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE); + + assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_unknownSigner_returnsFalse() throws Exception { + // An unknown SigningDetails for either instance should immediately result in false being + // returned. + SigningDetails firstDetails = SigningDetails.UNKNOWN; + SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE); + + assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception { int[] capabilities = new int[signers.length]; for (int i = 0; i < capabilities.length; i++) { @@ -629,10 +841,34 @@ public class SigningDetailsTest { } private SigningDetails createSigningDetails(String... signers) throws Exception { + return createSigningDetails(false, signers); + } + + private SigningDetails createSigningDetails(boolean useNullPastSigners, String... signers) + throws Exception { Signature[] currentSignatures = new Signature[signers.length]; for (int i = 0; i < signers.length; i++) { currentSignatures[i] = new Signature(signers[i]); } - return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null); + // If there are multiple signers then the pastSigningCertificates should be set to null, but + // if there is only a single signer both the current signer and the past signers should be + // set to that one signer. + if (signers.length > 1) { + return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null); + } + return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, currentSignatures); + } + + private void assertSigningDetailsContainsLineage(SigningDetails details, + String... pastSigners) { + // This method should only be invoked for results that contain a single signer. + assertEquals(1, details.signatures.length); + assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase( + pastSigners[pastSigners.length - 1])); + Set<String> signatures = new ArraySet<>(pastSigners); + for (Signature pastSignature : details.pastSigningCertificates) { + assertTrue(signatures.remove(pastSignature.toCharsString())); + } + assertEquals(0, signatures.size()); } } diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java index 7ab9d7f4ffe1..57f01e988440 100644 --- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java +++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java @@ -105,6 +105,7 @@ public class FontResourcesParserTest { assertEquals("com.example.test.fontprovider.authority", providerEntry.getAuthority()); assertEquals("com.example.test.fontprovider.package", providerEntry.getPackage()); assertEquals("MyRequestedFont", providerEntry.getQuery()); + assertEquals("my-request-font", providerEntry.getSystemFontFamilyName()); } @Test diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java index d02c6d588585..a5261aecdbfb 100644 --- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java +++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java @@ -22,6 +22,8 @@ import static android.view.DisplayCutout.fromSpec; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -30,6 +32,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import android.graphics.Insets; +import android.graphics.Path; import android.graphics.Rect; import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -130,6 +133,8 @@ public class DisplayCutoutTest { @Test public void testHasCutout_noCutout() throws Exception { assertTrue(NO_CUTOUT.isBoundsEmpty()); + assertThat(NO_CUTOUT.getWaterfallInsets(), equalTo(Insets.NONE)); + assertThat(NO_CUTOUT.getCutoutPath(), nullValue()); } @Test @@ -165,6 +170,59 @@ public class DisplayCutoutTest { } @Test + public void testGetCutoutPath() throws Exception { + final String cutoutSpecString = "L1,0 L1,1 L0,1 z"; + final int displayWidth = 200; + final int displayHeight = 400; + final float density = 1f; + final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight, + density, Insets.NONE); + assertThat(cutout.getCutoutPath(), notNullValue()); + } + + @Test + public void testGetCutoutPath_caches() throws Exception { + final String cutoutSpecString = "L1,0 L1,1 L0,1 z"; + final int displayWidth = 200; + final int displayHeight = 400; + final float density = 1f; + final Path first = fromSpec(cutoutSpecString, displayWidth, displayHeight, + density, Insets.NONE).getCutoutPath(); + final Path second = fromSpec(cutoutSpecString, displayWidth, displayHeight, + density, Insets.NONE).getCutoutPath(); + assertThat(first, equalTo(second)); + } + + @Test + public void testGetCutoutPath_wontCacheIfCutoutPathParerInfoChanged() throws Exception { + final int displayWidth = 200; + final int displayHeight = 400; + final float density = 1f; + final Path first = fromSpec("L1,0 L1,1 L0,1 z", displayWidth, displayHeight, + density, Insets.NONE).getCutoutPath(); + final Path second = fromSpec("L2,0 L2,2 L0,2 z", displayWidth, displayHeight, + density, Insets.NONE).getCutoutPath(); + assertThat(first, not(equalTo(second))); + } + + @Test + public void testGetCutoutPathParserInfo() throws Exception { + final String cutoutSpecString = "L1,0 L1,1 L0,1 z"; + final int displayWidth = 200; + final int displayHeight = 400; + final float density = 1f; + final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight, + density, Insets.NONE); + assertThat(displayWidth, equalTo(cutout.getCutoutPathParserInfo().getDisplayWidth())); + assertThat(displayHeight, equalTo(cutout.getCutoutPathParserInfo().getDisplayHeight())); + assertThat(density, equalTo(cutout.getCutoutPathParserInfo().getDensity())); + assertThat(cutoutSpecString.trim(), + equalTo(cutout.getCutoutPathParserInfo().getCutoutSpec())); + assertThat(0, equalTo(cutout.getCutoutPathParserInfo().getRotation())); + assertThat(1f, equalTo(cutout.getCutoutPathParserInfo().getScale())); + } + + @Test public void testHashCode() throws Exception { assertEquals(mCutoutWithWaterfall.hashCode(), createCutoutWithWaterfall().hashCode()); assertNotEquals(mCutoutWithWaterfall.hashCode(), mCutoutNumbers.hashCode()); diff --git a/core/tests/mockingcoretests/src/android/view/OWNERS b/core/tests/mockingcoretests/src/android/view/OWNERS new file mode 100644 index 000000000000..9c9f824ba12b --- /dev/null +++ b/core/tests/mockingcoretests/src/android/view/OWNERS @@ -0,0 +1,2 @@ +# Display +per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml index 99c38dbe6ac9..598d2027a0e9 100644 --- a/data/etc/com.android.launcher3.xml +++ b/data/etc/com.android.launcher3.xml @@ -21,5 +21,8 @@ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/> + <permission name="android.permission.START_TASKS_FROM_RECENTS"/> + <permission name="android.permission.STATUS_BAR"/> + <permission name="android.permission.STOP_APP_SWITCHES"/> </privapp-permissions> </permissions> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index f84d947b51ed..ae8e3ce854e3 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -355,7 +355,6 @@ applications that come with the platform <permission name="android.permission.MOVE_PACKAGE"/> <!-- Needed for test only --> <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/> - <permission name="android.permission.NETWORK_AIRPLANE_MODE"/> <permission name="android.permission.OBSERVE_APP_USAGE"/> <permission name="android.permission.NETWORK_SCAN"/> <permission name="android.permission.PACKAGE_USAGE_STATS" /> diff --git a/graphics/java/android/graphics/GameManager.java b/graphics/java/android/graphics/GameManager.java new file mode 100644 index 000000000000..a58aeb4298d6 --- /dev/null +++ b/graphics/java/android/graphics/GameManager.java @@ -0,0 +1,98 @@ +/* + * 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; + +import android.annotation.IntDef; +import android.annotation.SystemService; +import android.annotation.UserHandleAware; +import android.content.Context; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The GameManager allows system apps to modify and query the game mode of apps. + * + * @hide + */ +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +@SystemService(Context.GAME_SERVICE) +public final class GameManager { + + private static final String TAG = "GameManager"; + + private final Context mContext; + private final IGameManagerService mService; + + @IntDef(flag = false, prefix = { "GAME_MODE_" }, value = { + GAME_MODE_UNSUPPORTED, // 0 + GAME_MODE_STANDARD, // 1 + GAME_MODE_PERFORMANCE, // 2 + GAME_MODE_BATTERY, // 3 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface GameMode {} + + public static final int GAME_MODE_UNSUPPORTED = 0; + public static final int GAME_MODE_STANDARD = 1; + public static final int GAME_MODE_PERFORMANCE = 2; + public static final int GAME_MODE_BATTERY = 3; + + public GameManager(Context context, Handler handler) throws ServiceNotFoundException { + mContext = context; + mService = IGameManagerService.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.GAME_SERVICE)); + } + + @VisibleForTesting + public GameManager(Context context, IGameManagerService gameManagerService) { + mContext = context; + mService = gameManagerService; + } + + /** + * Returns the game mode for the given package. + */ + // TODO(b/178111358): Add @RequiresPermission. + @UserHandleAware + public @GameMode int getGameMode(String packageName) { + try { + return mService.getGameMode(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the game mode for the given package. + */ + // TODO(b/178111358): Add @RequiresPermission. + @UserHandleAware + public void setGameMode(String packageName, @GameMode int gameMode) { + try { + mService.setGameMode(packageName, gameMode, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/graphics/java/android/graphics/IGameManagerService.aidl b/graphics/java/android/graphics/IGameManagerService.aidl new file mode 100644 index 000000000000..7d3a4fba562e --- /dev/null +++ b/graphics/java/android/graphics/IGameManagerService.aidl @@ -0,0 +1,25 @@ +/* +** 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 android.graphics; + +/** + * @hide + */ +interface IGameManagerService { + int getGameMode(String packageName, int userId); + void setGameMode(String packageName, int gameMode, int userId); +}
\ No newline at end of file diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index a0568bf3bf5c..005a72661091 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -70,6 +70,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * The Typeface class specifies the typeface and intrinsic style of a font. @@ -249,6 +250,21 @@ public class Typeface { } /** + * Returns true if the system has the font family with the name [familyName]. For example + * querying with "sans-serif" would check if the "sans-serif" family is defined in the system + * and return true if does. + * + * @param familyName The name of the font family, cannot be null. If null, exception will be + * thrown. + */ + private static boolean hasFontFamily(@NonNull String familyName) { + Objects.requireNonNull(familyName, "familyName cannot be null"); + synchronized (SYSTEM_FONT_MAP_LOCK) { + return sSystemFontMap.containsKey(familyName); + } + } + + /** * @hide * Used by Resources to load a font resource of type xml. */ @@ -257,6 +273,11 @@ public class Typeface { FamilyResourceEntry entry, AssetManager mgr, String path) { if (entry instanceof ProviderResourceEntry) { final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry; + + String systemFontFamilyName = providerEntry.getSystemFontFamilyName(); + if (systemFontFamilyName != null && hasFontFamily(systemFontFamilyName)) { + return Typeface.create(systemFontFamilyName, NORMAL); + } // Downloadable font List<List<String>> givenCerts = providerEntry.getCerts(); List<List<byte[]>> certs = new ArrayList<>(); diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml index 2e0a5e09e34f..b581f555c234 100644 --- a/libs/WindowManager/Shell/res/layout/pip_menu.xml +++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml @@ -36,8 +36,8 @@ android:layout_height="match_parent"> <ImageButton android:id="@+id/expand_button" - android:layout_width="60dp" - android:layout_height="60dp" + android:layout_width="@dimen/pip_expand_action_size" + android:layout_height="@dimen/pip_expand_action_size" android:layout_gravity="center" android:contentDescription="@string/pip_phone_expand" android:padding="10dp" diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index e25a05c4cfaf..034e65c608a3 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -26,6 +26,9 @@ <!-- The height of the PiP actions container in which the actions are vertically centered. --> <dimen name="pip_action_size">48dp</dimen> + <!-- The width and height of the PiP expand action. --> + <dimen name="pip_expand_action_size">60dp</dimen> + <!-- The padding between actions in the PiP in landscape Note that the PiP does not reflect the configuration of the device, so we can't use -land resources. --> <dimen name="pip_between_action_padding_land">8dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 1df2a4a9030d..bb8a97344664 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -225,8 +225,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, mTaskOrganizer.applyTransaction(wct); // TODO(b/151449487): Only call callback once we enable synchronization if (mListener != null) { + final int taskId = mTaskInfo.taskId; mListenerExecutor.execute(() -> { - mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated); + mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated); }); } } @@ -256,8 +257,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, } if (mListener != null) { + final int taskId = taskInfo.taskId; + final ComponentName baseActivity = taskInfo.baseActivity; mListenerExecutor.execute(() -> { - mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity); + mListener.onTaskCreated(taskId, baseActivity); }); } } @@ -267,8 +270,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; if (mListener != null) { + final int taskId = taskInfo.taskId; mListenerExecutor.execute(() -> { - mListener.onTaskRemovalStarted(taskInfo.taskId); + mListener.onTaskRemovalStarted(taskId); }); } mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false); @@ -289,8 +293,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) { if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; if (mListener != null) { + final int taskId = taskInfo.taskId; mListenerExecutor.execute(() -> { - mListener.onBackPressedOnTaskRoot(taskInfo.taskId); + mListener.onBackPressedOnTaskRoot(taskId); }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java index 3181dbf74ace..58a4baf39614 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -356,11 +356,11 @@ public class DisplayLayout { if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) { return null; } - final Insets waterfallInsets = - RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); if (rotation == ROTATION_0) { return computeSafeInsets(cutout, displayWidth, displayHeight); } + final Insets waterfallInsets = + RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); Rect[] cutoutRects = cutout.getBoundingRectsAll(); final Rect[] newBounds = new Rect[cutoutRects.length]; @@ -372,8 +372,12 @@ public class DisplayLayout { } newBounds[getBoundIndexFromRotation(i, rotation)] = rect; } + final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo(); + final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo( + info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(), + info.getCutoutSpec(), rotation, info.getScale()); return computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets), + DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo), rotated ? displayHeight : displayWidth, rotated ? displayWidth : displayHeight); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java index 4874d3ccae7e..a4cd3c5a583d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java @@ -47,6 +47,11 @@ public class HandlerExecutor implements ShellExecutor { } @Override + public void removeAllCallbacks() { + mHandler.removeCallbacksAndMessages(null); + } + + @Override public void removeCallbacks(@NonNull Runnable r) { mHandler.removeCallbacks(r); } @@ -55,9 +60,4 @@ public class HandlerExecutor implements ShellExecutor { public boolean hasCallback(Runnable r) { return mHandler.hasCallbacks(r); } - - @Override - public Looper getLooper() { - return mHandler.getLooper(); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java index 1149cceb1068..b736fb0b9895 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java @@ -73,6 +73,11 @@ public interface ShellExecutor extends Executor { void executeDelayed(Runnable runnable, long delayMillis); /** + * Removes all pending callbacks. + */ + void removeAllCallbacks(); + + /** * See {@link android.os.Handler#removeCallbacks}. */ void removeCallbacks(Runnable runnable); @@ -81,9 +86,4 @@ public interface ShellExecutor extends Executor { * See {@link android.os.Handler#hasCallbacks(Runnable)}. */ boolean hasCallback(Runnable runnable); - - /** - * Returns the looper that this executor is running on. - */ - Looper getLooper(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java index a74f4761af0c..37a91d0c121c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java @@ -56,7 +56,7 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer private final float[] mColor; private final float mAlpha; private final Rect mRect; - private final Handler mHandler; + private final Executor mMainExecutor; private final Point mDisplaySize = new Point(); private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; @@ -76,13 +76,13 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer @Override public void onOneHandedAnimationStart( OneHandedAnimationController.OneHandedTransitionAnimator animator) { - mHandler.post(() -> showBackgroundPanelLayer()); + mMainExecutor.execute(() -> showBackgroundPanelLayer()); } }; @Override public void onStopFinished(Rect bounds) { - mHandler.post(() -> removeBackgroundPanelLayer()); + mMainExecutor.execute(() -> removeBackgroundPanelLayer()); } public OneHandedBackgroundPanelOrganizer(Context context, DisplayController displayController, @@ -94,7 +94,7 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer mColor = new float[]{defaultRGB, defaultRGB, defaultRGB}; mAlpha = res.getFloat(R.dimen.config_one_handed_background_alpha); mRect = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y); - mHandler = new Handler(); + mMainExecutor = executor; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java index 1ed121f35a59..49b7e050c48b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java @@ -221,8 +221,14 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, displaySize.y); mInputMonitor = InputManager.getInstance().monitorGestureInput( "onehanded-gesture-offset", DEFAULT_DISPLAY); - mInputEventReceiver = new EventReceiver( - mInputMonitor.getInputChannel(), mMainExecutor.getLooper()); + try { + mMainExecutor.executeBlocking(() -> { + mInputEventReceiver = new EventReceiver( + mInputMonitor.getInputChannel(), Looper.myLooper()); + }); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to create input event receiver", e); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java index 60709bef4daf..c7a49ff01d15 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java @@ -132,8 +132,14 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback { if (mIsEnabled) { mInputMonitor = InputManager.getInstance().monitorGestureInput( "onehanded-touch", DEFAULT_DISPLAY); - mInputEventReceiver = new EventReceiver( - mInputMonitor.getInputChannel(), mMainExecutor.getLooper()); + try { + mMainExecutor.executeBlocking(() -> { + mInputEventReceiver = new EventReceiver( + mInputMonitor.getInputChannel(), Looper.myLooper()); + }); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to create input event receiver", e); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index 8bf1b468cb7d..4b118f121767 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -34,6 +34,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import android.util.Size; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; @@ -210,6 +211,11 @@ public class PhonePipMenuController implements PipMenuController { } } + @Nullable + Size getEstimatedMenuSize() { + return mPipMenuView == null ? null : mPipMenuView.getEstimatedMenuSize(); + } + /** * When other components requests the menu controller directly to show the menu, we must * first fire off the request to the other listeners who will then propagate the call @@ -224,13 +230,13 @@ public class PhonePipMenuController implements PipMenuController { * Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu * upon PiP window transition is finished. */ - public void showMenuWithDelay(int menuState, Rect stackBounds, boolean allowMenuTimeout, + public void showMenuWithPossibleDelay(int menuState, Rect stackBounds, boolean allowMenuTimeout, boolean willResizeMenu, boolean showResizeHandle) { // hide all visible controls including close button and etc. first, this is to ensure // menu is totally invisible during the transition to eliminate unpleasant artifacts fadeOutMenu(); showMenuInternal(menuState, stackBounds, allowMenuTimeout, willResizeMenu, - true /* withDelay */, showResizeHandle); + willResizeMenu /* withDelay=willResizeMenu here */, showResizeHandle); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java index 7a634c3eef78..6e3a20d5f2b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java @@ -147,7 +147,7 @@ public class PipInputConsumer { // Choreographer.getSfInstance() must be called on the thread that the input event // receiver should be receiving events mInputEventReceiver = new InputEventReceiver(inputChannel, - mMainExecutor.getLooper(), Choreographer.getSfInstance()); + Looper.myLooper(), Choreographer.getSfInstance()); if (mRegistrationListener != null) { mRegistrationListener.onRegistrationChanged(true /* isRegistered */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 2e515ee91d3a..48942b604a8d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -48,6 +48,7 @@ import android.os.Handler; import android.os.UserHandle; import android.util.Log; import android.util.Pair; +import android.util.Size; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -76,9 +77,6 @@ public class PipMenuView extends FrameLayout { private static final String TAG = "PipMenuView"; - private static final int MESSAGE_INVALID_TYPE = -1; - public static final int MESSAGE_MENU_EXPANDED = 8; - private static final int INITIAL_DISMISS_DELAY = 3500; private static final int POST_INTERACTION_DISMISS_DELAY = 2000; private static final long MENU_FADE_DURATION = 125; @@ -86,8 +84,6 @@ public class PipMenuView extends FrameLayout { private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30; private static final float MENU_BACKGROUND_ALPHA = 0.3f; - private static final float DISMISS_BACKGROUND_ALPHA = 0.6f; - private static final float DISABLED_ACTION_ALPHA = 0.54f; private static final boolean ENABLE_RESIZE_HANDLE = false; @@ -370,6 +366,19 @@ public class PipMenuView extends FrameLayout { } } + /** + * @return estimated {@link Size} for which the width is based on number of actions and + * height based on the height of expand button + top and bottom action bar. + */ + Size getEstimatedMenuSize() { + final int pipActionSize = mContext.getResources().getDimensionPixelSize( + R.dimen.pip_action_size); + final int width = mActions.size() * pipActionSize; + final int height = pipActionSize * 2 + mContext.getResources().getDimensionPixelSize( + R.dimen.pip_expand_action_size); + return new Size(width, height); + } + void setActions(Rect stackBounds, List<RemoteAction> actions) { mActions.clear(); mActions.addAll(actions); 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 41cc59d138fa..8fb358ad74d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -212,8 +212,14 @@ public class PipResizeGestureHandler { // Register input event receiver mInputMonitor = InputManager.getInstance().monitorGestureInput( "pip-resize", mDisplayId); - mInputEventReceiver = new PipResizeInputEventReceiver( - mInputMonitor.getInputChannel(), mMainExecutor.getLooper()); + try { + mMainExecutor.executeBlocking(() -> { + mInputEventReceiver = new PipResizeInputEventReceiver( + mInputMonitor.getInputChannel(), Looper.myLooper()); + }); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to create input event receiver", e); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index c9f5ae28175c..128d13c2ce2e 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 @@ -32,6 +32,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; import android.provider.DeviceConfig; +import android.util.Log; import android.util.Size; import android.view.InputEvent; import android.view.MotionEvent; @@ -95,7 +96,6 @@ public class PipTouchHandler { private int mDeferResizeToNormalBoundsUntilRotation = -1; private int mDisplayRotation; - private final Handler mHandler = new Handler(); private final PipAccessibilityInteractionConnection mConnection; // Behaviour states @@ -176,7 +176,7 @@ public class PipTouchHandler { mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger, mMotionHelper, mainExecutor); mTouchState = new PipTouchState(ViewConfiguration.get(context), - () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, + () -> mMenuController.showMenuWithPossibleDelay(MENU_STATE_FULL, mPipBoundsState.getBounds(), true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()), menuController::hideMenu, @@ -925,16 +925,21 @@ public class PipTouchHandler { } /** - * @return whether the menu will resize as a part of showing the full menu. + * @return {@code true} if the menu should be resized on tap because app explicitly specifies + * PiP window size that is too small to hold all the actions. */ private boolean willResizeMenu() { if (!mEnableResize) { return false; } - return mPipBoundsState.getExpandedBounds().width() - != mPipBoundsState.getNormalBounds().width() - || mPipBoundsState.getExpandedBounds().height() - != mPipBoundsState.getNormalBounds().height(); + final Size estimatedMenuSize = mMenuController.getEstimatedMenuSize(); + if (estimatedMenuSize == null) { + Log.wtf(TAG, "Failed to get estimated menu size"); + return false; + } + final Rect currentBounds = mPipBoundsState.getBounds(); + return currentBounds.width() < estimatedMenuSize.getWidth() + || currentBounds.height() < estimatedMenuSize.getHeight(); } /** diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt index b5d5d0fe8a38..564a418a1082 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt @@ -54,7 +54,7 @@ class AppPairsTestCannotPairNonResizeableApps( @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<Array<Any>> { - val testTag = "testAppPairs_unpairPrimaryAndSecondaryApps" + val testTag = "testAppPairs_cannotPairNonResizeableApps" val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> withTestName { buildTestTag(testTag, configuration) @@ -71,7 +71,7 @@ class AppPairsTestCannotPairNonResizeableApps( appPairsDividerIsInvisible() } windowManagerTrace { - end { + end("onlyResizeableAppWindowVisible") { val nonResizeableApp = nonResizeableApp require(nonResizeableApp != null) { "Non resizeable app not initialized" diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt index 54e074c9c25d..f63eb1db4d5c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt @@ -76,7 +76,7 @@ class AppPairsTestPairPrimaryAndSecondaryApps( } } windowManagerTrace { - end { + end("bothAppWindowsVisible") { isVisible(primaryApp.defaultWindowName) isVisible(secondaryApp.defaultWindowName) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt index 854a5041f631..731d99829b1d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt @@ -85,7 +85,7 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps( } } windowManagerTrace { - end { + end("bothAppWindowsInvisible") { isInvisible(primaryApp.defaultWindowName) isInvisible(secondaryApp.defaultWindowName) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt index c436eb2d01d5..da3450bf9ff6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt @@ -66,7 +66,7 @@ class RotateTwoLaunchedAppsInAppPairsMode( val instrumentation = InstrumentationRegistry.getInstrumentation() val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> withTestName { - buildTestTag("testRotateAndEnterAppPairsMode", configuration) + buildTestTag("testRotateTwoLaunchedAppsInAppPairsMode", configuration) } transitions { executeShellCommand(composePairsCommand( @@ -88,7 +88,7 @@ class RotateTwoLaunchedAppsInAppPairsMode( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - end { + end("bothAppWindowsVisible") { isVisible(primaryApp.defaultWindowName) .isVisible(secondaryApp.defaultWindowName) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt index cd4f4f62d1b3..05543fdb3445 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt @@ -88,7 +88,7 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - end { + end("bothAppWindowsVisible") { isVisible(primaryApp.defaultWindowName) isVisible(secondaryApp.defaultWindowName) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt index af99543199ac..dea5c300f590 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.dsl.runWithFlicker @@ -115,7 +116,7 @@ class EnterLegacySplitScreenTest( ) } windowManagerTrace { - end { + end("appWindowIsVisible") { isVisible(splitScreenApp.defaultWindowName) } } @@ -150,7 +151,7 @@ class EnterLegacySplitScreenTest( ) } windowManagerTrace { - end { + end("appWindowIsVisible") { isVisible(splitScreenApp.defaultWindowName) .isVisible(secondaryApp.defaultWindowName) } @@ -162,6 +163,7 @@ class EnterLegacySplitScreenTest( } } + @FlakyTest(bugId = 173875043) @Test fun testNonResizeableNotDocked() { val testTag = "testNonResizeableNotDocked" @@ -185,7 +187,7 @@ class EnterLegacySplitScreenTest( ) } windowManagerTrace { - end { + end("appWindowIsVisible") { isInvisible(nonResizeableApp.defaultWindowName) } visibleWindowsShownMoreThanOneConsecutiveEntry( diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt index cd9a3c998d58..701b0d05e65c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt @@ -104,7 +104,7 @@ class ExitLegacySplitScreenTest( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - end { + end("appWindowIsInvisible") { isInvisible(splitScreenApp.defaultWindowName) } } @@ -132,7 +132,7 @@ class ExitLegacySplitScreenTest( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - end { + end("appWindowIsVisible") { isVisible(splitScreenApp.defaultWindowName) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt index e79820f520dd..6fca5809b4fa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt @@ -72,7 +72,7 @@ class NonResizableDismissInLegacySplitScreenTest( ) } windowManagerTrace { - end { + end("nonResizeableAppWindowIsVisible") { isVisible(nonResizeableApp.defaultWindowName) .isInvisible(splitScreenApp.defaultWindowName) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt index 280af5d708c9..deae41fae0ca 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt @@ -72,7 +72,7 @@ class NonResizableLaunchInLegacySplitScreenTest( ) } windowManagerTrace { - end { + end("nonResizeableAppWindowIsVisible") { isVisible(nonResizeableApp.defaultWindowName) .isInvisible(splitScreenApp.defaultWindowName) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt index fdf88df0f697..07571c3218a8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt @@ -99,7 +99,7 @@ class RotateOneLaunchedAppTest( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - end { + end("appWindowIsVisible") { isVisible(splitScreenApp.defaultWindowName) } } @@ -131,7 +131,7 @@ class RotateOneLaunchedAppTest( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - end { + end("appWindowIsVisible") { isVisible(splitScreenApp.defaultWindowName) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt index 785ccf003504..d8014d37dfad 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.dsl.runWithFlicker @@ -104,7 +105,7 @@ class RotateTwoLaunchedAppTest( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - end { + end("appWindowIsVisible") { isVisible(splitScreenApp.defaultWindowName) .isVisible(secondaryApp.defaultWindowName) } @@ -113,6 +114,7 @@ class RotateTwoLaunchedAppTest( } } + @FlakyTest(bugId = 173875043) @Test fun testRotateAndEnterSplitScreenMode() { val testTag = "testRotateAndEnterSplitScreenMode" @@ -141,7 +143,7 @@ class RotateTwoLaunchedAppTest( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - end { + end("appWindowIsVisible") { isVisible(splitScreenApp.defaultWindowName) .isVisible(secondaryApp.defaultWindowName) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index 812353ffe955..c21b594246b9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -131,7 +131,7 @@ class PipKeyboardTest( } assertions { windowManagerTrace { - end { + end("imeWindowAboveApp") { isAboveWindow(IME_WINDOW_NAME, testApp.defaultWindowName) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java index 5f5c30bb6207..bf84a6e30c98 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java @@ -40,6 +40,11 @@ public class TestShellExecutor implements ShellExecutor { } @Override + public void removeAllCallbacks() { + mRunnables.clear(); + } + + @Override public void removeCallbacks(Runnable r) { mRunnables.remove(r); } @@ -49,11 +54,6 @@ public class TestShellExecutor implements ShellExecutor { return mRunnables.contains(r); } - @Override - public Looper getLooper() { - return null; - } - public void flushAll() { for (Runnable r : mRunnables) { r.run(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java index 9219f15afc7f..bbe8891817d6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java @@ -33,6 +33,7 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; import org.junit.Before; @@ -48,7 +49,7 @@ import java.util.ArrayList; @RunWith(AndroidTestingRunner.class) public class OneHandedTimeoutHandlerTest extends OneHandedTestCase { private OneHandedTimeoutHandler mTimeoutHandler; - private ShellExecutor mMainExecutor; + private TestShellExecutor mMainExecutor; @Before public void setUp() throws Exception { @@ -104,34 +105,4 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase { mTimeoutHandler.resetTimer(); assertTrue(mTimeoutHandler.hasScheduledTimeout()); } - - private class TestShellExecutor implements ShellExecutor { - private ArrayList<Runnable> mExecuted = new ArrayList<>(); - private ArrayList<Runnable> mDelayed = new ArrayList<>(); - - @Override - public void execute(Runnable runnable) { - mExecuted.add(runnable); - } - - @Override - public void executeDelayed(Runnable r, long delayMillis) { - mDelayed.add(r); - } - - @Override - public void removeCallbacks(Runnable r) { - mDelayed.remove(r); - } - - @Override - public boolean hasCallback(Runnable r) { - return mDelayed.contains(r); - } - - @Override - public Looper getLooper() { - return Looper.myLooper(); - } - } } diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index ab9b8b55a4cb..859a5556323d 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -362,13 +362,8 @@ static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& sourc return source; } else { SkBitmap bitmap; - const SkImageInfo& info = source.info(); - bitmap.allocPixels(info.makeColorType(kN32_SkColorType)); - - SkCanvas canvas(bitmap); - canvas.drawColor(0); - canvas.drawBitmap(source, 0.0f, 0.0f, nullptr); - + bitmap.allocPixels(source.info().makeColorType(kN32_SkColorType)); + bitmap.writePixels(source.pixmap()); return bitmap; } } diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index f4c633fbe58f..ca2ada9e8141 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -127,10 +127,11 @@ void Layer::draw(SkCanvas* canvas) { const SkMatrix& totalMatrix = canvas->getTotalMatrix(); SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height()); + SkSamplingOptions sampling; if (getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) { - paint.setFilterQuality(kLow_SkFilterQuality); + sampling = SkSamplingOptions(SkFilterMode::kLinear); } - canvas->drawImage(layerImage.get(), 0, 0, &paint); + canvas->drawImage(layerImage.get(), 0, 0, sampling, &paint); // restore the original matrix if (nonIdentityMatrix) { canvas->restore(); diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 11a908696903..96118aaec29b 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -487,7 +487,9 @@ struct DrawVectorDrawable final : Op { tree->getPaintFor(&paint, tree->stagingProperties()); } - void draw(SkCanvas* canvas, const SkMatrix&) const { mRoot->draw(canvas, mBounds, paint); } + void draw(SkCanvas* canvas, const SkMatrix&) const { + mRoot->draw(canvas, mBounds, paint); + } sp<VectorDrawableRoot> mRoot; SkRect mBounds; diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 1a8d9eb1f74f..8fddf713f1fa 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -565,7 +565,8 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { auto image = bitmap.makeImage(); apply_looper(paint, [&](const SkPaint& p) { - mCanvas->drawImage(image, left, top, &p); + auto sampling = SkSamplingOptions(p.getFilterQuality()); + mCanvas->drawImage(image, left, top, sampling, &p); }); } @@ -574,7 +575,8 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* SkAutoCanvasRestore acr(mCanvas, true); mCanvas->concat(matrix); apply_looper(paint, [&](const SkPaint& p) { - mCanvas->drawImage(image, 0, 0, &p); + auto sampling = SkSamplingOptions(p.getFilterQuality()); + mCanvas->drawImage(image, 0, 0, sampling, &p); }); } @@ -586,10 +588,17 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); apply_looper(paint, [&](const SkPaint& p) { - mCanvas->drawImageRect(image, srcRect, dstRect, &p, SkCanvas::kFast_SrcRectConstraint); + auto sampling = SkSamplingOptions(p.getFilterQuality()); + mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p, + SkCanvas::kFast_SrcRectConstraint); }); } +static SkFilterMode paintToFilter(const Paint* paint) { + return paint && paint->isFilterBitmap() ? SkFilterMode::kLinear + : SkFilterMode::kNearest; +} + void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors, const Paint* paint) { const int ptCount = (meshWidth + 1) * (meshHeight + 1); @@ -664,18 +673,25 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, } #endif + auto image = bitmap.makeImage(); + // cons-up a shader for the bitmap Paint pnt; if (paint) { pnt = *paint; } - SkSamplingOptions sampling(pnt.isFilterBitmap() ? SkFilterMode::kLinear - : SkFilterMode::kNearest, - SkMipmapMode::kNone); - pnt.setShader(bitmap.makeImage()->makeShader(sampling)); + SkSamplingOptions sampling(paintToFilter(&pnt)); + pnt.setShader(image->makeShader(sampling)); + auto v = builder.detach(); apply_looper(&pnt, [&](const SkPaint& p) { - mCanvas->drawVertices(v, SkBlendMode::kModulate, p); + SkPaint copy(p); + auto s = SkSamplingOptions(p.getFilterQuality()); + if (s != sampling) { + // apply_looper changed the quality? + copy.setShader(image->makeShader(s)); + } + mCanvas->drawVertices(v, SkBlendMode::kModulate, copy); }); } @@ -700,13 +716,11 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get()); } - SkFilterMode filter = paint && paint->isFilterBitmap() ? SkFilterMode::kLinear - : SkFilterMode::kNearest; - lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); auto image = bitmap.makeImage(); apply_looper(paint, [&](const SkPaint& p) { + auto filter = SkSamplingOptions(p.getFilterQuality()).filter; mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p); }); } diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 6030c36add7a..4a21ad6ab945 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -507,10 +507,12 @@ void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) sk_sp<SkImage> cachedBitmap = getBitmapUpdateIfDirty().makeImage(); + // HWUI always draws VD with bilinear filtering. + auto sampling = SkSamplingOptions(SkFilterMode::kLinear); int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); canvas->drawImageRect(cachedBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, - &paint, SkCanvas::kFast_SrcRectConstraint); + sampling, &paint, SkCanvas::kFast_SrcRectConstraint); } void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) { diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h index cceba59cc639..86b1ac71f692 100644 --- a/libs/hwui/canvas/CanvasOps.h +++ b/libs/hwui/canvas/CanvasOps.h @@ -351,21 +351,24 @@ struct CanvasOp<CanvasOpType::DrawImage> { const sk_sp<Bitmap>& bitmap, float left, float top, + SkFilterMode filter, SkPaint paint ) : left(left), top(top), + filter(filter), paint(std::move(paint)), bitmap(bitmap), image(bitmap->makeImage()) { } float left; float top; + SkFilterMode filter; SkPaint paint; sk_sp<Bitmap> bitmap; sk_sp<SkImage> image; void draw(SkCanvas* canvas) const { - canvas->drawImage(image, left, top, &paint); + canvas->drawImage(image, left, top, SkSamplingOptions(filter), &paint); } ASSERT_DRAWABLE() }; @@ -377,15 +380,18 @@ struct CanvasOp<CanvasOpType::DrawImageRect> { const sk_sp<Bitmap>& bitmap, SkRect src, SkRect dst, + SkFilterMode filter, SkPaint paint ) : src(src), dst(dst), + filter(filter), paint(std::move(paint)), bitmap(bitmap), image(bitmap->makeImage()) { } SkRect src; SkRect dst; + SkFilterMode filter; SkPaint paint; sk_sp<Bitmap> bitmap; sk_sp<SkImage> image; @@ -394,6 +400,7 @@ struct CanvasOp<CanvasOpType::DrawImageRect> { canvas->drawImageRect(image, src, dst, + SkSamplingOptions(filter), &paint, SkCanvas::kFast_SrcRectConstraint ); diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index f055c6e0fa44..ade63e5b832c 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -437,11 +437,11 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { if (outputMatrix.invert(&inverse)) { SkCanvas canvas(tmp, SkCanvas::ColorBehavior::kLegacy); canvas.setMatrix(inverse); - SkPaint paint; - paint.setFilterQuality(kLow_SkFilterQuality); // bilinear SkBitmap priorFrame; priorFrame.installPixels(outputInfo, pixels, rowBytes); - canvas.drawBitmap(priorFrame, 0, 0, &paint); + priorFrame.setImmutable(); // Don't want asImage() to force a copy + canvas.drawImage(priorFrame.asImage(), 0, 0, + SkSamplingOptions(SkFilterMode::kLinear)); } else { ALOGE("Failed to invert matrix!"); } @@ -458,11 +458,11 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); - paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy); canvas.setMatrix(outputMatrix); - canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint); + tmp.setImmutable(); // Don't want asImage() to force copy + canvas.drawImage(tmp.asImage(), 0, 0, SkSamplingOptions(SkFilterMode::kLinear), &paint); } return result; diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index cf02051831c6..4e9daa4b0c16 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -457,11 +457,12 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, // outputBitmap. Otherwise we would blend by default, which is not // what we want. paint.setBlendMode(SkBlendMode::kSrc); - paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering SkCanvas canvas(outputBitmap, SkCanvas::ColorBehavior::kLegacy); canvas.scale(scaleX, scaleY); - canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); + decodingBitmap.setImmutable(); // so .asImage() doesn't make a copy + canvas.drawImage(decodingBitmap.asImage(), 0.0f, 0.0f, + SkSamplingOptions(SkFilterMode::kLinear), &paint); } else { outputBitmap.swap(decodingBitmap); } diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index f95f347cffaf..34df5ddbb210 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -141,18 +141,20 @@ bool LayerDrawable::DrawLayer(GrRecordingContext* context, // then use nearest neighbor, otherwise use bilerp sampling. // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works // only for SrcOver blending and without color filter (readback uses Src blending). + SkSamplingOptions sampling(SkFilterMode::kNearest); if (layer->getForceFilter() || shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) { - paint.setFilterQuality(kLow_SkFilterQuality); + sampling = SkSamplingOptions(SkFilterMode::kLinear); } - canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint, + canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint, SkCanvas::kFast_SrcRectConstraint); } else { SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height()); + SkSamplingOptions sampling(SkFilterMode::kNearest); if (layer->getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) { - paint.setFilterQuality(kLow_SkFilterQuality); + sampling = SkSamplingOptions(SkFilterMode::kLinear); } - canvas->drawImage(layerImage.get(), 0, 0, &paint); + canvas->drawImage(layerImage.get(), 0, 0, sampling, &paint); } // restore the original matrix if (nonIdentityMatrix) { diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index 070a765cf7ca..75815bb6e63d 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -169,7 +169,6 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const { static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier, SkPaint* paint) { - paint->setFilterQuality(kLow_SkFilterQuality); if (alphaMultiplier < 1.0f || properties.alpha() < 255 || properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr || properties.getImageFilter() != nullptr) { @@ -226,6 +225,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer); SkPaint paint; layerNeedsPaint(layerProperties, alphaMultiplier, &paint); + SkSamplingOptions sampling(SkFilterMode::kLinear); // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so // we need to restrict the portion of the surface drawn to the size of the renderNode. @@ -239,7 +239,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr); } canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds, - bounds, &paint); + bounds, sampling, &paint, SkCanvas::kStrict_SrcRectConstraint); if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 80eddafbde4f..6456e36a847a 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -656,7 +656,7 @@ void SkiaPipeline::renderOverdraw(const SkRect& clip, SkPaint paint; const SkColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)]; paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors)); - surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint); + surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, SkSamplingOptions(), &paint); } } /* namespace skiapipeline */ diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp index bc8ce428ce2e..bae11f7d074c 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp @@ -182,7 +182,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { auto functorImage = SkImage::MakeFromAHardwareBuffer(mFrameBuffer.get(), kPremul_SkAlphaType, canvas->imageInfo().refColorSpace(), kBottomLeft_GrSurfaceOrigin); - canvas->drawImage(functorImage, 0, 0, &paint); + canvas->drawImage(functorImage, 0, 0, SkSamplingOptions(), &paint); canvas->restore(); } diff --git a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp index 1d17a021069b..716d3979bdcb 100644 --- a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp +++ b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp @@ -51,7 +51,7 @@ public: hardwareBitmap->height(), &canvasBitmap)); SkCanvas skCanvas(canvasBitmap); - skCanvas.drawBitmap(readback, 0, 0); + skCanvas.drawImage(readback.asImage(), 0, 0); canvas.drawBitmap(*heapBitmap, 0, 0, nullptr); canvas.drawBitmap(*hardwareBitmap, 0, 500, nullptr); diff --git a/libs/hwui/tests/microbench/CanvasOpBench.cpp b/libs/hwui/tests/microbench/CanvasOpBench.cpp index ef5749e6b79b..e7ba471ee807 100644 --- a/libs/hwui/tests/microbench/CanvasOpBench.cpp +++ b/libs/hwui/tests/microbench/CanvasOpBench.cpp @@ -85,6 +85,7 @@ void BM_CanvasOpBuffer_record_simpleBitmapView(benchmark::State& benchState) { iconBitmap, 0, 0, + SkFilterMode::kNearest, SkPaint{} }); canvas.restore(); diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp index c9e8d808f995..54970df534b9 100644 --- a/libs/hwui/tests/unit/CanvasOpTests.cpp +++ b/libs/hwui/tests/unit/CanvasOpTests.cpp @@ -474,6 +474,7 @@ TEST(CanvasOp, simpleDrawImage) { bitmap, 7, 19, + SkFilterMode::kNearest, SkPaint{} } ); @@ -496,7 +497,7 @@ TEST(CanvasOp, simpleDrawImageRect) { buffer.push<Op::DrawImageRect> ({ bitmap, SkRect::MakeWH(100, 100), SkRect::MakeLTRB(120, 110, 220, 210), - SkPaint{} + SkFilterMode::kNearest, SkPaint{} } ); diff --git a/media/jni/Android.bp b/media/jni/Android.bp index decf68f26c0e..67a2c49e5746 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -166,6 +166,11 @@ cc_library_shared { "tv_tuner_aidl_interface-ndk_platform", "tv_tuner_resource_manager_aidl_interface-ndk_platform" ], + + static_libs: [ + "libaidlcommonsupport", + ], + defaults: [ "libcodec2-impl-defaults", ], diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index eb63d76741c4..9ec84d9d2265 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -1458,7 +1458,7 @@ int JTuner::tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } - return (int) mFeClient->tune(settings,settingsExt1_1); + return (int) mFeClient->tune(settings, settingsExt1_1); } int JTuner::stopTune() { diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp index e290c60db8ce..1a2f8c065bd3 100644 --- a/media/jni/tuner/DemuxClient.cpp +++ b/media/jni/tuner/DemuxClient.cpp @@ -142,7 +142,16 @@ long DemuxClient::getAvSyncTime(int avSyncHwId) { } sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) { - // TODO: pending aidl interface + if (mTunerDemux != NULL) { + shared_ptr<ITunerDvr> tunerDvr; + shared_ptr<TunerDvrCallback> callback = + ::ndk::SharedRefBase::make<TunerDvrCallback>(cb); + Status s = mTunerDemux->openDvr((int)dvbType, bufferSize, callback, &tunerDvr); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return NULL; + } + return new DvrClient(tunerDvr); + } if (mDemux != NULL) { sp<HidlDvrCallback> callback = new HidlDvrCallback(cb); @@ -178,7 +187,10 @@ Result DemuxClient::disconnectCiCam() { } Result DemuxClient::close() { - // TODO: pending aidl interface + if (mTunerDemux != NULL) { + Status s = mTunerDemux->close(); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mDemux != NULL) { Result res = mDemux->close(); diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp index be592af30f5d..04004858aaee 100644 --- a/media/jni/tuner/DvrClient.cpp +++ b/media/jni/tuner/DvrClient.cpp @@ -210,14 +210,13 @@ Result DvrClient::configure(DvrSettings settings) { return res; } - AidlMQDesc* aidlMqDesc = NULL; - s = mTunerDvr->getQueueDesc(aidlMqDesc); + AidlMQDesc aidlMqDesc; + s = mTunerDvr->getQueueDesc(&aidlMqDesc); res = ClientHelper::getServiceSpecificErrorCode(s); if (res != Result::SUCCESS) { return res; } - - mDvrMQ = new (nothrow) AidlMQ(*aidlMqDesc); + mDvrMQ = new (nothrow) AidlMQ(aidlMqDesc); EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag); return res; } diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp index bcef0a2f4413..bdc8a4f1eb36 100644 --- a/media/jni/tuner/FilterClient.cpp +++ b/media/jni/tuner/FilterClient.cpp @@ -16,14 +16,21 @@ #define LOG_TAG "FilterClient" +#include <aidlcommonsupport/NativeHandle.h> #include <android-base/logging.h> #include <utils/Log.h> #include "FilterClient.h" +using ::aidl::android::media::tv::tuner::TunerFilterAvSettings; +using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo; +using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration; + using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType; using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType; +using ::android::hardware::tv::tuner::V1_0::DemuxStreamId; +using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType; namespace android { @@ -48,7 +55,6 @@ FilterClient::~FilterClient() { void FilterClient::setHidlFilter(sp<IFilter> filter) { mFilter = filter; mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter); - handleAvShareMemory(); } int FilterClient::read(uint8_t* buffer, int size) { @@ -66,23 +72,20 @@ int FilterClient::read(uint8_t* buffer, int size) { } SharedHandleInfo FilterClient::getAvSharedHandleInfo() { + handleAvShareMemory(); SharedHandleInfo info{ - .sharedHandle = NULL, - .size = 0, + .sharedHandle = mAvSharedHandle, + .size = mAvSharedMemSize, }; - // TODO: pending aidl interface - - if (mFilter_1_1 != NULL) { - info.sharedHandle = mAvSharedHandle; - info.size = mAvSharedMemSize; - } - return info; } Result FilterClient::configure(DemuxFilterSettings configure) { - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + Status s = mTunerFilter->configure(getAidlFilterSettings(configure)); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter != NULL) { return mFilter->configure(configure); @@ -122,7 +125,10 @@ Result FilterClient::configureAvStreamType(AvStreamType avStreamType) { } Result FilterClient::start() { - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + Status s = mTunerFilter->start(); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter != NULL) { return mFilter->start(); @@ -132,7 +138,10 @@ Result FilterClient::start() { } Result FilterClient::stop() { - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + Status s = mTunerFilter->stop(); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter != NULL) { return mFilter->stop(); @@ -142,7 +151,10 @@ Result FilterClient::stop() { } Result FilterClient::flush() { - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + Status s = mTunerFilter->flush(); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter != NULL) { return mFilter->flush(); @@ -192,7 +204,10 @@ Result FilterClient::getId64Bit(uint64_t& id) { } Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId) { - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + Status s = mTunerFilter->releaseAvHandle(makeToAidl(handle), avDataId); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter != NULL) { return mFilter->releaseAvHandle(hidl_handle(handle), avDataId); @@ -216,13 +231,18 @@ Result FilterClient::setDataSource(sp<FilterClient> filterClient){ } Result FilterClient::close() { - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + Status s = mTunerFilter->close(); + closeAvSharedMemory(); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter != NULL) { Result res = mFilter->close(); if (res == Result::SUCCESS) { mFilter = NULL; } + closeAvSharedMemory(); return res; } @@ -269,13 +289,103 @@ Status TunerFilterCallback::onFilterStatus(int status) { return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE)); } -Status TunerFilterCallback::onFilterEvent(vector<TunerFilterEvent>* /*filterEvent*/) { - // TODO: complete onFilterEvent +Status TunerFilterCallback::onFilterEvent(const vector<TunerFilterEvent>& filterEvents) { + if (mFilterClientCallback == NULL) { + return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE)); + } + + DemuxFilterEvent event; + DemuxFilterEventExt eventExt; + getHidlFilterEvent(filterEvents, event, eventExt); + if (eventExt.events.size() > 0) { + mFilterClientCallback->onFilterEvent_1_1(event, eventExt); + } else { + mFilterClientCallback->onFilterEvent(event); + } + return Status::ok(); } /////////////// FilterClient Helper Methods /////////////////////// +TunerFilterConfiguration FilterClient::getAidlFilterSettings(DemuxFilterSettings configure) { + TunerFilterConfiguration config; + // TODO: complete filter setting conversion + switch (configure.getDiscriminator()) { + case DemuxFilterSettings::hidl_discriminator::ts: { + TunerFilterSettings filterSettings; + switch (configure.ts().filterSettings.getDiscriminator()) { + case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::av: { + TunerFilterAvSettings av{ + .isPassthrough = configure.ts().filterSettings.av().isPassthrough, + }; + filterSettings.set<TunerFilterSettings::av>(av); + break; + } + default: + break; + } + + TunerFilterTsConfiguration ts{ + .tpid = configure.ts().tpid, + .filterSettings = filterSettings, + }; + config.set<TunerFilterConfiguration::ts>(ts); + + return config; + } + case DemuxFilterSettings::hidl_discriminator::mmtp: + break; + case DemuxFilterSettings::hidl_discriminator::ip: + break; + case DemuxFilterSettings::hidl_discriminator::tlv: + break; + default: + break; + } + return config; +} + + +void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& filterEvents, + DemuxFilterEvent& event, DemuxFilterEventExt& /*eventExt*/) { + // TODO: finish handling extended evets and other filter event types + switch (filterEvents[0].getTag()) { + case TunerFilterEvent::media: { + for (int i = 0; i < filterEvents.size(); i++) { + hidl_handle handle = hidl_handle( + makeFromAidl(filterEvents[i].get<TunerFilterEvent::media>().avMemory)); + int size = event.events.size(); + event.events.resize(size + 1); + event.events[size].media({ + .avMemory = handle, + .streamId = static_cast<DemuxStreamId>( + filterEvents[i].get<TunerFilterEvent::media>().streamId), + .isPtsPresent = + filterEvents[i].get<TunerFilterEvent::media>().isPtsPresent, + .pts = static_cast<uint64_t>( + filterEvents[i].get<TunerFilterEvent::media>().pts), + .dataLength = static_cast<uint32_t>( + filterEvents[i].get<TunerFilterEvent::media>().dataLength), + .offset = static_cast<uint32_t>( + filterEvents[i].get<TunerFilterEvent::media>().offset), + .isSecureMemory = + filterEvents[i].get<TunerFilterEvent::media>().isSecureMemory, + .avDataId = static_cast<uint64_t>( + filterEvents[i].get<TunerFilterEvent::media>().avDataId), + .mpuSequenceNumber = static_cast<uint32_t>( + filterEvents[i].get<TunerFilterEvent::media>().offset), + .isPesPrivateData = + filterEvents[i].get<TunerFilterEvent::media>().isPesPrivateData, + }); + } + break; + } + default: + break; + } +} + Result FilterClient::getFilterMq() { if (mFilter == NULL) { return Result::INVALID_STATE; @@ -333,6 +443,20 @@ void FilterClient::checkIsMediaFilter(DemuxFilterType type) { } void FilterClient::handleAvShareMemory() { + if (mAvSharedHandle != NULL) { + return; + } + + if (mTunerFilter != NULL && mIsMediaFilter) { + TunerFilterSharedHandleInfo aidlHandleInfo; + Status s = mTunerFilter->getAvSharedHandleInfo(&aidlHandleInfo); + if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) { + mAvSharedHandle = native_handle_clone(makeFromAidl(aidlHandleInfo.handle)); + mAvSharedMemSize = aidlHandleInfo.size; + } + return; + } + if (mFilter_1_1 != NULL && mIsMediaFilter) { mFilter_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) { if (r == Result::SUCCESS) { @@ -342,4 +466,10 @@ void FilterClient::handleAvShareMemory() { }); } } + +void FilterClient::closeAvSharedMemory() { + native_handle_close(mAvSharedHandle); + native_handle_delete(mAvSharedHandle); + mAvSharedMemSize = 0; +} } // namespace android diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h index 7c85125c4c75..f5539e0cc54a 100644 --- a/media/jni/tuner/FilterClient.h +++ b/media/jni/tuner/FilterClient.h @@ -20,6 +20,8 @@ #include <aidl/android/media/tv/tuner/ITunerFilter.h> #include <aidl/android/media/tv/tuner/BnTunerFilterCallback.h> #include <aidl/android/media/tv/tuner/TunerFilterEvent.h> +#include <aidl/android/media/tv/tuner/TunerFilterSettings.h> +#include <aidlcommonsupport/NativeHandle.h> #include <android/hardware/tv/tuner/1.1/IFilter.h> #include <android/hardware/tv/tuner/1.1/IFilterCallback.h> #include <android/hardware/tv/tuner/1.1/types.h> @@ -31,7 +33,9 @@ using Status = ::ndk::ScopedAStatus; using ::aidl::android::media::tv::tuner::BnTunerFilterCallback; using ::aidl::android::media::tv::tuner::ITunerFilter; +using ::aidl::android::media::tv::tuner::TunerFilterConfiguration; using ::aidl::android::media::tv::tuner::TunerFilterEvent; +using ::aidl::android::media::tv::tuner::TunerFilterSettings; using ::android::hardware::EventFlag; using ::android::hardware::MessageQueue; @@ -61,11 +65,13 @@ class TunerFilterCallback : public BnTunerFilterCallback { public: TunerFilterCallback(sp<FilterClientCallback> filterClientCallback); - // TODO: complete TunerFilterCallback Status onFilterStatus(int status); - Status onFilterEvent(vector<TunerFilterEvent>* filterEvent); + Status onFilterEvent(const vector<TunerFilterEvent>& filterEvents); private: + void getHidlFilterEvent(const vector<TunerFilterEvent>& filterEvents, + DemuxFilterEvent& event, DemuxFilterEventExt& eventExt); + sp<FilterClientCallback> mFilterClientCallback; }; @@ -174,10 +180,12 @@ public: Result close(); private: + TunerFilterConfiguration getAidlFilterSettings(DemuxFilterSettings configure); Result getFilterMq(); int copyData(uint8_t* buffer, int size); void checkIsMediaFilter(DemuxFilterType type); void handleAvShareMemory(); + void closeAvSharedMemory(); /** * An AIDL Tuner Filter Singleton assigned at the first time when the Tuner Client diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp index ef8f57f69f31..0540aac3672d 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::TunerFrontendSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType; @@ -86,10 +86,9 @@ void FrontendClient::setHidlFrontend(sp<IFrontend> frontend) { Result FrontendClient::tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1) { if (mTunerFrontend != NULL) { - // TODO: parse hidl settings to aidl settings // TODO: aidl frontend settings to include Tuner HAL 1.1 settings - TunerFrontendSettings settings; - Status s = mTunerFrontend->tune(settings); + TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1); + Status s = mTunerFrontend->tune(tunerFeSettings); return ClientHelper::getServiceSpecificErrorCode(s); } @@ -124,10 +123,9 @@ Result FrontendClient::stopTune() { Result FrontendClient::scan(const FrontendSettings& settings, FrontendScanType type, const FrontendSettingsExt1_1& settingsExt1_1) { if (mTunerFrontend != NULL) { - // TODO: parse hidl settings to aidl settings // TODO: aidl frontend settings to include Tuner HAL 1.1 settings - TunerFrontendSettings settings; - Status s = mTunerFrontend->scan(settings, (int)type); + TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1); + Status s = mTunerFrontend->scan(tunerFeSettings, (int)type); return ClientHelper::getServiceSpecificErrorCode(s); } @@ -279,7 +277,6 @@ Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) { Result FrontendClient::close() { if (mTunerFrontend != NULL) { - // TODO: handle error message. Status s = mTunerFrontend->close(); return ClientHelper::getServiceSpecificErrorCode(s); } @@ -304,6 +301,61 @@ int FrontendClient::getId() { return mId; } +TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSettings& settings, + const FrontendSettingsExt1_1& /*settingsExt1_1*/) { + // TODO: complete hidl to aidl frontend settings conversion + TunerFrontendSettings s; + switch (settings.getDiscriminator()) { + case FrontendSettings::hidl_discriminator::analog: { + break; + } + case FrontendSettings::hidl_discriminator::atsc: { + break; + } + case FrontendSettings::hidl_discriminator::atsc3: { + break; + } + case FrontendSettings::hidl_discriminator::dvbs: { + break; + } + case FrontendSettings::hidl_discriminator::dvbc: { + 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); + break; + } + case FrontendSettings::hidl_discriminator::isdbs: { + break; + } + case FrontendSettings::hidl_discriminator::isdbs3: { + break; + } + case FrontendSettings::hidl_discriminator::isdbt: { + break; + } + default: + break; + } + return s; +} + /////////////// TunerFrontendCallback /////////////////////// TunerFrontendCallback::TunerFrontendCallback(sp<FrontendClientCallback> frontendClientCallback) diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h index 03ebb8704ef7..17fd583cbaae 100644 --- a/media/jni/tuner/FrontendClient.h +++ b/media/jni/tuner/FrontendClient.h @@ -32,6 +32,7 @@ using Status = ::ndk::ScopedAStatus; using ::aidl::android::media::tv::tuner::BnTunerFrontendCallback; using ::aidl::android::media::tv::tuner::ITunerFrontend; using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage; +using ::aidl::android::media::tv::tuner::TunerFrontendSettings; using ::android::hardware::Return; using ::android::hardware::Void; @@ -171,6 +172,9 @@ public: int getId(); private: + TunerFrontendSettings getAidlFrontendSettings(const FrontendSettings& settings, + const FrontendSettingsExt1_1& settingsExt1_1); + /** * An AIDL Tuner Frontend Singleton assigned at the first time when the Tuner Client * opens a frontend cient. Default null when the service does not exist. diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp index 4498f54b71cc..14393a1081c6 100644 --- a/media/jni/tuner/TunerClient.cpp +++ b/media/jni/tuner/TunerClient.cpp @@ -235,7 +235,8 @@ sp<DescramblerClient> TunerClient::openDescrambler(int /*descramblerHandle*/) { } } - return NULL;} + return NULL; +} sp<LnbClient> TunerClient::openLnb(int lnbHandle) { if (mTunerService != NULL) { diff --git a/media/native/midi/include/amidi/AMidi.h b/media/native/midi/include/amidi/AMidi.h index 0f930b5443e4..742db34b74a7 100644 --- a/media/native/midi/include/amidi/AMidi.h +++ b/media/native/midi/include/amidi/AMidi.h @@ -61,8 +61,6 @@ enum { AMIDI_DEVICE_TYPE_BLUETOOTH = 3 /* A MIDI device connected via BlueTooth */ }; -#if __ANDROID_API__ >= 29 - /* * Device API */ @@ -249,8 +247,6 @@ media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPor */ void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29); -#endif /* __ANDROID_API__ >= 29 */ - #ifdef __cplusplus } #endif diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java index 6d7e86f64944..34da30555fb3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java @@ -108,7 +108,7 @@ public class BatterySaverUtils { * - If it's the first time and needFirstTimeWarning, show the first time dialog. * - If it's 4th time through 8th time, show the schedule suggestion notification. * - * @param enable true to disable battery saver. + * @param enable true to enable battery saver. * * @return true if the request succeeded. */ diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 266bfe0a22b5..6568bffddecc 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -846,8 +846,8 @@ class DatabaseHelper extends SQLiteOpenHelper { try { stmt = db.compileStatement("INSERT INTO system(name,value)" + " VALUES(?,?);"); - loadBooleanSetting(stmt, Settings.System.USER_ROTATION, - R.integer.def_user_rotation); // should be zero degrees + loadIntegerSetting(stmt, Settings.System.USER_ROTATION, + R.integer.def_user_rotation); db.setTransactionSuccessful(); } finally { db.endTransaction(); @@ -2265,6 +2265,8 @@ class DatabaseHelper extends SQLiteOpenHelper { loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION, R.bool.def_accelerometer_rotation); + loadIntegerSetting(stmt, Settings.System.USER_ROTATION, R.integer.def_user_rotation); + loadDefaultHapticSettings(stmt); loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 7eab559cb9b9..1af7781dff96 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -304,9 +304,6 @@ <uses-permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" /> <!-- Permission needed to read wifi network credentials for CtsNetTestCases --> - <uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" /> - - <!-- Permission needed to read wifi network credentials for CtsNetTestCases --> <uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL" /> <!-- Permission needed to use wifi usability API's for CtsNetTestCases --> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml index 796123db7c79..04e645bd0a32 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml @@ -32,9 +32,8 @@ <com.android.keyguard.KeyguardSecurityContainer android:id="@+id/keyguard_security_container" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - androidprv:layout_maxHeight="@dimen/keyguard_security_max_height" + android:layout_width="match_parent" + android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" android:padding="0dp" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index dc77bd356e55..70f495c1245b 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -46,7 +46,6 @@ android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center" - android:layout_weight="1" android:layoutDirection="ltr" > <include layout="@layout/keyguard_esim_area" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 9df1230d7656..7ba906986fa3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -192,19 +192,6 @@ public class RemoteTransitionCompat implements Parcelable { // Settings > Editor > Code Style > Formatter Control //@formatter:off - - @DataClass.Generated.Member - /* package-private */ RemoteTransitionCompat( - @NonNull IRemoteTransition transition, - @Nullable TransitionFilter filter) { - this.mTransition = transition; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mTransition); - this.mFilter = filter; - - // onConstructed(); // You can define this method to get a callback - } - @DataClass.Generated.Member public @NonNull IRemoteTransition getTransition() { return mTransition; @@ -308,9 +295,8 @@ public class RemoteTransitionCompat implements Parcelable { if ((mBuilderFieldsSet & 0x2) == 0) { mFilter = null; } - RemoteTransitionCompat o = new RemoteTransitionCompat( - mTransition, - mFilter); + RemoteTransitionCompat o = new RemoteTransitionCompat(mTransition); + o.mFilter = this.mFilter; return o; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java index fbabaa489d74..70021b6f3d45 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java @@ -114,13 +114,10 @@ public class SyncRtSurfaceTransactionApplierCompat { for (int i = params.length - 1; i >= 0; i--) { SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = params[i]; + t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame); surfaceParams.applyTo(t); } - if (mTargetViewRootImpl != null) { - mTargetViewRootImpl.mergeWithNextTransaction(t, frame); - } else { - t.apply(); - } + t.apply(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) .sendToTarget(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java index 89c60f1d3f06..4a28d56a41e1 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java @@ -56,12 +56,4 @@ public class ViewRootImplCompat { }); } } - - public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) { - if (mViewRoot != null) { - mViewRoot.mergeWithNextTransaction(t, frame); - } else { - t.apply(); - } - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index a40dc7abf063..85e9ca05d428 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -16,6 +16,7 @@ package com.android.keyguard; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.DEFAULT_DISPLAY_GROUP; import android.app.Presentation; import android.content.Context; @@ -116,6 +117,14 @@ public class KeyguardDisplayManager { if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display"); return false; } + if (mTmpDisplayInfo.displayGroupId != DEFAULT_DISPLAY_GROUP) { + if (DEBUG) { + Log.i(TAG, + "Do not show KeyguardPresentation on a non-default group display"); + } + return false; + } + return true; } /** @@ -130,8 +139,7 @@ public class KeyguardDisplayManager { final int displayId = display.getDisplayId(); Presentation presentation = mPresentations.get(displayId); if (presentation == null) { - final Presentation newPresentation = new KeyguardPresentation(mContext, display, - mKeyguardStatusViewComponentFactory); + final Presentation newPresentation = createPresentation(display); newPresentation.setOnDismissListener(dialog -> { if (newPresentation.equals(mPresentations.get(displayId))) { mPresentations.remove(displayId); @@ -152,6 +160,10 @@ public class KeyguardDisplayManager { return false; } + KeyguardPresentation createPresentation(Display display) { + return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory); + } + /** * @param displayId The id of the display to hide the presentation off. */ diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java index 93a8df41c673..cd3d6a84a352 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java @@ -26,8 +26,7 @@ public class AppOpItem { private String mPackageName; private long mTimeStarted; private StringBuilder mState; - // This is only used for items with mCode == AppOpsManager.OP_RECORD_AUDIO - private boolean mSilenced; + private boolean mIsDisabled; public AppOpItem(int code, int uid, String packageName, long timeStarted) { this.mCode = code; @@ -58,16 +57,16 @@ public class AppOpItem { return mTimeStarted; } - public void setSilenced(boolean silenced) { - mSilenced = silenced; + public void setDisabled(boolean misDisabled) { + this.mIsDisabled = misDisabled; } - public boolean isSilenced() { - return mSilenced; + public boolean isDisabled() { + return mIsDisabled; } @Override public String toString() { - return mState.append(mSilenced).append(")").toString(); + return mState.append(mIsDisabled).append(")").toString(); } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 1036c9916c8b..d8ca63960b30 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.appops; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED; import android.Manifest; @@ -45,6 +47,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.util.Assert; import java.io.FileDescriptor; @@ -64,7 +67,8 @@ import javax.inject.Inject; @SysUISingleton public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController, AppOpsManager.OnOpActiveChangedInternalListener, - AppOpsManager.OnOpNotedListener, Dumpable { + AppOpsManager.OnOpNotedListener, IndividualSensorPrivacyController.Callback, + Dumpable { // This is the minimum time that we will keep AppOps that are noted on record. If multiple // occurrences of the same (op, package, uid) happen in a shorter interval, they will not be @@ -77,8 +81,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon private final AppOpsManager mAppOps; private final AudioManager mAudioManager; private final LocationManager mLocationManager; - // TODO ntmyren: remove t private final PackageManager mPackageManager; + private final IndividualSensorPrivacyController mSensorPrivacyController; // mLocationProviderPackages are cached and updated only occasionally private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000; @@ -91,6 +95,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon private final PermissionFlagsCache mFlagsCache; private boolean mListening; private boolean mMicMuted; + private boolean mCameraDisabled; @GuardedBy("mActiveItems") private final List<AppOpItem> mActiveItems = new ArrayList<>(); @@ -118,6 +123,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon DumpManager dumpManager, PermissionFlagsCache cache, AudioManager audioManager, + IndividualSensorPrivacyController sensorPrivacyController, BroadcastDispatcher dispatcher ) { mDispatcher = dispatcher; @@ -129,7 +135,10 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mCallbacksByCode.put(OPS[i], new ArraySet<>()); } mAudioManager = audioManager; - mMicMuted = audioManager.isMicrophoneMute(); + mSensorPrivacyController = sensorPrivacyController; + mMicMuted = audioManager.isMicrophoneMute() + || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); + mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA); mLocationManager = context.getSystemService(LocationManager.class); mPackageManager = context.getPackageManager(); dumpManager.registerDumpable(TAG, this); @@ -147,6 +156,12 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mAppOps.startWatchingActive(OPS, this); mAppOps.startWatchingNoted(OPS, this); mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler); + mSensorPrivacyController.addCallback(this); + + mMicMuted = mAudioManager.isMicrophoneMute() + || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); + mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA); + mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( mAudioManager.getActiveRecordingConfigurations())); mDispatcher.registerReceiverWithHandler(this, @@ -156,6 +171,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mAppOps.stopWatchingActive(this); mAppOps.stopWatchingNoted(this); mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback); + mSensorPrivacyController.removeCallback(this); mBGHandler.removeCallbacksAndMessages(null); // null removes all mDispatcher.unregisterReceiver(this); @@ -235,11 +251,13 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon if (item == null && active) { item = new AppOpItem(code, uid, packageName, System.currentTimeMillis()); if (code == AppOpsManager.OP_RECORD_AUDIO) { - item.setSilenced(isAnyRecordingPausedLocked(uid)); + item.setDisabled(isAnyRecordingPausedLocked(uid)); + } else if (code == AppOpsManager.OP_CAMERA) { + item.setDisabled(mCameraDisabled); } mActiveItems.add(item); if (DEBUG) Log.w(TAG, "Added item: " + item.toString()); - return !item.isSilenced(); + return !item.isDisabled(); } else if (item != null && !active) { mActiveItems.remove(item); if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); @@ -409,7 +427,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon AppOpItem item = mActiveItems.get(i); if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserVisible(item) && !item.isSilenced()) { + && isUserVisible(item) && !item.isDisabled()) { list.add(item); } } @@ -512,22 +530,27 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon return false; } - private void updateRecordingPausedStatus() { + private void updateSensorDisabledStatus() { synchronized (mActiveItems) { int size = mActiveItems.size(); for (int i = 0; i < size; i++) { AppOpItem item = mActiveItems.get(i); + + boolean paused = false; if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) { - boolean paused = isAnyRecordingPausedLocked(item.getUid()); - if (item.isSilenced() != paused) { - item.setSilenced(paused); - notifySuscribers( - item.getCode(), - item.getUid(), - item.getPackageName(), - !item.isSilenced() - ); - } + paused = isAnyRecordingPausedLocked(item.getUid()); + } else if (item.getCode() == AppOpsManager.OP_CAMERA) { + paused = mCameraDisabled; + } + + if (item.isDisabled() != paused) { + item.setDisabled(paused); + notifySuscribers( + item.getCode(), + item.getUid(), + item.getPackageName(), + !item.isDisabled() + ); } } } @@ -552,14 +575,27 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon recordings.add(recording); } } - updateRecordingPausedStatus(); + updateSensorDisabledStatus(); } }; @Override public void onReceive(Context context, Intent intent) { - mMicMuted = mAudioManager.isMicrophoneMute(); - updateRecordingPausedStatus(); + mMicMuted = mAudioManager.isMicrophoneMute() + || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); + updateSensorDisabledStatus(); + } + + @Override + public void onSensorBlockedChanged(int sensor, boolean blocked) { + mBGHandler.post(() -> { + if (sensor == INDIVIDUAL_SENSOR_CAMERA) { + mCameraDisabled = blocked; + } else if (sensor == INDIVIDUAL_SENSOR_MICROPHONE) { + mMicMuted = mAudioManager.isMicrophoneMute() || blocked; + } + updateSensorDisabledStatus(); + }); } protected class H extends Handler { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index c2c67903da86..9be3566e1f63 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -23,7 +23,6 @@ import android.content.Intent; import android.net.Uri; import android.os.UserHandle; import android.util.Log; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewTreeObserver.InternalInsetsInfo; import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; @@ -59,7 +58,6 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener private final Executor mBgExecutor; private final ImageExporter mImageExporter; private final ImageTileSet mImageTileSet; - private final LayoutInflater mLayoutInflater; private ZonedDateTime mCaptureTime; private UUID mRequestId; @@ -81,7 +79,6 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener mBgExecutor = bgExecutor; mImageExporter = exporter; mImageTileSet = new ImageTileSet(); - mLayoutInflater = mContext.getSystemService(LayoutInflater.class); } /** @@ -114,7 +111,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener mEdit.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); - mPreview.setImageDrawable(mImageTileSet.getDrawable()); + //mPreview.setImageDrawable(mImageTileSet.getDrawable()); mConnection.start(this::startCapture); } @@ -242,6 +239,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener if (mImageTileSet.isEmpty()) { session.end(mCallback::onFinish); } else { + mPreview.setImageDrawable(mImageTileSet.getDrawable()); mExportFuture = mImageExporter.export( mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime); // The user chose an action already, link it to the result diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index d4a2b4157338..b20c45780183 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2331,11 +2331,11 @@ public class StatusBar extends SystemUI implements DemoMode, && mStatusBarWindowState != state) { mStatusBarWindowState = state; if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state)); - if (!showing && mState == StatusBarState.SHADE) { - mStatusBarView.collapsePanel(false /* animate */, false /* delayed */, - 1.0f /* speedUpFactor */); - } if (mStatusBarView != null) { + if (!showing && mState == StatusBarState.SHADE) { + mStatusBarView.collapsePanel(false /* animate */, false /* delayed */, + 1.0f /* speedUpFactor */); + } mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN; updateHideIconsForBouncer(false /* animate */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java index 231fe08e6a99..32d15ed41648 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.policy; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; import android.hardware.SensorPrivacyManager; import android.hardware.SensorPrivacyManager.IndividualSensor; @@ -30,7 +30,8 @@ import java.util.Set; public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController { - private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE}; + private static final int[] SENSORS = new int[] {INDIVIDUAL_SENSOR_CAMERA, + INDIVIDUAL_SENSOR_MICROPHONE}; private final @NonNull SensorPrivacyManager mSensorPrivacyManager; private final SparseBooleanArray mState = new SparseBooleanArray(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java new file mode 100644 index 000000000000..826be2ba0d83 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java @@ -0,0 +1,130 @@ +/* + * 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.keyguard; + +import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.Display; +import android.view.DisplayInfo; + +import androidx.test.filters.SmallTest; + +import com.android.keyguard.dagger.KeyguardStatusViewComponent; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.navigationbar.NavigationBarController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.concurrent.Executor; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class KeyguardDisplayManagerTest extends SysuiTestCase { + + @Mock + private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; + + @Mock + private DisplayManager mDisplayManager; + + @Mock + private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation; + + private Executor mBackgroundExecutor = Runnable::run; + private KeyguardDisplayManager mManager; + + // The default and secondary displays are both in the default group + private Display mDefaultDisplay; + private Display mSecondaryDisplay; + + // This display is in a different group from the default and secondary displays. + private Display mDifferentGroupDisplay; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext.addMockSystemService(DisplayManager.class, mDisplayManager); + mDependency.injectMockDependency(NavigationBarController.class); + mManager = spy(new KeyguardDisplayManager(mContext, mKeyguardStatusViewComponentFactory, + mBackgroundExecutor)); + doReturn(mKeyguardPresentation).when(mManager).createPresentation(any()); + + mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY, + new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); + mSecondaryDisplay = new Display(DisplayManagerGlobal.getInstance(), + Display.DEFAULT_DISPLAY + 1, + new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); + + DisplayInfo differentGroupInfo = new DisplayInfo(); + differentGroupInfo.displayId = Display.DEFAULT_DISPLAY + 2; + differentGroupInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; + mDifferentGroupDisplay = new Display(DisplayManagerGlobal.getInstance(), + Display.DEFAULT_DISPLAY, + differentGroupInfo, DEFAULT_DISPLAY_ADJUSTMENTS); + } + + @Test + public void testShow_defaultDisplayOnly() { + when(mDisplayManager.getDisplays()).thenReturn(new Display[]{mDefaultDisplay}); + mManager.show(); + verify(mManager, never()).createPresentation(any()); + } + + @Test + public void testShow_includeSecondaryDisplay() { + when(mDisplayManager.getDisplays()).thenReturn( + new Display[]{mDefaultDisplay, mSecondaryDisplay}); + mManager.show(); + verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay)); + } + + @Test + public void testShow_includeNonDefaultGroupDisplay() { + when(mDisplayManager.getDisplays()).thenReturn( + new Display[]{mDefaultDisplay, mDifferentGroupDisplay}); + + mManager.show(); + verify(mManager, never()).createPresentation(any()); + } + + @Test + public void testShow_includeSecondaryAndNonDefaultGroupDisplays() { + when(mDisplayManager.getDisplays()).thenReturn( + new Display[]{mDefaultDisplay, mSecondaryDisplay, mDifferentGroupDisplay}); + + mManager.show(); + verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 02143a750cae..bc322f7f18fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -16,6 +16,9 @@ package com.android.systemui.appops; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; + import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertEquals; @@ -49,6 +52,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import org.junit.Before; import org.junit.Test; @@ -81,6 +85,8 @@ public class AppOpsControllerTest extends SysuiTestCase { private PermissionFlagsCache mFlagsCache; @Mock private PackageManager mPackageManager; + @Mock + private IndividualSensorPrivacyController mSensorPrivacyController; @Mock(stubOnly = true) private AudioManager mAudioManager; @Mock() @@ -118,12 +124,18 @@ public class AppOpsControllerTest extends SysuiTestCase { when(mAudioManager.getActiveRecordingConfigurations()) .thenReturn(List.of(mPausedMockRecording)); + when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA)) + .thenReturn(false); + when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA)) + .thenReturn(false); + mController = new AppOpsControllerImpl( mContext, mTestableLooper.getLooper(), mDumpManager, mFlagsCache, mAudioManager, + mSensorPrivacyController, mDispatcher ); } @@ -133,6 +145,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.setListening(true); verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController); verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any()); + verify(mSensorPrivacyController, times(1)).addCallback(mController); } @Test @@ -140,6 +153,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.setListening(false); verify(mAppOpsManager, times(1)).stopWatchingActive(mController); verify(mDispatcher, times(1)).unregisterReceiver(mController); + verify(mSensorPrivacyController, times(1)).removeCallback(mController); } @Test @@ -476,6 +490,71 @@ public class AppOpsControllerTest extends SysuiTestCase { AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false); } + @Test + public void testAudioFilteredWhenMicDisabled() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA}, + mCallback); + mTestableLooper.processAllMessages(); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + List<AppOpItem> list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); + assertFalse(list.get(0).isDisabled()); + + // Add a camera op, and disable the microphone. The camera op should be the only op returned + mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, true); + mController.onOpActiveChanged( + AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); + + + // Re enable the microphone, and verify the op returns + mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, false); + mTestableLooper.processAllMessages(); + + list = mController.getActiveAppOps(); + assertEquals(2, list.size()); + int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0; + assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(micIdx).getCode()); + } + + @Test + public void testCameraFilteredWhenCameraDisabled() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA}, + mCallback); + mTestableLooper.processAllMessages(); + mController.onOpActiveChanged( + AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + List<AppOpItem> list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); + assertFalse(list.get(0).isDisabled()); + + // Add an audio op, and disable the camera. The audio op should be the only op returned + mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, true); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); + + // Re enable the camera, and verify the op returns + mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, false); + mTestableLooper.processAllMessages(); + + list = mController.getActiveAppOps(); + assertEquals(2, list.size()); + int cameraIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 0 : 1; + assertEquals(AppOpsManager.OP_CAMERA, list.get(cameraIdx).getCode()); + } + private class TestHandler extends AppOpsControllerImpl.H { TestHandler(Looper looper) { mController.super(looper); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 9aa0aed06892..ea1473ea3db7 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -166,8 +166,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // their capabilities are ready. private static final int WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS = 1000; - static final String FUNCTION_REGISTER_SYSTEM_ACTION = "registerSystemAction"; - static final String FUNCTION_UNREGISTER_SYSTEM_ACTION = "unregisterSystemAction"; private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE = "registerUiTestAutomationService"; @@ -748,9 +746,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public void registerSystemAction(RemoteAction action, int actionId) { - mSecurityPolicy.enforceCallerIsRecentsOrHasPermission( - Manifest.permission.MANAGE_ACCESSIBILITY, - FUNCTION_REGISTER_SYSTEM_ACTION); + mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); getSystemActionPerformer().registerSystemAction(actionId, action); } @@ -761,9 +757,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public void unregisterSystemAction(int actionId) { - mSecurityPolicy.enforceCallerIsRecentsOrHasPermission( - Manifest.permission.MANAGE_ACCESSIBILITY, - FUNCTION_UNREGISTER_SYSTEM_ACTION); + mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); getSystemActionPerformer().unregisterSystemAction(actionId); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index d7664312e2e6..bef6d3e950c1 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -38,8 +38,6 @@ import android.util.Slog; import android.view.accessibility.AccessibilityEvent; import com.android.internal.util.ArrayUtils; -import com.android.server.LocalServices; -import com.android.server.wm.ActivityTaskManagerInternal; import libcore.util.EmptyArray; @@ -88,7 +86,6 @@ public class AccessibilitySecurityPolicy { private final AccessibilityUserManager mAccessibilityUserManager; private AccessibilityWindowManager mAccessibilityWindowManager; - private final ActivityTaskManagerInternal mAtmInternal; /** * Constructor for AccessibilityManagerService. @@ -100,7 +97,6 @@ public class AccessibilitySecurityPolicy { mPackageManager = mContext.getPackageManager(); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); } /** @@ -572,13 +568,4 @@ public class AccessibilitySecurityPolicy { + permission); } } - - /** - * Enforcing permission check to IPC caller or grant it if it's recents. - * - * @param permission The permission to check - */ - public void enforceCallerIsRecentsOrHasPermission(@NonNull String permission, String func) { - mAtmInternal.enforceCallerIsRecentsOrHasPermission(permission, func); - } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d129b9c074a9..554edc6d74bd 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -132,8 +132,8 @@ import android.net.SocketKeepalive; import android.net.TetheringManager; import android.net.UidRange; import android.net.UidRangeParcel; +import android.net.UnderlyingNetworkInfo; import android.net.Uri; -import android.net.VpnInfo; import android.net.VpnManager; import android.net.VpnService; import android.net.metrics.INetdEventListener; @@ -4854,28 +4854,28 @@ public class ConnectivityService extends IConnectivityManager.Stub * * <p>Must be called on the handler thread. */ - private VpnInfo[] getAllVpnInfo() { + private UnderlyingNetworkInfo[] getAllVpnInfo() { ensureRunningOnConnectivityServiceThread(); synchronized (mVpns) { if (mLockdownEnabled) { - return new VpnInfo[0]; + return new UnderlyingNetworkInfo[0]; } } - List<VpnInfo> infoList = new ArrayList<>(); + List<UnderlyingNetworkInfo> infoList = new ArrayList<>(); for (NetworkAgentInfo nai : mNetworkAgentInfos) { - VpnInfo info = createVpnInfo(nai); + UnderlyingNetworkInfo info = createVpnInfo(nai); if (info != null) { infoList.add(info); } } - return infoList.toArray(new VpnInfo[infoList.size()]); + return infoList.toArray(new UnderlyingNetworkInfo[infoList.size()]); } /** * @return VPN information for accounting, or null if we can't retrieve all required * information, e.g underlying ifaces. */ - private VpnInfo createVpnInfo(NetworkAgentInfo nai) { + private UnderlyingNetworkInfo createVpnInfo(NetworkAgentInfo nai) { if (!nai.isVPN()) return null; Network[] underlyingNetworks = nai.declaredUnderlyingNetworks; @@ -4907,11 +4907,11 @@ public class ConnectivityService extends IConnectivityManager.Stub // Must be non-null or NetworkStatsService will crash. // Cannot happen in production code because Vpn only registers the NetworkAgent after the // tun or ipsec interface is created. + // TODO: Remove this check. if (nai.linkProperties.getInterfaceName() == null) return null; - return new VpnInfo(nai.networkCapabilities.getOwnerUid(), - nai.linkProperties.getInterfaceName(), - interfaces.toArray(new String[0])); + return new UnderlyingNetworkInfo(nai.networkCapabilities.getOwnerUid(), + nai.linkProperties.getInterfaceName(), interfaces); } /** @@ -5707,6 +5707,9 @@ public class ConnectivityService extends IConnectivityManager.Stub networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid); enforceAccessPermission(); break; + case BACKGROUND_REQUEST: + enforceNetworkStackOrSettingsPermission(); + // Fall-through since other checks are the same with normal requests. case REQUEST: networkCapabilities = new NetworkCapabilities(networkCapabilities); enforceNetworkRequestPermissions(networkCapabilities, callingPackageName, @@ -7998,10 +8001,10 @@ public class ConnectivityService extends IConnectivityManager.Stub activeIface = activeLinkProperties.getInterfaceName(); } - final VpnInfo[] vpnInfos = getAllVpnInfo(); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = getAllVpnInfo(); try { - mStatsService.forceUpdateIfaces( - getDefaultNetworks(), getAllNetworkState(), activeIface, vpnInfos); + mStatsService.forceUpdateIfaces(getDefaultNetworks(), getAllNetworkState(), activeIface, + underlyingNetworkInfos); } catch (Exception ignored) { } } @@ -8261,6 +8264,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return getVpnIfOwner(mDeps.getCallingUid()); } + // TODO: stop calling into Vpn.java and get this information from data in this class. @GuardedBy("mVpns") private Vpn getVpnIfOwner(int uid) { final int user = UserHandle.getUserId(uid); @@ -8269,7 +8273,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (vpn == null) { return null; } else { - final VpnInfo info = vpn.getVpnInfo(); + final UnderlyingNetworkInfo info = vpn.getUnderlyingNetworkInfo(); return (info == null || info.ownerUid != uid) ? null : vpn; } } diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 76db0192cb00..8562b0d9cb82 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -25,9 +25,12 @@ import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; +import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; @@ -527,7 +530,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull IVcnUnderlyingNetworkPolicyListener listener) { requireNonNull(listener, "listener was null"); - mContext.enforceCallingPermission( + mContext.enforceCallingOrSelfPermission( android.Manifest.permission.NETWORK_FACTORY, "Must have permission NETWORK_FACTORY to register a policy listener"); @@ -561,4 +564,27 @@ public class VcnManagementService extends IVcnManagementService.Stub { } } } + + /** + * Gets the UnderlyingNetworkPolicy as determined by the provided NetworkCapabilities and + * LinkProperties. + */ + @NonNull + @Override + public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties) { + requireNonNull(networkCapabilities, "networkCapabilities was null"); + requireNonNull(linkProperties, "linkProperties was null"); + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.NETWORK_FACTORY, + "Must have permission NETWORK_FACTORY or be the SystemServer to get underlying" + + " Network policies"); + + // TODO(b/175914059): implement policy generation once VcnManagementService is able to + // determine policies + + return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities); + } } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index efe82df628d2..d63a6c3a0a6f 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -31,6 +31,7 @@ import android.os.IPowerManager; import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceDebugInfo; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; @@ -136,6 +137,11 @@ public class Watchdog { "android.system.suspend@1.0::ISystemSuspend" ); + public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] { + "android.hardware.light.ILights/", + "android.hardware.power.stats.IPowerStats/", + }; + private static Watchdog sWatchdog; private final Thread mThread; @@ -527,12 +533,11 @@ public class Watchdog { return builder.toString(); } - private static ArrayList<Integer> getInterestingHalPids() { + private static void addInterestingHidlPids(HashSet<Integer> pids) { try { IServiceManager serviceManager = IServiceManager.getService(); ArrayList<IServiceManager.InstanceDebugInfo> dump = serviceManager.debugDump(); - HashSet<Integer> pids = new HashSet<>(); for (IServiceManager.InstanceDebugInfo info : dump) { if (info.pid == IServiceManager.PidConstant.NO_PID) { continue; @@ -544,24 +549,37 @@ public class Watchdog { pids.add(info.pid); } - return new ArrayList<Integer>(pids); } catch (RemoteException e) { - return new ArrayList<Integer>(); + Log.w(TAG, e); + } + } + + private static void addInterestingAidlPids(HashSet<Integer> pids) { + ServiceDebugInfo[] infos = ServiceManager.getServiceDebugInfo(); + if (infos == null) return; + + for (ServiceDebugInfo info : infos) { + for (String prefix : AIDL_INTERFACE_PREFIXES_OF_INTEREST) { + if (info.name.startsWith(prefix)) { + pids.add(info.debugPid); + } + } } } static ArrayList<Integer> getInterestingNativePids() { - ArrayList<Integer> pids = getInterestingHalPids(); + HashSet<Integer> pids = new HashSet<>(); + addInterestingAidlPids(pids); + addInterestingHidlPids(pids); int[] nativePids = Process.getPidsForCommands(NATIVE_STACKS_OF_INTEREST); if (nativePids != null) { - pids.ensureCapacity(pids.size() + nativePids.length); for (int i : nativePids) { pids.add(i); } } - return pids; + return new ArrayList<Integer>(pids); } private void run() { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index e476ca908b5d..02613cfe0771 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -900,7 +900,7 @@ public final class ActiveServices { String callingPackage, @Nullable String callingFeatureId, int callingUid, Intent service, boolean callerFg, final int userId, final boolean isBinding, final IServiceConnection connection) { - if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired( + if (mAm.getPackageManagerInternal().isPermissionsReviewRequired( r.packageName, r.userId)) { // Show a permission review UI only for starting/binding from a foreground app @@ -932,7 +932,7 @@ public final class ActiveServices { // binding request is still valid, so hook them up. We // proceed only if the caller cleared the review requirement // otherwise we unbind because the user didn't approve. - if (!mAm.getPackageManagerInternalLocked() + if (!mAm.getPackageManagerInternal() .isPermissionsReviewRequired(r.packageName, r.userId)) { try { @@ -941,9 +941,14 @@ public final class ActiveServices { callerFg, false /* whileRestarting */, false /* permissionsReviewRequired */, - false /* packageFrozen */); + false /* packageFrozen */, + true /* enqueueOomAdj */); } catch (RemoteException e) { /* ignore - local call */ + } finally { + /* Will be a no-op if nothing pending */ + mAm.updateOomAdjPendingTargetsLocked( + OomAdjuster.OOM_ADJ_REASON_START_SERVICE); } } else { unbindServiceLocked(connection); @@ -994,7 +999,7 @@ public final class ActiveServices { int callingUid, int callingPid, boolean fgRequired, boolean callerFg, int userId, boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken, boolean isBinding, IServiceConnection connection) { - final PackageManagerInternal pm = mAm.getPackageManagerInternalLocked(); + final PackageManagerInternal pm = mAm.getPackageManagerInternal(); final boolean frozen = pm.isPackageFrozen(s.packageName, callingUid, s.userId); if (!frozen) { // Not frozen, it's okay to go @@ -1025,9 +1030,14 @@ public final class ActiveServices { bringUpServiceLocked(s, serviceIntent.getFlags(), callerFg, false /* whileRestarting */, false /* permissionsReviewRequired */, - false /* packageFrozen */); + false /* packageFrozen */, + true /* enqueueOomAdj */); } catch (TransactionTooLargeException e) { /* ignore - local call */ + } finally { + /* Will be a no-op if nothing pending */ + mAm.updateOomAdjPendingTargetsLocked( + OomAdjuster.OOM_ADJ_REASON_START_SERVICE); } } else { // Starting a service try { @@ -1091,7 +1101,13 @@ public final class ActiveServices { FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName, serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START); mAm.mBatteryStatsService.noteServiceStartRunning(uid, packageName, serviceName); - String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false, false); + String error = bringUpServiceLocked(r, service.getFlags(), callerFg, + false /* whileRestarting */, + false /* permissionsReviewRequired */, + false /* packageFrozen */, + true /* enqueueOomAdj */); + /* Will be a no-op if nothing pending */ + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE); if (error != null) { return new ComponentName("!!", error); } @@ -1117,7 +1133,7 @@ public final class ActiveServices { return r.name; } - private void stopServiceLocked(ServiceRecord service) { + private void stopServiceLocked(ServiceRecord service, boolean enqueueOomAdj) { if (service.delayed) { // If service isn't actually running, but is being held in the // delayed list, then we need to keep it started but note that it @@ -1140,7 +1156,7 @@ public final class ActiveServices { } service.callStart = false; - bringDownServiceIfNeededLocked(service, false, false); + bringDownServiceIfNeededLocked(service, false, false, enqueueOomAdj); } int stopServiceLocked(IApplicationThread caller, Intent service, @@ -1163,7 +1179,7 @@ public final class ActiveServices { if (r.record != null) { final long origId = Binder.clearCallingIdentity(); try { - stopServiceLocked(r.record); + stopServiceLocked(r.record, false); } finally { Binder.restoreCallingIdentity(origId); } @@ -1213,11 +1229,15 @@ public final class ActiveServices { } } if (stopping != null) { - for (int i=stopping.size()-1; i>=0; i--) { + final int size = stopping.size(); + for (int i = size - 1; i >= 0; i--) { ServiceRecord service = stopping.get(i); service.delayed = false; services.ensureNotStartingBackgroundLocked(service); - stopServiceLocked(service); + stopServiceLocked(service, true); + } + if (size > 0) { + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); } } } @@ -1226,7 +1246,7 @@ public final class ActiveServices { void killMisbehavingService(ServiceRecord r, int appUid, int appPid, String localPackageName) { synchronized (mAm) { - stopServiceLocked(r); + stopServiceLocked(r, false); mAm.crashApplication(appUid, appPid, localPackageName, -1, "Bad notification for startForeground", true /*force*/); } @@ -1301,7 +1321,7 @@ public final class ActiveServices { } r.callStart = false; final long origId = Binder.clearCallingIdentity(); - bringDownServiceIfNeededLocked(r, false, false); + bringDownServiceIfNeededLocked(r, false, false, false); Binder.restoreCallingIdentity(origId); return true; } @@ -2229,12 +2249,12 @@ public final class ActiveServices { mAm.updateProcessForegroundLocked(proc, anyForeground, fgServiceTypes, oomAdj); } - private void updateWhitelistManagerLocked(ProcessRecord proc) { - proc.whitelistManager = false; + private void updateAllowlistManagerLocked(ProcessRecord proc) { + proc.mAllowlistManager = false; for (int i = proc.numberOfRunningServices() - 1; i >= 0; i--) { ServiceRecord sr = proc.getRunningServiceAt(i); if (sr.whitelistManager) { - proc.whitelistManager = true; + proc.mAllowlistManager = true; break; } } @@ -2475,10 +2495,13 @@ public final class ActiveServices { } clist.add(c); + boolean needOomAdj = false; if ((flags&Context.BIND_AUTO_CREATE) != 0) { s.lastActivity = SystemClock.uptimeMillis(); + needOomAdj = true; if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, - permissionsReviewRequired, packageFrozen) != null) { + permissionsReviewRequired, packageFrozen, true) != null) { + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE); return 0; } } @@ -2489,7 +2512,7 @@ public final class ActiveServices { s.app.treatLikeActivity = true; } if (s.whitelistManager) { - s.app.whitelistManager = true; + s.app.mAllowlistManager = true; } // This could have made the service more important. mAm.updateLruProcessLocked(s.app, @@ -2497,7 +2520,11 @@ public final class ActiveServices { || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0), b.client); - mAm.updateOomAdjLocked(s.app, OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE); + needOomAdj = true; + mAm.enqueueOomAdjTargetLocked(s.app); + } + if (needOomAdj) { + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE); } if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b @@ -2591,7 +2618,7 @@ public final class ActiveServices { } } - serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false); + serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false, false); } } finally { Binder.restoreCallingIdentity(origId); @@ -2647,7 +2674,7 @@ public final class ActiveServices { try { while (clist.size() > 0) { ConnectionRecord r = clist.get(0); - removeConnectionLocked(r, null, null); + removeConnectionLocked(r, null, null, true); if (clist.size() > 0 && clist.get(0) == r) { // In case it didn't get removed above, do it now. Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder); @@ -2656,8 +2683,8 @@ public final class ActiveServices { final ProcessRecord app = r.binding.service.app; if (app != null) { - if (app.whitelistManager) { - updateWhitelistManagerLocked(app); + if (app.mAllowlistManager) { + updateAllowlistManagerLocked(app); } // This could have made the service less important. if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { @@ -2716,7 +2743,7 @@ public final class ActiveServices { } } - serviceDoneExecutingLocked(r, inDestroying, false); + serviceDoneExecutingLocked(r, inDestroying, false, false); } } finally { Binder.restoreCallingIdentity(origId); @@ -2803,7 +2830,7 @@ public final class ActiveServices { flags |= PackageManager.MATCH_INSTANT; } // TODO: come back and remove this assumption to triage all services - ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service, + ResolveInfo rInfo = mAm.getPackageManagerInternal().resolveService(service, resolvedType, flags, userId, callingUid); ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null; if (sInfo == null) { @@ -2876,7 +2903,7 @@ public final class ActiveServices { final long token = Binder.clearCallingIdentity(); try { ResolveInfo rInfoForUserId0 = - mAm.getPackageManagerInternalLocked().resolveService(service, + mAm.getPackageManagerInternal().resolveService(service, resolvedType, flags, userId, callingUid); if (rInfoForUserId0 == null) { Slog.w(TAG_SERVICE, @@ -3055,13 +3082,13 @@ public final class ActiveServices { // Keep the executeNesting count accurate. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e); final boolean inDestroying = mDestroyingServices.contains(r); - serviceDoneExecutingLocked(r, inDestroying, inDestroying); + serviceDoneExecutingLocked(r, inDestroying, inDestroying, false); throw e; } catch (RemoteException e) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r); // Keep the executeNesting count accurate. final boolean inDestroying = mDestroyingServices.contains(r); - serviceDoneExecutingLocked(r, inDestroying, inDestroying); + serviceDoneExecutingLocked(r, inDestroying, inDestroying, false); return false; } } @@ -3217,9 +3244,12 @@ public final class ActiveServices { } try { bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false, - false); + false, true); } catch (TransactionTooLargeException e) { // Ignore, it's been logged and nothing upstack cares. + } finally { + /* Will be a no-op if nothing pending */ + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE); } } @@ -3262,7 +3292,8 @@ public final class ActiveServices { } private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, - boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen) + boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen, + boolean enqueueOomAdj) throws TransactionTooLargeException { if (r.app != null && r.app.thread != null) { sendServiceArgsLocked(r, execInFg, false); @@ -3299,7 +3330,7 @@ public final class ActiveServices { + r.appInfo.uid + " for service " + r.intent.getIntent() + ": user " + r.userId + " is stopped"; Slog.w(TAG, msg); - bringDownServiceLocked(r); + bringDownServiceLocked(r, enqueueOomAdj); return msg; } @@ -3325,7 +3356,7 @@ public final class ActiveServices { if (app != null && app.thread != null) { try { app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats); - realStartServiceLocked(r, app, execInFg); + realStartServiceLocked(r, app, execInFg, enqueueOomAdj); return null; } catch (TransactionTooLargeException e) { throw e; @@ -3366,7 +3397,7 @@ public final class ActiveServices { + r.appInfo.uid + " for service " + r.intent.getIntent() + ": process is bad"; Slog.w(TAG, msg); - bringDownServiceLocked(r); + bringDownServiceLocked(r, enqueueOomAdj); return msg; } if (isolated) { @@ -3379,7 +3410,7 @@ public final class ActiveServices { Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid) + " for fg-service launch"); } - mAm.tempWhitelistUidLocked(r.appInfo.uid, + mAm.tempAllowlistUidLocked(r.appInfo.uid, SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch", BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED); } @@ -3394,7 +3425,7 @@ public final class ActiveServices { if (r.startRequested) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Applying delayed stop (in bring up): " + r); - stopServiceLocked(r); + stopServiceLocked(r, enqueueOomAdj); } } @@ -3417,7 +3448,7 @@ public final class ActiveServices { * from bindService() as well. */ private final void realStartServiceLocked(ServiceRecord r, - ProcessRecord app, boolean execInFg) throws RemoteException { + ProcessRecord app, boolean execInFg, boolean enqueueOomAdj) throws RemoteException { if (app.thread == null) { throw new RemoteException(); } @@ -3431,7 +3462,11 @@ public final class ActiveServices { bumpServiceExecutingLocked(r, execInFg, "create"); mAm.updateLruProcessLocked(app, false, null); updateServiceForegroundLocked(r.app, /* oomAdj= */ false); - mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE); + if (enqueueOomAdj) { + mAm.enqueueOomAdjTargetLocked(app); + } else { + mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE); + } boolean created = false; try { @@ -3466,7 +3501,7 @@ public final class ActiveServices { if (!created) { // Keep the executeNesting count accurate. final boolean inDestroying = mDestroyingServices.contains(r); - serviceDoneExecutingLocked(r, inDestroying, inDestroying); + serviceDoneExecutingLocked(r, inDestroying, inDestroying, false); // Cleanup. if (newService) { @@ -3482,7 +3517,7 @@ public final class ActiveServices { } if (r.whitelistManager) { - app.whitelistManager = true; + app.mAllowlistManager = true; } requestServiceBindingsLocked(r, execInFg); @@ -3515,7 +3550,7 @@ public final class ActiveServices { if (r.startRequested) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Applying delayed stop (from start): " + r); - stopServiceLocked(r); + stopServiceLocked(r, enqueueOomAdj); } } } @@ -3603,9 +3638,11 @@ public final class ActiveServices { if (caughtException != null) { // Keep nesting count correct final boolean inDestroying = mDestroyingServices.contains(r); - for (int i = 0; i < args.size(); i++) { - serviceDoneExecutingLocked(r, inDestroying, inDestroying); + for (int i = 0, size = args.size(); i < size; i++) { + serviceDoneExecutingLocked(r, inDestroying, inDestroying, true); } + /* Will be a no-op if nothing pending */ + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); if (caughtException instanceof TransactionTooLargeException) { throw (TransactionTooLargeException)caughtException; } @@ -3631,7 +3668,7 @@ public final class ActiveServices { } private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn, - boolean hasConn) { + boolean hasConn, boolean enqueueOomAdj) { //Slog.i(TAG, "Bring down service:"); //r.dump(" "); @@ -3644,10 +3681,10 @@ public final class ActiveServices { return; } - bringDownServiceLocked(r); + bringDownServiceLocked(r, enqueueOomAdj); } - private final void bringDownServiceLocked(ServiceRecord r) { + private void bringDownServiceLocked(ServiceRecord r, boolean enqueueOomAdj) { //Slog.i(TAG, "Bring down service:"); //r.dump(" "); @@ -3672,9 +3709,9 @@ public final class ActiveServices { } } + boolean needOomAdj = false; // Tell the service that it has been unbound. if (r.app != null && r.app.thread != null) { - boolean needOomAdj = false; 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 @@ -3691,15 +3728,11 @@ public final class ActiveServices { Slog.w(TAG, "Exception when unbinding service " + r.shortInstanceName, e); needOomAdj = false; - serviceProcessGoneLocked(r); + serviceProcessGoneLocked(r, enqueueOomAdj); break; } } } - if (needOomAdj) { - mAm.updateOomAdjLocked(r.app, true, - OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); - } } // Check to see if the service had been started as foreground, but being @@ -3801,7 +3834,7 @@ public final class ActiveServices { r.app.stopService(r); r.app.updateBoundClientUids(); if (r.whitelistManager) { - updateWhitelistManagerLocked(r.app); + updateAllowlistManagerLocked(r.app); } if (r.app.thread != null) { updateServiceForegroundLocked(r.app, false); @@ -3815,7 +3848,7 @@ public final class ActiveServices { } catch (Exception e) { Slog.w(TAG, "Exception when destroying service " + r.shortInstanceName, e); - serviceProcessGoneLocked(r); + serviceProcessGoneLocked(r, enqueueOomAdj); } } else { if (DEBUG_SERVICE) Slog.v( @@ -3826,6 +3859,13 @@ public final class ActiveServices { TAG_SERVICE, "Removed service that is not running: " + r); } + if (needOomAdj) { + if (enqueueOomAdj) { + mAm.enqueueOomAdjTargetLocked(r.app); + } else { + mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); + } + } if (r.bindings.size() > 0) { r.bindings.clear(); } @@ -3849,7 +3889,7 @@ public final class ActiveServices { } void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp, - ActivityServiceConnectionsHolder skipAct) { + ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) { IBinder binder = c.conn.asBinder(); AppBindRecord b = c.binding; ServiceRecord s = b.service; @@ -3875,7 +3915,7 @@ public final class ActiveServices { if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { s.updateWhitelistManager(); if (!s.whitelistManager && s.app != null) { - updateWhitelistManagerLocked(s.app); + updateAllowlistManagerLocked(s.app); } } // And do the same for bg activity starts ability. @@ -3918,8 +3958,12 @@ public final class ActiveServices { // it to go down there and we want it to start out near the top. mAm.updateLruProcessLocked(s.app, false, null); } - mAm.updateOomAdjLocked(s.app, true, - OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); + if (enqueueOomAdj) { + mAm.enqueueOomAdjTargetLocked(s.app); + } else { + mAm.updateOomAdjLocked(s.app, true, + OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); + } b.intent.hasBound = false; // Assume the client doesn't want to know about a rebind; // we will deal with that later if it asks for one. @@ -3927,7 +3971,7 @@ public final class ActiveServices { s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent()); } catch (Exception e) { Slog.w(TAG, "Exception when unbinding service " + s.shortInstanceName, e); - serviceProcessGoneLocked(s); + serviceProcessGoneLocked(s, enqueueOomAdj); } } @@ -3946,12 +3990,13 @@ public final class ActiveServices { SystemClock.uptimeMillis()); } } - bringDownServiceIfNeededLocked(s, true, hasAutoCreate); + bringDownServiceIfNeededLocked(s, true, hasAutoCreate, enqueueOomAdj); } } } - void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) { + void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res, + boolean enqueueOomAdj) { boolean inDestroying = mDestroyingServices.contains(r); if (r != null) { if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) { @@ -4024,7 +4069,7 @@ public final class ActiveServices { } } final long origId = Binder.clearCallingIdentity(); - serviceDoneExecutingLocked(r, inDestroying, inDestroying); + serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj); Binder.restoreCallingIdentity(origId); } else { Slog.w(TAG, "Done executing unknown service from pid " @@ -4032,7 +4077,7 @@ public final class ActiveServices { } } - private void serviceProcessGoneLocked(ServiceRecord r) { + private void serviceProcessGoneLocked(ServiceRecord r, boolean enqueueOomAdj) { if (r.tracker != null) { int memFactor = mAm.mProcessStats.getMemFactorLocked(); long now = SystemClock.uptimeMillis(); @@ -4041,11 +4086,11 @@ public final class ActiveServices { r.tracker.setBound(false, memFactor, now); r.tracker.setStarted(false, memFactor, now); } - serviceDoneExecutingLocked(r, true, true); + serviceDoneExecutingLocked(r, true, true, enqueueOomAdj); } private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, - boolean finishing) { + boolean finishing, boolean enqueueOomAdj) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "<<< DONE EXECUTING " + r + ": nesting=" + r.executeNesting + ", inDestroying=" + inDestroying + ", app=" + r.app); @@ -4077,7 +4122,11 @@ public final class ActiveServices { mDestroyingServices.remove(r); r.bindings.clear(); } - mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); + if (enqueueOomAdj) { + mAm.enqueueOomAdjTargetLocked(r.app); + } else { + mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); + } } r.executeFg = false; if (r.tracker != null) { @@ -4095,7 +4144,7 @@ public final class ActiveServices { r.app.stopService(r); r.app.updateBoundClientUids(); if (r.whitelistManager) { - updateWhitelistManagerLocked(r.app); + updateAllowlistManagerLocked(r.app); } } r.setProcess(null); @@ -4121,15 +4170,17 @@ public final class ActiveServices { i--; proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode, mAm.mProcessStats); - realStartServiceLocked(sr, proc, sr.createdFromFg); + realStartServiceLocked(sr, proc, sr.createdFromFg, true); didSomething = true; if (!isServiceNeededLocked(sr, false, false)) { // We were waiting for this service to start, but it is actually no // longer needed. This could happen because bringDownServiceIfNeeded // won't bring down a service that is pending... so now the pending // is done, so let's drop it. - bringDownServiceLocked(sr); + bringDownServiceLocked(sr, true); } + /* Will be a no-op if nothing pending */ + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE); } } catch (RemoteException e) { Slog.w(TAG, "Exception in new application when starting service " @@ -4157,7 +4208,8 @@ public final class ActiveServices { } void processStartTimedOutLocked(ProcessRecord proc) { - for (int i=0; i<mPendingServices.size(); i++) { + boolean needOomAdj = false; + for (int i = 0, size = mPendingServices.size(); i < size; i++) { ServiceRecord sr = mPendingServices.get(i); if ((proc.uid == sr.appInfo.uid && proc.processName.equals(sr.processName)) @@ -4166,9 +4218,13 @@ public final class ActiveServices { sr.isolatedProc = null; mPendingServices.remove(i); i--; - bringDownServiceLocked(sr); + needOomAdj = true; + bringDownServiceLocked(sr, true); } } + if (needOomAdj) { + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); + } } private boolean collectPackageServicesLocked(String packageName, Set<String> filterByClasses, @@ -4191,7 +4247,7 @@ public final class ActiveServices { service.app.stopService(service); service.app.updateBoundClientUids(); if (service.whitelistManager) { - updateWhitelistManagerLocked(service.app); + updateAllowlistManagerLocked(service.app); } } service.setProcess(null); @@ -4237,8 +4293,12 @@ public final class ActiveServices { } if (mTmpCollectionResults != null) { - for (int i = mTmpCollectionResults.size() - 1; i >= 0; i--) { - bringDownServiceLocked(mTmpCollectionResults.get(i)); + final int size = mTmpCollectionResults.size(); + for (int i = size - 1; i >= 0; i--) { + bringDownServiceLocked(mTmpCollectionResults.get(i), true); + } + if (size > 0) { + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); } mTmpCollectionResults.clear(); } @@ -4279,12 +4339,14 @@ public final class ActiveServices { } // Take care of any running services associated with the app. + boolean needOomAdj = false; for (int i = services.size() - 1; i >= 0; i--) { ServiceRecord sr = services.get(i); if (sr.startRequested) { if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) { Slog.i(TAG, "Stopping service " + sr.shortInstanceName + ": remove task"); - stopServiceLocked(sr); + needOomAdj = true; + stopServiceLocked(sr, true); } else { sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true, sr.getLastStartId(), baseIntent, null, 0)); @@ -4300,6 +4362,9 @@ public final class ActiveServices { } } } + if (needOomAdj) { + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); + } } final void killServicesLocked(ProcessRecord app, boolean allowRestart) { @@ -4333,12 +4398,12 @@ 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); - removeConnectionLocked(r, app, null); + removeConnectionLocked(r, app, null, true); } updateServiceConnectionActivitiesLocked(app); app.connections.clear(); - app.whitelistManager = false; + app.mAllowlistManager = false; // Clear app state from services. for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) { @@ -4435,10 +4500,10 @@ public final class ActiveServices { + " times, stopping: " + sr); EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, sr.userId, sr.crashCount, sr.shortInstanceName, app.pid); - bringDownServiceLocked(sr); + bringDownServiceLocked(sr, true); } else if (!allowRestart || !mAm.mUserController.isUserRunning(sr.userId, 0)) { - bringDownServiceLocked(sr); + bringDownServiceLocked(sr, true); } else { final boolean scheduled = scheduleServiceRestartLocked(sr, true /* allowCancel */); @@ -4446,7 +4511,7 @@ public final class ActiveServices { // extreme case of so many attempts to deliver a command // that it failed we also will stop it here. if (!scheduled) { - bringDownServiceLocked(sr); + bringDownServiceLocked(sr, true); } else if (sr.canStopIfKilled(false /* isStartCanceled */)) { // Update to stopped state because the explicit start is gone. The service is // scheduled to restart for other reason (e.g. connections) so we don't bring @@ -4460,6 +4525,8 @@ public final class ActiveServices { } } + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); + if (!allowRestart) { app.stopAllServices(); app.clearBoundClientUids(); @@ -4684,7 +4751,7 @@ public final class ActiveServices { Slog.i(TAG, "Service foreground-required timeout for " + r); } r.fgWaiting = false; - stopServiceLocked(r); + stopServiceLocked(r, false); } if (app != null) { @@ -5490,7 +5557,7 @@ public final class ActiveServices { } if (ret == FGS_FEATURE_DENIED) { - if (mAm.isWhitelistedForFgsStartLocked(callingUid)) { + if (mAm.isAllowlistedForFgsStartLocked(callingUid)) { // uid is on DeviceIdleController's user/system allowlist // or AMS's FgsStartTempAllowList. ret = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST; @@ -5593,7 +5660,7 @@ public final class ActiveServices { case FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION: return "ALLOWED_BY_BACKGROUND_FGS_PERMISSION"; case FGS_FEATURE_ALLOWED_BY_ALLOWLIST: - return "ALLOWED_BY_WHITELIST"; + return "ALLOWED_BY_ALLOWLIST"; case FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER: return "ALLOWED_BY_DEVICE_OWNER"; case FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST: diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 05f2fa096e4d..f63b994307b0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -99,7 +99,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP; @@ -251,7 +250,6 @@ import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.Process; import android.os.RemoteCallback; -import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -278,7 +276,6 @@ import android.text.format.DateUtils; import android.text.style.SuggestionSpan; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.DebugUtils; import android.util.EventLog; import android.util.IntArray; import android.util.Log; @@ -395,6 +392,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; public class ActivityManagerService extends IActivityManager.Stub implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { @@ -535,16 +533,16 @@ public class ActivityManagerService extends IActivityManager.Stub public OomAdjProfiler mOomAdjProfiler = new OomAdjProfiler(); // Whether we should use SCHED_FIFO for UI and RenderThreads. - boolean mUseFifoUiScheduling = false; + final boolean mUseFifoUiScheduling; // Use an offload queue for long broadcasts, e.g. BOOT_COMPLETED. // For simplicity, since we statically declare the size of the array of BroadcastQueues, // we still create this new offload queue, but never ever put anything on it. - boolean mEnableOffloadQueue; + final boolean mEnableOffloadQueue; - BroadcastQueue mFgBroadcastQueue; - BroadcastQueue mBgBroadcastQueue; - BroadcastQueue mOffloadBroadcastQueue; + final BroadcastQueue mFgBroadcastQueue; + final BroadcastQueue mBgBroadcastQueue; + final BroadcastQueue mOffloadBroadcastQueue; // Convenient for easy iteration over the queues. Foreground is first // so that dispatch of foreground broadcasts gets precedence. final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[3]; @@ -662,9 +660,9 @@ public class ActivityManagerService extends IActivityManager.Stub final ProcessStatsService mProcessStats; /** - * Non-persistent appId whitelist for background restrictions + * Non-persistent appId allowlist for background restrictions */ - int[] mBackgroundAppIdWhitelist = new int[] { + int[] mBackgroundAppIdAllowlist = new int[] { BLUETOOTH_UID }; @@ -881,11 +879,6 @@ public class ActivityManagerService extends IActivityManager.Stub */ final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>(); - /** - * List of processes that should gc as soon as things are idle. - */ - final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>(); - private final ActivityMetricsLaunchObserver mActivityLaunchObserver = new ActivityMetricsLaunchObserver() { @Override @@ -915,7 +908,7 @@ public class ActivityManagerService extends IActivityManager.Stub } }; - private boolean mBinderTransactionTrackingEnabled = false; + private volatile boolean mBinderTransactionTrackingEnabled = false; /** * Fingerprints (hashCode()) of stack traces that we've @@ -923,6 +916,7 @@ public class ActivityManagerService extends IActivityManager.Stub * something (rogue user app) forces this over * MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared. */ + @GuardedBy("mAlreadyLoggedViolatedStacks") private final HashSet<Integer> mAlreadyLoggedViolatedStacks = new HashSet<Integer>(); private static final int MAX_DUP_SUPPRESSED_STACKS = 5000; @@ -1062,7 +1056,7 @@ public class ActivityManagerService extends IActivityManager.Stub /** * Information about component usage */ - UsageStatsManagerInternal mUsageStatsService; + volatile UsageStatsManagerInternal mUsageStatsService; /** * Access to DeviceIdleController service. @@ -1072,29 +1066,29 @@ public class ActivityManagerService extends IActivityManager.Stub /** * Power-save whitelisted app-ids (not including except-idle-whitelisted ones). */ - int[] mDeviceIdleWhitelist = new int[0]; + int[] mDeviceIdleAllowlist = new int[0]; /** * Power-save whitelisted app-ids (including except-idle-whitelisted ones). */ - int[] mDeviceIdleExceptIdleWhitelist = new int[0]; + int[] mDeviceIdleExceptIdleAllowlist = new int[0]; /** * Set of app ids that are temporarily allowed to escape bg check due to high-pri message */ - int[] mDeviceIdleTempWhitelist = new int[0]; + int[] mDeviceIdleTempAllowlist = new int[0]; - static final class PendingTempWhitelist { + static final class PendingTempAllowlist { final int targetUid; final long duration; final String tag; final int type; - PendingTempWhitelist(int _targetUid, long _duration, String _tag, int _type) { - targetUid = _targetUid; - duration = _duration; - tag = _tag; - type = _type; + PendingTempAllowlist(int targetUid, long duration, String tag, int type) { + this.targetUid = targetUid; + this.duration = duration; + this.tag = tag; + this.type = type; } void dumpDebug(ProtoOutputStream proto, long fieldId) { @@ -1107,7 +1101,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - final PendingTempWhitelists mPendingTempWhitelist = new PendingTempWhitelists(this); + final PendingTempAllowlists mPendingTempAllowlist = new PendingTempAllowlists(this); /** * The temp-allowlist that is allowed to start FGS from background. @@ -1162,7 +1156,7 @@ public class ActivityManagerService extends IActivityManager.Stub /** * State of external calls telling us if the device is awake or asleep. */ - int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + AtomicInteger mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); /** * The uptime of the last time we performed idle maintenance. @@ -1172,10 +1166,18 @@ public class ActivityManagerService extends IActivityManager.Stub /** * For reporting to battery stats the current top application. */ + @GuardedBy("mCurResumedAppLock") private String mCurResumedPackage = null; + + @GuardedBy("mCurResumedAppLock") private int mCurResumedUid = -1; /** + * Dedicated lock for {@link #mCurResumedPackage} and {@link #mCurResumedUid}. + */ + private final Object mCurResumedAppLock = new Object(); + + /** * For reporting to battery stats the apps currently running foreground * service. The ProcessMap is package/uid tuples; each of these contain * an array of the currently foreground processes. @@ -1213,16 +1215,17 @@ public class ActivityManagerService extends IActivityManager.Stub int foregroundServiceTypes; } - // TODO: Move below 4 members and code to ProcessList - final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>(); - ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5]; - - final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>(); - final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>(); - + @GuardedBy("mOomAdjObserverLock") OomAdjObserver mCurOomAdjObserver; + + @GuardedBy("mOomAdjObserverLock") int mCurOomAdjUid; + /** + * Dedicated lock for {@link #mCurOomAdjObserver} and {@link #mCurOomAdjUid}. + */ + final Object mOomAdjObserverLock = new Object(); + interface OomAdjObserver { void onOomAdjMessage(String msg); } @@ -1301,7 +1304,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int IDLE_UIDS_MSG = 58; static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63; static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66; - static final int PUSH_TEMP_WHITELIST_UI_MSG = 68; + static final int PUSH_TEMP_ALLOWLIST_UI_MSG = 68; static final int SERVICE_FOREGROUND_CRASH_MSG = 69; static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70; static final int KILL_APP_ZYGOTE_MSG = 71; @@ -1312,13 +1315,11 @@ public class ActivityManagerService extends IActivityManager.Stub static final String SERVICE_RECORD_KEY = "servicerecord"; - long mLastMemUsageReportTime = 0; - /** * Flag whether the current user is a "monkey", i.e. whether * the UI is driven by a UI automation tool. */ - private boolean mUserIsMonkey; + private volatile boolean mUserIsMonkey; @VisibleForTesting public final ServiceThread mHandlerThread; @@ -1351,7 +1352,7 @@ public class ActivityManagerService extends IActivityManager.Stub /** * Used to notify activity lifecycle events. */ - @Nullable ContentCaptureManagerInternal mContentCaptureService; + @Nullable volatile ContentCaptureManagerInternal mContentCaptureService; /* * The default duration for the binder heavy hitter auto sampler @@ -1438,7 +1439,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } break; case DISPATCH_PROCESSES_CHANGED_UI_MSG: { - dispatchProcessesChanged(); + mProcessList.dispatchProcessesChanged(); break; } case DISPATCH_PROCESS_DIED_UI_MSG: { @@ -1447,14 +1448,14 @@ public class ActivityManagerService extends IActivityManager.Stub } final int pid = msg.arg1; final int uid = msg.arg2; - dispatchProcessDied(pid, uid); + mProcessList.dispatchProcessDied(pid, uid); break; } case DISPATCH_OOM_ADJ_OBSERVER_MSG: { dispatchOomAdjObserver((String) msg.obj); } break; - case PUSH_TEMP_WHITELIST_UI_MSG: { - pushTempWhitelist(); + case PUSH_TEMP_ALLOWLIST_UI_MSG: { + pushTempAllowlist(); } break; } } @@ -1470,18 +1471,18 @@ public class ActivityManagerService extends IActivityManager.Stub switch (msg.what) { case GC_BACKGROUND_PROCESSES_MSG: { synchronized (ActivityManagerService.this) { - performAppGcsIfAppropriateLocked(); + mAppProfiler.performAppGcsIfAppropriateLocked(); } } break; case SERVICE_TIMEOUT_MSG: { - mServices.serviceTimeout((ProcessRecord)msg.obj); + mServices.serviceTimeout((ProcessRecord) msg.obj); } break; case SERVICE_FOREGROUND_TIMEOUT_MSG: { - mServices.serviceForegroundTimeout((ServiceRecord)msg.obj); + mServices.serviceForegroundTimeout((ServiceRecord) msg.obj); } break; case SERVICE_FOREGROUND_CRASH_MSG: { - mServices.serviceForegroundCrash( - (ProcessRecord) msg.obj, msg.getData().getCharSequence(SERVICE_RECORD_KEY)); + mServices.serviceForegroundCrash((ProcessRecord) msg.obj, + msg.getData().getCharSequence(SERVICE_RECORD_KEY)); } break; case UPDATE_TIME_ZONE: { synchronized (ActivityManagerService.this) { @@ -1491,7 +1492,8 @@ public class ActivityManagerService extends IActivityManager.Stub try { r.thread.updateTimeZone(); } catch (RemoteException ex) { - Slog.w(TAG, "Failed to update time zone for: " + r.info.processName); + Slog.w(TAG, "Failed to update time zone for: " + + r.info.processName); } } } @@ -1506,13 +1508,13 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessList.setAllHttpProxy(); } break; case PROC_START_TIMEOUT_MSG: { - ProcessRecord app = (ProcessRecord)msg.obj; + ProcessRecord app = (ProcessRecord) msg.obj; synchronized (ActivityManagerService.this) { processStartTimedOutLocked(app); } } break; case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: { - ProcessRecord app = (ProcessRecord)msg.obj; + ProcessRecord app = (ProcessRecord) msg.obj; synchronized (ActivityManagerService.this) { mCpHelper.processContentProviderPublishTimedOutLocked(app); } @@ -1521,13 +1523,14 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (ActivityManagerService.this) { final int appId = msg.arg1; final int userId = msg.arg2; - Bundle bundle = (Bundle)msg.obj; + Bundle bundle = (Bundle) msg.obj; String pkg = bundle.getString("pkg"); String reason = bundle.getString("reason"); forceStopPackageLocked(pkg, appId, false, false, true, false, false, userId, reason); } } break; + case KILL_APP_ZYGOTE_MSG: { synchronized (ActivityManagerService.this) { final AppZygote appZygote = (AppZygote) msg.obj; @@ -1541,10 +1544,10 @@ public class ActivityManagerService extends IActivityManager.Stub sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL); } break; case REPORT_MEM_USAGE_MSG: { - final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj; + final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>) msg.obj; Thread thread = new Thread() { @Override public void run() { - reportMemUsage(memInfos); + mAppProfiler.reportMemUsage(memInfos); } }; thread.start(); @@ -1680,7 +1683,10 @@ public class ActivityManagerService extends IActivityManager.Stub } } - public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) { + /** + * @param usageStatsManager shouldn't be null + */ + public void setUsageStatsManager(@NonNull UsageStatsManagerInternal usageStatsManager) { mUsageStatsService = usageStatsManager; mActivityTaskManager.setUsageStatsManager(usageStatsManager); } @@ -2034,6 +2040,9 @@ public class ActivityManagerService extends IActivityManager.Stub mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); mInternal = new LocalService(); mPendingStartActivityUids = new PendingStartActivityUids(mContext); + mUseFifoUiScheduling = false; + mEnableOffloadQueue = false; + mFgBroadcastQueue = mBgBroadcastQueue = mOffloadBroadcastQueue = null; } // Note: This method is invoked on the main thread but may need to attach various @@ -2128,9 +2137,7 @@ public class ActivityManagerService extends IActivityManager.Stub mPendingIntentController = new PendingIntentController( mHandlerThread.getLooper(), mUserController, mConstants); - if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0) { - mUseFifoUiScheduling = true; - } + mUseFifoUiScheduling = SystemProperties.getInt("sys.use_fifo_ui", 0) != 0; mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations")); mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler); @@ -2244,7 +2251,7 @@ public class ActivityManagerService extends IActivityManager.Stub ArrayMap<String, ArraySet<String>> allowedAssociations = SystemConfig.getInstance().getAllowedAssociations(); mAllowedAssociations = new ArrayMap<>(allowedAssociations.size()); - PackageManagerInternal pm = getPackageManagerInternalLocked(); + PackageManagerInternal pm = getPackageManagerInternal(); for (int i = 0; i < allowedAssociations.size(); i++) { final String pkg = allowedAssociations.keyAt(i); final ArraySet<String> asc = allowedAssociations.valueAt(i); @@ -2323,8 +2330,8 @@ public class ActivityManagerService extends IActivityManager.Stub } } - void updateCpuStatsLocked() { - mAppProfiler.updateCpuStatsLocked(); + void updateCpuStats() { + mAppProfiler.updateCpuStats(); } void updateCpuStatsNow() { @@ -2457,9 +2464,7 @@ public class ActivityManagerService extends IActivityManager.Stub } void notifyPackageUse(String packageName, int reason) { - synchronized(this) { - getPackageManagerInternalLocked().notifyPackageUse(packageName, reason); - } + getPackageManagerInternal().notifyPackageUse(packageName, reason); } boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs, @@ -2538,10 +2543,11 @@ public class ActivityManagerService extends IActivityManager.Stub if (mUsageStatsService != null) { mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(), taskRoot); } - if (mContentCaptureService != null && (event == Event.ACTIVITY_PAUSED + final ContentCaptureManagerInternal contentCaptureService = mContentCaptureService; + if (contentCaptureService != null && (event == Event.ACTIVITY_PAUSED || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED || event == Event.ACTIVITY_DESTROYED)) { - mContentCaptureService.notifyActivityEvent(userId, activity, event); + contentCaptureService.notifyActivityEvent(userId, activity, event); } } @@ -2616,8 +2622,8 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) { final ProcessRecord proc = mProcessList.mLruProcesses.get(i); if (procState > proc.setProcState) { - if (proc.pkgList.containsKey(packageName) || - (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) { + if (proc.getPkgList().containsKey(packageName) + || (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) { procState = proc.setProcState; } } @@ -2640,7 +2646,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (app.thread == null) { throw new IllegalArgumentException("Process has no app thread"); } - if (app.trimMemoryLevel >= level) { + if (app.mProfile.getTrimMemoryLevel() >= level) { throw new IllegalArgumentException( "Unable to set a higher trim level than current level"); } @@ -2650,119 +2656,14 @@ public class ActivityManagerService extends IActivityManager.Stub + "on a foreground process"); } app.thread.scheduleTrimMemory(level); - app.trimMemoryLevel = level; + app.mProfile.setTrimMemoryLevel(level); return true; } } - private void dispatchProcessesChanged() { - int N; - synchronized (this) { - N = mPendingProcessChanges.size(); - if (mActiveProcessChanges.length < N) { - mActiveProcessChanges = new ProcessChangeItem[N]; - } - mPendingProcessChanges.toArray(mActiveProcessChanges); - mPendingProcessChanges.clear(); - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, - "*** Delivering " + N + " process changes"); - } - - int i = mProcessObservers.beginBroadcast(); - while (i > 0) { - i--; - final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); - if (observer != null) { - try { - for (int j=0; j<N; j++) { - ProcessChangeItem item = mActiveProcessChanges[j]; - if ((item.changes&ProcessChangeItem.CHANGE_ACTIVITIES) != 0) { - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, - "ACTIVITIES CHANGED pid=" + item.pid + " uid=" - + item.uid + ": " + item.foregroundActivities); - observer.onForegroundActivitiesChanged(item.pid, item.uid, - item.foregroundActivities); - } - if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES) != 0) { - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, - "FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid=" - + item.uid + ": " + item.foregroundServiceTypes); - observer.onForegroundServicesChanged(item.pid, item.uid, - item.foregroundServiceTypes); - } - } - } catch (RemoteException e) { - } - } - } - mProcessObservers.finishBroadcast(); - - synchronized (this) { - for (int j=0; j<N; j++) { - mAvailProcessChanges.add(mActiveProcessChanges[j]); - } - } - } - - @GuardedBy("this") - ProcessChangeItem enqueueProcessChangeItemLocked(int pid, int uid) { - int i = mPendingProcessChanges.size()-1; - ActivityManagerService.ProcessChangeItem item = null; - while (i >= 0) { - item = mPendingProcessChanges.get(i); - if (item.pid == pid) { - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, - "Re-using existing item: " + item); - break; - } - i--; - } - - if (i < 0) { - // No existing item in pending changes; need a new one. - final int NA = mAvailProcessChanges.size(); - if (NA > 0) { - item = mAvailProcessChanges.remove(NA-1); - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, - "Retrieving available item: " + item); - } else { - item = new ActivityManagerService.ProcessChangeItem(); - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, - "Allocating new item: " + item); - } - item.changes = 0; - item.pid = pid; - item.uid = uid; - if (mPendingProcessChanges.size() == 0) { - if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, - "*** Enqueueing dispatch processes changed!"); - mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG) - .sendToTarget(); - } - mPendingProcessChanges.add(item); - } - - return item; - } - - private void dispatchProcessDied(int pid, int uid) { - int i = mProcessObservers.beginBroadcast(); - while (i > 0) { - i--; - final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); - if (observer != null) { - try { - observer.onProcessDied(pid, uid); - } catch (RemoteException e) { - } - } - } - mProcessObservers.finishBroadcast(); - } - void dispatchOomAdjObserver(String msg) { OomAdjObserver observer; - synchronized (this) { + synchronized (mOomAdjObserverLock) { observer = mCurOomAdjObserver; } @@ -2772,32 +2673,26 @@ public class ActivityManagerService extends IActivityManager.Stub } void setOomAdjObserver(int uid, OomAdjObserver observer) { - synchronized (this) { + synchronized (mOomAdjObserverLock) { mCurOomAdjUid = uid; mCurOomAdjObserver = observer; } } void clearOomAdjObserver() { - synchronized (this) { + synchronized (mOomAdjObserverLock) { mCurOomAdjUid = -1; mCurOomAdjObserver = null; } } - void reportOomAdjMessageLocked(String tag, String msg) { - Slog.d(tag, msg); - if (mCurOomAdjObserver != null) { - mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget(); - } - } - void reportUidInfoMessageLocked(String tag, String msg, int uid) { Slog.i(TAG, msg); - if (mCurOomAdjObserver != null && uid == mCurOomAdjUid) { - mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget(); + synchronized (mOomAdjObserverLock) { + if (mCurOomAdjObserver != null && uid == mCurOomAdjUid) { + mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget(); + } } - } /** @@ -2969,57 +2864,6 @@ public class ActivityManagerService extends IActivityManager.Stub return null; } - final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) { - // 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 (!mProcessList.haveBackgroundProcessLocked()) { - boolean doReport = Build.IS_DEBUGGABLE; - if (doReport) { - long now = SystemClock.uptimeMillis(); - if (now < (mLastMemUsageReportTime+5*60*1000)) { - doReport = false; - } else { - mLastMemUsageReportTime = now; - } - } - final ArrayList<ProcessMemInfo> memInfos - = doReport ? new ArrayList<ProcessMemInfo>(mProcessList.getLruSizeLocked()) - : null; - EventLogTags.writeAmLowMemory(mProcessList.getLruSizeLocked()); - long now = SystemClock.uptimeMillis(); - for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) { - ProcessRecord rec = mProcessList.mLruProcesses.get(i); - if (rec == dyingProc || rec.thread == null) { - continue; - } - if (doReport) { - memInfos.add(new ProcessMemInfo(rec.processName, rec.pid, rec.setAdj, - rec.setProcState, rec.adjType, rec.makeAdjReason())); - } - if ((rec.lastLowMemory+mConstants.GC_MIN_INTERVAL) <= now) { - // The low memory report is overriding any current - // state for a GC request. Make sure to do - // heavy/important/visible/foreground processes first. - if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) { - rec.lastRequestedGc = 0; - } else { - rec.lastRequestedGc = rec.lastLowMemory; - } - rec.reportLowMemory = true; - rec.lastLowMemory = now; - mProcessesToGc.remove(rec); - addProcessToGcListLocked(rec); - } - } - if (doReport) { - Message msg = mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos); - mHandler.sendMessage(msg); - } - scheduleAppGcsLocked(); - } - } - @GuardedBy("this") final void appDiedLocked(ProcessRecord app, String reason) { appDiedLocked(app, app.pid, app.thread, false, reason); @@ -3076,7 +2920,7 @@ public class ActivityManagerService extends IActivityManager.Stub updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); } if (doLowMem) { - doLowMemReportIfNeededLocked(app); + mAppProfiler.doLowMemReportIfNeededLocked(app); } } else if (app.pid != pid) { // A new process has already been started. @@ -3379,55 +3223,55 @@ public class ActivityManagerService extends IActivityManager.Stub final long callingId = Binder.clearCallingIdentity(); try { IPackageManager pm = AppGlobals.getPackageManager(); - synchronized(this) { - // Instant packages are not protected - if (getPackageManagerInternalLocked().isPackageDataProtected( - resolvedUserId, packageName)) { - throw new SecurityException( - "Cannot clear data for a protected package: " + packageName); - } + // Instant packages are not protected + if (getPackageManagerInternal().isPackageDataProtected( + resolvedUserId, packageName)) { + throw new SecurityException( + "Cannot clear data for a protected package: " + packageName); + } - ApplicationInfo applicationInfo = null; - try { - applicationInfo = pm.getApplicationInfo(packageName, - MATCH_UNINSTALLED_PACKAGES, resolvedUserId); - } catch (RemoteException e) { - /* ignore */ - } - appInfo = applicationInfo; + ApplicationInfo applicationInfo = null; + try { + applicationInfo = pm.getApplicationInfo(packageName, + MATCH_UNINSTALLED_PACKAGES, resolvedUserId); + } catch (RemoteException e) { + /* ignore */ + } + appInfo = applicationInfo; - final boolean clearingOwnUidData = appInfo != null && appInfo.uid == uid; + final boolean clearingOwnUidData = appInfo != null && appInfo.uid == uid; - if (!clearingOwnUidData && checkComponentPermission(permission.CLEAR_APP_USER_DATA, + if (!clearingOwnUidData && checkComponentPermission(permission.CLEAR_APP_USER_DATA, pid, uid, -1, true) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("PID " + pid + " does not have permission " - + android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data" - + " of package " + packageName); - } - - final boolean hasInstantMetadata = getPackageManagerInternalLocked() - .hasInstantApplicationMetadata(packageName, resolvedUserId); - final boolean isUninstalledAppWithoutInstantMetadata = - (appInfo == null && !hasInstantMetadata); - isInstantApp = (appInfo != null && appInfo.isInstantApp()) - || hasInstantMetadata; - final boolean canAccessInstantApps = checkComponentPermission( - permission.ACCESS_INSTANT_APPS, pid, uid, -1, true) - == PackageManager.PERMISSION_GRANTED; + throw new SecurityException("PID " + pid + " does not have permission " + + android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data" + + " of package " + packageName); + } + + final boolean hasInstantMetadata = getPackageManagerInternal() + .hasInstantApplicationMetadata(packageName, resolvedUserId); + final boolean isUninstalledAppWithoutInstantMetadata = + (appInfo == null && !hasInstantMetadata); + isInstantApp = (appInfo != null && appInfo.isInstantApp()) + || hasInstantMetadata; + final boolean canAccessInstantApps = checkComponentPermission( + permission.ACCESS_INSTANT_APPS, pid, uid, -1, true) + == PackageManager.PERMISSION_GRANTED; - if (isUninstalledAppWithoutInstantMetadata || (isInstantApp + if (isUninstalledAppWithoutInstantMetadata || (isInstantApp && !canAccessInstantApps)) { - Slog.w(TAG, "Invalid packageName: " + packageName); - if (observer != null) { - try { - observer.onRemoveCompleted(packageName, false); - } catch (RemoteException e) { - Slog.i(TAG, "Observer no longer exists."); - } + Slog.w(TAG, "Invalid packageName: " + packageName); + if (observer != null) { + try { + observer.onRemoveCompleted(packageName, false); + } catch (RemoteException e) { + Slog.i(TAG, "Observer no longer exists."); } - return false; } + return false; + } + synchronized (this) { if (appInfo != null) { forceStopPackageLocked(packageName, appInfo.uid, "clear data"); mAtmInternal.removeRecentTasksByPackageName(packageName, resolvedUserId); @@ -3566,7 +3410,7 @@ public class ActivityManagerService extends IActivityManager.Stub ApplicationExitInfo.SUBREASON_UNKNOWN, "kill all background"); - doLowMemReportIfNeededLocked(null); + mAppProfiler.doLowMemReportIfNeededLocked(null); } } finally { Binder.restoreCallingIdentity(callingId); @@ -3623,7 +3467,7 @@ public class ActivityManagerService extends IActivityManager.Stub int[] users = userId == UserHandle.USER_ALL ? mUserController.getUsers() : new int[] { userId }; for (int user : users) { - if (getPackageManagerInternalLocked().isPackageStateProtected( + if (getPackageManagerInternal().isPackageStateProtected( packageName, user)) { Slog.w(TAG, "Ignoring request to force stop protected package " + packageName + " u" + user); @@ -3660,17 +3504,17 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void addPackageDependency(String packageName) { - synchronized (this) { - int callingPid = Binder.getCallingPid(); - if (callingPid == myPid()) { - // Yeah, um, no. - return; - } - ProcessRecord proc; - synchronized (mPidsSelfLocked) { - proc = mPidsSelfLocked.get(Binder.getCallingPid()); - } - if (proc != null) { + int callingPid = Binder.getCallingPid(); + if (callingPid == myPid()) { + // Yeah, um, no. + return; + } + ProcessRecord proc; + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(Binder.getCallingPid()); + } + if (proc != null) { + synchronized (this) { if (proc.pkgDeps == null) { proc.pkgDeps = new ArraySet<String>(1); } @@ -3743,15 +3587,22 @@ public class ActivityManagerService extends IActivityManager.Stub } } - Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length]; + final Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length]; for (int i=pids.length-1; i>=0; i--) { - infos[i] = new Debug.MemoryInfo(); + final Debug.MemoryInfo mi = infos[i] = new Debug.MemoryInfo(); final ProcessRecord proc; final int oomAdj; - synchronized (this) { + final ProcessProfileRecord profile; + synchronized (mAppProfiler.mProfilerLock) { synchronized (mPidsSelfLocked) { proc = mPidsSelfLocked.get(pids[i]); - oomAdj = proc != null ? proc.setAdj : 0; + if (proc != null) { + profile = proc.mProfile; + oomAdj = profile.getSetAdj(); + } else { + profile = null; + oomAdj = 0; + } } } final int targetUid = (proc != null) ? proc.uid : -1; @@ -3766,43 +3617,43 @@ public class ActivityManagerService extends IActivityManager.Stub continue; // Not allowed to see other users. } } - if (proc != null && proc.lastMemInfoTime >= lastNow && proc.lastMemInfo != null - && !isCallerInstrumentedFromShell) { - // It hasn't been long enough that we want to take another sample; return - // the last one. - infos[i].set(proc.lastMemInfo); - continue; + if (proc != null) { + synchronized (mAppProfiler.mProfilerLock) { + if (profile.getLastMemInfoTime() >= lastNow && profile.getLastMemInfo() != null + && !isCallerInstrumentedFromShell) { + // It hasn't been long enough that we want to take another sample; return + // the last one. + mi.set(profile.getLastMemInfo()); + continue; + } + } } final long startTime = SystemClock.currentThreadTimeMillis(); final Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); Debug.getMemoryInfo(pids[i], memInfo); - final long endTime = SystemClock.currentThreadTimeMillis(); - infos[i].set(memInfo); + final long duration = SystemClock.currentThreadTimeMillis() - startTime; + mi.set(memInfo); if (proc != null) { - synchronized (this) { - proc.lastMemInfo = memInfo; - proc.lastMemInfoTime = SystemClock.uptimeMillis(); - if (proc.thread != null && proc.setAdj == oomAdj) { + synchronized (mAppProfiler.mProfilerLock) { + profile.setLastMemInfo(memInfo); + profile.setLastMemInfoTime(SystemClock.uptimeMillis()); + if (profile.getThread() != null && profile.getSetAdj() == oomAdj) { // Record this for posterity if the process has been stable. - synchronized (mProcessStats.mLock) { - proc.baseProcessTracker.addPss(infos[i].getTotalPss(), - infos[i].getTotalUss(), infos[i].getTotalRss(), false, - ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime - startTime, - proc.pkgList.mPkgList); - } - for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); + profile.addPss(mi.getTotalPss(), + mi.getTotalUss(), mi.getTotalRss(), false, + ProcessStats.ADD_PSS_EXTERNAL_SLOW, duration); + proc.getPkgList().forEachPackageProcessStats(holder -> { FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, proc.info.uid, holder.state.getName(), holder.state.getPackage(), - infos[i].getTotalPss(), - infos[i].getTotalUss(), - infos[i].getTotalRss(), + mi.getTotalPss(), + mi.getTotalUss(), + mi.getTotalRss(), ProcessStats.ADD_PSS_EXTERNAL_SLOW, - endTime-startTime, + duration, holder.appVersion); - } + }); } } } @@ -3823,7 +3674,7 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean allUids = mAtmInternal.isGetTasksAllowed( "getProcessPss", callingPid, callingUid); - long[] pss = new long[pids.length]; + final long[] pss = new long[pids.length]; for (int i=pids.length-1; i>=0; i--) { ProcessRecord proc; int oomAdj; @@ -3838,29 +3689,29 @@ public class ActivityManagerService extends IActivityManager.Stub // just leave it empty. continue; } - long[] tmpUss = new long[3]; - long startTime = SystemClock.currentThreadTimeMillis(); - pss[i] = Debug.getPss(pids[i], tmpUss, null); - long endTime = SystemClock.currentThreadTimeMillis(); + final long[] tmpUss = new long[3]; + final long startTime = SystemClock.currentThreadTimeMillis(); + final long pi = pss[i] = Debug.getPss(pids[i], tmpUss, null); + final long duration = SystemClock.currentThreadTimeMillis() - startTime; if (proc != null) { - synchronized (this) { - if (proc.thread != null && proc.setAdj == oomAdj) { + final ProcessProfileRecord profile = proc.mProfile; + synchronized (mAppProfiler.mProfilerLock) { + if (profile.getThread() != null && profile.getSetAdj() == oomAdj) { // Record this for posterity if the process has been stable. - synchronized (mProcessStats.mLock) { - proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false, - ProcessStats.ADD_PSS_EXTERNAL, endTime - startTime, - proc.pkgList.mPkgList); - } - for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); + profile.addPss(pi, tmpUss[0], tmpUss[2], false, + ProcessStats.ADD_PSS_EXTERNAL, duration); + proc.getPkgList().forEachPackageProcessStats(holder -> { FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, proc.info.uid, holder.state.getName(), holder.state.getPackage(), - pss[i], tmpUss[0], tmpUss[2], - ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, + pi, + tmpUss[0], + tmpUss[2], + ProcessStats.ADD_PSS_EXTERNAL, + duration, holder.appVersion); - } + }); } } } @@ -4051,7 +3902,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.i(TAG, "Force stopping u" + userId + ": " + reason); } - mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId); + mAppErrors.resetProcessCrashTime(packageName == null, appId, userId); } // Notify first that the package is stopped, so its process won't be restarted unexpectedly @@ -4425,7 +4276,11 @@ public class ActivityManagerService extends IActivityManager.Stub checkTime(startTime, "attachApplicationLocked: immediately after bindApplication"); mProcessList.updateLruProcessLocked(app, false, null); checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked"); - app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis(); + final long now = SystemClock.uptimeMillis(); + synchronized (mAppProfiler.mProfilerLock) { + app.mProfile.setLastRequestedGc(now); + app.mProfile.setLastLowMemory(now); + } } catch (Exception e) { // We need kill the process group here. (b/148588589) Slog.wtf(TAG, "Exception thrown during bind of " + app, e); @@ -5439,7 +5294,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) && - !isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ true)) { + !isOnDeviceIdleAllowlistLocked(uid, /*allowExceptIdleToo=*/ true)) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "Force background check: " + uid + "/" + packageName + " restricted"); @@ -5468,7 +5323,7 @@ public class ActivityManagerService extends IActivityManager.Stub } // Non-persistent but background whitelisted? - if (uidOnBackgroundWhitelist(uid)) { + if (uidOnBackgroundAllowlist(uid)) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid + "/" + packageName + " on background whitelist; not restricted in background"); @@ -5477,7 +5332,7 @@ public class ActivityManagerService extends IActivityManager.Stub } // Is this app on the battery whitelist? - if (isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ false)) { + if (isOnDeviceIdleAllowlistLocked(uid, /*allowExceptIdleToo=*/ false)) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid + "/" + packageName + " on idle whitelist; not restricted in background"); @@ -5501,7 +5356,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) { boolean ephemeral; if (uidRec == null) { - ephemeral = getPackageManagerInternalLocked().isPackageEphemeral( + ephemeral = getPackageManagerInternal().isPackageEphemeral( UserHandle.getUserId(uid), packageName); } else { ephemeral = uidRec.ephemeral; @@ -5525,8 +5380,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_BACKGROUND_CHECK) { Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg=" + packageName + " startMode=" + startMode - + " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid, false) - + " onwhitelist(ei)=" + isOnDeviceIdleWhitelistLocked(uid, true)); + + " onallowlist=" + isOnDeviceIdleAllowlistLocked(uid, false) + + " onallowlist(ei)=" + isOnDeviceIdleAllowlistLocked(uid, true)); } if (startMode == ActivityManager.APP_START_MODE_DELAYED) { // This is an old app that has been forced into a "compatible as possible" @@ -5552,38 +5407,38 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * @return whether a UID is in the system, user or temp doze whitelist. + * @return whether a UID is in the system, user or temp doze allowlist. */ - boolean isOnDeviceIdleWhitelistLocked(int uid, boolean allowExceptIdleToo) { + boolean isOnDeviceIdleAllowlistLocked(int uid, boolean allowExceptIdleToo) { final int appId = UserHandle.getAppId(uid); - final int[] whitelist = allowExceptIdleToo - ? mDeviceIdleExceptIdleWhitelist - : mDeviceIdleWhitelist; + final int[] allowlist = allowExceptIdleToo + ? mDeviceIdleExceptIdleAllowlist + : mDeviceIdleAllowlist; - return Arrays.binarySearch(whitelist, appId) >= 0 - || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0 - || mPendingTempWhitelist.indexOfKey(uid) >= 0; + return Arrays.binarySearch(allowlist, appId) >= 0 + || Arrays.binarySearch(mDeviceIdleTempAllowlist, appId) >= 0 + || mPendingTempAllowlist.indexOfKey(uid) >= 0; } - boolean isWhitelistedForFgsStartLocked(int uid) { - return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, UserHandle.getAppId(uid)) >= 0 + boolean isAllowlistedForFgsStartLocked(int uid) { + return Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0 || mFgsStartTempAllowList.isAllowed(uid); } /** - * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on - * the whitelist + * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on + * the allowlist */ - String getPendingTempWhitelistTagForUidLocked(int uid) { - final PendingTempWhitelist ptw = mPendingTempWhitelist.get(uid); + String getPendingTempAllowlistTagForUidLocked(int uid) { + final PendingTempAllowlist ptw = mPendingTempAllowlist.get(uid); return ptw != null ? ptw.tag : null; } @VisibleForTesting public void grantImplicitAccess(int userId, Intent intent, int visibleUid, int recipientAppId) { - getPackageManagerInternalLocked(). - grantImplicitAccess(userId, intent, recipientAppId, visibleUid, true /*direct*/); + getPackageManagerInternal() + .grantImplicitAccess(userId, intent, recipientAppId, visibleUid, true /*direct*/); } /** @@ -5831,14 +5686,18 @@ public class ActivityManagerService extends IActivityManager.Stub } @VisibleForTesting - public PackageManagerInternal getPackageManagerInternalLocked() { + public PackageManagerInternal getPackageManagerInternal() { + // Intentionally hold no locks: in case of race conditions, the mPackageManagerInt will + // be set to the same value anyway. if (mPackageManagerInt == null) { mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); } return mPackageManagerInt; } - private PermissionManagerServiceInternal getPermissionManagerInternalLocked() { + private PermissionManagerServiceInternal getPermissionManagerInternal() { + // Intentionally hold no locks: in case of race conditions, the mPermissionManagerInt will + // be set to the same value anyway. if (mPermissionManagerInt == null) { mPermissionManagerInt = LocalServices.getService(PermissionManagerServiceInternal.class); @@ -5983,12 +5842,11 @@ public class ActivityManagerService extends IActivityManager.Stub // GLOBAL MANAGEMENT // ========================================================= - private boolean uidOnBackgroundWhitelist(final int uid) { + private boolean uidOnBackgroundAllowlist(final int uid) { final int appId = UserHandle.getAppId(uid); - final int[] whitelist = mBackgroundAppIdWhitelist; - final int N = whitelist.length; - for (int i = 0; i < N; i++) { - if (appId == whitelist[i]) { + final int[] allowlist = mBackgroundAppIdAllowlist; + for (int i = 0, len = allowlist.length; i < len; i++) { + if (appId == allowlist[i]) { return true; } } @@ -6028,11 +5886,11 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist"); } synchronized (this) { - final int N = mBackgroundAppIdWhitelist.length; - int[] newList = new int[N+1]; - System.arraycopy(mBackgroundAppIdWhitelist, 0, newList, 0, N); - newList[N] = UserHandle.getAppId(uid); - mBackgroundAppIdWhitelist = newList; + 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; } } @@ -6150,16 +6008,16 @@ public class ActivityManagerService extends IActivityManager.Stub } void reportCurWakefulnessUsageEvent() { - reportGlobalUsageEvent(mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE + reportGlobalUsageEvent(mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE ? UsageEvents.Event.SCREEN_INTERACTIVE : UsageEvents.Event.SCREEN_NON_INTERACTIVE); } void onWakefulnessChanged(int wakefulness) { synchronized(this) { - boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE; + boolean wasAwake = mWakefulness.getAndSet(wakefulness) + == PowerManagerInternal.WAKEFULNESS_AWAKE; boolean isAwake = wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE; - mWakefulness = wakefulness; if (wasAwake != isAwake) { // Also update state in a special way for running foreground services UI. @@ -6269,17 +6127,16 @@ public class ActivityManagerService extends IActivityManager.Stub */ @Override public void setAgentApp(@NonNull String packageName, @Nullable String agent) { - synchronized (this) { - // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to - // its own permission. - if (checkCallingPermission( - android.Manifest.permission.SET_ACTIVITY_WATCHER) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER); - } + // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to + // its own permission. + if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER); + } - mAppProfiler.setAgentAppLocked(packageName, agent); + synchronized (mAppProfiler.mProfilerLock) { + mAppProfiler.setAgentAppLPf(packageName, agent); } } @@ -6296,7 +6153,7 @@ public class ActivityManagerService extends IActivityManager.Stub } void setProfileApp(ApplicationInfo app, String processName, ProfilerInfo profilerInfo) { - synchronized (this) { + synchronized (mAppProfiler.mProfilerLock) { if (!Build.IS_DEBUGGABLE) { boolean isAppDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; boolean isAppProfileable = app.isProfileableByShell(); @@ -6305,7 +6162,7 @@ public class ActivityManagerService extends IActivityManager.Stub + "and not profileable by shell: " + app.packageName); } } - mAppProfiler.setProfileAppLocked(processName, profilerInfo); + mAppProfiler.setProfileAppLPf(processName, profilerInfo); } } @@ -6391,8 +6248,8 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, "system process not in mPidsSelfLocked: " + myPid()); return; } - synchronized (this) { - mAppProfiler.startHeapDumpLocked(pr, true); + synchronized (mAppProfiler.mProfilerLock) { + mAppProfiler.startHeapDumpLPf(pr.mProfile, true); } } @@ -6572,16 +6429,12 @@ public class ActivityManagerService extends IActivityManager.Stub public void registerProcessObserver(IProcessObserver observer) { enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, "registerProcessObserver()"); - synchronized (this) { - mProcessObservers.register(observer); - } + mProcessList.registerProcessObserver(observer); } @Override public void unregisterProcessObserver(IProcessObserver observer) { - synchronized (this) { - mProcessObservers.unregister(observer); - } + mProcessList.unregisterProcessObserver(observer); } @Override @@ -6603,17 +6456,13 @@ public class ActivityManagerService extends IActivityManager.Stub enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, "registerUidObserver"); } - synchronized (this) { - mUidObserverController.register(observer, which, cutpoint, callingPackage, - Binder.getCallingUid()); - } + mUidObserverController.register(observer, which, cutpoint, callingPackage, + Binder.getCallingUid()); } @Override public void unregisterUidObserver(IUidObserver observer) { - synchronized (this) { - mUidObserverController.unregister(observer); - } + mUidObserverController.unregister(observer); } @Override @@ -7205,28 +7054,35 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) { ProcessRecord proc = mProcessList.mLruProcesses.get(i); + final ProcessProfileRecord pr = proc.mProfile; if (proc.notCachedSinceIdle) { if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) { - if (doKilling && proc.initialIdlePss != 0 - && proc.lastPss > ((proc.initialIdlePss * 3) / 2) - && proc.lastPss > (proc.initialIdlePss + memoryGrowthThreshold)) { + final long initialIdlePss, lastPss, lastSwapPss; + synchronized (mAppProfiler.mProfilerLock) { + initialIdlePss = pr.getInitialIdlePss(); + lastPss = pr.getLastPss(); + lastSwapPss = pr.getLastSwapPss(); + } + if (doKilling && initialIdlePss != 0 + && 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(proc.lastPss); + sb.append(lastPss); sb.append(", swapPss="); - sb.append(proc.lastSwapPss); + sb.append(lastSwapPss); sb.append(", initialPss="); - sb.append(proc.initialIdlePss); + 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 " + proc.lastPss - + " from " + proc.initialIdlePss + ")", + proc.kill("idle maint (pss " + lastPss + + " from " + initialIdlePss + ")", ApplicationExitInfo.REASON_OTHER, ApplicationExitInfo.SUBREASON_MEMORY_PRESSURE, true); @@ -7235,8 +7091,11 @@ public class ActivityManagerService extends IActivityManager.Stub } else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME && proc.setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) { proc.notCachedSinceIdle = true; - proc.initialIdlePss = 0; - mAppProfiler.updateNextPssTimeLocked(proc.setProcState, proc, now, true); + synchronized (mAppProfiler.mProfilerLock) { + pr.setInitialIdlePss(0); + mAppProfiler.updateNextPssTimeLPf( + proc.setProcState, proc.mProfile, now, true); + } } } } @@ -7277,14 +7136,13 @@ public class ActivityManagerService extends IActivityManager.Stub mAppProfiler.retrieveSettings(); + final Resources res; synchronized (this) { mDebugApp = mOrigDebugApp = debugApp; mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger; mAlwaysFinishActivities = alwaysFinishActivities; // Load resources only after the current configuration has been set. - final Resources res = mContext.getResources(); - mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString( - com.android.internal.R.string.config_appsNotReportingCrashes)); + res = mContext.getResources(); final boolean userSwitchUiEnabled = !res.getBoolean( com.android.internal.R.bool.config_customUserSwitchUi); final int maxRunningUsers = res.getInteger( @@ -7295,6 +7153,8 @@ public class ActivityManagerService extends IActivityManager.Stub delayUserDataLocking); mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs; } + mAppErrors.loadAppsNotReportingCrashesFromConfig(res.getString( + com.android.internal.R.string.config_appsNotReportingCrashes)); } /** @@ -7714,9 +7574,8 @@ public class ActivityManagerService extends IActivityManager.Stub if ((penaltyMask & StrictMode.PENALTY_DIALOG) != 0) { AppErrorResult result = new AppErrorResult(); - synchronized (this) { - final long origId = Binder.clearCallingIdentity(); - + final long origId = Binder.clearCallingIdentity(); + try { Message msg = Message.obtain(); msg.what = SHOW_STRICT_MODE_VIOLATION_UI_MSG; HashMap<String, Object> data = new HashMap<String, Object>(); @@ -7725,7 +7584,7 @@ public class ActivityManagerService extends IActivityManager.Stub data.put("info", info); msg.obj = data; mUiHandler.sendMessage(msg); - + } finally { Binder.restoreCallingIdentity(origId); } int res = result.get(); @@ -7927,8 +7786,7 @@ public class ActivityManagerService extends IActivityManager.Stub int flags = process.info.flags; IPackageManager pm = AppGlobals.getPackageManager(); sb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n"); - for (int ip=0; ip<process.pkgList.size(); ip++) { - String pkg = process.pkgList.keyAt(ip); + process.getPkgList().forEachPackage(pkg -> { sb.append("Package: ").append(pkg); try { PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId()); @@ -7942,7 +7800,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.e(TAG, "Error getting package info: " + pkg, e); } sb.append("\n"); - } + }); if (process.info.isInstantApp()) { sb.append("Instant-App: true\n"); } @@ -8373,7 +8231,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage); + dumpPermissions(fd, pw, args, opti, dumpAll, dumpPackage); pw.println(); sdumper = mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll, dumpPackage); if (!dumpClient) { @@ -8470,12 +8328,12 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId); + mProcessList.dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId); pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpUsersLocked(pw); + dumpUsers(pw); } } @@ -8574,7 +8432,7 @@ public class ActivityManagerService extends IActivityManager.Stub } // output proto is ProcessProto synchronized (this) { - writeProcessesToProtoLocked(proto, dumpPackage); + mProcessList.writeProcessesToProtoLocked(proto, dumpPackage); } } else { // default option, dump everything, output is ActivityManagerServiceProto @@ -8593,7 +8451,7 @@ public class ActivityManagerService extends IActivityManager.Stub proto.end(serviceToken); long processToken = proto.start(ActivityManagerServiceProto.PROCESSES); - writeProcessesToProtoLocked(proto, dumpPackage); + mProcessList.writeProcessesToProtoLocked(proto, dumpPackage); proto.end(processToken); } } @@ -8660,20 +8518,19 @@ public class ActivityManagerService extends IActivityManager.Stub dumpPackage = args[opti]; opti++; } - synchronized (this) { - mPendingIntentController.dumpPendingIntents(pw, true, dumpPackage); - } + mPendingIntentController.dumpPendingIntents(pw, true, dumpPackage); } else if ("processes".equals(cmd) || "p".equals(cmd)) { if (opti < args.length) { dumpPackage = args[opti]; opti++; } synchronized (this) { - dumpProcessesLocked(fd, pw, args, opti, true, dumpPackage, dumpAppId); + mProcessList.dumpProcessesLocked( + fd, pw, args, opti, true, dumpPackage, dumpAppId); } } else if ("oom".equals(cmd) || "o".equals(cmd)) { synchronized (this) { - dumpOomLocked(fd, pw, false, args, opti, true, dumpPackage, true); + mProcessList.dumpOomLocked(fd, pw, false, args, opti, true, dumpPackage, true); } } else if ("lmk".equals(cmd)) { synchronized (this) { @@ -8681,12 +8538,10 @@ public class ActivityManagerService extends IActivityManager.Stub } } else if ("lru".equals(cmd)) { synchronized (this) { - dumpLruLocked(pw, dumpPackage, null); + mProcessList.dumpLruLocked(pw, dumpPackage, null); } } else if ("permissions".equals(cmd) || "perm".equals(cmd)) { - synchronized (this) { - dumpPermissionsLocked(fd, pw, args, opti, true, dumpPackage); - } + dumpPermissions(fd, pw, args, opti, true, dumpPackage); } else if ("provider".equals(cmd)) { String[] newArgs; String name; @@ -8779,9 +8634,7 @@ public class ActivityManagerService extends IActivityManager.Stub } else if ("locks".equals(cmd)) { LockGuard.dump(fd, pw, args); } else if ("users".equals(cmd)) { - synchronized (this) { - dumpUsersLocked(pw); - } + dumpUsers(pw); } else if ("exit-info".equals(cmd)) { if (opti < args.length) { dumpPackage = args[opti]; @@ -8918,7 +8771,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private int getAppId(String dumpPackage) { + int getAppId(String dumpPackage) { if (dumpPackage != null) { try { ApplicationInfo info = mContext.getPackageManager().getApplicationInfo( @@ -8983,177 +8836,12 @@ public class ActivityManagerService extends IActivityManager.Stub " Counts of Binder Proxies held by SYSTEM"); } - void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc, String prefix) { - pw.print(prefix); - pw.print('#'); - if (index < 10) { - pw.print(' '); - } - pw.print(index); - pw.print(": "); - pw.print(ProcessList.makeOomAdjString(proc.setAdj, false)); - pw.print(' '); - pw.print(ProcessList.makeProcStateString(proc.getCurProcState())); - pw.print(' '); - ActivityManager.printCapabilitiesSummary(pw, proc.curCapability); - pw.print(' '); - pw.print(proc.toShortString()); - if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities() - || proc.treatLikeActivity) { - pw.print(" act:"); - boolean printed = false; - if (proc.hasActivities()) { - pw.print("activities"); - printed = true; - } - if (proc.hasRecentTasks()) { - if (printed) { - pw.print("|"); - } - pw.print("recents"); - printed = true; - } - if (proc.hasClientActivities()) { - if (printed) { - pw.print("|"); - } - pw.print("client"); - printed = true; - } - if (proc.treatLikeActivity) { - if (printed) { - pw.print("|"); - } - pw.print("treated"); - } - } - pw.println(); - } - - // TODO: Move to ProcessList? - boolean dumpLruLocked(PrintWriter pw, String dumpPackage, String prefix) { - final int N = mProcessList.mLruProcesses.size(); - final String innerPrefix; - if (prefix == null) { - pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)"); - innerPrefix = " "; - } else { - boolean haveAny = false; - for (int i = N - 1; i >= 0; i--) { - final ProcessRecord r = mProcessList.mLruProcesses.get(i); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { - continue; - } - haveAny = true; - break; - } - if (!haveAny) { - return false; - } - pw.print(prefix); - pw.println("Raw LRU list (dumpsys activity lru):"); - innerPrefix = prefix + " "; - } - int i; - boolean first = true; - for (i = N - 1; i >= mProcessList.mLruProcessActivityStart; i--) { - final ProcessRecord r = mProcessList.mLruProcesses.get(i); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { - continue; - } - if (first) { - pw.print(innerPrefix); - pw.println("Activities:"); - first = false; - } - dumpLruEntryLocked(pw, i, r, innerPrefix); - } - first = true; - for (; i >= mProcessList.mLruProcessServiceStart; i--) { - final ProcessRecord r = mProcessList.mLruProcesses.get(i); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { - continue; - } - if (first) { - pw.print(innerPrefix); - pw.println("Services:"); - first = false; - } - dumpLruEntryLocked(pw, i, r, innerPrefix); - } - first = true; - for (; i >= 0; i--) { - final ProcessRecord r = mProcessList.mLruProcesses.get(i); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { - continue; - } - if (first) { - pw.print(innerPrefix); - pw.println("Other:"); - first = false; - } - dumpLruEntryLocked(pw, i, r, innerPrefix); - } - return true; - } - - // TODO: Move to ProcessList? @GuardedBy("this") - void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, String dumpPackage, int dumpAppId) { - boolean needSep = false; - int numPers = 0; - - pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)"); - - if (dumpAll || dumpPackage != null) { - final int NP = mProcessList.mProcessNames.getMap().size(); - for (int ip=0; ip<NP; ip++) { - SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip); - final int NA = procs.size(); - for (int ia=0; ia<NA; ia++) { - ProcessRecord r = procs.valueAt(ia); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { - continue; - } - if (!needSep) { - pw.println(" All known processes:"); - needSep = true; - } - pw.print(r.isPersistent() ? " *PERS*" : " *APP*"); - pw.print(" UID "); pw.print(procs.keyAt(ia)); - pw.print(" "); pw.println(r); - r.dump(pw, " "); - if (r.isPersistent()) { - numPers++; - } - } - } - } - - if (mProcessList.mIsolatedProcesses.size() > 0) { - boolean printed = false; - for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) { - ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { - continue; - } - if (!printed) { - if (needSep) { - pw.println(); - } - pw.println(" Isolated process list (sorted by uid):"); - printed = true; - needSep = true; - } - pw.print(" Isolated #"); pw.print(i); pw.print(": "); - pw.println(r); - } - } - - if (mActiveInstrumentation.size() > 0) { + boolean dumpActiveInstruments(PrintWriter pw, String dumpPackage, boolean needSep) { + final int size = mActiveInstrumentation.size(); + if (size > 0) { boolean printed = false; - for (int i=0; i<mActiveInstrumentation.size(); i++) { + for (int i = 0; i < size; i++) { ActiveInstrumentation ai = mActiveInstrumentation.get(i); if (dumpPackage != null && !ai.mClass.getPackageName().equals(dumpPackage) && !ai.mTargetInfo.packageName.equals(dumpPackage)) { @@ -9172,48 +8860,20 @@ public class ActivityManagerService extends IActivityManager.Stub ai.dump(pw, " "); } } + return needSep; + } - if (dumpOomLocked(fd, pw, needSep, args, opti, dumpAll, dumpPackage, false)) { - needSep = true; - } - - needSep = dumpProcessesToGc(pw, needSep, dumpPackage); - - if (mProcessList.mActiveUids.size() > 0) { - needSep |= mProcessList.mActiveUids.dump(pw, dumpPackage, dumpAppId, - "UID states:", needSep); - } - - if (dumpAll) { - needSep |= mUidObserverController.dumpValidateUids(pw, - dumpPackage, dumpAppId, "UID validation:", needSep); - } - - if (needSep) { - pw.println(); - } - if (dumpLruLocked(pw, dumpPackage, " ")) { - needSep = true; - } - - if (mProcessList.getLruSizeLocked() > 0) { - if (needSep) { - pw.println(); - } - mProcessList.dumpLruListHeaderLocked(pw); - dumpProcessOomList(pw, this, mProcessList.mLruProcesses, " ", "Proc", "PERS", false, - dumpPackage); - needSep = true; - } - + @GuardedBy("this") + void dumpOtherProcessesInfoLocked(FileDescriptor fd, PrintWriter pw, + boolean dumpAll, String dumpPackage, int dumpAppId, int numPers, boolean needSep) { if (dumpAll || dumpPackage != null) { final SparseArray<ProcessRecord> pidToProcess = new SparseArray<>(); synchronized (mPidsSelfLocked) { boolean printed = false; - for (int i=0; i<mPidsSelfLocked.size(); i++) { + for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) { ProcessRecord r = mPidsSelfLocked.valueAt(i); pidToProcess.put(r.pid, r); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { continue; } if (!printed) { @@ -9229,10 +8889,11 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (sActiveProcessInfoSelfLocked) { boolean printed = false; - for (int i = 0; i < sActiveProcessInfoSelfLocked.size(); i++) { + for (int i = 0, size = sActiveProcessInfoSelfLocked.size(); i < size; i++) { ProcessInfo info = sActiveProcessInfoSelfLocked.valueAt(i); ProcessRecord r = pidToProcess.get(sActiveProcessInfoSelfLocked.keyAt(i)); - if (r != null && dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { + if (r != null && dumpPackage != null + && !r.getPkgList().containsKey(dumpPackage)) { continue; } if (!printed) { @@ -9261,11 +8922,11 @@ public class ActivityManagerService extends IActivityManager.Stub if (mImportantProcesses.size() > 0) { synchronized (mPidsSelfLocked) { boolean printed = false; - for (int i = 0; i< mImportantProcesses.size(); i++) { + for (int i = 0, size = mImportantProcesses.size(); i < size; i++) { ProcessRecord r = mPidsSelfLocked.get( mImportantProcesses.valueAt(i).pid); if (dumpPackage != null && (r == null - || !r.pkgList.containsKey(dumpPackage))) { + || !r.getPkgList().containsKey(dumpPackage))) { continue; } if (!printed) { @@ -9307,7 +8968,7 @@ public class ActivityManagerService extends IActivityManager.Stub needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage); needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep, - mAppProfiler.getTestPssModeLocked(), mWakefulness); + mAppProfiler.getTestPssMode(), mWakefulness.get()); if (dumpAll && mProcessList.mPendingStarts.size() > 0) { if (needSep) pw.println(); @@ -9321,14 +8982,14 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpAll) { mUidObserverController.dump(pw, dumpPackage); - pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist)); - pw.println(" mDeviceIdleExceptIdleWhitelist=" - + Arrays.toString(mDeviceIdleExceptIdleWhitelist)); - pw.println(" mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist)); - if (mPendingTempWhitelist.size() > 0) { - pw.println(" mPendingTempWhitelist:"); - for (int i = 0; i < mPendingTempWhitelist.size(); i++) { - PendingTempWhitelist ptw = mPendingTempWhitelist.valueAt(i); + pw.println(" mDeviceIdleAllowlist=" + Arrays.toString(mDeviceIdleAllowlist)); + pw.println(" mDeviceIdleExceptIdleAllowlist=" + + Arrays.toString(mDeviceIdleExceptIdleAllowlist)); + pw.println(" mDeviceIdleTempAllowlist=" + Arrays.toString(mDeviceIdleTempAllowlist)); + if (mPendingTempAllowlist.size() > 0) { + pw.println(" mPendingTempAllowlist:"); + for (int i = 0, size = mPendingTempAllowlist.size(); i < size; i++) { + PendingTempAllowlist ptw = mPendingTempAllowlist.valueAt(i); pw.print(" "); UserHandle.formatUid(pw, ptw.targetUid); pw.print(": "); @@ -9353,7 +9014,9 @@ public class ActivityManagerService extends IActivityManager.Stub + " mOrigWaitForDebugger=" + mOrigWaitForDebugger); } } - needSep = mAppProfiler.dumpMemWatchProcessesLocked(pw, needSep); + synchronized (mAppProfiler.mProfilerLock) { + needSep = mAppProfiler.dumpMemWatchProcessesLPf(pw, needSep); + } if (mTrackAllocationApp != null) { if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) { if (needSep) { @@ -9409,45 +9072,15 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" mForceBackgroundCheck=" + mForceBackgroundCheck); } - @GuardedBy("this") - private void dumpUsersLocked(PrintWriter pw) { + private void dumpUsers(PrintWriter pw) { pw.println("ACTIVITY MANAGER USERS (dumpsys activity users)"); mUserController.dump(pw); } @GuardedBy("this") - void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) { - int numPers = 0; - - final int NP = mProcessList.mProcessNames.getMap().size(); - for (int ip=0; ip<NP; ip++) { - SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip); - final int NA = procs.size(); - for (int ia = 0; ia<NA; ia++) { - ProcessRecord r = procs.valueAt(ia); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { - continue; - } - r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.PROCS, - mProcessList.mLruProcesses.indexOf(r) - ); - if (r.isPersistent()) { - numPers++; - } - } - } - - for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) { - ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { - continue; - } - r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS, - mProcessList.mLruProcesses.indexOf(r) - ); - } - - for (int i=0; i<mActiveInstrumentation.size(); i++) { + void writeOtherProcessesInfoToProtoLocked(ProtoOutputStream proto, String dumpPackage, + int dumpAppId, int numPers) { + for (int i = 0, size = mActiveInstrumentation.size(); i < size; i++) { ActiveInstrumentation ai = mActiveInstrumentation.get(i); if (dumpPackage != null && !ai.mClass.getPackageName().equals(dumpPackage) && !ai.mTargetInfo.packageName.equals(dumpPackage)) { @@ -9457,32 +9090,14 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS); } - final int dumpAppId = getAppId(dumpPackage); - mProcessList.mActiveUids.dumpProto(proto, dumpPackage, dumpAppId, - ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS); - mUidObserverController.dumpValidateUidsProto(proto, dumpPackage, dumpAppId, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS); - if (mProcessList.getLruSizeLocked() > 0) { - long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS); - int total = mProcessList.getLruSizeLocked(); - proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total); - proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT, - total - mProcessList.mLruProcessActivityStart); - proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT, - total - mProcessList.mLruProcessServiceStart); - writeProcessOomListToProto(proto, - ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this, - mProcessList.mLruProcesses,false, dumpPackage); - proto.end(lruToken); - } - if (dumpPackage != null) { synchronized (mPidsSelfLocked) { for (int i=0; i<mPidsSelfLocked.size(); i++) { ProcessRecord r = mPidsSelfLocked.valueAt(i); - if (!r.pkgList.containsKey(dumpPackage)) { + if (!r.getPkgList().containsKey(dumpPackage)) { continue; } r.dumpDebug(proto, @@ -9493,11 +9108,11 @@ public class ActivityManagerService extends IActivityManager.Stub if (mImportantProcesses.size() > 0) { synchronized (mPidsSelfLocked) { - for (int i=0; i<mImportantProcesses.size(); i++) { + for (int i = 0, size = mImportantProcesses.size(); i < size; i++) { ImportanceToken it = mImportantProcesses.valueAt(i); ProcessRecord r = mPidsSelfLocked.get(it.pid); if (dumpPackage != null && (r == null - || !r.pkgList.containsKey(dumpPackage))) { + || !r.getPkgList().containsKey(dumpPackage))) { continue; } it.dumpDebug(proto, @@ -9506,7 +9121,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - for (int i=0; i<mPersistentStartingProcesses.size(); i++) { + for (int i = 0, size = mPersistentStartingProcesses.size(); i < size; i++) { ProcessRecord r = mPersistentStartingProcesses.get(i); if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { continue; @@ -9515,7 +9130,7 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS); } - for (int i = 0; i < mProcessList.mRemovedProcesses.size(); i++) { + for (int i = 0, size = mProcessList.mRemovedProcesses.size(); i < size; i++) { ProcessRecord r = mProcessList.mRemovedProcesses.get(i); if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { continue; @@ -9523,7 +9138,7 @@ public class ActivityManagerService extends IActivityManager.Stub r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.REMOVED_PROCS); } - for (int i=0; i<mProcessesOnHold.size(); i++) { + for (int i = 0, size = mProcessesOnHold.size(); i < size; i++) { ProcessRecord r = mProcessesOnHold.get(i); if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { continue; @@ -9531,12 +9146,15 @@ public class ActivityManagerService extends IActivityManager.Stub r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ON_HOLD_PROCS); } - writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, - dumpPackage); + synchronized (mAppProfiler.mProfilerLock) { + mAppProfiler.writeProcessesToGcToProto(proto, + ActivityManagerServiceDumpProcessesProto.GC_PROCS, + dumpPackage); + } mAppErrors.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage); - mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness, - mAppProfiler.getTestPssModeLocked()); + mAtmInternal.writeProcessesToProto(proto, dumpPackage, mWakefulness.get(), + mAppProfiler.getTestPssMode()); if (dumpPackage == null) { mUserController.dumpDebug(proto, @@ -9545,17 +9163,17 @@ public class ActivityManagerService extends IActivityManager.Stub mUidObserverController.dumpDebug(proto, dumpPackage); - for (int v : mDeviceIdleWhitelist) { + for (int v : mDeviceIdleAllowlist) { proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_WHITELIST, v); } - for (int v : mDeviceIdleTempWhitelist) { + for (int v : mDeviceIdleTempAllowlist) { proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_TEMP_WHITELIST, v); } - if (mPendingTempWhitelist.size() > 0) { - for (int i=0; i < mPendingTempWhitelist.size(); i++) { - mPendingTempWhitelist.valueAt(i).dumpDebug(proto, + if (mPendingTempAllowlist.size() > 0) { + for (int i = 0, size = mPendingTempAllowlist.size(); i < size; i++) { + mPendingTempAllowlist.valueAt(i).dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.PENDING_TEMP_WHITELIST); } } @@ -9573,7 +9191,9 @@ public class ActivityManagerService extends IActivityManager.Stub } } - mAppProfiler.writeMemWatchProcessToProtoLocked(proto); + synchronized (mAppProfiler.mProfilerLock) { + mAppProfiler.writeMemWatchProcessToProtoLPf(proto); + } if (mTrackAllocationApp != null) { if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) { @@ -9608,113 +9228,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) { - if (mProcessesToGc.size() > 0) { - long now = SystemClock.uptimeMillis(); - for (int i=0; i<mProcessesToGc.size(); i++) { - ProcessRecord r = mProcessesToGc.get(i); - if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { - continue; - } - final long token = proto.start(fieldId); - r.dumpDebug(proto, ProcessToGcProto.PROC); - proto.write(ProcessToGcProto.REPORT_LOW_MEMORY, r.reportLowMemory); - proto.write(ProcessToGcProto.NOW_UPTIME_MS, now); - proto.write(ProcessToGcProto.LAST_GCED_MS, r.lastRequestedGc); - proto.write(ProcessToGcProto.LAST_LOW_MEMORY_MS, r.lastLowMemory); - proto.end(token); - } - } - } - - boolean dumpProcessesToGc(PrintWriter pw, boolean needSep, String dumpPackage) { - if (mProcessesToGc.size() > 0) { - boolean printed = false; - long now = SystemClock.uptimeMillis(); - for (int i=0; i<mProcessesToGc.size(); i++) { - ProcessRecord proc = mProcessesToGc.get(i); - if (dumpPackage != null && !dumpPackage.equals(proc.info.packageName)) { - continue; - } - if (!printed) { - if (needSep) pw.println(); - needSep = true; - pw.println(" Processes that are waiting to GC:"); - printed = true; - } - pw.print(" Process "); pw.println(proc); - pw.print(" lowMem="); pw.print(proc.reportLowMemory); - pw.print(", last gced="); - pw.print(now-proc.lastRequestedGc); - pw.print(" ms ago, last lowMem="); - pw.print(now-proc.lastLowMemory); - pw.println(" ms ago"); - - } - } - return needSep; - } - - void printOomLevel(PrintWriter pw, String name, int adj) { - pw.print(" "); - if (adj >= 0) { - pw.print(' '); - if (adj < 10) pw.print(' '); - } else { - if (adj > -10) pw.print(' '); - } - pw.print(adj); - pw.print(": "); - pw.print(name); - pw.print(" ("); - pw.print(stringifySize(mProcessList.getMemLevel(adj), 1024)); - pw.println(")"); - } - - boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String[] args, - int opti, boolean dumpAll, String dumpPackage, boolean inclGc) { - if (mProcessList.getLruSizeLocked() > 0) { - if (needSep) pw.println(); - needSep = true; - pw.println(" OOM levels:"); - printOomLevel(pw, "SYSTEM_ADJ", ProcessList.SYSTEM_ADJ); - printOomLevel(pw, "PERSISTENT_PROC_ADJ", ProcessList.PERSISTENT_PROC_ADJ); - printOomLevel(pw, "PERSISTENT_SERVICE_ADJ", ProcessList.PERSISTENT_SERVICE_ADJ); - printOomLevel(pw, "FOREGROUND_APP_ADJ", ProcessList.FOREGROUND_APP_ADJ); - printOomLevel(pw, "VISIBLE_APP_ADJ", ProcessList.VISIBLE_APP_ADJ); - printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", ProcessList.PERCEPTIBLE_APP_ADJ); - printOomLevel(pw, "PERCEPTIBLE_LOW_APP_ADJ", ProcessList.PERCEPTIBLE_LOW_APP_ADJ); - printOomLevel(pw, "BACKUP_APP_ADJ", ProcessList.BACKUP_APP_ADJ); - printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", ProcessList.HEAVY_WEIGHT_APP_ADJ); - printOomLevel(pw, "SERVICE_ADJ", ProcessList.SERVICE_ADJ); - printOomLevel(pw, "HOME_APP_ADJ", ProcessList.HOME_APP_ADJ); - printOomLevel(pw, "PREVIOUS_APP_ADJ", ProcessList.PREVIOUS_APP_ADJ); - printOomLevel(pw, "SERVICE_B_ADJ", ProcessList.SERVICE_B_ADJ); - printOomLevel(pw, "CACHED_APP_MIN_ADJ", ProcessList.CACHED_APP_MIN_ADJ); - printOomLevel(pw, "CACHED_APP_MAX_ADJ", ProcessList.CACHED_APP_MAX_ADJ); - - if (needSep) pw.println(); - pw.print(" Process OOM control ("); pw.print(mProcessList.getLruSizeLocked()); - pw.print(" total, non-act at "); - pw.print(mProcessList.getLruSizeLocked() - - mProcessList.mLruProcessActivityStart); - pw.print(", non-svc at "); - pw.print(mProcessList.getLruSizeLocked() - - mProcessList.mLruProcessServiceStart); - pw.println("):"); - dumpProcessOomList(pw, this, mProcessList.mLruProcesses, " ", "Proc", "PERS", true, - dumpPackage); - needSep = true; - } - - dumpProcessesToGc(pw, needSep, dumpPackage); - - pw.println(); - mAtmInternal.dumpForOom(pw); - - return true; - } - private boolean reportLmkKillAtOrBelow(PrintWriter pw, int oom_adj) { Integer cnt = ProcessList.getLmkdKillCount(0, oom_adj); if (cnt != null) { @@ -10050,8 +9563,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - @GuardedBy("this") - void dumpPermissionsLocked(FileDescriptor fd, PrintWriter pw, String[] args, + void dumpPermissions(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, String dumpPackage) { pw.println("ACTIVITY MANAGER URI PERMISSIONS (dumpsys activity permissions)"); @@ -10080,270 +9592,6 @@ public class ActivityManagerService extends IActivityManager.Stub return numPers; } - private static final ArrayList<Pair<ProcessRecord, Integer>> - sortProcessOomList(List<ProcessRecord> origList, String dumpPackage) { - ArrayList<Pair<ProcessRecord, Integer>> list - = new ArrayList<Pair<ProcessRecord, Integer>>(origList.size()); - for (int i=0; i<origList.size(); i++) { - ProcessRecord r = origList.get(i); - if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { - continue; - } - list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i)); - } - - Comparator<Pair<ProcessRecord, Integer>> comparator - = new Comparator<Pair<ProcessRecord, Integer>>() { - @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; - } - if (object1.first.setProcState != object2.first.setProcState) { - return object1.first.setProcState > object2.first.setProcState ? -1 : 1; - } - if (object1.second.intValue() != object2.second.intValue()) { - return object1.second.intValue() > object2.second.intValue() ? -1 : 1; - } - return 0; - } - }; - - Collections.sort(list, comparator); - return list; - } - - private static final boolean writeProcessOomListToProto(ProtoOutputStream proto, long fieldId, - ActivityManagerService service, List<ProcessRecord> origList, - boolean inclDetails, String dumpPackage) { - ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage); - if (list.isEmpty()) return false; - - final long curUptime = SystemClock.uptimeMillis(); - - for (int i = list.size() - 1; i >= 0; i--) { - ProcessRecord r = list.get(i).first; - long token = proto.start(fieldId); - String oomAdj = ProcessList.makeOomAdjString(r.setAdj, 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) { - case ProcessList.SCHED_GROUP_BACKGROUND: - schedGroup = ProcessOomProto.SCHED_GROUP_BACKGROUND; - break; - case ProcessList.SCHED_GROUP_DEFAULT: - schedGroup = ProcessOomProto.SCHED_GROUP_DEFAULT; - break; - case ProcessList.SCHED_GROUP_TOP_APP: - schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP; - break; - case ProcessList.SCHED_GROUP_TOP_APP_BOUND: - schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP_BOUND; - break; - } - if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) { - proto.write(ProcessOomProto.SCHED_GROUP, schedGroup); - } - if (r.hasForegroundActivities()) { - proto.write(ProcessOomProto.ACTIVITIES, true); - } else if (r.hasForegroundServices()) { - proto.write(ProcessOomProto.SERVICES, true); - } - proto.write(ProcessOomProto.STATE, - ProcessList.makeProcStateProtoEnum(r.getCurProcState())); - proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.trimMemoryLevel); - 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; - cn.dumpDebug(proto, ProcessOomProto.ADJ_TARGET_COMPONENT_NAME); - } else if (r.adjTarget != null) { - proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, r.adjTarget.toString()); - } - if (r.adjSource instanceof ProcessRecord) { - ProcessRecord p = (ProcessRecord) r.adjSource; - p.dumpDebug(proto, ProcessOomProto.ADJ_SOURCE_PROC); - } else if (r.adjSource != null) { - proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, r.adjSource.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.CURRENT_STATE, - ProcessList.makeProcStateProtoEnum(r.getCurProcState())); - proto.write(ProcessOomProto.Detail.SET_STATE, - ProcessList.makeProcStateProtoEnum(r.setProcState)); - proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString( - r.lastPss*1024, new StringBuilder())); - proto.write(ProcessOomProto.Detail.LAST_SWAP_PSS, DebugUtils.sizeValueToString( - r.lastSwapPss*1024, new StringBuilder())); - proto.write(ProcessOomProto.Detail.LAST_CACHED_PSS, DebugUtils.sizeValueToString( - r.lastCachedPss*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); - - if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) { - if (r.lastCpuTime != 0) { - long uptimeSince = curUptime - service.mLastPowerCheckUptime; - long timeUsed = r.curCpuTime - r.lastCpuTime; - long cpuTimeToken = proto.start(ProcessOomProto.Detail.SERVICE_RUN_TIME); - proto.write(ProcessOomProto.Detail.CpuRunTime.OVER_MS, uptimeSince); - proto.write(ProcessOomProto.Detail.CpuRunTime.USED_MS, timeUsed); - proto.write(ProcessOomProto.Detail.CpuRunTime.ULTILIZATION, - (100.0*timeUsed)/uptimeSince); - proto.end(cpuTimeToken); - } - } - proto.end(detailToken); - } - proto.end(token); - } - - return true; - } - - private static final boolean dumpProcessOomList(PrintWriter pw, - ActivityManagerService service, List<ProcessRecord> origList, - String prefix, String normalLabel, String persistentLabel, - boolean inclDetails, String dumpPackage) { - - ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage); - if (list.isEmpty()) return false; - - final long curUptime = SystemClock.uptimeMillis(); - final long uptimeSince = curUptime - service.mLastPowerCheckUptime; - - for (int i=list.size()-1; i>=0; i--) { - ProcessRecord r = list.get(i).first; - String oomAdj = ProcessList.makeOomAdjString(r.setAdj, false); - char schedGroup; - switch (r.setSchedGroup) { - case ProcessList.SCHED_GROUP_BACKGROUND: - schedGroup = 'b'; - break; - case ProcessList.SCHED_GROUP_DEFAULT: - schedGroup = 'F'; - break; - case ProcessList.SCHED_GROUP_TOP_APP: - schedGroup = 'T'; - break; - case ProcessList.SCHED_GROUP_RESTRICTED: - schedGroup = 'R'; - break; - case ProcessList.SCHED_GROUP_TOP_APP_BOUND: - schedGroup = 'B'; - break; - default: - schedGroup = '?'; - break; - } - char foreground; - if (r.hasForegroundActivities()) { - foreground = 'A'; - } else if (r.hasForegroundServices()) { - foreground = 'S'; - } else { - foreground = ' '; - } - String procState = ProcessList.makeProcStateString(r.getCurProcState()); - pw.print(prefix); - pw.print(r.isPersistent() ? persistentLabel : normalLabel); - pw.print(" #"); - int num = (origList.size()-1)-list.get(i).second; - if (num < 10) pw.print(' '); - pw.print(num); - pw.print(": "); - pw.print(oomAdj); - pw.print(' '); - pw.print(schedGroup); - pw.print('/'); - pw.print(foreground); - pw.print('/'); - pw.print(procState); - pw.print(' '); - ActivityManager.printCapabilitiesSummary(pw, r.curCapability); - pw.print(' '); - pw.print(" t:"); - if (r.trimMemoryLevel < 10) pw.print(' '); - pw.print(r.trimMemoryLevel); - pw.print(' '); - pw.print(r.toShortString()); - pw.print(" ("); - pw.print(r.adjType); - pw.println(')'); - if (r.adjSource != null || r.adjTarget != 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()); - } else { - pw.print("{null}"); - } - pw.print("<="); - if (r.adjSource instanceof ProcessRecord) { - pw.print("Proc{"); - pw.print(((ProcessRecord)r.adjSource).toShortString()); - pw.println("}"); - } else if (r.adjSource != null) { - pw.println(r.adjSource.toString()); - } else { - pw.println("{null}"); - } - } - 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(prefix); - pw.print(" "); - pw.print("state: cur="); pw.print( - ProcessList.makeProcStateString(r.getCurProcState())); - pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState)); - pw.print(" lastPss="); DebugUtils.printSizeValue(pw, r.lastPss*1024); - pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, r.lastSwapPss*1024); - pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, r.lastCachedPss*1024); - 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); - - if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) { - if (r.lastCpuTime != 0) { - long timeUsed = r.curCpuTime - r.lastCpuTime; - pw.print(prefix); - pw.print(" "); - pw.print("run cpu over "); - TimeUtils.formatDuration(uptimeSince, pw); - pw.print(" used "); - TimeUtils.formatDuration(timeUsed, pw); - pw.print(" ("); - pw.print((timeUsed*100)/uptimeSince); - pw.println("%)"); - } - } - } - } - return true; - } - ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs, String[] args) { synchronized (this) { @@ -10637,12 +9885,12 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private static final int KSM_SHARED = 0; - private static final int KSM_SHARING = 1; - private static final int KSM_UNSHARED = 2; - private static final int KSM_VOLATILE = 3; + static final int KSM_SHARED = 0; + static final int KSM_SHARING = 1; + static final int KSM_UNSHARED = 2; + static final int KSM_VOLATILE = 3; - private final long[] getKsmInfo() { + static final long[] getKsmInfo() { long[] longOut = new long[4]; final int[] SINGLE_LONG_FORMAT = new int[] { PROC_SPACE_TERM| PROC_OUT_LONG @@ -10666,7 +9914,7 @@ public class ActivityManagerService extends IActivityManager.Stub return longOut; } - private static String stringifySize(long size, int order) { + static String stringifySize(long size, int order) { Locale locale = Locale.US; switch (order) { case 1: @@ -10682,7 +9930,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private static String stringifyKBSize(long size) { + static String stringifyKBSize(long size) { return stringifySize(size * 1024, 1024); } @@ -10978,12 +10226,9 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { // Record this for posterity if the process has been stable. - synchronized (mProcessStats.mLock) { - r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true, - reportType, endTime - startTime, r.pkgList.mPkgList); - } - for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg); + r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true, + reportType, endTime - startTime); + r.getPkgList().forEachPackageProcessStats(holder -> { FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, r.info.uid, holder.state.getName(), @@ -10991,7 +10236,7 @@ public class ActivityManagerService extends IActivityManager.Stub myTotalPss, myTotalUss, myTotalRss, reportType, endTime-startTime, holder.appVersion); - } + }); } } @@ -11561,19 +10806,16 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { // Record this for posterity if the process has been stable. - synchronized (mProcessStats.mLock) { - r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true, - reportType, endTime - startTime, r.pkgList.mPkgList); - } - for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg); + r.mProfile.addPss(myTotalPss, myTotalUss, myTotalRss, true, + reportType, endTime - startTime); + r.getPkgList().forEachPackageProcessStats(holder -> { FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, r.info.uid, holder.state.getName(), holder.state.getPackage(), myTotalPss, myTotalUss, myTotalRss, reportType, endTime-startTime, holder.appVersion); - } + }); } } @@ -11844,7 +11086,7 @@ public class ActivityManagerService extends IActivityManager.Stub proto.flush(); } - private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss, + static void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss, long memtrack, String name) { sb.append(" "); sb.append(ProcessList.makeOomAdjString(oomAdj, false)); @@ -11861,7 +11103,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private void appendMemInfo(StringBuilder sb, ProcessMemInfo mi) { + static void appendMemInfo(StringBuilder sb, ProcessMemInfo mi) { appendBasicMemEntry(sb, mi.oomAdj, mi.procState, mi.pss, mi.memtrack, mi.name); sb.append(" (pid "); sb.append(mi.pid); @@ -11875,283 +11117,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - void reportMemUsage(ArrayList<ProcessMemInfo> memInfos) { - final SparseArray<ProcessMemInfo> infoMap = new SparseArray<>(memInfos.size()); - for (int i=0, N=memInfos.size(); i<N; i++) { - ProcessMemInfo mi = memInfos.get(i); - infoMap.put(mi.pid, mi); - } - updateCpuStatsNow(); - long[] memtrackTmp = new long[1]; - long[] swaptrackTmp = new long[2]; - // Get a list of Stats that have vsize > 0 - final List<ProcessCpuTracker.Stats> stats = mAppProfiler.getCpuStats(st -> st.vsize > 0); - final int statsCount = stats.size(); - for (int i = 0; i < statsCount; i++) { - ProcessCpuTracker.Stats st = stats.get(i); - long pss = Debug.getPss(st.pid, swaptrackTmp, memtrackTmp); - if (pss > 0) { - if (infoMap.indexOfKey(st.pid) < 0) { - ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid, - ProcessList.NATIVE_ADJ, -1, "native", null); - mi.pss = pss; - mi.swapPss = swaptrackTmp[1]; - mi.memtrack = memtrackTmp[0]; - memInfos.add(mi); - } - } - } - - long totalPss = 0; - long totalSwapPss = 0; - long totalMemtrack = 0; - for (int i=0, N=memInfos.size(); i<N; i++) { - ProcessMemInfo mi = memInfos.get(i); - if (mi.pss == 0) { - mi.pss = Debug.getPss(mi.pid, swaptrackTmp, memtrackTmp); - mi.swapPss = swaptrackTmp[1]; - mi.memtrack = memtrackTmp[0]; - } - totalPss += mi.pss; - totalSwapPss += mi.swapPss; - totalMemtrack += mi.memtrack; - } - Collections.sort(memInfos, new Comparator<ProcessMemInfo>() { - @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) { - if (lhs.oomAdj != rhs.oomAdj) { - return lhs.oomAdj < rhs.oomAdj ? -1 : 1; - } - if (lhs.pss != rhs.pss) { - return lhs.pss < rhs.pss ? 1 : -1; - } - return 0; - } - }); - - StringBuilder tag = new StringBuilder(128); - StringBuilder stack = new StringBuilder(128); - tag.append("Low on memory -- "); - appendMemBucket(tag, totalPss, "total", false); - appendMemBucket(stack, totalPss, "total", true); - - StringBuilder fullNativeBuilder = new StringBuilder(1024); - StringBuilder shortNativeBuilder = new StringBuilder(1024); - StringBuilder fullJavaBuilder = new StringBuilder(1024); - - boolean firstLine = true; - int lastOomAdj = Integer.MIN_VALUE; - long extraNativeRam = 0; - long extraNativeMemtrack = 0; - long cachedPss = 0; - for (int i=0, N=memInfos.size(); i<N; i++) { - ProcessMemInfo mi = memInfos.get(i); - - if (mi.oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) { - cachedPss += mi.pss; - } - - if (mi.oomAdj != ProcessList.NATIVE_ADJ - && (mi.oomAdj < ProcessList.SERVICE_ADJ - || mi.oomAdj == ProcessList.HOME_APP_ADJ - || mi.oomAdj == ProcessList.PREVIOUS_APP_ADJ)) { - if (lastOomAdj != mi.oomAdj) { - lastOomAdj = mi.oomAdj; - if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { - tag.append(" / "); - } - if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ) { - if (firstLine) { - stack.append(":"); - firstLine = false; - } - stack.append("\n\t at "); - } else { - stack.append("$"); - } - } else { - tag.append(" "); - stack.append("$"); - } - if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { - appendMemBucket(tag, mi.pss, mi.name, false); - } - appendMemBucket(stack, mi.pss, mi.name, true); - if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ - && ((i+1) >= N || memInfos.get(i+1).oomAdj != lastOomAdj)) { - stack.append("("); - for (int k=0; k<DUMP_MEM_OOM_ADJ.length; k++) { - if (DUMP_MEM_OOM_ADJ[k] == mi.oomAdj) { - stack.append(DUMP_MEM_OOM_LABEL[k]); - stack.append(":"); - stack.append(DUMP_MEM_OOM_ADJ[k]); - } - } - stack.append(")"); - } - } - - appendMemInfo(fullNativeBuilder, mi); - if (mi.oomAdj == ProcessList.NATIVE_ADJ) { - // The short form only has native processes that are >= 512K. - if (mi.pss >= 512) { - appendMemInfo(shortNativeBuilder, mi); - } else { - extraNativeRam += mi.pss; - extraNativeMemtrack += mi.memtrack; - } - } else { - // Short form has all other details, but if we have collected RAM - // from smaller native processes let's dump a summary of that. - if (extraNativeRam > 0) { - appendBasicMemEntry(shortNativeBuilder, ProcessList.NATIVE_ADJ, - -1, extraNativeRam, extraNativeMemtrack, "(Other native)"); - shortNativeBuilder.append('\n'); - extraNativeRam = 0; - } - appendMemInfo(fullJavaBuilder, mi); - } - } - - fullJavaBuilder.append(" "); - ProcessList.appendRamKb(fullJavaBuilder, totalPss); - fullJavaBuilder.append(": TOTAL"); - if (totalMemtrack > 0) { - fullJavaBuilder.append(" ("); - fullJavaBuilder.append(stringifyKBSize(totalMemtrack)); - fullJavaBuilder.append(" memtrack)"); - } else { - } - fullJavaBuilder.append("\n"); - - MemInfoReader memInfo = new MemInfoReader(); - memInfo.readMemInfo(); - final long[] infos = memInfo.getRawInfo(); - - StringBuilder memInfoBuilder = new StringBuilder(1024); - Debug.getMemInfo(infos); - memInfoBuilder.append(" MemInfo: "); - memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SLAB])).append(" slab, "); - memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SHMEM])).append(" shmem, "); - memInfoBuilder.append(stringifyKBSize( - infos[Debug.MEMINFO_VM_ALLOC_USED])).append(" vm alloc, "); - memInfoBuilder.append(stringifyKBSize( - infos[Debug.MEMINFO_PAGE_TABLES])).append(" page tables "); - memInfoBuilder.append(stringifyKBSize( - infos[Debug.MEMINFO_KERNEL_STACK])).append(" kernel stack\n"); - memInfoBuilder.append(" "); - memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_BUFFERS])).append(" buffers, "); - memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_CACHED])).append(" cached, "); - memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_MAPPED])).append(" mapped, "); - memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_FREE])).append(" free\n"); - if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) { - memInfoBuilder.append(" ZRAM: "); - memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_ZRAM_TOTAL])); - memInfoBuilder.append(" RAM, "); - memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_TOTAL])); - memInfoBuilder.append(" swap total, "); - memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_FREE])); - memInfoBuilder.append(" swap free\n"); - } - final long[] ksm = getKsmInfo(); - if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0 - || ksm[KSM_VOLATILE] != 0) { - memInfoBuilder.append(" KSM: "); - memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARING])); - memInfoBuilder.append(" saved from shared "); - memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARED])); - memInfoBuilder.append("\n "); - memInfoBuilder.append(stringifyKBSize(ksm[KSM_UNSHARED])); - memInfoBuilder.append(" unshared; "); - memInfoBuilder.append(stringifyKBSize(ksm[KSM_VOLATILE])); - memInfoBuilder.append(" volatile\n"); - } - memInfoBuilder.append(" Free RAM: "); - memInfoBuilder.append(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb() - + memInfo.getFreeSizeKb())); - memInfoBuilder.append("\n"); - long kernelUsed = memInfo.getKernelUsedSizeKb(); - final long ionHeap = Debug.getIonHeapsSizeKb(); - final long ionPool = Debug.getIonPoolsSizeKb(); - if (ionHeap >= 0 && ionPool >= 0) { - final long ionMapped = Debug.getIonMappedSizeKb(); - final long ionUnmapped = ionHeap - ionMapped; - memInfoBuilder.append(" ION: "); - memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool)); - memInfoBuilder.append("\n"); - // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being - // set on ION VMAs, therefore consider the entire ION heap as used kernel memory - kernelUsed += ionHeap; - } - final long gpuUsage = Debug.getGpuTotalUsageKb(); - if (gpuUsage >= 0) { - memInfoBuilder.append(" GPU: "); - memInfoBuilder.append(stringifyKBSize(gpuUsage)); - memInfoBuilder.append("\n"); - } - memInfoBuilder.append(" Used RAM: "); - memInfoBuilder.append(stringifyKBSize( - totalPss - cachedPss + kernelUsed)); - memInfoBuilder.append("\n"); - memInfoBuilder.append(" Lost RAM: "); - memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb() - - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() - - kernelUsed - memInfo.getZramTotalSizeKb())); - memInfoBuilder.append("\n"); - Slog.i(TAG, "Low on memory:"); - Slog.i(TAG, shortNativeBuilder.toString()); - Slog.i(TAG, fullJavaBuilder.toString()); - Slog.i(TAG, memInfoBuilder.toString()); - - StringBuilder dropBuilder = new StringBuilder(1024); - /* - StringWriter oomSw = new StringWriter(); - PrintWriter oomPw = new FastPrintWriter(oomSw, false, 256); - StringWriter catSw = new StringWriter(); - PrintWriter catPw = new FastPrintWriter(catSw, false, 256); - String[] emptyArgs = new String[] { }; - dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw); - oomPw.flush(); - String oomString = oomSw.toString(); - */ - dropBuilder.append("Low on memory:"); - dropBuilder.append(stack); - dropBuilder.append('\n'); - dropBuilder.append(fullNativeBuilder); - dropBuilder.append(fullJavaBuilder); - dropBuilder.append('\n'); - dropBuilder.append(memInfoBuilder); - dropBuilder.append('\n'); - /* - dropBuilder.append(oomString); - dropBuilder.append('\n'); - */ - StringWriter catSw = new StringWriter(); - synchronized (ActivityManagerService.this) { - PrintWriter catPw = new FastPrintWriter(catSw, false, 256); - String[] emptyArgs = new String[] { }; - catPw.println(); - dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null, -1); - catPw.println(); - mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0, - false, null).dumpLocked(); - catPw.println(); - mAtmInternal.dump(DUMP_ACTIVITIES_CMD, null, catPw, emptyArgs, 0, false, false, null); - catPw.flush(); - } - dropBuilder.append(catSw.toString()); - FrameworkStatsLog.write(FrameworkStatsLog.LOW_MEM_REPORTED); - addErrorToDropBox("lowmem", null, "system_server", null, - null, null, tag.toString(), dropBuilder.toString(), null, null); - //Slog.i(TAG, "Sent to dropbox:"); - //Slog.i(TAG, dropBuilder.toString()); - synchronized (ActivityManagerService.this) { - long now = SystemClock.uptimeMillis(); - if (mLastMemUsageReportTime < now) { - mLastMemUsageReportTime = now; - } - } - } - /** * Searches array of arguments for the specified string * @param args array of argument strings @@ -12185,7 +11150,6 @@ public class ActivityManagerService extends IActivityManager.Stub ProcessList.remove(app.pid); } - mProcessesToGc.remove(app); mAppProfiler.onCleanupApplicationRecordLocked(app); // Dismiss any open dialogs. @@ -12285,15 +11249,7 @@ public class ActivityManagerService extends IActivityManager.Stub }); } - for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) { - ProcessChangeItem item = mPendingProcessChanges.get(i); - if (app.pid > 0 && item.pid == app.pid) { - mPendingProcessChanges.remove(i); - mAvailProcessChanges.add(item); - } - } - mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid, - null).sendToTarget(); + mProcessList.scheduleDispatchProcessDiedLocked(app.pid, app.info.uid); // If this is a precede instance of another process instance allowRestart = true; @@ -12626,7 +11582,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token); throw new IllegalArgumentException("Invalid service token"); } - mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res); + mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false); } } @@ -12938,9 +11894,9 @@ public class ActivityManagerService extends IActivityManager.Stub + " (pid=" + Binder.getCallingPid() + ") when registering receiver " + receiver); } - if (callerApp.info.uid != SYSTEM_UID && - !callerApp.pkgList.containsKey(callerPackage) && - !"android".equals(callerPackage)) { + if (callerApp.info.uid != SYSTEM_UID + && !callerApp.getPkgList().containsKey(callerPackage) + && !"android".equals(callerPackage)) { throw new SecurityException("Given caller package " + callerPackage + " is not running in process " + callerApp); } @@ -14509,8 +13465,11 @@ public class ActivityManagerService extends IActivityManager.Stub return; } final long origId = Binder.clearCallingIdentity(); - addInstrumentationResultsLocked(app, results); - Binder.restoreCallingIdentity(origId); + try { + addInstrumentationResultsLocked(app, results); + } finally { + Binder.restoreCallingIdentity(origId); + } } } @@ -14542,7 +13501,7 @@ public class ActivityManagerService extends IActivityManager.Stub mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid, app.info.packageName, AppOpsManager.MODE_ERRORED); mAppOpsService.setAppOpsServiceDelegate(null); - getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation(); + getPermissionManagerInternal().stopShellPermissionIdentityDelegation(); mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG, instr.mUiAutomationConnection).sendToTarget(); } @@ -14813,28 +13772,9 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * Ask a given process to GC right now. - */ - final void performAppGcLocked(ProcessRecord app) { - try { - app.lastRequestedGc = SystemClock.uptimeMillis(); - if (app.thread != null) { - if (app.reportLowMemory) { - app.reportLowMemory = false; - app.thread.scheduleLowMemory(); - } else { - app.thread.processInBackground(); - } - } - } catch (Exception e) { - // whatever. - } - } - - /** * Returns true if things are idle enough to perform GCs. */ - private final boolean canGcNowLocked() { + final boolean canGcNowLocked() { for (BroadcastQueue q : mBroadcastQueues) { if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isEmpty()) { return false; @@ -14843,107 +13783,6 @@ public class ActivityManagerService extends IActivityManager.Stub return mAtmInternal.canGcNow(); } - /** - * Perform GCs on all processes that are waiting for it, but only - * if things are idle. - */ - final void performAppGcsLocked() { - final int N = mProcessesToGc.size(); - if (N <= 0) { - return; - } - if (canGcNowLocked()) { - while (mProcessesToGc.size() > 0) { - ProcessRecord proc = mProcessesToGc.remove(0); - if (proc.getCurRawAdj() > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) { - if ((proc.lastRequestedGc+mConstants.GC_MIN_INTERVAL) - <= SystemClock.uptimeMillis()) { - // To avoid spamming the system, we will GC processes one - // at a time, waiting a few seconds between each. - performAppGcLocked(proc); - scheduleAppGcsLocked(); - return; - } else { - // It hasn't been long enough since we last GCed this - // process... put it in the list to wait for its time. - addProcessToGcListLocked(proc); - break; - } - } - } - - scheduleAppGcsLocked(); - } - } - - /** - * If all looks good, perform GCs on all processes waiting for them. - */ - final void performAppGcsIfAppropriateLocked() { - if (canGcNowLocked()) { - performAppGcsLocked(); - return; - } - // Still not idle, wait some more. - scheduleAppGcsLocked(); - } - - /** - * Schedule the execution of all pending app GCs. - */ - final void scheduleAppGcsLocked() { - mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG); - - if (mProcessesToGc.size() > 0) { - // Schedule a GC for the time to the next process. - ProcessRecord proc = mProcessesToGc.get(0); - Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG); - - long when = proc.lastRequestedGc + mConstants.GC_MIN_INTERVAL; - long now = SystemClock.uptimeMillis(); - if (when < (now+mConstants.GC_TIMEOUT)) { - when = now + mConstants.GC_TIMEOUT; - } - mHandler.sendMessageAtTime(msg, when); - } - } - - /** - * Add a process to the array of processes waiting to be GCed. Keeps the - * list in sorted order by the last GC time. The process can't already be - * on the list. - */ - final void addProcessToGcListLocked(ProcessRecord proc) { - boolean added = false; - for (int i=mProcessesToGc.size()-1; i>=0; i--) { - if (mProcessesToGc.get(i).lastRequestedGc < - proc.lastRequestedGc) { - added = true; - mProcessesToGc.add(i+1, proc); - break; - } - } - if (!added) { - mProcessesToGc.add(0, proc); - } - } - - /** - * Set up to ask a process to GC itself. This will either do it - * immediately, or put it on the list of processes to gc the next - * time things are idle. - */ - final void scheduleAppGcLocked(ProcessRecord app) { - long now = SystemClock.uptimeMillis(); - if ((app.lastRequestedGc+mConstants.GC_MIN_INTERVAL) > now) { - return; - } - if (!mProcessesToGc.contains(app)) { - addProcessToGcListLocked(app); - scheduleAppGcsLocked(); - } - } - private void checkExcessivePowerUsage() { updateCpuStatsNow(); @@ -14972,22 +13811,25 @@ public class ActivityManagerService extends IActivityManager.Stub } else { cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4; } - if (app.lastCpuTime > 0) { - final long cputimeUsed = app.curCpuTime - app.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); - synchronized (mProcessStats.mLock) { - app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList); + 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); } - app.lastCpuTime = app.curCpuTime; // Also check the phantom processes if there is any final long chkDur = checkDur; @@ -15036,15 +13878,14 @@ public class ActivityManagerService extends IActivityManager.Stub if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) { mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName, uptimeSince, cputimeUsed); - for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg); + app.getPkgList().forEachPackageProcessStats(holder -> { FrameworkStatsLog.write( FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED, app.info.uid, processName, holder.state.getPackage(), holder.appVersion); - } + }); return true; } } @@ -15056,7 +13897,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid return false; } - return getPackageManagerInternalLocked().isPackageEphemeral( + return getPackageManagerInternal().isPackageEphemeral( UserHandle.getUserId(uid), packages[0]); } @@ -15106,12 +13947,9 @@ public class ActivityManagerService extends IActivityManager.Stub final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) { synchronized (mProcessStats.mLock) { - if (proc.thread != null && proc.baseProcessTracker != null) { - final int procState = proc.getReportedProcState(); - if (procState != PROCESS_STATE_NONEXISTENT) { - proc.baseProcessTracker.setState( - procState, memFactor, now, proc.pkgList.mPkgList); - } + if (proc.thread != null) { + proc.mProfile.setProcessTrackerState( + proc.getReportedProcState(), memFactor, now); } } } @@ -15148,7 +13986,8 @@ public class ActivityManagerService extends IActivityManager.Stub } proc.setReportedForegroundServiceTypes(fgServiceTypes); - ProcessChangeItem item = enqueueProcessChangeItemLocked(proc.pid, proc.info.uid); + ProcessChangeItem item = mProcessList.enqueueProcessChangeItemLocked( + proc.pid, proc.info.uid); item.changes |= ProcessChangeItem.CHANGE_FOREGROUND_SERVICES; item.foregroundServiceTypes = fgServiceTypes; } @@ -15159,7 +13998,7 @@ public class ActivityManagerService extends IActivityManager.Stub // TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update // the logic there and in mBatteryStatsService to make them aware of multiple resumed activities - ProcessRecord getTopAppLocked() { + ProcessRecord getTopApp() { final WindowProcessController wpc = mAtmInternal != null ? mAtmInternal.getTopApp() : null; final ProcessRecord r = wpc != null ? (ProcessRecord) wpc.mOwner : null; String pkg; @@ -15172,25 +14011,27 @@ public class ActivityManagerService extends IActivityManager.Stub uid = -1; } // Has the UID or resumed package name changed? - if (uid != mCurResumedUid || (pkg != mCurResumedPackage - && (pkg == null || !pkg.equals(mCurResumedPackage)))) { + synchronized (mCurResumedAppLock) { + if (uid != mCurResumedUid || (pkg != mCurResumedPackage + && (pkg == null || !pkg.equals(mCurResumedPackage)))) { - final long identity = Binder.clearCallingIdentity(); - try { - if (mCurResumedPackage != null) { - mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH, - mCurResumedPackage, mCurResumedUid); - } - mCurResumedPackage = pkg; - mCurResumedUid = uid; - if (mCurResumedPackage != null) { - mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START, - mCurResumedPackage, mCurResumedUid); + final long identity = Binder.clearCallingIdentity(); + try { + if (mCurResumedPackage != null) { + mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH, + mCurResumedPackage, mCurResumedUid); + } + mCurResumedPackage = pkg; + mCurResumedUid = uid; + if (mCurResumedPackage != null) { + mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START, + mCurResumedPackage, mCurResumedUid); + } + } finally { + Binder.restoreCallingIdentity(identity); } - } finally { - Binder.restoreCallingIdentity(identity); - } + } } return r; } @@ -15279,47 +14120,49 @@ public class ActivityManagerService extends IActivityManager.Stub userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(), userId, true, ALLOW_FULL_ONLY, "makePackageIdle", null); final long callingId = Binder.clearCallingIdentity(); - synchronized(this) { + try { + IPackageManager pm = AppGlobals.getPackageManager(); + int pkgUid = -1; try { - IPackageManager pm = AppGlobals.getPackageManager(); - int pkgUid = -1; + pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES + | MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM); + } catch (RemoteException e) { + } + if (pkgUid == -1) { + throw new IllegalArgumentException("Unknown package name " + packageName); + } + + synchronized (this) { try { - pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES - | MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM); - } catch (RemoteException e) { - } - if (pkgUid == -1) { - throw new IllegalArgumentException("Unknown package name " + packageName); - } - - if (mLocalPowerManager != null) { - mLocalPowerManager.startUidChanges(); - } - final int appId = UserHandle.getAppId(pkgUid); - final int N = mProcessList.mActiveUids.size(); - for (int i = N - 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) { - 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) - + " from package " + packageName + " user " + userId); - doStopUidLocked(uidRec.uid, uidRec); + if (mLocalPowerManager != null) { + mLocalPowerManager.startUidChanges(); + } + 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) { + 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) + + " from package " + packageName + " user " + userId); + doStopUidLocked(uidRec.uid, uidRec); + } } } } + } finally { + if (mLocalPowerManager != null) { + mLocalPowerManager.finishUidChanges(); + } } - } finally { - if (mLocalPowerManager != null) { - mLocalPowerManager.finishUidChanges(); - } - Binder.restoreCallingIdentity(callingId); } + } finally { + Binder.restoreCallingIdentity(callingId); } } @@ -15373,24 +14216,24 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * Whitelists {@code targetUid} to temporarily bypass Power Save mode. + * Allowlists {@code targetUid} to temporarily bypass Power Save mode. */ @GuardedBy("this") - void tempWhitelistForPendingIntentLocked(int callerPid, int callerUid, int targetUid, + void tempAllowlistForPendingIntentLocked(int callerPid, int callerUid, int targetUid, long duration, int type, String tag) { if (DEBUG_WHITELISTS) { - Slog.d(TAG, "tempWhitelistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", " + Slog.d(TAG, "tempAllowlistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", " + targetUid + ", " + duration + ", " + type + ")"); } synchronized (mPidsSelfLocked) { final ProcessRecord pr = mPidsSelfLocked.get(callerPid); if (pr == null) { - Slog.w(TAG, "tempWhitelistForPendingIntentLocked() no ProcessRecord for pid " + Slog.w(TAG, "tempAllowlistForPendingIntentLocked() no ProcessRecord for pid " + callerPid); return; } - if (!pr.whitelistManager) { + if (!pr.mAllowlistManager) { if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid) != PackageManager.PERMISSION_GRANTED && checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callerPid, callerUid) @@ -15398,7 +14241,7 @@ public class ActivityManagerService extends IActivityManager.Stub && checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callerPid, callerUid) != PackageManager.PERMISSION_GRANTED) { if (DEBUG_WHITELISTS) { - Slog.d(TAG, "tempWhitelistForPendingIntentLocked() for target " + targetUid + Slog.d(TAG, "tempAllowlistForPendingIntentLocked() for target " + targetUid + ": pid " + callerPid + " is not allowed"); } return; @@ -15406,35 +14249,35 @@ public class ActivityManagerService extends IActivityManager.Stub } } - tempWhitelistUidLocked(targetUid, duration, tag, type); + tempAllowlistUidLocked(targetUid, duration, tag, type); } /** - * Whitelists {@code targetUid} to temporarily bypass Power Save mode. + * Allowlists {@code targetUid} to temporarily bypass Power Save mode. */ @GuardedBy("this") - void tempWhitelistUidLocked(int targetUid, long duration, String tag, int type) { - mPendingTempWhitelist.put(targetUid, - new PendingTempWhitelist(targetUid, duration, tag, type)); - setUidTempWhitelistStateLocked(targetUid, true); - mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget(); + 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(); if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { mFgsStartTempAllowList.add(targetUid, duration); } } - void pushTempWhitelist() { + void pushTempAllowlist() { final int N; - final PendingTempWhitelist[] list; + final PendingTempAllowlist[] list; // 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) { - N = mPendingTempWhitelist.size(); - list = new PendingTempWhitelist[N]; + N = mPendingTempAllowlist.size(); + list = new PendingTempAllowlist[N]; for (int i = 0; i < N; i++) { - list[i] = mPendingTempWhitelist.valueAt(i); + list[i] = mPendingTempAllowlist.valueAt(i); } } @@ -15443,7 +14286,7 @@ public class ActivityManagerService extends IActivityManager.Stub // device idle policy anyway at this phase. if (mLocalDeviceIdleController != null) { for (int i = 0; i < N; i++) { - PendingTempWhitelist ptw = list[i]; + PendingTempAllowlist ptw = list[i]; mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid, ptw.duration, ptw.type, true, ptw.tag); } @@ -15452,23 +14295,23 @@ 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++) { - PendingTempWhitelist ptw = list[i]; - int index = mPendingTempWhitelist.indexOfKey(ptw.targetUid); - if (index >= 0 && mPendingTempWhitelist.valueAt(index) == ptw) { - mPendingTempWhitelist.removeAt(index); + PendingTempAllowlist ptw = list[i]; + int index = mPendingTempAllowlist.indexOfKey(ptw.targetUid); + if (index >= 0 && mPendingTempAllowlist.valueAt(index) == ptw) { + mPendingTempAllowlist.removeAt(index); } } } } @GuardedBy("this") - final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) { - mOomAdjuster.setAppIdTempWhitelistStateLocked(uid, onWhitelist); + final void setAppIdTempAllowlistStateLocked(int uid, boolean onAllowlist) { + mOomAdjuster.setAppIdTempAllowlistStateLocked(uid, onAllowlist); } @GuardedBy("this") - final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) { - mOomAdjuster.setUidTempWhitelistStateLocked(uid, onWhitelist); + final void setUidTempAllowlistStateLocked(int uid, boolean onAllowlist) { + mOomAdjuster.setUidTempAllowlistStateLocked(uid, onAllowlist); } private void trimApplications(boolean forceFullOomAdj, String oomAdjReason) { @@ -15530,13 +14373,13 @@ public class ActivityManagerService extends IActivityManager.Stub throw new SecurityException("Only SIGNAL_USR1 is allowed"); } - synchronized (this) { - if (checkCallingPermission(android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires permission " - + android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES); - } + if (checkCallingPermission(android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + 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()) { @@ -15548,20 +14391,20 @@ public class ActivityManagerService extends IActivityManager.Stub public boolean profileControl(String process, int userId, boolean start, ProfilerInfo profilerInfo, int profileType) throws RemoteException { - synchronized (this) { - // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to - // its own permission. - if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires permission " - + android.Manifest.permission.SET_ACTIVITY_WATCHER); - } + // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to + // its own permission. + if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.SET_ACTIVITY_WATCHER); + } - if (start && (profilerInfo == null || profilerInfo.profileFd == null)) { - throw new IllegalArgumentException("null profile info or fd"); - } + if (start && (profilerInfo == null || profilerInfo.profileFd == null)) { + throw new IllegalArgumentException("null profile info or fd"); + } - ProcessRecord proc = null; + ProcessRecord proc = null; + synchronized (this) { if (process != null) { proc = findProcessLocked(process, userId, "profileControl"); } @@ -15569,8 +14412,9 @@ public class ActivityManagerService extends IActivityManager.Stub if (start && (proc == null || proc.thread == null)) { throw new IllegalArgumentException("Unknown process: " + process); } - - return mAppProfiler.profileControlLocked(proc, start, profilerInfo, profileType); + } + synchronized (mAppProfiler.mProfilerLock) { + return mAppProfiler.profileControlLPf(proc, start, profilerInfo, profileType); } } @@ -15612,19 +14456,19 @@ public class ActivityManagerService extends IActivityManager.Stub boolean runGc, String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) { try { - synchronized (this) { - // note: 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); - } + // note: 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); + } - if (fd == null) { - throw new IllegalArgumentException("null fd"); - } + if (fd == null) { + throw new IllegalArgumentException("null fd"); + } + synchronized (this) { ProcessRecord proc = findProcessLocked(process, userId, "dumpHeap"); if (proc == null || proc.thread == null) { throw new IllegalArgumentException("Unknown process: " + process); @@ -15683,7 +14527,7 @@ public class ActivityManagerService extends IActivityManager.Stub } processName = proc.processName; uid = proc.uid; - if (reportPackage != null && !proc.pkgList.containsKey(reportPackage)) { + if (reportPackage != null && !proc.getPkgList().containsKey(reportPackage)) { throw new SecurityException("Package " + reportPackage + " is not running in " + proc); } @@ -15871,16 +14715,16 @@ public class ActivityManagerService extends IActivityManager.Stub } public boolean startBinderTracking() 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) { mBinderTransactionTrackingEnabled = true; - // 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); - } - for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) { ProcessRecord process = mProcessList.mLruProcesses.get(i); if (!processSanityChecksLocked(process)) { @@ -15898,19 +14742,19 @@ public class ActivityManagerService extends IActivityManager.Stub public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException { try { - synchronized (this) { - mBinderTransactionTrackingEnabled = false; - // 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); - } + // 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) { if (fd == null) { throw new IllegalArgumentException("null fd"); } + mBinderTransactionTrackingEnabled = false; PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor())); pw.println("Binder transaction traces for all processes.\n"); @@ -16059,8 +14903,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) { synchronized (ActivityManagerService.this) { - mDeviceIdleWhitelist = allAppids; - mDeviceIdleExceptIdleWhitelist = exceptIdleAppids; + mDeviceIdleAllowlist = allAppids; + mDeviceIdleExceptIdleAllowlist = exceptIdleAppids; } } @@ -16068,13 +14912,13 @@ public class ActivityManagerService extends IActivityManager.Stub public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding, long durationMs, @BroadcastOptions.TempAllowListType int type) { synchronized (ActivityManagerService.this) { - mDeviceIdleTempWhitelist = appids; + mDeviceIdleTempAllowlist = appids; if (adding) { if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { mFgsStartTempAllowList.add(changingUid, durationMs); } } - setAppIdTempWhitelistStateLocked(changingUid, adding); + setAppIdTempAllowlistStateLocked(changingUid, adding); } } @@ -16301,26 +15145,20 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void updateCpuStats() { - synchronized (ActivityManagerService.this) { - ActivityManagerService.this.updateCpuStatsLocked(); - } + ActivityManagerService.this.updateCpuStats(); } @Override public void updateBatteryStats(ComponentName activity, int uid, int userId, boolean resumed) { - synchronized (ActivityManagerService.this) { - ActivityManagerService.this.updateBatteryStats(activity, uid, userId, resumed); - } + ActivityManagerService.this.updateBatteryStats(activity, uid, userId, resumed); } @Override public void updateActivityUsageStats(ComponentName activity, int userId, int event, IBinder appToken, ComponentName taskRoot) { - synchronized (ActivityManagerService.this) { - ActivityManagerService.this.updateActivityUsageStats(activity, userId, event, - appToken, taskRoot); - } + ActivityManagerService.this.updateActivityUsageStats(activity, userId, event, + appToken, taskRoot); } @Override @@ -16397,16 +15235,14 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void scheduleAppGcs() { - synchronized (ActivityManagerService.this) { - ActivityManagerService.this.scheduleAppGcsLocked(); + synchronized (mAppProfiler.mProfilerLock) { + mAppProfiler.scheduleAppGcsLPf(); } } @Override public int getTaskIdForActivity(IBinder token, boolean onlyRoot) { - synchronized (ActivityManagerService.this) { - return ActivityManagerService.this.getTaskForActivity(token, onlyRoot); - } + return ActivityManagerService.this.getTaskForActivity(token, onlyRoot); } @Override @@ -16446,7 +15282,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid, long duration, int type, String tag) { synchronized (ActivityManagerService.this) { - ActivityManagerService.this.tempWhitelistForPendingIntentLocked( + ActivityManagerService.this.tempAllowlistForPendingIntentLocked( callerPid, callerUid, targetUid, duration, type, tag); } } @@ -16527,7 +15363,8 @@ public class ActivityManagerService extends IActivityManager.Stub (ActivityServiceConnectionsHolder) connectionHolder; synchronized (ActivityManagerService.this) { holder.forEachConnection(cr -> mServices.removeConnectionLocked( - (ConnectionRecord) cr, null /* skipApp */, holder /* skipAct */)); + (ConnectionRecord) cr, null /* skipApp */, holder /* skipAct */, + false /* enqueueOomAdj */)); } } @@ -16872,14 +15709,11 @@ public class ActivityManagerService extends IActivityManager.Stub throw new SecurityException("Requires permission " + FILTER_EVENTS); } ProcessRecord proc; - long timeoutMillis; - synchronized (this) { - synchronized (mPidsSelfLocked) { - proc = mPidsSelfLocked.get(pid); - } - timeoutMillis = proc != null ? proc.getInputDispatchingTimeoutMillis() : - DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(pid); } + final long timeoutMillis = proc != null ? proc.getInputDispatchingTimeoutMillis() : + DEFAULT_DISPATCHING_TIMEOUT_MILLIS; if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) { return 0; @@ -17093,10 +15927,7 @@ public class ActivityManagerService extends IActivityManager.Stub * resources and overlaid values are available immediately. */ public void updateSystemUiContext() { - PackageManagerInternal packageManagerInternal; - synchronized (this) { - packageManagerInternal = getPackageManagerInternalLocked(); - } + final PackageManagerInternal packageManagerInternal = getPackageManagerInternal(); ApplicationInfo ai = packageManagerInternal.getApplicationInfo("android", GET_SHARED_LIBRARY_FILES, Binder.getCallingUid(), UserHandle.USER_SYSTEM); @@ -17279,10 +16110,8 @@ public class ActivityManagerService extends IActivityManager.Stub * like persisting database etc. */ public void prepareForPossibleShutdown() { - synchronized (this) { - if (mUsageStatsService != null) { - mUsageStatsService.prepareForPossibleShutdown(); - } + if (mUsageStatsService != null) { + mUsageStatsService.prepareForPossibleShutdown(); } } @@ -17371,7 +16200,7 @@ public class ActivityManagerService extends IActivityManager.Stub final String packageName = instr.mTargetInfo.packageName; final List<String> permissionNames = permissions != null ? Arrays.asList(permissions) : null; - getPermissionManagerInternalLocked().startShellPermissionIdentityDelegation( + getPermissionManagerInternal().startShellPermissionIdentityDelegation( delegateUid, packageName, permissionNames); return; } @@ -17386,7 +16215,7 @@ public class ActivityManagerService extends IActivityManager.Stub } synchronized (ActivityManagerService.this) { mAppOpsService.setAppOpsServiceDelegate(null); - getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation(); + getPermissionManagerInternal().stopShellPermissionIdentityDelegation(); } } @@ -17525,7 +16354,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void setActivityLocusContext(ComponentName activity, LocusId locusId, IBinder appToken) { final int callingUid = Binder.getCallingUid(); final int userId = UserHandle.getCallingUserId(); - if (getPackageManagerInternalLocked().getPackageUid(activity.getPackageName(), + if (getPackageManagerInternal().getPackageUid(activity.getPackageName(), /*flags=*/ 0, userId) != callingUid) { throw new SecurityException("Calling uid " + callingUid + " cannot set locusId" + "for package " + activity.getPackageName()); @@ -17555,9 +16384,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void resetAppErrors() { enforceCallingPermission(Manifest.permission.RESET_APP_ERRORS, "resetAppErrors"); - synchronized (this) { - mAppErrors.resetStateLocked(); - } + mAppErrors.resetState(); } @Override diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java index d76e2d712ad9..779d37858ce2 100644 --- a/services/core/java/com/android/server/am/AppErrorDialog.java +++ b/services/core/java/com/android/server/am/AppErrorDialog.java @@ -71,8 +71,8 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen BidiFormatter bidi = BidiFormatter.getInstance(); CharSequence name; - if ((mProc.pkgList.size() == 1) && - (name = context.getPackageManager().getApplicationLabel(mProc.info)) != null) { + 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 : com.android.internal.R.string.aerr_application, diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 35f46896ac94..7be54c29a218 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -52,6 +52,7 @@ import android.util.SparseArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.ProcessMap; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; @@ -74,27 +75,32 @@ class AppErrors { private final Context mContext; private final PackageWatchdog mPackageWatchdog; + @GuardedBy("mBadProcessLock") private ArraySet<String> mAppsNotReportingCrashes; /** * The last time that various processes have crashed since they were last explicitly started. */ + @GuardedBy("mBadProcessLock") private final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<>(); /** * The last time that various processes have crashed (not reset even when explicitly started). */ + @GuardedBy("mBadProcessLock") private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>(); /** * The last time that various processes have crashed and shown an error dialog. */ + @GuardedBy("mBadProcessLock") private final ProcessMap<Long> mProcessCrashShowDialogTimes = new ProcessMap<>(); /** * A pairing between how many times various processes have crashed since a given time. * Entry and exit conditions for this map are similar to mProcessCrashTimes. */ + @GuardedBy("mBadProcessLock") private final ProcessMap<Pair<Long, Integer>> mProcessCrashCounts = new ProcessMap<>(); /** @@ -116,6 +122,16 @@ class AppErrors { * lock operations from the simple lookup cases. */ private volatile ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>(); + + /** + * Dedicated lock for {@link #mAppsNotReportingCrashes}, {@link #mProcessCrashTimes}, + * {@link #mProcessCrashTimesPersistent}, {@link #mProcessCrashShowDialogTimes}, + * {@link #mProcessCrashCounts} and {@link #mBadProcesses}. + * + * <p>The naming convention of the function with this lock should be "-LBp"</b> + * + * @See mBadProcesses + */ private final Object mBadProcessLock = new Object(); AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog) { @@ -126,147 +142,152 @@ class AppErrors { } /** Resets the current state but leaves the constructor-provided fields unchanged. */ - public void resetStateLocked() { + public void resetState() { Slog.i(TAG, "Resetting AppErrors"); - mAppsNotReportingCrashes.clear(); - mProcessCrashTimes.clear(); - mProcessCrashTimesPersistent.clear(); - mProcessCrashShowDialogTimes.clear(); - mProcessCrashCounts.clear(); synchronized (mBadProcessLock) { + mAppsNotReportingCrashes.clear(); + mProcessCrashTimes.clear(); + mProcessCrashTimesPersistent.clear(); + mProcessCrashShowDialogTimes.clear(); + mProcessCrashCounts.clear(); mBadProcesses = new ProcessMap<>(); } } void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) { - final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses; - if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) { - return; - } - - final long token = proto.start(fieldId); - final long now = SystemClock.uptimeMillis(); - proto.write(AppErrorsProto.NOW_UPTIME_MS, now); - - if (!mProcessCrashTimes.getMap().isEmpty()) { - final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); - final int procCount = pmap.size(); - for (int ip = 0; ip < procCount; ip++) { - final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES); - final String pname = pmap.keyAt(ip); - final SparseArray<Long> uids = pmap.valueAt(ip); - final int uidCount = uids.size(); + synchronized (mBadProcessLock) { + final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses; + if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) { + return; + } - 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); - if (dumpPackage != null - && (r == null || !r.pkgList.containsKey(dumpPackage))) { - continue; + final long token = proto.start(fieldId); + final long now = SystemClock.uptimeMillis(); + proto.write(AppErrorsProto.NOW_UPTIME_MS, now); + + if (!mProcessCrashTimes.getMap().isEmpty()) { + final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); + final int procCount = pmap.size(); + for (int ip = 0; ip < procCount; ip++) { + final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES); + final String pname = pmap.keyAt(ip); + final SparseArray<Long> uids = pmap.valueAt(ip); + final int uidCount = uids.size(); + + 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); + if (dumpPackage != null + && (r == null || !r.getPkgList().containsKey(dumpPackage))) { + continue; + } + final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES); + proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid); + proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS, + uids.valueAt(i)); + proto.end(etoken); } - final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES); - proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid); - proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS, - uids.valueAt(i)); - proto.end(etoken); + proto.end(ctoken); } - 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.pkgList.containsKey(dumpPackage))) { - continue; + 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); } - 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(btoken); } - } - proto.end(token); + proto.end(token); + } } boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) { final long now = SystemClock.uptimeMillis(); - if (!mProcessCrashTimes.getMap().isEmpty()) { - boolean printed = false; - final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); - final int processCount = pmap.size(); - for (int ip = 0; ip < processCount; ip++) { - final String pname = pmap.keyAt(ip); - final SparseArray<Long> uids = pmap.valueAt(ip); - 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.pkgList.containsKey(dumpPackage))) { - continue; - } - if (!printed) { - if (needSep) pw.println(); - needSep = true; - pw.println(" Time since processes crashed:"); - printed = true; + synchronized (mBadProcessLock) { + if (!mProcessCrashTimes.getMap().isEmpty()) { + boolean printed = false; + final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); + final int processCount = pmap.size(); + for (int ip = 0; ip < processCount; ip++) { + final String pname = pmap.keyAt(ip); + final SparseArray<Long> uids = pmap.valueAt(ip); + 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))) { + continue; + } + if (!printed) { + if (needSep) pw.println(); + needSep = true; + pw.println(" Time since processes crashed:"); + printed = true; + } + pw.print(" Process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); + pw.print(": last crashed "); + TimeUtils.formatDuration(now - uids.valueAt(i), pw); + pw.println(" ago"); } - pw.print(" Process "); pw.print(pname); - pw.print(" uid "); pw.print(puid); - pw.print(": last crashed "); - TimeUtils.formatDuration(now-uids.valueAt(i), pw); - pw.println(" ago"); } } - } - if (!mProcessCrashCounts.getMap().isEmpty()) { - boolean printed = false; - final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pmap = - mProcessCrashCounts.getMap(); - final int processCount = pmap.size(); - for (int ip = 0; ip < processCount; ip++) { - final String pname = pmap.keyAt(ip); - final SparseArray<Pair<Long, Integer>> uids = pmap.valueAt(ip); - 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.pkgList.containsKey(dumpPackage))) { - continue; - } - if (!printed) { - if (needSep) pw.println(); - needSep = true; - pw.println(" First time processes crashed and counts:"); - printed = true; + if (!mProcessCrashCounts.getMap().isEmpty()) { + boolean printed = false; + final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pmap = + mProcessCrashCounts.getMap(); + final int processCount = pmap.size(); + for (int ip = 0; ip < processCount; ip++) { + final String pname = pmap.keyAt(ip); + final SparseArray<Pair<Long, Integer>> uids = pmap.valueAt(ip); + 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))) { + continue; + } + if (!printed) { + if (needSep) pw.println(); + needSep = true; + pw.println(" First time processes crashed and counts:"); + printed = true; + } + pw.print(" Process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); + pw.print(": first crashed "); + TimeUtils.formatDuration(now - uids.valueAt(i).first, pw); + pw.print(" ago; crashes since then: "); pw.println(uids.valueAt(i).second); } - pw.print(" Process "); pw.print(pname); - pw.print(" uid "); pw.print(puid); - pw.print(": first crashed "); - TimeUtils.formatDuration(now - uids.valueAt(i).first, pw); - pw.print(" ago; crashes since then: "); pw.println(uids.valueAt(i).second); } } } @@ -284,7 +305,7 @@ class AppErrors { final int puid = uids.keyAt(i); final ProcessRecord r = mService.getProcessNames().get(pname, puid); if (dumpPackage != null && (r == null - || !r.pkgList.containsKey(dumpPackage))) { + || !r.getPkgList().containsKey(dumpPackage))) { continue; } if (!printed) { @@ -349,33 +370,38 @@ class AppErrors { } } - void resetProcessCrashTimeLocked(final String processName, final int uid) { - mProcessCrashTimes.remove(processName, uid); - mProcessCrashCounts.remove(processName, uid); + void resetProcessCrashTime(final String processName, final int uid) { + synchronized (mBadProcessLock) { + mProcessCrashTimes.remove(processName, uid); + mProcessCrashCounts.remove(processName, uid); + } } - void resetProcessCrashTimeLocked(boolean resetEntireUser, int appId, int userId) { - final ArrayMap<String, SparseArray<Long>> pTimeMap = mProcessCrashTimes.getMap(); - for (int ip = pTimeMap.size() - 1; ip >= 0; ip--) { - SparseArray<Long> ba = pTimeMap.valueAt(ip); - resetProcessCrashMapLocked(ba, resetEntireUser, appId, userId); - if (ba.size() == 0) { - pTimeMap.removeAt(ip); + void resetProcessCrashTime(boolean resetEntireUser, int appId, int userId) { + synchronized (mBadProcessLock) { + final ArrayMap<String, SparseArray<Long>> pTimeMap = mProcessCrashTimes.getMap(); + for (int ip = pTimeMap.size() - 1; ip >= 0; ip--) { + SparseArray<Long> ba = pTimeMap.valueAt(ip); + resetProcessCrashMapLBp(ba, resetEntireUser, appId, userId); + if (ba.size() == 0) { + pTimeMap.removeAt(ip); + } } - } - final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pCountMap = - mProcessCrashCounts.getMap(); - for (int ip = pCountMap.size() - 1; ip >= 0; ip--) { - SparseArray<Pair<Long, Integer>> ba = pCountMap.valueAt(ip); - resetProcessCrashMapLocked(ba, resetEntireUser, appId, userId); - if (ba.size() == 0) { - pCountMap.removeAt(ip); + final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pCountMap = + mProcessCrashCounts.getMap(); + for (int ip = pCountMap.size() - 1; ip >= 0; ip--) { + SparseArray<Pair<Long, Integer>> ba = pCountMap.valueAt(ip); + resetProcessCrashMapLBp(ba, resetEntireUser, appId, userId); + if (ba.size() == 0) { + pCountMap.removeAt(ip); + } } } } - private void resetProcessCrashMapLocked(SparseArray<?> ba, boolean resetEntireUser, + @GuardedBy("mBadProcessLock") + private void resetProcessCrashMapLBp(SparseArray<?> ba, boolean resetEntireUser, int appId, int userId) { for (int i = ba.size() - 1; i >= 0; i--) { boolean remove = false; @@ -399,12 +425,14 @@ class AppErrors { } } - void loadAppsNotReportingCrashesFromConfigLocked(String appsNotReportingCrashesConfig) { + void loadAppsNotReportingCrashesFromConfig(String appsNotReportingCrashesConfig) { if (appsNotReportingCrashesConfig != null) { final String[] split = appsNotReportingCrashesConfig.split(","); if (split.length > 0) { - mAppsNotReportingCrashes = new ArraySet<>(); - Collections.addAll(mAppsNotReportingCrashes, split); + synchronized (mBadProcessLock) { + mAppsNotReportingCrashes = new ArraySet<>(); + Collections.addAll(mAppsNotReportingCrashes, split); + } } } } @@ -465,7 +493,7 @@ class AppErrors { proc = p; break; } - if (p.pkgList.containsKey(packageName) + if (p.getPkgList().containsKey(packageName) && (userId < 0 || p.userId == userId)) { proc = p; } @@ -516,7 +544,7 @@ class AppErrors { } } - void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo, + private void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo, int callingPid, int callingUid) { long timeMillis = System.currentTimeMillis(); String shortMsg = crashInfo.exceptionClassName; @@ -599,44 +627,50 @@ class AppErrors { if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) { res = AppErrorDialog.FORCE_QUIT; } - synchronized (mService) { - if (res == AppErrorDialog.MUTE) { - stopReportingCrashesLocked(r); + if (res == AppErrorDialog.MUTE) { + synchronized (mBadProcessLock) { + stopReportingCrashesLBp(r); } - if (res == AppErrorDialog.RESTART) { + } + 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); - } - } } - if (res == AppErrorDialog.FORCE_QUIT) { - final long orig = Binder.clearCallingIdentity(); + if (taskId != INVALID_TASK_ID) { try { - // Kill it with fire! - mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController()); - if (!r.isPersistent()) { + 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); + } + } + } + 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"); - mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */); } - } finally { - Binder.restoreCallingIdentity(orig); + 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) { + } + 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); } } @@ -769,7 +803,7 @@ class AppErrors { return report; } - boolean handleAppCrashLocked(ProcessRecord app, String reason, + private boolean handleAppCrashLocked(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(), @@ -783,109 +817,116 @@ class AppErrors { Long crashTimePersistent; boolean tryAgain = false; - 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; + synchronized (mBadProcessLock) { + if (!app.isolated) { + crashTime = mProcessCrashTimes.get(app.processName, app.uid); + crashTimePersistent = mProcessCrashTimesPersistent.get(app.processName, app.uid); } else { - sr.crashCount++; + crashTime = crashTimePersistent = null; } - // Allow restarting for started or bound foreground services that are crashing. - // This includes wallpapers. - if (sr.crashCount < mService.mConstants.BOUND_SERVICE_MAX_CRASH_RETRY - && (sr.isForeground || procIsBoundForeground)) { - tryAgain = true; + + // Bump up the crash count of any services currently running in the proc. + 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 boolean quickCrash = crashTime != null - && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL; - if (quickCrash || isProcOverCrashLimit(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); + 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; + } } - 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; + } 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; } } - 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 (data != null && tryAgain) { - data.isRestartableForService = true; - } + 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. - final WindowProcessController proc = app.getWindowProcessController(); - if (proc.isHomeProcess() && proc.hasActivities() && (app.info.flags & FLAG_SYSTEM) == 0) { - proc.clearPackagePreferredForHomeActivities(); - } + // If the crashing process is what we consider to be the "home process" and it has been + // replaced by a third-party app, clear the package preferred activities from packages + // with a home activity running in the process to prevent a repeatedly crashing app + // from blocking the user to manually clear the list. + final WindowProcessController proc = app.getWindowProcessController(); + if (proc.isHomeProcess() && proc.hasActivities() + && (app.info.flags & FLAG_SYSTEM) == 0) { + proc.clearPackagePreferredForHomeActivities(); + } - if (!app.isolated) { - // XXX Can't keep track of crash times for isolated processes, - // because they don't have a persistent identity. - mProcessCrashTimes.put(app.processName, app.uid, now); - mProcessCrashTimesPersistent.put(app.processName, app.uid, now); - updateProcessCrashCount(app.processName, app.uid, now); + 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 (app.crashHandler != null) mService.mHandler.post(app.crashHandler); return true; } - private void updateProcessCrashCount(String processName, int uid, long now) { + @GuardedBy("mBadProcessLock") + private void updateProcessCrashCountLBp(String processName, int uid, long now) { Pair<Long, Integer> count = mProcessCrashCounts.get(processName, uid); if (count == null || (count.first + PROCESS_CRASH_COUNT_RESET_INTERVAL) < now) { count = new Pair<>(now, 1); @@ -895,7 +936,8 @@ class AppErrors { mProcessCrashCounts.put(processName, uid, count); } - private boolean isProcOverCrashLimit(ProcessRecord app, long now) { + @GuardedBy("mBadProcessLock") + private boolean isProcOverCrashLimitLBp(ProcessRecord app, long now) { final Pair<Long, Integer> crashCount = mProcessCrashCounts.get(app.processName, app.uid); return !app.isolated && crashCount != null && now < (crashCount.first + PROCESS_CRASH_COUNT_RESET_INTERVAL) @@ -938,41 +980,44 @@ class AppErrors { return; } Long crashShowErrorTime = null; - if (!proc.isolated) { - crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.processName, - proc.uid); - } - final boolean showFirstCrash = Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0; - final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser( - mContext.getContentResolver(), - Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, - 0, - mService.mUserController.getCurrentUserId()) != 0; - final boolean crashSilenced = mAppsNotReportingCrashes != null && - mAppsNotReportingCrashes.contains(proc.info.packageName); - final long now = SystemClock.uptimeMillis(); - final boolean shouldThottle = crashShowErrorTime != null - && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL; - if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground) - && !crashSilenced && !shouldThottle - && (showFirstCrash || showFirstCrashDevOption || data.repeating)) { - proc.getDialogController().showCrashDialogs(data); + synchronized (mBadProcessLock) { if (!proc.isolated) { - mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now); + crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.processName, + proc.uid); } - } else { - // The device is asleep, so just pretend that the user - // saw a crash dialog and hit "force quit". - if (res != null) { - res.set(AppErrorDialog.CANT_SHOW); + final boolean showFirstCrash = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0; + final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, + 0, + mService.mUserController.getCurrentUserId()) != 0; + final boolean crashSilenced = mAppsNotReportingCrashes != null + && mAppsNotReportingCrashes.contains(proc.info.packageName); + final long now = SystemClock.uptimeMillis(); + final boolean shouldThottle = crashShowErrorTime != null + && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL; + if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground) + && !crashSilenced && !shouldThottle + && (showFirstCrash || showFirstCrashDevOption || data.repeating)) { + proc.getDialogController().showCrashDialogs(data); + if (!proc.isolated) { + mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now); + } + } else { + // The device is asleep, so just pretend that the user + // saw a crash dialog and hit "force quit". + if (res != null) { + res.set(AppErrorDialog.CANT_SHOW); + } } } } } - private void stopReportingCrashesLocked(ProcessRecord proc) { + @GuardedBy("mBadProcessLock") + private void stopReportingCrashesLBp(ProcessRecord proc) { if (mAppsNotReportingCrashes == null) { mAppsNotReportingCrashes = new ArraySet<>(); } diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java index 20cad185d8d0..48aa8be1e2d5 100644 --- a/services/core/java/com/android/server/am/AppExitInfoTracker.java +++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java @@ -79,6 +79,7 @@ import java.util.Collections; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -159,8 +160,7 @@ public final class AppExitInfoTracker { * persistent storage. */ @VisibleForTesting - @GuardedBy("mLock") - boolean mAppExitInfoLoaded = false; + AtomicBoolean mAppExitInfoLoaded = new AtomicBoolean(); /** * Temporary list being used to filter/sort intermediate results in {@link #getExitInfo}. @@ -273,51 +273,45 @@ public final class AppExitInfoTracker { return; } - synchronized (mLock) { - if (!mAppExitInfoLoaded) { - return; - } - mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecordLocked(app)) - .sendToTarget(); + if (!mAppExitInfoLoaded.get()) { + return; } + mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecord(app)) + .sendToTarget(); } void scheduleNoteAppKill(final ProcessRecord app, final @Reason int reason, final @SubReason int subReason, final String msg) { - synchronized (mLock) { - if (!mAppExitInfoLoaded) { - return; - } - if (app == null || app.info == null) { - return; - } - - ApplicationExitInfo raw = obtainRawRecordLocked(app); - raw.setReason(reason); - raw.setSubReason(subReason); - raw.setDescription(msg); - mKillHandler.obtainMessage(KillHandler.MSG_APP_KILL, raw).sendToTarget(); + if (!mAppExitInfoLoaded.get()) { + return; + } + if (app == null || app.info == null) { + return; } + + ApplicationExitInfo raw = obtainRawRecord(app); + raw.setReason(reason); + raw.setSubReason(subReason); + raw.setDescription(msg); + mKillHandler.obtainMessage(KillHandler.MSG_APP_KILL, raw).sendToTarget(); } void scheduleNoteAppKill(final int pid, final int uid, final @Reason int reason, final @SubReason int subReason, final String msg) { - synchronized (mLock) { - if (!mAppExitInfoLoaded) { - return; - } - ProcessRecord app; - synchronized (mService.mPidsSelfLocked) { - app = mService.mPidsSelfLocked.get(pid); - } - if (app == null) { - if (DEBUG_PROCESSES) { - Slog.w(TAG, "Skipping saving the kill reason for pid " + pid - + "(uid=" + uid + ") since its process record is not found"); - } - } else { - scheduleNoteAppKill(app, reason, subReason, msg); + if (!mAppExitInfoLoaded.get()) { + return; + } + ProcessRecord app; + synchronized (mService.mPidsSelfLocked) { + app = mService.mPidsSelfLocked.get(pid); + } + if (app == null) { + if (DEBUG_PROCESSES) { + Slog.w(TAG, "Skipping saving the kill reason for pid " + pid + + "(uid=" + uid + ") since its process record is not found"); } + } else { + scheduleNoteAppKill(app, reason, subReason, msg); } } @@ -414,7 +408,7 @@ public final class AppExitInfoTracker { @GuardedBy("mLock") private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw) { - if (!mAppExitInfoLoaded) { + if (!mAppExitInfoLoaded.get()) { Slog.w(TAG, "Skipping saving the exit info due to ongoing loading from storage"); return null; } @@ -627,9 +621,7 @@ public final class AppExitInfoTracker { @VisibleForTesting void loadExistingProcessExitInfo() { if (!mProcExitInfoFile.canRead()) { - synchronized (mLock) { - mAppExitInfoLoaded = true; - } + mAppExitInfoLoaded.set(true); return; } @@ -665,7 +657,7 @@ public final class AppExitInfoTracker { } synchronized (mLock) { pruneAnrTracesIfNecessaryLocked(); - mAppExitInfoLoaded = true; + mAppExitInfoLoaded.set(true); } } @@ -834,7 +826,7 @@ public final class AppExitInfoTracker { container.addExitInfoLocked(info); } - @GuardedBy("mLocked") + @GuardedBy("mLock") private void forEachPackageLocked( BiFunction<String, SparseArray<AppExitInfoContainer>, Integer> callback) { if (callback != null) { @@ -859,7 +851,7 @@ public final class AppExitInfoTracker { } } - @GuardedBy("mLocked") + @GuardedBy("mLock") private void removePackageLocked(String packageName, int uid, boolean removeUid, int userId) { if (removeUid) { mActiveAppStateSummary.remove(uid); @@ -896,7 +888,7 @@ public final class AppExitInfoTracker { } } - @GuardedBy("mLocked") + @GuardedBy("mLock") private void removeByUserIdLocked(final int userId) { if (userId == UserHandle.USER_ALL) { mData.getMap().clear(); @@ -922,35 +914,36 @@ public final class AppExitInfoTracker { } @VisibleForTesting - @GuardedBy("mLock") - ApplicationExitInfo obtainRawRecordLocked(ProcessRecord app) { + ApplicationExitInfo obtainRawRecord(ProcessRecord app) { ApplicationExitInfo info = mRawRecordsPool.acquire(); if (info == null) { info = new ApplicationExitInfo(); } - final int definingUid = app.hostingRecord != null ? app.hostingRecord.getDefiningUid() : 0; - info.setPid(app.pid); - 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.setPackageName(app.info.packageName); - info.setPackageList(app.getPackageList()); - info.setReason(ApplicationExitInfo.REASON_UNKNOWN); - info.setStatus(0); - info.setImportance(procStateToImportance(app.setProcState)); - info.setPss(app.lastPss); - info.setRss(app.mLastRss); - info.setTimestamp(System.currentTimeMillis()); + synchronized (mService) { + final int definingUid = app.hostingRecord != null + ? app.hostingRecord.getDefiningUid() : 0; + info.setPid(app.pid); + 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.setPackageName(app.info.packageName); + info.setPackageList(app.getPackageList()); + info.setReason(ApplicationExitInfo.REASON_UNKNOWN); + info.setStatus(0); + info.setImportance(procStateToImportance(app.setProcState)); + info.setPss(app.mProfile.getLastPss()); + info.setRss(app.mProfile.getLastRss()); + info.setTimestamp(System.currentTimeMillis()); + } return info; } @VisibleForTesting - @GuardedBy("mLock") - void recycleRawRecordLocked(ApplicationExitInfo info) { + void recycleRawRecord(ApplicationExitInfo info) { info.setProcessName(null); info.setDescription(null); info.setPackageList(null); @@ -1549,16 +1542,16 @@ public final class AppExitInfoTracker { ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj; synchronized (mLock) { handleNoteProcessDiedLocked(raw); - recycleRawRecordLocked(raw); } + recycleRawRecord(raw); } break; case MSG_APP_KILL: { ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj; synchronized (mLock) { handleNoteAppKillLocked(raw); - recycleRawRecordLocked(raw); } + recycleRawRecord(raw); } break; default: diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java index dac5325f239d..96e6f6ea4476 100644 --- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java @@ -63,8 +63,8 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli ? data.aInfo.loadLabel(context.getPackageManager()) : null; CharSequence name2 = null; - if ((mProc.pkgList.size() == 1) && - (name2=context.getPackageManager().getApplicationLabel(mProc.info)) != null) { + if ((mProc.getPkgList().size() == 1) + && (name2 = context.getPackageManager().getApplicationLabel(mProc.info)) != null) { if (name1 != null) { resid = com.android.internal.R.string.anr_activity_application; } else { diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index cace260c3c26..3df058c8ed82 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -29,8 +29,22 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS; 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.DUMP_MEM_OOM_ADJ; +import static com.android.server.am.ActivityManagerService.DUMP_MEM_OOM_LABEL; +import static com.android.server.am.ActivityManagerService.GC_BACKGROUND_PROCESSES_MSG; +import static com.android.server.am.ActivityManagerService.KSM_SHARED; +import static com.android.server.am.ActivityManagerService.KSM_SHARING; +import static com.android.server.am.ActivityManagerService.KSM_UNSHARED; +import static com.android.server.am.ActivityManagerService.KSM_VOLATILE; +import static com.android.server.am.ActivityManagerService.REPORT_MEM_USAGE_MSG; +import static com.android.server.am.ActivityManagerService.appendBasicMemEntry; +import static com.android.server.am.ActivityManagerService.appendMemBucket; +import static com.android.server.am.ActivityManagerService.appendMemInfo; +import static com.android.server.am.ActivityManagerService.getKsmInfo; +import static com.android.server.am.ActivityManagerService.stringifyKBSize; import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; +import static com.android.server.wm.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD; import android.annotation.BroadcastBehavior; import android.annotation.NonNull; @@ -75,16 +89,19 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.MemInfoReader; import com.android.server.am.LowMemDetector.MemFactor; -import com.android.server.am.ProcessList.ProcStateMemTracker; import com.android.server.utils.PriorityDump; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -162,8 +179,8 @@ public class AppProfiler { /** * Processes we want to collect PSS data from. */ - @GuardedBy("mService") - private final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>(); + @GuardedBy("mProfilerLock") + private final ArrayList<ProcessProfileRecord> mPendingPssProfiles = new ArrayList<>(); /** * Depth of overlapping activity-start PSS deferral notes @@ -173,14 +190,14 @@ public class AppProfiler { /** * Last time we requested PSS data of all processes. */ - @GuardedBy("mService") + @GuardedBy("mProfilerLock") private long mLastFullPssTime = SystemClock.uptimeMillis(); /** * If set, the next time we collect PSS data we should do a full collection * with data from native processes and the kernel. */ - @GuardedBy("mService") + @GuardedBy("mProfilerLock") private boolean mFullPssPending = false; /** @@ -188,8 +205,7 @@ public class AppProfiler { * much more rapidly to try to collect better data when the tests are rapidly * running through apps. */ - @GuardedBy("mService") - private boolean mTestPssMode = false; + private volatile boolean mTestPssMode = false; @GuardedBy("mService") private final LowMemDetector mLowMemDetector; @@ -233,21 +249,53 @@ public class AppProfiler { private long mLowRamStartTime = 0; /** + * Last time we report a memory usage. + */ + @GuardedBy("mService") + private long mLastMemUsageReportTime = 0; + + /** + * List of processes that should gc as soon as things are idle. + */ + @GuardedBy("mProfilerLock") + private final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<>(); + + /** * Stores a map of process name -> agent string. When a process is started and mAgentAppMap * is not null, this map is checked and the mapped agent installed during bind-time. Note: * A non-null agent in mProfileInfo overrides this. */ + @GuardedBy("mProfilerLock") private @Nullable Map<String, String> mAppAgentMap = null; + @GuardedBy("mProfilerLock") private int mProfileType = 0; + + @GuardedBy("mProfilerLock") + private final ProfileData mProfileData = new ProfileData(); + + @GuardedBy("mProfilerLock") private final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>(); + + @GuardedBy("mProfilerLock") private String mMemWatchDumpProcName; + + @GuardedBy("mProfilerLock") private Uri mMemWatchDumpUri; + + @GuardedBy("mProfilerLock") private int mMemWatchDumpPid; + + @GuardedBy("mProfilerLock") private int mMemWatchDumpUid; + + @GuardedBy("mProfilerLock") private boolean mMemWatchIsUserInitiated; + @GuardedBy("mService") boolean mHasHomeProcess; + + @GuardedBy("mService") boolean mHasPreviousProcess; /** @@ -263,8 +311,7 @@ public class AppProfiler { private final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true); private final CountDownLatch mProcessCpuInitLatch = new CountDownLatch(1); - private long mLastWriteTime = 0; - private final ProfileData mProfileData = new ProfileData(); + private volatile long mLastWriteTime = 0; /** * Runtime CPU use collection thread. This object's lock is used to @@ -276,6 +323,15 @@ public class AppProfiler { private final Handler mBgHandler; /** + * The lock to guard some of the profiling data here and {@link ProcessProfileRecord}. + * + * <p> + * The function suffix with this lock would be "-LPf" (Locked with Profiler lock). + * </p> + */ + final Object mProfilerLock = new Object(); + + /** * Observe DeviceConfig changes to the PSS calculation interval */ private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener = @@ -359,7 +415,7 @@ public class AppProfiler { private void collectPssInBackground() { long start = SystemClock.uptimeMillis(); MemInfoReader memInfo = null; - synchronized (mService) { + synchronized (mProfilerLock) { if (mFullPssPending) { mFullPssPending = false; memInfo = new MemInfoReader(); @@ -404,65 +460,67 @@ public class AppProfiler { int num = 0; long[] tmp = new long[3]; do { - ProcessRecord proc; + ProcessProfileRecord profile; int procState; int statType; int pid = -1; long lastPssTime; - synchronized (mService) { - if (mPendingPssProcesses.size() <= 0) { + synchronized (mProfilerLock) { + if (mPendingPssProfiles.size() <= 0) { if (mTestPssMode || DEBUG_PSS) { Slog.d(TAG_PSS, "Collected pss of " + num + " processes in " + (SystemClock.uptimeMillis() - start) + "ms"); } - mPendingPssProcesses.clear(); + mPendingPssProfiles.clear(); return; } - proc = mPendingPssProcesses.remove(0); - procState = proc.pssProcState; - statType = proc.pssStatType; - lastPssTime = proc.lastPssTime; + profile = mPendingPssProfiles.remove(0); + procState = profile.getPssProcState(); + statType = profile.getPssStatType(); + lastPssTime = profile.getLastPssTime(); long now = SystemClock.uptimeMillis(); - if (proc.thread != null && procState == proc.setProcState + if (profile.getThread() != null && procState == profile.getSetProcState() && (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE) < now) { - pid = proc.pid; + pid = profile.getPid(); } else { - abortNextPssTime(proc.procStateMemTracker); + profile.abortNextPssTime(); if (DEBUG_PSS) { Slog.d(TAG_PSS, "Skipped pss collection of " + pid + ": still need " + (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE - now) + "ms until safe"); } - proc = null; + profile = null; pid = 0; } } - if (proc != null) { + if (profile != null) { long startTime = SystemClock.currentThreadTimeMillis(); // skip background PSS calculation of apps that are capturing // camera imagery - final boolean usingCamera = mService.isCameraActiveForUid(proc.uid); + final boolean usingCamera = mService.isCameraActiveForUid(profile.mApp.uid); long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null); long endTime = SystemClock.currentThreadTimeMillis(); - synchronized (mService) { - if (pss != 0 && proc.thread != null && proc.setProcState == procState - && proc.pid == pid && proc.lastPssTime == lastPssTime) { + synchronized (mProfilerLock) { + if (pss != 0 && profile.getThread() != null + && profile.getSetProcState() == procState + && profile.getPid() == pid && profile.getLastPssTime() == lastPssTime) { num++; - commitNextPssTime(proc.procStateMemTracker); - recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], tmp[2], + profile.commitNextPssTime(); + recordPssSampleLPf(profile, procState, pss, tmp[0], tmp[1], tmp[2], statType, endTime - startTime, SystemClock.uptimeMillis()); } else { - abortNextPssTime(proc.procStateMemTracker); + profile.abortNextPssTime(); if (DEBUG_PSS) { Slog.d(TAG_PSS, "Skipped pss collection of " + pid - + ": " + (proc.thread == null ? "NO_THREAD " : "") + + ": " + (profile.getThread() == null ? "NO_THREAD " : "") + (usingCamera ? "CAMERA " : "") - + (proc.pid != pid ? "PID_CHANGED " : "") + + (profile.getPid() != pid ? "PID_CHANGED " : "") + " initState=" + procState + " curState=" - + proc.setProcState + " " - + (proc.lastPssTime != lastPssTime ? "TIME_CHANGED" : "")); + + profile.getSetProcState() + " " + + (profile.getLastPssTime() != lastPssTime + ? "TIME_CHANGED" : "")); } } } @@ -470,73 +528,61 @@ public class AppProfiler { } while (true); } - private static void commitNextPssTime(ProcStateMemTracker tracker) { - if (tracker.mPendingMemState >= 0) { - tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState; - tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor; - tracker.mTotalHighestMem = tracker.mPendingHighestMemState; - tracker.mPendingMemState = -1; - } - } - - private static void abortNextPssTime(ProcStateMemTracker tracker) { - tracker.mPendingMemState = -1; - } - - @GuardedBy("mService") - void updateNextPssTimeLocked(int procState, ProcessRecord app, long now, boolean forceUpdate) { + @GuardedBy("mProfilerLock") + void updateNextPssTimeLPf(int procState, ProcessProfileRecord profile, long now, + boolean forceUpdate) { if (!forceUpdate) { - if (now <= app.nextPssTime - && now <= Math.max(app.lastPssTime + ProcessList.PSS_MAX_INTERVAL, - app.lastStateTime + ProcessList.minTimeFromStateChange(mTestPssMode))) { + if (now <= profile.getNextPssTime() && now <= Math.max(profile.getLastPssTime() + + ProcessList.PSS_MAX_INTERVAL, profile.getLastStateTime() + + ProcessList.minTimeFromStateChange(mTestPssMode))) { // update is not due, ignore it. return; } - if (!requestPssLocked(app, app.setProcState)) { + if (!requestPssLPf(profile, procState)) { return; } } - app.nextPssTime = ProcessList.computeNextPssTime(procState, app.procStateMemTracker, - mTestPssMode, mService.mAtmInternal.isSleeping(), now); + profile.setNextPssTime(profile.computeNextPssTime(procState, + mTestPssMode, mService.mAtmInternal.isSleeping(), now)); } /** * Record new PSS sample for a process. */ - @GuardedBy("mService") - private void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, + @GuardedBy("mProfilerLock") + private void recordPssSampleLPf(ProcessProfileRecord profile, int procState, long pss, long uss, long swapPss, long rss, int statType, long pssDuration, long now) { - EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024, + final ProcessRecord proc = profile.mApp; + EventLogTags.writeAmPss( + profile.getPid(), proc.uid, proc.processName, pss * 1024, uss * 1024, swapPss * 1024, rss * 1024, statType, procState, pssDuration); - proc.lastPssTime = now; - synchronized (mService.mProcessStats.mLock) { - proc.baseProcessTracker.addPss( - pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList); - } - for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); + profile.setLastPssTime(now); + profile.addPss(pss, uss, rss, true, statType, pssDuration); + proc.getPkgList().forEachPackageProcessStats(holder -> { FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, proc.info.uid, holder.state.getName(), holder.state.getPackage(), - pss, uss, rss, statType, pssDuration, + pss, uss, rss, + statType, pssDuration, holder.appVersion); - } + }); if (DEBUG_PSS) { Slog.d(TAG_PSS, - "pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss + "pss of " + proc.toShortString() + ": " + pss + + " lastPss=" + profile.getLastPss() + " state=" + ProcessList.makeProcStateString(procState)); } - if (proc.initialIdlePss == 0) { - proc.initialIdlePss = pss; + if (profile.getInitialIdlePss() == 0) { + profile.setInitialIdlePss(pss); } - proc.lastPss = pss; - proc.lastSwapPss = swapPss; + profile.setLastPss(pss); + profile.setLastSwapPss(swapPss); if (procState >= ActivityManager.PROCESS_STATE_HOME) { - proc.lastCachedPss = pss; - proc.lastCachedSwapPss = swapPss; + profile.setLastCachedPss(pss); + profile.setLastCachedSwapPss(swapPss); } - proc.mLastRss = rss; + profile.setLastRss(rss); final SparseArray<Pair<Long, String>> watchUids = mMemWatchProcesses.getMap().get(proc.processName); @@ -551,7 +597,8 @@ public class AppProfiler { } } if (check != null) { - if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) { + if ((pss * 1024) >= check && profile.getThread() != null + && mMemWatchDumpProcName == null) { boolean isDebuggable = Build.IS_DEBUGGABLE; if (!isDebuggable) { if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { @@ -560,7 +607,7 @@ public class AppProfiler { } if (isDebuggable) { Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + "; reporting"); - startHeapDumpLocked(proc, false); + startHeapDumpLPf(profile, false); } else { Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + ", but debugging not enabled"); @@ -570,12 +617,13 @@ public class AppProfiler { } private final class RecordPssRunnable implements Runnable { - private final ProcessRecord mProc; + private final ProcessProfileRecord mProfile; private final Uri mDumpUri; private final ContentResolver mContentResolver; - RecordPssRunnable(ProcessRecord proc, Uri dumpUri, ContentResolver contentResolver) { - mProc = proc; + RecordPssRunnable(ProcessProfileRecord profile, Uri dumpUri, + ContentResolver contentResolver) { + mProfile = profile; mDumpUri = dumpUri; mContentResolver = contentResolver; } @@ -583,12 +631,12 @@ public class AppProfiler { @Override public void run() { try (ParcelFileDescriptor fd = mContentResolver.openFileDescriptor(mDumpUri, "rw")) { - IApplicationThread thread = mProc.thread; + IApplicationThread thread = mProfile.getThread(); if (thread != null) { try { if (DEBUG_PSS) { Slog.d(TAG_PSS, "Requesting dump heap from " - + mProc + " to " + mDumpUri.getPath()); + + mProfile.mApp + " to " + mDumpUri.getPath()); } thread.dumpHeap(/* managed= */ true, /* mallocInfo= */ false, /* runGc= */ false, @@ -601,16 +649,17 @@ public class AppProfiler { Slog.e(TAG, "Failed to dump heap", e); // Need to clear the heap dump variables, otherwise no further heap dumps will be // attempted. - abortHeapDump(mProc.processName); + abortHeapDump(mProfile.mApp.processName); } } } - @GuardedBy("mService") - void startHeapDumpLocked(ProcessRecord proc, boolean isUserInitiated) { + @GuardedBy("mProfilerLock") + void startHeapDumpLPf(ProcessProfileRecord profile, boolean isUserInitiated) { + final ProcessRecord proc = profile.mApp; mMemWatchDumpProcName = proc.processName; mMemWatchDumpUri = makeHeapDumpUri(proc.processName); - mMemWatchDumpPid = proc.pid; + mMemWatchDumpPid = profile.getPid(); mMemWatchDumpUid = proc.uid; mMemWatchIsUserInitiated = isUserInitiated; Context ctx; @@ -621,11 +670,11 @@ public class AppProfiler { throw new RuntimeException("android package not found."); } BackgroundThread.getHandler().post( - new RecordPssRunnable(proc, mMemWatchDumpUri, ctx.getContentResolver())); + new RecordPssRunnable(profile, mMemWatchDumpUri, ctx.getContentResolver())); } void dumpHeapFinished(String path, int callerPid) { - synchronized (mService) { + synchronized (mProfilerLock) { if (callerPid != mMemWatchDumpPid) { Slog.w(TAG, "dumpHeapFinished: Calling pid " + Binder.getCallingPid() + " does not match last pid " + mMemWatchDumpPid); @@ -651,7 +700,7 @@ public class AppProfiler { final long memLimit; final String reportPackage; final boolean isUserInitiated; - synchronized (mService) { + synchronized (mProfilerLock) { uid = mMemWatchDumpUid; procName = mMemWatchDumpProcName; Pair<Long, String> val = mMemWatchProcesses.get(procName, uid); @@ -695,7 +744,7 @@ public class AppProfiler { void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize, String reportPackage) { - synchronized (mService) { + synchronized (mProfilerLock) { if (maxMemSize > 0) { mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage)); } else { @@ -717,7 +766,7 @@ public class AppProfiler { void handleAbortDumpHeap(String procName) { if (procName != null) { - synchronized (mService) { + synchronized (mProfilerLock) { if (procName.equals(mMemWatchDumpProcName)) { mMemWatchDumpProcName = null; mMemWatchDumpUri = null; @@ -736,24 +785,24 @@ public class AppProfiler { /** * Schedule PSS collection of a process. */ - @GuardedBy("mService") - private boolean requestPssLocked(ProcessRecord proc, int procState) { - if (mPendingPssProcesses.contains(proc)) { + @GuardedBy("mProfilerLock") + private boolean requestPssLPf(ProcessProfileRecord profile, int procState) { + if (mPendingPssProfiles.contains(profile)) { return false; } - if (mPendingPssProcesses.size() == 0) { + if (mPendingPssProfiles.size() == 0) { final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0) ? mPssDeferralTime : 0; if (DEBUG_PSS && deferral > 0) { - Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by " + Slog.d(TAG_PSS, "requestPssLPf() deferring PSS request by " + deferral + " ms"); } mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, deferral); } - if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc); - proc.pssProcState = procState; - proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE; - mPendingPssProcesses.add(proc); + if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + profile.mApp); + profile.setPssProcState(procState); + profile.setPssStatType(ProcessStats.ADD_PSS_INTERNAL_SINGLE); + mPendingPssProfiles.add(profile); return true; } @@ -761,9 +810,9 @@ public class AppProfiler { * Re-defer a posted PSS collection pass, if one exists. Assumes deferral is * currently active policy when called. */ - @GuardedBy("mService") - private void deferPssIfNeededLocked() { - if (mPendingPssProcesses.size() > 0) { + @GuardedBy("mProfilerLock") + private void deferPssIfNeededLPf() { + if (mPendingPssProfiles.size() > 0) { mBgHandler.removeMessages(BgHandler.COLLECT_PSS_BG_MSG); mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, mPssDeferralTime); } @@ -774,7 +823,9 @@ public class AppProfiler { if (DEBUG_PSS) { Slog.d(TAG_PSS, "Deferring PSS collection for activity start"); } - deferPssIfNeededLocked(); + synchronized (mProfilerLock) { + deferPssIfNeededLPf(); + } mActivityStartingNesting.getAndIncrement(); mBgHandler.sendEmptyMessageDelayed(BgHandler.STOP_DEFERRING_PSS_MSG, mPssDeferralTime); } @@ -803,40 +854,45 @@ public class AppProfiler { */ @GuardedBy("mService") void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) { - if (!always) { - if (now < (mLastFullPssTime - + (memLowered ? mService.mConstants.FULL_PSS_LOWERED_INTERVAL - : mService.mConstants.FULL_PSS_MIN_INTERVAL))) { - return; + synchronized (mProfilerLock) { + if (!always) { + if (now < (mLastFullPssTime + + (memLowered ? mService.mConstants.FULL_PSS_LOWERED_INTERVAL + : mService.mConstants.FULL_PSS_MIN_INTERVAL))) { + return; + } } - } - if (DEBUG_PSS) { - Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered); - } - mLastFullPssTime = now; - mFullPssPending = true; - for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) { - abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker); - } - mPendingPssProcesses.ensureCapacity(mService.mProcessList.getLruSizeLocked()); - mPendingPssProcesses.clear(); - for (int i = mService.mProcessList.getLruSizeLocked() - 1; i >= 0; i--) { - ProcessRecord app = mService.mProcessList.mLruProcesses.get(i); - if (app.thread == null || app.getCurProcState() == PROCESS_STATE_NONEXISTENT) { - continue; + if (DEBUG_PSS) { + Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered); } - if (memLowered || (always && now - > app.lastStateTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE) - || now > (app.lastStateTime + ProcessList.PSS_ALL_INTERVAL)) { - app.pssProcState = app.setProcState; - app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL - : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM; - updateNextPssTimeLocked(app.getCurProcState(), app, now, true); - mPendingPssProcesses.add(app); + mLastFullPssTime = now; + mFullPssPending = true; + for (int i = mPendingPssProfiles.size() - 1; i >= 0; i--) { + mPendingPssProfiles.get(i).abortNextPssTime(); + } + mPendingPssProfiles.ensureCapacity(mService.mProcessList.getLruSizeLocked()); + mPendingPssProfiles.clear(); + for (int i = mService.mProcessList.getLruSizeLocked() - 1; i >= 0; i--) { + ProcessRecord app = mService.mProcessList.mLruProcesses.get(i); + final ProcessProfileRecord profile = app.mProfile; + if (profile.getThread() == null + || profile.getSetProcState() == PROCESS_STATE_NONEXISTENT) { + return; + } + final long lastStateTime = profile.getLastStateTime(); + if (memLowered || (always + && now > lastStateTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE) + || now > (lastStateTime + ProcessList.PSS_ALL_INTERVAL)) { + profile.setPssProcState(profile.getSetProcState()); + profile.setPssStatType(always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL + : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM); + updateNextPssTimeLPf(profile.getSetProcState(), profile, now, true); + mPendingPssProfiles.add(profile); + } + } + if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) { + mBgHandler.sendEmptyMessage(BgHandler.COLLECT_PSS_BG_MSG); } - } - if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) { - mBgHandler.sendEmptyMessage(BgHandler.COLLECT_PSS_BG_MSG); } } @@ -851,8 +907,7 @@ public class AppProfiler { } } - @GuardedBy("mService") - boolean getTestPssModeLocked() { + boolean getTestPssMode() { return mTestPssMode; } @@ -969,14 +1024,16 @@ public class AppProfiler { if (factor < minFactor) factor = minFactor; int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; for (int i = 0; i < numOfLru; i++) { - ProcessRecord app = mService.mProcessList.mLruProcesses.get(i); + final ProcessRecord app = mService.mProcessList.mLruProcesses.get(i); + final ProcessProfileRecord profile = app.mProfile; + final int trimMemoryLevel = profile.getTrimMemoryLevel(); if (allChanged || app.procStateChanged) { mService.setProcessTrackerStateLocked(app, trackerMemFactor, now); app.procStateChanged = false; } if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME && !app.killedByAm) { - if (app.trimMemoryLevel < curLevel && app.thread != null) { + if (trimMemoryLevel < curLevel && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { Slog.v(TAG_OOM_ADJ, @@ -987,7 +1044,7 @@ public class AppProfiler { } catch (RemoteException e) { } } - app.trimMemoryLevel = curLevel; + profile.setTrimMemoryLevel(curLevel); step++; if (step >= factor) { step = 0; @@ -1002,7 +1059,7 @@ public class AppProfiler { } } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT && !app.killedByAm) { - if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND + if (trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { @@ -1015,15 +1072,15 @@ public class AppProfiler { } catch (RemoteException e) { } } - app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; + profile.setTrimMemoryLevel(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); } else { if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND - || app.systemNoUi) && app.hasPendingUiClean()) { + || app.systemNoUi) && 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 (app.trimMemoryLevel < level && app.thread != null) { + if (trimMemoryLevel < level && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui " @@ -1033,9 +1090,9 @@ public class AppProfiler { } catch (RemoteException e) { } } - app.setPendingUiClean(false); + profile.setPendingUiClean(false); } - if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) { + if (trimMemoryLevel < fgTrimLevel && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName @@ -1045,7 +1102,7 @@ public class AppProfiler { } catch (RemoteException e) { } } - app.trimMemoryLevel = fgTrimLevel; + profile.setTrimMemoryLevel(fgTrimLevel); } } } else { @@ -1054,14 +1111,15 @@ public class AppProfiler { mLowRamStartTime = 0; } for (int i = 0; i < numOfLru; i++) { - ProcessRecord app = mService.mProcessList.mLruProcesses.get(i); + final ProcessRecord app = mService.mProcessList.mLruProcesses.get(i); + final ProcessProfileRecord profile = app.mProfile; if (allChanged || app.procStateChanged) { mService.setProcessTrackerStateLocked(app, trackerMemFactor, now); app.procStateChanged = false; } if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND - || app.systemNoUi) && app.hasPendingUiClean()) { - if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN + || app.systemNoUi) && profile.hasPendingUiClean()) { + if (profile.getTrimMemoryLevel() < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) { @@ -1074,9 +1132,9 @@ public class AppProfiler { } catch (RemoteException e) { } } - app.setPendingUiClean(false); + profile.setPendingUiClean(false); } - app.trimMemoryLevel = 0; + profile.setTrimMemoryLevel(0); } } return allChanged; @@ -1087,25 +1145,459 @@ public class AppProfiler { return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now - mLowRamStartTime) : 0); } + /** + * Ask a given process to GC right now. + */ + @GuardedBy("mProfilerLock") + private void performAppGcLPf(ProcessRecord app) { + try { + final ProcessProfileRecord profile = app.mProfile; + profile.setLastRequestedGc(SystemClock.uptimeMillis()); + IApplicationThread thread = profile.getThread(); + if (thread != null) { + if (profile.getReportLowMemory()) { + profile.setReportLowMemory(false); + thread.scheduleLowMemory(); + } else { + thread.processInBackground(); + } + } + } catch (Exception e) { + // whatever. + } + } + + /** + * Perform GCs on all processes that are waiting for it, but only + * if things are idle. + */ + @GuardedBy("mProfilerLock") + private void performAppGcsLPf() { + if (mProcessesToGc.size() <= 0) { + return; + } + while (mProcessesToGc.size() > 0) { + final ProcessRecord proc = mProcessesToGc.remove(0); + final ProcessProfileRecord profile = proc.mProfile; + if (profile.getCurRawAdj() > ProcessList.PERCEPTIBLE_APP_ADJ + || profile.getReportLowMemory()) { + if ((profile.getLastRequestedGc() + mService.mConstants.GC_MIN_INTERVAL) + <= SystemClock.uptimeMillis()) { + // To avoid spamming the system, we will GC processes one + // at a time, waiting a few seconds between each. + performAppGcLPf(proc); + scheduleAppGcsLPf(); + return; + } else { + // It hasn't been long enough since we last GCed this + // process... put it in the list to wait for its time. + addProcessToGcListLPf(proc); + break; + } + } + } + + scheduleAppGcsLPf(); + } + + /** + * If all looks good, perform GCs on all processes waiting for them. + */ @GuardedBy("mService") - private void stopProfilerLocked(ProcessRecord proc, int profileType) { + final void performAppGcsIfAppropriateLocked() { + synchronized (mProfilerLock) { + if (mService.canGcNowLocked()) { + performAppGcsLPf(); + return; + } + // Still not idle, wait some more. + scheduleAppGcsLPf(); + } + } + + /** + * Schedule the execution of all pending app GCs. + */ + @GuardedBy("mProfilerLock") + final void scheduleAppGcsLPf() { + mService.mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG); + + if (mProcessesToGc.size() > 0) { + // Schedule a GC for the time to the next process. + ProcessRecord proc = mProcessesToGc.get(0); + Message msg = mService.mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG); + + long when = proc.mProfile.getLastRequestedGc() + mService.mConstants.GC_MIN_INTERVAL; + long now = SystemClock.uptimeMillis(); + if (when < (now + mService.mConstants.GC_TIMEOUT)) { + when = now + mService.mConstants.GC_TIMEOUT; + } + mService.mHandler.sendMessageAtTime(msg, when); + } + } + + /** + * Add a process to the array of processes waiting to be GCed. Keeps the + * list in sorted order by the last GC time. The process can't already be + * on the list. + */ + @GuardedBy("mProfilerLock") + private void addProcessToGcListLPf(ProcessRecord proc) { + boolean added = false; + for (int i = mProcessesToGc.size() - 1; i >= 0; i--) { + if (mProcessesToGc.get(i).mProfile.getLastRequestedGc() + < proc.mProfile.getLastRequestedGc()) { + added = true; + mProcessesToGc.add(i + 1, proc); + break; + } + } + if (!added) { + mProcessesToGc.add(0, proc); + } + } + + @GuardedBy("mService") + final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) { + // 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()) { + boolean doReport = Build.IS_DEBUGGABLE; + final long now = SystemClock.uptimeMillis(); + if (doReport) { + if (now < (mLastMemUsageReportTime + 5 * 60 * 1000)) { + doReport = false; + } else { + mLastMemUsageReportTime = now; + } + } + final int lruSize = mService.mProcessList.getLruSizeLocked(); + 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) { + return; + } + if (memInfos != null) { + memInfos.add(new ProcessMemInfo(rec.processName, rec.pid, + rec.setAdj, rec.setProcState, + rec.adjType, rec.makeAdjReason())); + } + final ProcessProfileRecord profile = rec.mProfile; + if ((profile.getLastLowMemory() + mService.mConstants.GC_MIN_INTERVAL) <= now) { + // The low memory report is overriding any current + // 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) { + profile.setLastRequestedGc(0); + } else { + profile.setLastRequestedGc(profile.getLastLowMemory()); + } + profile.setReportLowMemory(true); + profile.setLastLowMemory(now); + mProcessesToGc.remove(rec); + addProcessToGcListLPf(rec); + } + } + } + if (doReport) { + Message msg = mService.mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos); + mService.mHandler.sendMessage(msg); + } + } + synchronized (mProfilerLock) { + scheduleAppGcsLPf(); + } + } + + void reportMemUsage(ArrayList<ProcessMemInfo> memInfos) { + final SparseArray<ProcessMemInfo> infoMap = new SparseArray<>(memInfos.size()); + for (int i = 0, size = memInfos.size(); i < size; i++) { + ProcessMemInfo mi = memInfos.get(i); + infoMap.put(mi.pid, mi); + } + updateCpuStatsNow(); + long[] memtrackTmp = new long[1]; + long[] swaptrackTmp = new long[2]; + // Get a list of Stats that have vsize > 0 + final List<ProcessCpuTracker.Stats> stats = getCpuStats(st -> st.vsize > 0); + final int statsCount = stats.size(); + for (int i = 0; i < statsCount; i++) { + ProcessCpuTracker.Stats st = stats.get(i); + long pss = Debug.getPss(st.pid, swaptrackTmp, memtrackTmp); + if (pss > 0) { + if (infoMap.indexOfKey(st.pid) < 0) { + ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid, + ProcessList.NATIVE_ADJ, -1, "native", null); + mi.pss = pss; + mi.swapPss = swaptrackTmp[1]; + mi.memtrack = memtrackTmp[0]; + memInfos.add(mi); + } + } + } + + long totalPss = 0; + long totalSwapPss = 0; + long totalMemtrack = 0; + for (int i = 0, size = memInfos.size(); i < size; i++) { + ProcessMemInfo mi = memInfos.get(i); + if (mi.pss == 0) { + mi.pss = Debug.getPss(mi.pid, swaptrackTmp, memtrackTmp); + mi.swapPss = swaptrackTmp[1]; + mi.memtrack = memtrackTmp[0]; + } + totalPss += mi.pss; + totalSwapPss += mi.swapPss; + totalMemtrack += mi.memtrack; + } + Collections.sort(memInfos, new Comparator<ProcessMemInfo>() { + @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) { + if (lhs.oomAdj != rhs.oomAdj) { + return lhs.oomAdj < rhs.oomAdj ? -1 : 1; + } + if (lhs.pss != rhs.pss) { + return lhs.pss < rhs.pss ? 1 : -1; + } + return 0; + } + }); + + StringBuilder tag = new StringBuilder(128); + StringBuilder stack = new StringBuilder(128); + tag.append("Low on memory -- "); + appendMemBucket(tag, totalPss, "total", false); + appendMemBucket(stack, totalPss, "total", true); + + StringBuilder fullNativeBuilder = new StringBuilder(1024); + StringBuilder shortNativeBuilder = new StringBuilder(1024); + StringBuilder fullJavaBuilder = new StringBuilder(1024); + + boolean firstLine = true; + int lastOomAdj = Integer.MIN_VALUE; + long extraNativeRam = 0; + long extraNativeMemtrack = 0; + long cachedPss = 0; + for (int i = 0, size = memInfos.size(); i < size; i++) { + ProcessMemInfo mi = memInfos.get(i); + + if (mi.oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) { + cachedPss += mi.pss; + } + + if (mi.oomAdj != ProcessList.NATIVE_ADJ + && (mi.oomAdj < ProcessList.SERVICE_ADJ + || mi.oomAdj == ProcessList.HOME_APP_ADJ + || mi.oomAdj == ProcessList.PREVIOUS_APP_ADJ)) { + if (lastOomAdj != mi.oomAdj) { + lastOomAdj = mi.oomAdj; + if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { + tag.append(" / "); + } + if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ) { + if (firstLine) { + stack.append(":"); + firstLine = false; + } + stack.append("\n\t at "); + } else { + stack.append("$"); + } + } else { + tag.append(" "); + stack.append("$"); + } + if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { + appendMemBucket(tag, mi.pss, mi.name, false); + } + appendMemBucket(stack, mi.pss, mi.name, true); + if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ + && ((i + 1) >= size || memInfos.get(i + 1).oomAdj != lastOomAdj)) { + stack.append("("); + for (int k = 0; k < DUMP_MEM_OOM_ADJ.length; k++) { + if (DUMP_MEM_OOM_ADJ[k] == mi.oomAdj) { + stack.append(DUMP_MEM_OOM_LABEL[k]); + stack.append(":"); + stack.append(DUMP_MEM_OOM_ADJ[k]); + } + } + stack.append(")"); + } + } + + appendMemInfo(fullNativeBuilder, mi); + if (mi.oomAdj == ProcessList.NATIVE_ADJ) { + // The short form only has native processes that are >= 512K. + if (mi.pss >= 512) { + appendMemInfo(shortNativeBuilder, mi); + } else { + extraNativeRam += mi.pss; + extraNativeMemtrack += mi.memtrack; + } + } else { + // Short form has all other details, but if we have collected RAM + // from smaller native processes let's dump a summary of that. + if (extraNativeRam > 0) { + appendBasicMemEntry(shortNativeBuilder, ProcessList.NATIVE_ADJ, + -1, extraNativeRam, extraNativeMemtrack, "(Other native)"); + shortNativeBuilder.append('\n'); + extraNativeRam = 0; + } + appendMemInfo(fullJavaBuilder, mi); + } + } + + fullJavaBuilder.append(" "); + ProcessList.appendRamKb(fullJavaBuilder, totalPss); + fullJavaBuilder.append(": TOTAL"); + if (totalMemtrack > 0) { + fullJavaBuilder.append(" ("); + fullJavaBuilder.append(stringifyKBSize(totalMemtrack)); + fullJavaBuilder.append(" memtrack)"); + } + fullJavaBuilder.append("\n"); + + MemInfoReader memInfo = new MemInfoReader(); + memInfo.readMemInfo(); + final long[] infos = memInfo.getRawInfo(); + + StringBuilder memInfoBuilder = new StringBuilder(1024); + Debug.getMemInfo(infos); + memInfoBuilder.append(" MemInfo: "); + memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SLAB])).append(" slab, "); + memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SHMEM])).append(" shmem, "); + memInfoBuilder.append(stringifyKBSize( + infos[Debug.MEMINFO_VM_ALLOC_USED])).append(" vm alloc, "); + memInfoBuilder.append(stringifyKBSize( + infos[Debug.MEMINFO_PAGE_TABLES])).append(" page tables "); + memInfoBuilder.append(stringifyKBSize( + infos[Debug.MEMINFO_KERNEL_STACK])).append(" kernel stack\n"); + memInfoBuilder.append(" "); + memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_BUFFERS])).append(" buffers, "); + memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_CACHED])).append(" cached, "); + memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_MAPPED])).append(" mapped, "); + memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_FREE])).append(" free\n"); + if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) { + memInfoBuilder.append(" ZRAM: "); + memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_ZRAM_TOTAL])); + memInfoBuilder.append(" RAM, "); + memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_TOTAL])); + memInfoBuilder.append(" swap total, "); + memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_FREE])); + memInfoBuilder.append(" swap free\n"); + } + final long[] ksm = getKsmInfo(); + if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0 + || ksm[KSM_VOLATILE] != 0) { + memInfoBuilder.append(" KSM: "); + memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARING])); + memInfoBuilder.append(" saved from shared "); + memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARED])); + memInfoBuilder.append("\n "); + memInfoBuilder.append(stringifyKBSize(ksm[KSM_UNSHARED])); + memInfoBuilder.append(" unshared; "); + memInfoBuilder.append(stringifyKBSize(ksm[KSM_VOLATILE])); + memInfoBuilder.append(" volatile\n"); + } + memInfoBuilder.append(" Free RAM: "); + memInfoBuilder.append(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb() + + memInfo.getFreeSizeKb())); + memInfoBuilder.append("\n"); + long kernelUsed = memInfo.getKernelUsedSizeKb(); + final long ionHeap = Debug.getIonHeapsSizeKb(); + final long ionPool = Debug.getIonPoolsSizeKb(); + if (ionHeap >= 0 && ionPool >= 0) { + final long ionMapped = Debug.getIonMappedSizeKb(); + final long ionUnmapped = ionHeap - ionMapped; + memInfoBuilder.append(" ION: "); + memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool)); + memInfoBuilder.append("\n"); + // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being + // set on ION VMAs, therefore consider the entire ION heap as used kernel memory + kernelUsed += ionHeap; + } + final long gpuUsage = Debug.getGpuTotalUsageKb(); + if (gpuUsage >= 0) { + memInfoBuilder.append(" GPU: "); + memInfoBuilder.append(stringifyKBSize(gpuUsage)); + memInfoBuilder.append("\n"); + } + memInfoBuilder.append(" Used RAM: "); + memInfoBuilder.append(stringifyKBSize( + totalPss - cachedPss + kernelUsed)); + memInfoBuilder.append("\n"); + memInfoBuilder.append(" Lost RAM: "); + memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb() + - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() + - kernelUsed - memInfo.getZramTotalSizeKb())); + memInfoBuilder.append("\n"); + Slog.i(TAG, "Low on memory:"); + Slog.i(TAG, shortNativeBuilder.toString()); + Slog.i(TAG, fullJavaBuilder.toString()); + Slog.i(TAG, memInfoBuilder.toString()); + + StringBuilder dropBuilder = new StringBuilder(1024); + dropBuilder.append("Low on memory:"); + dropBuilder.append(stack); + dropBuilder.append('\n'); + dropBuilder.append(fullNativeBuilder); + dropBuilder.append(fullJavaBuilder); + dropBuilder.append('\n'); + dropBuilder.append(memInfoBuilder); + dropBuilder.append('\n'); + StringWriter catSw = new StringWriter(); + synchronized (mService) { + PrintWriter catPw = new FastPrintWriter(catSw, false, 256); + String[] emptyArgs = new String[] { }; + catPw.println(); + mService.mProcessList.dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null, -1); + catPw.println(); + mService.mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0, + false, null).dumpLocked(); + catPw.println(); + mService.mAtmInternal.dump( + DUMP_ACTIVITIES_CMD, null, catPw, emptyArgs, 0, false, false, null); + catPw.flush(); + } + dropBuilder.append(catSw.toString()); + FrameworkStatsLog.write(FrameworkStatsLog.LOW_MEM_REPORTED); + mService.addErrorToDropBox("lowmem", null, "system_server", null, + null, null, tag.toString(), dropBuilder.toString(), null, null); + synchronized (mService) { + long now = SystemClock.uptimeMillis(); + if (mLastMemUsageReportTime < now) { + mLastMemUsageReportTime = now; + } + } + } + + @GuardedBy("mService") + private void stopProfilerLPf(ProcessRecord proc, int profileType) { if (proc == null || proc == mProfileData.getProfileProc()) { proc = mProfileData.getProfileProc(); profileType = mProfileType; - clearProfilerLocked(); + clearProfilerLPf(); } if (proc == null) { return; } + final IApplicationThread thread = proc.mProfile.getThread(); + if (thread == null) { + return; + } try { - proc.thread.profilerControl(false, null, profileType); + thread.profilerControl(false, null, profileType); } catch (RemoteException e) { throw new IllegalStateException("Process disappeared"); } } - @GuardedBy("mService") - void clearProfilerLocked() { + @GuardedBy("mProfilerLock") + void clearProfilerLPf() { if (mProfileData.getProfilerInfo() != null && mProfileData.getProfilerInfo().profileFd != null) { try { @@ -1118,22 +1610,22 @@ public class AppProfiler { mProfileData.setProfilerInfo(null); } - @GuardedBy("mService") - void clearProfilerLocked(ProcessRecord app) { + @GuardedBy("mProfilerLock") + void clearProfilerLPf(ProcessRecord app) { if (mProfileData.getProfileProc() == null || mProfileData.getProfilerInfo() == null || mProfileData.getProfileProc() != app) { return; } - clearProfilerLocked(); + clearProfilerLPf(); } - @GuardedBy("mService") - boolean profileControlLocked(ProcessRecord proc, boolean start, + @GuardedBy("mProfilerLock") + boolean profileControlLPf(ProcessRecord proc, boolean start, ProfilerInfo profilerInfo, int profileType) { try { if (start) { - stopProfilerLocked(null, 0); + stopProfilerLPf(null, 0); mService.setProfileApp(proc.info, proc.processName, profilerInfo); mProfileData.setProfileProc(proc); mProfileType = profileType; @@ -1144,7 +1636,7 @@ public class AppProfiler { fd = null; } profilerInfo.profileFd = fd; - proc.thread.profilerControl(start, profilerInfo, profileType); + proc.mProfile.getThread().profilerControl(start, profilerInfo, profileType); fd = null; try { mProfileData.getProfilerInfo().profileFd.close(); @@ -1160,7 +1652,7 @@ public class AppProfiler { profilerInfo = null; } } else { - stopProfilerLocked(proc, profileType); + stopProfilerLPf(proc, profileType); if (profilerInfo != null && profilerInfo.profileFd != null) { try { profilerInfo.profileFd.close(); @@ -1182,8 +1674,8 @@ public class AppProfiler { } } - @GuardedBy("mService") - void setProfileAppLocked(String processName, ProfilerInfo profilerInfo) { + @GuardedBy("mProfilerLock") + void setProfileAppLPf(String processName, ProfilerInfo profilerInfo) { mProfileData.setProfileApp(processName); if (mProfileData.getProfilerInfo() != null) { @@ -1198,13 +1690,13 @@ public class AppProfiler { mProfileType = 0; } - @GuardedBy("mService") - void setProfileProcLocked(ProcessRecord proc) { + @GuardedBy("mProfilerLock") + void setProfileProcLPf(ProcessRecord proc) { mProfileData.setProfileProc(proc); } - @GuardedBy("mService") - void setAgentAppLocked(@NonNull String packageName, @Nullable String agent) { + @GuardedBy("mProfilerLock") + void setAgentAppLPf(@NonNull String packageName, @Nullable String agent) { if (agent == null) { if (mAppAgentMap != null) { mAppAgentMap.remove(packageName); @@ -1226,8 +1718,7 @@ public class AppProfiler { } } - @GuardedBy("mService") - void updateCpuStatsLocked() { + void updateCpuStats() { final long now = SystemClock.uptimeMillis(); if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) { return; @@ -1301,17 +1792,18 @@ public class AppProfiler { totalUTime += st.rel_utime; totalSTime += st.rel_stime; if (pr != null) { - BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats; + final ProcessProfileRecord profile = pr.mProfile; + BatteryStatsImpl.Uid.Proc ps = profile.getCurProcBatteryStats(); if (ps == null || !ps.isActive()) { - pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked( + profile.setCurProcBatteryStats( + ps = bstats.getProcessStatsLocked( pr.info.uid, pr.processName, - elapsedRealtime, uptime); + elapsedRealtime, uptime)); } ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); - pr.curCpuTime += st.rel_utime + st.rel_stime; - if (pr.lastCpuTime == 0) { - pr.lastCpuTime = pr.curCpuTime; - } + final long curCpuTime = profile.mCurCpuTime.addAndGet( + st.rel_utime + st.rel_stime); + profile.mLastCpuTime.compareAndSet(0, curCpuTime); } else { BatteryStatsImpl.Uid.Proc ps = st.batteryStats; if (ps == null || !ps.isActive()) { @@ -1483,44 +1975,46 @@ public class AppProfiler { ProfilerInfo profilerInfo = null; String preBindAgent = null; final String processName = app.processName; - if (mProfileData.getProfileApp() != null - && mProfileData.getProfileApp().equals(processName)) { - mProfileData.setProfileProc(app); - if (mProfileData.getProfilerInfo() != null) { - // Send a profiler info object to the app if either a file is given, or - // an agent should be loaded at bind-time. - boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null - || mProfileData.getProfilerInfo().attachAgentDuringBind; - profilerInfo = needsInfo - ? new ProfilerInfo(mProfileData.getProfilerInfo()) : null; - if (mProfileData.getProfilerInfo().agent != null) { - preBindAgent = mProfileData.getProfilerInfo().agent; + synchronized (mProfilerLock) { + if (mProfileData.getProfileApp() != null + && mProfileData.getProfileApp().equals(processName)) { + mProfileData.setProfileProc(app); + if (mProfileData.getProfilerInfo() != null) { + // Send a profiler info object to the app if either a file is given, or + // an agent should be loaded at bind-time. + boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null + || mProfileData.getProfilerInfo().attachAgentDuringBind; + profilerInfo = needsInfo + ? new ProfilerInfo(mProfileData.getProfilerInfo()) : null; + if (mProfileData.getProfilerInfo().agent != null) { + preBindAgent = mProfileData.getProfilerInfo().agent; + } } + } else if (instr != null && instr.mProfileFile != null) { + profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false, + null, false); } - } else if (instr != null && instr.mProfileFile != null) { - profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false, - null, false); - } - if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) { - // We need to do a debuggable check here. See setAgentApp for why the check is - // postponed to here. - if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { - String agent = mAppAgentMap.get(processName); - // Do not overwrite already requested agent. - if (profilerInfo == null) { - profilerInfo = new ProfilerInfo(null, null, 0, false, false, - mAppAgentMap.get(processName), true); - } else if (profilerInfo.agent == null) { - profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true); + if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) { + // We need to do a debuggable check here. See setAgentApp for why the check is + // postponed to here. + if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + String agent = mAppAgentMap.get(processName); + // Do not overwrite already requested agent. + if (profilerInfo == null) { + profilerInfo = new ProfilerInfo(null, null, 0, false, false, + mAppAgentMap.get(processName), true); + } else if (profilerInfo.agent == null) { + profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true); + } } } - } - if (profilerInfo != null && profilerInfo.profileFd != null) { - profilerInfo.profileFd = profilerInfo.profileFd.dup(); - if (TextUtils.equals(mProfileData.getProfileApp(), processName) - && mProfileData.getProfilerInfo() != null) { - clearProfilerLocked(); + if (profilerInfo != null && profilerInfo.profileFd != null) { + profilerInfo.profileFd = profilerInfo.profileFd.dup(); + if (TextUtils.equals(mProfileData.getProfileApp(), processName) + && mProfileData.getProfilerInfo() != null) { + clearProfilerLPf(); + } } } @@ -1566,19 +2060,25 @@ public class AppProfiler { @GuardedBy("mService") void onCleanupApplicationRecordLocked(ProcessRecord app) { - mPendingPssProcesses.remove(app); - abortNextPssTime(app.procStateMemTracker); + synchronized (mProfilerLock) { + final ProcessProfileRecord profile = app.mProfile; + mProcessesToGc.remove(app); + mPendingPssProfiles.remove(profile); + profile.abortNextPssTime(); + } } @GuardedBy("mService") void onAppDiedLocked(ProcessRecord app) { - if (mProfileData.getProfileProc() == app) { - clearProfilerLocked(); + synchronized (mProfilerLock) { + if (mProfileData.getProfileProc() == app) { + clearProfilerLPf(); + } } } - @GuardedBy("mService") - boolean dumpMemWatchProcessesLocked(PrintWriter pw, boolean needSep) { + @GuardedBy("mProfilerLock") + boolean dumpMemWatchProcessesLPf(PrintWriter pw, boolean needSep) { if (mMemWatchProcesses.getMap().size() > 0) { pw.println(" Mem watch processes:"); final ArrayMap<String, SparseArray<Pair<Long, String>>> procs = @@ -1669,8 +2169,8 @@ public class AppProfiler { + " mLastNumProcesses=" + mLastNumProcesses); } - @GuardedBy("mService") - void writeMemWatchProcessToProtoLocked(ProtoOutputStream proto) { + @GuardedBy("mProfilerLock") + void writeMemWatchProcessToProtoLPf(ProtoOutputStream proto) { if (mMemWatchProcesses.getMap().size() > 0) { final long token = proto.start( ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES); @@ -1753,4 +2253,55 @@ public class AppProfiler { report.append(mProcessCpuTracker.printCurrentState(time)); } } + + @GuardedBy("mProfilerLock") + void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) { + if (mProcessesToGc.size() > 0) { + long now = SystemClock.uptimeMillis(); + for (int i = 0, size = mProcessesToGc.size(); i < size; i++) { + ProcessRecord r = mProcessesToGc.get(i); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; + } + final long token = proto.start(fieldId); + final ProcessProfileRecord profile = r.mProfile; + r.dumpDebug(proto, ProcessToGcProto.PROC); + proto.write(ProcessToGcProto.REPORT_LOW_MEMORY, profile.getReportLowMemory()); + proto.write(ProcessToGcProto.NOW_UPTIME_MS, now); + proto.write(ProcessToGcProto.LAST_GCED_MS, profile.getLastRequestedGc()); + proto.write(ProcessToGcProto.LAST_LOW_MEMORY_MS, profile.getLastLowMemory()); + proto.end(token); + } + } + } + + @GuardedBy("mProfilerLock") + boolean dumpProcessesToGc(PrintWriter pw, boolean needSep, String dumpPackage) { + if (mProcessesToGc.size() > 0) { + boolean printed = false; + long now = SystemClock.uptimeMillis(); + for (int i = 0, size = mProcessesToGc.size(); i < size; i++) { + ProcessRecord proc = mProcessesToGc.get(i); + if (dumpPackage != null && !dumpPackage.equals(proc.info.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(); + needSep = true; + pw.println(" Processes that are waiting to GC:"); + printed = true; + } + pw.print(" Process "); pw.println(proc); + final ProcessProfileRecord profile = proc.mProfile; + pw.print(" lowMem="); pw.print(profile.getReportLowMemory()); + pw.print(", last gced="); + pw.print(now - profile.getLastRequestedGc()); + pw.print(" ms ago, last lowMem="); + pw.print(now - profile.getLastLowMemory()); + pw.println(" ms ago"); + + } + } + return needSep; + } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index d2ee69e0e00d..4de12183e403 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -849,7 +849,7 @@ public final class BroadcastQueue { private boolean requestStartTargetPermissionsReviewIfNeededLocked( BroadcastRecord receiverRecord, String receivingPackageName, final int receivingUserId) { - if (!mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( + if (!mService.getPackageManagerInternal().isPermissionsReviewRequired( receivingPackageName, receivingUserId)) { return true; } @@ -922,7 +922,7 @@ public final class BroadcastQueue { Slog.v(TAG, "Broadcast temp whitelist uid=" + uid + " duration=" + duration + " type=" + type + " : " + b.toString()); } - mService.tempWhitelistUidLocked(uid, duration, b.toString(), type); + mService.tempAllowlistUidLocked(uid, duration, b.toString(), type); } /** @@ -969,7 +969,7 @@ public final class BroadcastQueue { + mParallelBroadcasts.size() + " parallel broadcasts; " + mDispatcher.describeStateLocked()); - mService.updateCpuStatsLocked(); + mService.updateCpuStats(); if (fromMsg) { mBroadcastsScheduled = false; @@ -1050,7 +1050,9 @@ public final class BroadcastQueue { if (r == null) { // No more broadcasts are deliverable right now, so all done! mDispatcher.scheduleDeferralCheckLocked(false); - mService.scheduleAppGcsLocked(); + synchronized (mService.mAppProfiler.mProfilerLock) { + mService.mAppProfiler.scheduleAppGcsLPf(); + } if (looped && !skipOomAdj) { // If we had finished the last ordered broadcast, then // make sure all processes have correct oom and sched diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java index 26cfd62d40ad..45ce4c5021bb 100644 --- a/services/core/java/com/android/server/am/CacheOomRanker.java +++ b/services/core/java/com/android/server/am/CacheOomRanker.java @@ -60,6 +60,9 @@ public class CacheOomRanker { private final Object mPhenotypeFlagLock = new Object(); + private final ActivityManagerService mService; + private final Object mProfilerLock; + @GuardedBy("mPhenotypeFlagLock") private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING; // Weight to apply to the LRU ordering. @@ -101,6 +104,11 @@ public class CacheOomRanker { } }; + CacheOomRanker(final ActivityManagerService service) { + mService = service; + mProfilerLock = service.mAppProfiler.mProfilerLock; + } + /** Load settings from device config and register a listener for changes. */ public void init(Executor executor) { DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -223,7 +231,9 @@ public class CacheOomRanker { addToScore(scoredProcessRecords, lruWeight); } if (rssWeight > 0.0f) { - Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR); + synchronized (mService.mAppProfiler.mProfilerLock) { + Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR); + } addToScore(scoredProcessRecords, rssWeight); } if (usesWeight > 0.0f) { @@ -297,7 +307,7 @@ public class CacheOomRanker { @Override public int compare(RankedProcessRecord o1, RankedProcessRecord o2) { // High RSS first to match least recently used. - return Long.compare(o2.proc.mLastRss, o1.proc.mLastRss); + return Long.compare(o2.proc.mProfile.getLastRss(), o1.proc.mProfile.getLastRss()); } } diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java index 6d9d3fbe41bd..e6cd509f50dc 100644 --- a/services/core/java/com/android/server/am/ConnectionRecord.java +++ b/services/core/java/com/android/server/am/ConnectionRecord.java @@ -128,7 +128,7 @@ final class ConnectionRecord { && association == null && binding.service.app != null && (binding.service.appInfo.uid != clientUid || !binding.service.processName.equals(clientProcessName))) { - ProcessStats.ProcessStateHolder holder = binding.service.app.pkgList.get( + ProcessStats.ProcessStateHolder holder = binding.service.app.getPkgList().get( binding.service.instanceName.getPackageName()); if (holder == null) { Slog.wtf(TAG_AM, "No package in referenced service " diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java index be49ce4b02ce..efee4321fd9f 100644 --- a/services/core/java/com/android/server/am/ContentProviderConnection.java +++ b/services/core/java/com/android/server/am/ContentProviderConnection.java @@ -16,6 +16,9 @@ package com.android.server.am; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; + import android.os.Binder; import android.os.SystemClock; import android.util.Slog; @@ -25,9 +28,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.AssociationState; import com.android.internal.app.procstats.ProcessStats; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER; -import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; - /** * Represents a link between a content provider and client. */ @@ -71,7 +71,7 @@ public final class ContentProviderConnection extends Binder { && association == null && provider.proc != null && (provider.appInfo.uid != client.uid || !provider.info.processName.equals(client.processName))) { - ProcessStats.ProcessStateHolder holder = provider.proc.pkgList.get( + ProcessStats.ProcessStateHolder holder = provider.proc.getPkgList().get( provider.name.getPackageName()); if (holder == null) { Slog.wtf(TAG_AM, "No package in referenced provider " diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 89ed42377b07..478c512338f0 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -1214,9 +1214,8 @@ public class ContentProviderHelper { final ProcessRecord app = apps.valueAt(iApp); if (app.userId != userId || app.thread == null || app.unlocked) continue; - for (int iPkg = 0, numPkgs = app.pkgList.size(); iPkg < numPkgs; iPkg++) { + app.getPkgList().forEachPackage(pkgName -> { try { - final String pkgName = app.pkgList.keyAt(iPkg); final PackageInfo pkgInfo = AppGlobals.getPackageManager() .getPackageInfo(pkgName, matchFlags, userId); if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) { @@ -1243,7 +1242,7 @@ public class ContentProviderHelper { } } catch (RemoteException ignored) { } - } + }); } } } @@ -1430,13 +1429,14 @@ public class ContentProviderHelper { return mService.validateAssociationAllowedLocked(cpi.packageName, cpi.applicationInfo.uid, null, callingUid) ? null : "<null>"; } - for (int i = callingApp.pkgList.size() - 1; i >= 0; i--) { - if (!mService.validateAssociationAllowedLocked(callingApp.pkgList.keyAt(i), + final String r = callingApp.getPkgList().forEachPackage(pkgName -> { + if (!mService.validateAssociationAllowedLocked(pkgName, callingApp.uid, cpi.packageName, cpi.applicationInfo.uid)) { return cpi.packageName; } - } - return null; + return null; + }); + return r; } ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId, int pmFlags) { @@ -1531,7 +1531,7 @@ public class ContentProviderHelper { private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi, ProcessRecord r, final int userId, Context context) { - if (!mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( + if (!mService.getPackageManagerInternal().isPermissionsReviewRequired( cpi.packageName, userId)) { return true; } diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java index fb8b5d480b27..59a19398420d 100644 --- a/services/core/java/com/android/server/am/ContentProviderRecord.java +++ b/services/core/java/com/android/server/am/ContentProviderRecord.java @@ -343,7 +343,7 @@ 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.pkgList.get( + ProcessStats.ProcessStateHolder holder = provider.proc.getPkgList().get( provider.name.getPackageName()); if (holder == null) { Slog.wtf(TAG_AM, "No package in referenced provider " diff --git a/services/core/java/com/android/server/am/FgsStartTempAllowList.java b/services/core/java/com/android/server/am/FgsStartTempAllowList.java index 3aca4cfd0162..4d8749c05294 100644 --- a/services/core/java/com/android/server/am/FgsStartTempAllowList.java +++ b/services/core/java/com/android/server/am/FgsStartTempAllowList.java @@ -70,4 +70,8 @@ final class FgsStartTempAllowList { return true; } } + + void remove(int uid) { + mTempAllowListFgs.delete(uid); + } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index de7931535297..5e146e16deb0 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -65,10 +65,10 @@ import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG; import static com.android.server.am.ActivityManagerService.TAG_BACKUP; import static com.android.server.am.ActivityManagerService.TAG_LRU; import static com.android.server.am.ActivityManagerService.TAG_OOM_ADJ; -import static com.android.server.am.ActivityManagerService.TAG_PROCESS_OBSERVERS; import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS; import static com.android.server.am.ActivityManagerService.TOP_APP_PRIORITY_BOOST; import static com.android.server.am.AppProfiler.TAG_PSS; +import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import android.app.ActivityManager; @@ -131,7 +131,7 @@ public final class OomAdjuster { static final String OOM_ADJ_REASON_GET_PROVIDER = OOM_ADJ_REASON_METHOD + "_getProvider"; static final String OOM_ADJ_REASON_REMOVE_PROVIDER = OOM_ADJ_REASON_METHOD + "_removeProvider"; static final String OOM_ADJ_REASON_UI_VISIBILITY = OOM_ADJ_REASON_METHOD + "_uiVisibility"; - static final String OOM_ADJ_REASON_WHITELIST = OOM_ADJ_REASON_METHOD + "_whitelistChange"; + static final String OOM_ADJ_REASON_ALLOWLIST = OOM_ADJ_REASON_METHOD + "_allowlistChange"; static final String OOM_ADJ_REASON_PROCESS_BEGIN = OOM_ADJ_REASON_METHOD + "_processBegin"; static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd"; @@ -337,7 +337,7 @@ public final class OomAdjuster { mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class); mConstants = mService.mConstants; mCachedAppOptimizer = new CachedAppOptimizer(mService); - mCacheOomRanker = new CacheOomRanker(); + mCacheOomRanker = new CacheOomRanker(service); mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> { final int pid = msg.arg1; @@ -394,7 +394,7 @@ public final class OomAdjuster { final ProcessRecord app = processes.get(i); boolean includeWarmPkg = false; for (int j = warmServices.size() - 1; j >= 0; j--) { - if (app.pkgList.containsKey(warmServices.valueAt(j).getPackageName())) { + if (app.getPkgList().containsKey(warmServices.valueAt(j).getPackageName())) { includeWarmPkg = true; break; } @@ -422,7 +422,7 @@ public final class OomAdjuster { if (oomAdjAll && mConstants.OOMADJ_UPDATE_QUICK) { return updateOomAdjLocked(app, oomAdjReason); } - final ProcessRecord TOP_APP = mService.getTopAppLocked(); + final ProcessRecord topApp = mService.getTopApp(); final boolean wasCached = app.isCached(); mAdjSeq++; @@ -435,7 +435,7 @@ public final class OomAdjuster { ? app.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, TOP_APP, false, + boolean success = updateOomAdjLocked(app, cachedAdj, topApp, false, SystemClock.uptimeMillis()); if (oomAdjAll && (wasCached != app.isCached() || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) { @@ -488,7 +488,7 @@ public final class OomAdjuster { if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT && (uidRec.setProcState != uidRec.getCurProcState() || uidRec.setCapability != uidRec.curCapability - || uidRec.setWhitelist != uidRec.curWhitelist)) { + || uidRec.mSetAllowlist != uidRec.mCurAllowlist)) { ActiveUids uids = mTmpUidRecords; uids.clear(); uids.put(uidRec.uid, uidRec); @@ -505,7 +505,7 @@ public final class OomAdjuster { */ @GuardedBy("mService") void updateOomAdjLocked(String oomAdjReason) { - final ProcessRecord topApp = mService.getTopAppLocked(); + 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; @@ -527,7 +527,7 @@ public final class OomAdjuster { return true; } - final ProcessRecord topApp = mService.getTopAppLocked(); + final ProcessRecord topApp = mService.getTopApp(); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason); mService.mOomAdjProfiler.oomAdjStarted(); @@ -676,7 +676,7 @@ public final class OomAdjuster { if (mPendingProcessSet.isEmpty()) { return; } - final ProcessRecord topApp = mService.getTopAppLocked(); + final ProcessRecord topApp = mService.getTopApp(); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason); mService.mOomAdjProfiler.oomAdjStarted(); @@ -1112,19 +1112,19 @@ public final class OomAdjuster { if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT && (uidRec.setProcState != uidRec.getCurProcState() || uidRec.setCapability != uidRec.curCapability - || uidRec.setWhitelist != uidRec.curWhitelist)) { + || uidRec.mSetAllowlist != uidRec.mCurAllowlist)) { if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec + ": proc state from " + uidRec.setProcState + " to " + uidRec.getCurProcState() + ", capability from " + uidRec.setCapability + " to " + uidRec.curCapability - + ", whitelist from " + uidRec.setWhitelist - + " to " + uidRec.curWhitelist); + + ", allowlist from " + uidRec.mSetAllowlist + + " to " + uidRec.mCurAllowlist); if (ActivityManager.isProcStateBackground(uidRec.getCurProcState()) - && !uidRec.curWhitelist) { + && !uidRec.mCurAllowlist) { // 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.setWhitelist) { + || uidRec.mSetAllowlist) { uidRec.lastBackgroundTime = nowElapsed; if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { // Note: the background settle time is in elapsed realtime, while @@ -1156,7 +1156,7 @@ public final class OomAdjuster { } uidRec.setProcState = uidRec.getCurProcState(); uidRec.setCapability = uidRec.curCapability; - uidRec.setWhitelist = uidRec.curWhitelist; + uidRec.mSetAllowlist = uidRec.mCurAllowlist; uidRec.setIdle = uidRec.idle; mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState); mService.enqueueUidChangeLocked(uidRec, -1, uidChange); @@ -1353,7 +1353,7 @@ public final class OomAdjuster { // 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) { - mService.reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app); + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app); } app.adjType = "fixed"; app.adjSeq = mAdjSeq; @@ -1379,7 +1379,7 @@ public final class OomAdjuster { app.systemNoUi = false; } if (!app.systemNoUi) { - if (mService.mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) { + 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); @@ -1912,7 +1912,7 @@ public final class OomAdjuster { clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; app.bumpAllowStartFgsState( PROCESS_STATE_BOUND_FOREGROUND_SERVICE); - } else if (mService.mWakefulness + } else if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE && (cr.flags & Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE) != 0) { @@ -2197,7 +2197,8 @@ public final class OomAdjuster { // is large we want to force it down since we would prefer to // keep launcher over it. if (!mService.mAppProfiler.isLastMemoryLevelNormal() - && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) { + && app.mProfile.getLastPss() + >= mProcessList.getCachedRestoreThresholdKb()) { app.serviceHighRam = true; app.serviceb = true; //Slog.i(TAG, "ADJ " + app + " high ram!"); @@ -2228,7 +2229,7 @@ public final class OomAdjuster { // Put bound foreground services in a special sched group for additional // restrictions on screen off if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE - && mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) { + && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE) { if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) { schedGroup = ProcessList.SCHED_GROUP_RESTRICTED; } @@ -2324,11 +2325,13 @@ public final class OomAdjuster { } /** Inform the oomadj observer of changes to oomadj. Used by tests. */ - @GuardedBy("mService") void reportOomAdjMessageLocked(String tag, String msg) { Slog.d(tag, msg); - if (mService.mCurOomAdjObserver != null) { - mService.mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget(); + synchronized (mService.mOomAdjObserverLock) { + if (mService.mCurOomAdjObserver != null) { + mService.mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg) + .sendToTarget(); + } } } @@ -2361,7 +2364,7 @@ public final class OomAdjuster { && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) { mCachedAppOptimizer.compactAppFull(app); } - } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE + } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE && app.setAdj < 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 @@ -2369,7 +2372,7 @@ public final class OomAdjuster { // and in CachedAppOptimizer. && mCachedAppOptimizer.shouldCompactPersistent(app, now)) { mCachedAppOptimizer.compactAppPersistent(app); - } else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE + } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE && app.getCurProcState() == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE && mCachedAppOptimizer.shouldCompactBFGS(app, now)) { @@ -2511,18 +2514,22 @@ public final class OomAdjuster { } } } + boolean forceUpdatePssTime = false; if (app.setProcState == PROCESS_STATE_NONEXISTENT || ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) { app.lastStateTime = now; - mService.mAppProfiler.updateNextPssTimeLocked(app.getCurProcState(), app, now, true); + 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 " - + (app.nextPssTime - now) + ": " + app); + + (app.mProfile.getNextPssTime() - now) + ": " + app); } - } else { - mService.mAppProfiler.updateNextPssTimeLocked(app.getCurProcState(), app, now, false); + } + synchronized (mService.mAppProfiler.mProfilerLock) { + app.mProfile.updateProcState(app); + mService.mAppProfiler.updateNextPssTimeLPf( + app.getCurProcState(), app.mProfile, now, forceUpdatePssTime); } if (app.setProcState != app.getCurProcState()) { if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) { @@ -2538,7 +2545,7 @@ public final class OomAdjuster { // 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); - app.lastCpuTime = 0; + app.mProfile.mLastCpuTime.set(0); } // Inform UsageStats of important process state change // Must be called before updating setProcState @@ -2576,7 +2583,7 @@ public final class OomAdjuster { if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, "Changes in " + app + ": " + changes); ActivityManagerService.ProcessChangeItem item = - mService.enqueueProcessChangeItemLocked(app.pid, app.info.uid); + mProcessList.enqueueProcessChangeItemLocked(app.pid, app.info.uid); item.changes |= changes; item.foregroundActivities = app.repForegroundActivities; item.capability = app.setCapability; @@ -2730,27 +2737,27 @@ public final class OomAdjuster { } @GuardedBy("mService") - final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) { + void setAppIdTempAllowlistStateLocked(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.curWhitelist != onWhitelist) { - uidRec.curWhitelist = onWhitelist; + if (uidRec.uid == uid && uidRec.mCurAllowlist != onAllowlist) { + uidRec.mCurAllowlist = onAllowlist; changed = true; } } if (changed) { - updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST); + updateOomAdjLocked(OOM_ADJ_REASON_ALLOWLIST); } } @GuardedBy("mService") - final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) { + void setUidTempAllowlistStateLocked(int uid, boolean onAllowlist) { boolean changed = false; final UidRecord uidRec = mActiveUids.get(uid); - if (uidRec != null && uidRec.curWhitelist != onWhitelist) { - uidRec.curWhitelist = onWhitelist; - updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST); + if (uidRec != null && uidRec.mCurAllowlist != onAllowlist) { + uidRec.mCurAllowlist = onAllowlist; + updateOomAdjLocked(OOM_ADJ_REASON_ALLOWLIST); } } diff --git a/services/core/java/com/android/server/am/PackageList.java b/services/core/java/com/android/server/am/PackageList.java new file mode 100644 index 000000000000..978bcb7afee4 --- /dev/null +++ b/services/core/java/com/android/server/am/PackageList.java @@ -0,0 +1,155 @@ +/* + * 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.content.pm.VersionedPackage; +import android.util.ArrayMap; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.procstats.ProcessStats; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * List of packages running in the process, self locked. + */ +final class PackageList { + private final ProcessRecord mProcess; + + private final ArrayMap<String, ProcessStats.ProcessStateHolder> mPkgList = new ArrayMap<>(); + + PackageList(final ProcessRecord app) { + mProcess = app; + } + + ProcessStats.ProcessStateHolder put(String key, ProcessStats.ProcessStateHolder value) { + synchronized (this) { + mProcess.getWindowProcessController().addPackage(key); + return mPkgList.put(key, value); + } + } + + void clear() { + synchronized (this) { + mPkgList.clear(); + mProcess.getWindowProcessController().clearPackageList(); + } + } + + int size() { + synchronized (this) { + return mPkgList.size(); + } + } + + boolean containsKey(Object key) { + synchronized (this) { + return mPkgList.containsKey(key); + } + } + + ProcessStats.ProcessStateHolder get(String pkgName) { + synchronized (this) { + return mPkgList.get(pkgName); + } + } + + void forEachPackage(Consumer<String> callback) { + synchronized (this) { + for (int i = 0, size = mPkgList.size(); i < size; i++) { + callback.accept(mPkgList.keyAt(i)); + } + } + } + + void forEachPackage(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)); + } + } + } + + <R> R forEachPackage(Function<String, R> callback) { + synchronized (this) { + for (int i = 0, size = mPkgList.size(); i < size; i++) { + R r = callback.apply(mPkgList.keyAt(i)); + if (r != null) { + return r; + } + } + } + return null; + } + + void forEachPackageProcessStats(Consumer<ProcessStats.ProcessStateHolder> callback) { + synchronized (this) { + for (int i = 0, size = mPkgList.size(); i < size; i++) { + callback.accept(mPkgList.valueAt(i)); + } + } + } + + @GuardedBy("this") + ArrayMap<String, ProcessStats.ProcessStateHolder> getPackageListLocked() { + return mPkgList; + } + + String[] getPackageList() { + synchronized (this) { + int size = mPkgList.size(); + if (size == 0) { + return null; + } + final String[] list = new String[size]; + for (int i = 0; i < size; i++) { + list[i] = mPkgList.keyAt(i); + } + return list; + } + } + + List<VersionedPackage> getPackageListWithVersionCode() { + synchronized (this) { + int size = mPkgList.size(); + if (size == 0) { + return null; + } + List<VersionedPackage> list = new ArrayList<>(); + for (int i = 0; i < size; i++) { + list.add(new VersionedPackage(mPkgList.keyAt(i), mPkgList.valueAt(i).appVersion)); + } + return list; + } + } + + void dump(PrintWriter pw, String prefix) { + synchronized (this) { + pw.print(prefix); pw.print("packageList={"); + for (int i = 0, size = mPkgList.size(); i < size; i++) { + if (i > 0) pw.print(", "); + pw.print(mPkgList.keyAt(i)); + } + pw.println("}"); + } + } +} diff --git a/services/core/java/com/android/server/am/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempAllowlists.java index 50d58f02baa7..75935c4f22fa 100644 --- a/services/core/java/com/android/server/am/PendingTempWhitelists.java +++ b/services/core/java/com/android/server/am/PendingTempAllowlists.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,49 +11,49 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.server.am; import android.util.SparseArray; -/** Whitelists of uids to temporarily bypass Power Save mode. */ -final class PendingTempWhitelists { +/** Allowlists of uids to temporarily bypass Power Save mode. */ +final class PendingTempAllowlists { private ActivityManagerService mService; - private final SparseArray<ActivityManagerService.PendingTempWhitelist> mPendingTempWhitelist = + private final SparseArray<ActivityManagerService.PendingTempAllowlist> mPendingTempAllowlist = new SparseArray<>(); - PendingTempWhitelists(ActivityManagerService service) { + PendingTempAllowlists(ActivityManagerService service) { mService = service; } - void put(int uid, ActivityManagerService.PendingTempWhitelist value) { - mPendingTempWhitelist.put(uid, value); + void put(int uid, ActivityManagerService.PendingTempAllowlist value) { + mPendingTempAllowlist.put(uid, value); mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag); } void removeAt(int index) { - final int uid = mPendingTempWhitelist.keyAt(index); - mPendingTempWhitelist.removeAt(index); + final int uid = mPendingTempAllowlist.keyAt(index); + mPendingTempAllowlist.removeAt(index); mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid); } - ActivityManagerService.PendingTempWhitelist get(int uid) { - return mPendingTempWhitelist.get(uid); + ActivityManagerService.PendingTempAllowlist get(int uid) { + return mPendingTempAllowlist.get(uid); } int size() { - return mPendingTempWhitelist.size(); + return mPendingTempAllowlist.size(); } - ActivityManagerService.PendingTempWhitelist valueAt(int index) { - return mPendingTempWhitelist.valueAt(index); + ActivityManagerService.PendingTempAllowlist valueAt(int index) { + return mPendingTempAllowlist.valueAt(index); } int indexOfKey(int key) { - return mPendingTempWhitelist.indexOfKey(key); + return mPendingTempAllowlist.indexOfKey(key); } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 2273779c2127..a768532a9260 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -34,10 +34,14 @@ import static android.os.Process.startWebView; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS; 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.DISPATCH_PROCESSES_CHANGED_UI_MSG; +import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESS_DIED_UI_MSG; import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_DELAY_MS; import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_MSG; import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK; @@ -59,6 +63,7 @@ import android.app.ApplicationExitInfo; import android.app.ApplicationExitInfo.Reason; import android.app.ApplicationExitInfo.SubReason; import android.app.IApplicationThread; +import android.app.IProcessObserver; import android.app.IUidObserver; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; @@ -86,6 +91,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.StrictMode; import android.os.SystemClock; @@ -97,18 +103,20 @@ import android.system.Os; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.DebugUtils; import android.util.EventLog; import android.util.LongSparseArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ProcessMap; -import com.android.internal.app.procstats.ProcessStats; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -117,6 +125,7 @@ import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; +import com.android.server.am.ActivityManagerService.ProcessChangeItem; import com.android.server.compat.PlatformCompat; import com.android.server.pm.dex.DexManager; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -134,6 +143,8 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -145,6 +156,8 @@ import java.util.Set; public final class ProcessList { static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM; + static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS; + // A system property to control if app data isolation is enabled. static final String ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.zygote.app_data_isolation"; @@ -416,7 +429,7 @@ public final class ProcessList { private boolean mVoldAppDataIsolationEnabled = false; - private ArrayList<String> mAppDataIsolationWhitelistedApps; + private ArrayList<String> mAppDataIsolationAllowlistedApps; /** * Temporary to avoid allocations. Protected by main lock. @@ -643,6 +656,25 @@ public final class ProcessList { */ final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>(); + // Self locked with the inner lock within the RemoteCallbackList + private final RemoteCallbackList<IProcessObserver> mProcessObservers = + new RemoteCallbackList<>(); + + // No lock is needed as it's accessed from single thread only + private ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5]; + + @GuardedBy("mProcessChangeLock") + private final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>(); + + @GuardedBy("mProcessChangeLock") + final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>(); + + /** + * A dedicated lock for dispatching the process changes as it occurs frequently + */ + 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 @@ -723,7 +755,7 @@ public final class ProcessList { SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); - mAppDataIsolationWhitelistedApps = new ArrayList<>( + mAppDataIsolationAllowlistedApps = new ArrayList<>( SystemConfig.getInstance().getAppDataIsolationWhitelistedApps()); if (sKillHandler == null) { @@ -1523,47 +1555,34 @@ public final class ProcessList { ProcessRecord proc = mProcessNames.get(processName, uid); if (false && proc != null && !keepIfLarge && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY - && proc.lastCachedPss >= 4000) { + && proc.mProfile.getLastCachedPss() >= 4000) { // Turn this condition on to cause killing to happen regularly, for testing. - synchronized (mService.mProcessStats.mLock) { - if (proc.baseProcessTracker != null) { - proc.baseProcessTracker.reportCachedKill( - proc.pkgList.mPkgList, proc.lastCachedPss); - for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); - FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, - proc.info.uid, - holder.state.getName(), - holder.state.getPackage(), - proc.lastCachedPss, holder.appVersion); - } - } + final long lastCachedPss; + synchronized (mService.mAppProfiler.mProfilerLock) { + proc.mProfile.reportCachedKill(); + lastCachedPss = proc.mProfile.getLastCachedPss(); } - proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", + proc.kill(Long.toString(lastCachedPss) + "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) { - if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc - .lastCachedPss); - if (proc.lastCachedPss >= getCachedRestoreThresholdKb()) { - synchronized (mService.mProcessStats.mLock) { - if (proc.baseProcessTracker != null) { - proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, - proc.lastCachedPss); - for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); - FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, - proc.info.uid, - holder.state.getName(), - holder.state.getPackage(), - proc.lastCachedPss, holder.appVersion); - } - } + final long lastCachedPss; + boolean doKilling = false; + synchronized (mService.mAppProfiler.mProfilerLock) { + lastCachedPss = proc.mProfile.getLastCachedPss(); + if (lastCachedPss >= getCachedRestoreThresholdKb()) { + proc.mProfile.reportCachedKill(); + doKilling = true; } - proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", + } + if (DEBUG_PSS) { + Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + lastCachedPss); + } + if (doKilling) { + proc.kill(Long.toString(lastCachedPss) + "k from cached", ApplicationExitInfo.REASON_OTHER, ApplicationExitInfo.SUBREASON_LARGE_CACHED, true); @@ -1770,7 +1789,7 @@ public final class ProcessList { mService.mProcessesOnHold.remove(app); checkSlow(startTime, "startProcess: starting to update cpu stats"); - mService.updateCpuStatsLocked(); + mService.updateCpuStats(); checkSlow(startTime, "startProcess: done updating cpu stats"); try { @@ -1873,10 +1892,16 @@ public final class ProcessList { mService.mNativeDebuggingApp = null; } - if (app.info.isEmbeddedDexUsed() - || (app.info.isPrivilegedApp() - && DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet()))) { + if (app.info.isEmbeddedDexUsed()) { runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES; + } else if (app.info.isPrivilegedApp()) { + final PackageList pkgList = app.getPkgList(); + synchronized (pkgList) { + if (DexManager.isPackageSelectedToRunOob( + pkgList.getPackageListLocked().keySet())) { + runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES; + } + } } if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) { @@ -2240,7 +2265,7 @@ public final class ProcessList { } Map<String, Pair<String, Long>> pkgDataInfoMap; - Map<String, Pair<String, Long>> whitelistedAppDataInfoMap; + Map<String, Pair<String, Long>> allowlistedAppDataInfoMap; boolean bindMountAppStorageDirs = false; boolean bindMountAppsData = mAppDataIsolationEnabled && (UserHandle.isApp(app.uid) || UserHandle.isIsolated(app.uid)) @@ -2248,7 +2273,7 @@ public final class ProcessList { // Get all packages belongs to the same shared uid. sharedPackages is empty array // if it doesn't have shared uid. - final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked(); + final PackageManagerInternal pmInt = mService.getPackageManagerInternal(); final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage( app.info.packageName, app.userId); final String[] targetPackagesList = sharedPackages.length == 0 @@ -2261,16 +2286,16 @@ public final class ProcessList { bindMountAppsData = false; } - // Remove all packages in pkgDataInfoMap from mAppDataIsolationWhitelistedApps, so + // Remove all packages in pkgDataInfoMap from mAppDataIsolationAllowlistedApps, so // it won't be mounted twice. - final Set<String> whitelistedApps = new ArraySet<>(mAppDataIsolationWhitelistedApps); + final Set<String> allowlistedApps = new ArraySet<>(mAppDataIsolationAllowlistedApps); for (String pkg : targetPackagesList) { - whitelistedApps.remove(pkg); + allowlistedApps.remove(pkg); } - whitelistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt, - whitelistedApps.toArray(new String[0]), uid); - if (whitelistedAppDataInfoMap == null) { + allowlistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt, + allowlistedApps.toArray(new String[0]), uid); + if (allowlistedAppDataInfoMap == null) { // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a // tmp free pass. bindMountAppsData = false; @@ -2295,7 +2320,7 @@ public final class ProcessList { // since it has no access to them anyway. if (app.isolated) { pkgDataInfoMap = null; - whitelistedAppDataInfoMap = null; + allowlistedAppDataInfoMap = null; } final Process.ProcessStartResult startResult; @@ -2314,7 +2339,7 @@ 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, whitelistedAppDataInfoMap, + app.mDisabledCompatChanges, pkgDataInfoMap, allowlistedAppDataInfoMap, false, false, new String[]{PROC_START_SEQ_IDENT + app.startSeq}); } else { @@ -2323,7 +2348,7 @@ public final class ProcessList { app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags, isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap, - whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs, + allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs, new String[]{PROC_START_SEQ_IDENT + app.startSeq}); } checkSlow(startTime, "startProcess: returned from zygote!"); @@ -2373,7 +2398,7 @@ public final class ProcessList { // if it had been bad. if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid + "/" + processName); - mService.mAppErrors.resetProcessCrashTimeLocked(processName, info.uid); + mService.mAppErrors.resetProcessCrashTime(processName, info.uid); if (mService.mAppErrors.isBadProcess(processName, info.uid)) { EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, UserHandle.getUserId(info.uid), info.uid, @@ -2421,7 +2446,7 @@ public final class ProcessList { if (!app.killed || mService.mAppProfiler.isLastMemoryLevelNormal() || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY - || app.lastCachedPss < getCachedRestoreThresholdKb()) { + || 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 Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process"); @@ -2736,7 +2761,7 @@ public final class ProcessList { if (userId != UserHandle.USER_ALL && app.userId != userId) { continue; } - if (!app.pkgList.containsKey(packageName) && !isDep) { + if (!app.getPkgList().containsKey(packageName) && !isDep) { continue; } } @@ -2797,7 +2822,7 @@ public final class ProcessList { mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid); if (app.isolated) { mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid); - mService.getPackageManagerInternalLocked().removeIsolatedUid(app.uid); + mService.getPackageManagerInternal().removeIsolatedUid(app.uid); } } boolean willRestart = false; @@ -2847,10 +2872,10 @@ public final class ProcessList { // 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.mDeviceIdleTempWhitelist, + if (Arrays.binarySearch(mService.mDeviceIdleTempAllowlist, UserHandle.getAppId(proc.uid)) >= 0 - || mService.mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) { - uidRec.setWhitelist = uidRec.curWhitelist = true; + || mService.mPendingTempAllowlist.indexOfKey(proc.uid) >= 0) { + uidRec.mSetAllowlist = uidRec.mCurAllowlist = true; } uidRec.updateHasInternetPermission(); mActiveUids.put(proc.uid, uidRec); @@ -2904,7 +2929,7 @@ public final class ProcessList { uid = isolatedUid; } mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(uid, info.uid); - mService.getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid); + mService.getPackageManagerInternal().addIsolatedUid(uid, info.uid); // Register the isolated UID with this application so BatteryStats knows to // attribute resource usage to the application. @@ -2964,6 +2989,7 @@ public final class ProcessList { 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); } @@ -3582,14 +3608,14 @@ public final class ProcessList { if (app.hasActivities()) { outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES; } - outInfo.lastTrimLevel = app.trimMemoryLevel; + outInfo.lastTrimLevel = app.mProfile.getTrimMemoryLevel(); int adj = app.curAdj; int procState = app.getCurProcState(); outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk); outInfo.importanceReasonCode = app.adjTypeCode; outInfo.processState = app.getCurProcState(); - outInfo.isFocused = (app == mService.getTopAppLocked()); + outInfo.isFocused = (app == mService.getTopApp()); outInfo.lastActivityTime = app.lastActivityTime; } @@ -3653,6 +3679,729 @@ public final class ProcessList { pw.println("):"); } + void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc, String prefix) { + pw.print(prefix); + pw.print('#'); + if (index < 10) { + pw.print(' '); + } + pw.print(index); + pw.print(": "); + pw.print(makeOomAdjString(proc.setAdj, false)); + pw.print(' '); + pw.print(makeProcStateString(proc.getCurProcState())); + pw.print(' '); + ActivityManager.printCapabilitiesSummary(pw, proc.curCapability); + pw.print(' '); + pw.print(proc.toShortString()); + if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities() + || proc.treatLikeActivity) { + pw.print(" act:"); + boolean printed = false; + if (proc.hasActivities()) { + pw.print("activities"); + printed = true; + } + if (proc.hasRecentTasks()) { + if (printed) { + pw.print("|"); + } + pw.print("recents"); + printed = true; + } + if (proc.hasClientActivities()) { + if (printed) { + pw.print("|"); + } + pw.print("client"); + printed = true; + } + if (proc.treatLikeActivity) { + if (printed) { + pw.print("|"); + } + pw.print("treated"); + } + } + pw.println(); + } + + boolean dumpLruLocked(PrintWriter pw, String dumpPackage, String prefix) { + final int lruSize = mLruProcesses.size(); + final String innerPrefix; + if (prefix == null) { + pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)"); + innerPrefix = " "; + } else { + boolean haveAny = false; + for (int i = lruSize - 1; i >= 0; i--) { + final ProcessRecord r = mLruProcesses.get(i); + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { + continue; + } + haveAny = true; + break; + } + if (!haveAny) { + return false; + } + pw.print(prefix); + pw.println("Raw LRU list (dumpsys activity lru):"); + innerPrefix = prefix + " "; + } + int i; + boolean first = true; + for (i = lruSize - 1; i >= mLruProcessActivityStart; i--) { + final ProcessRecord r = mLruProcesses.get(i); + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { + continue; + } + if (first) { + pw.print(innerPrefix); + pw.println("Activities:"); + first = false; + } + dumpLruEntryLocked(pw, i, r, innerPrefix); + } + first = true; + for (; i >= mLruProcessServiceStart; i--) { + final ProcessRecord r = mLruProcesses.get(i); + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { + continue; + } + if (first) { + pw.print(innerPrefix); + pw.println("Services:"); + first = false; + } + dumpLruEntryLocked(pw, i, r, innerPrefix); + } + first = true; + for (; i >= 0; i--) { + final ProcessRecord r = mLruProcesses.get(i); + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { + continue; + } + if (first) { + pw.print(innerPrefix); + pw.println("Other:"); + first = false; + } + dumpLruEntryLocked(pw, i, r, innerPrefix); + } + return true; + } + + @GuardedBy("mService") + void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll, String dumpPackage, int dumpAppId) { + boolean needSep = false; + int numPers = 0; + + pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)"); + + if (dumpAll || dumpPackage != null) { + final int numOfNames = mProcessNames.getMap().size(); + for (int ip = 0; ip < numOfNames; ip++) { + SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip); + for (int ia = 0, size = procs.size(); ia < size; ia++) { + ProcessRecord r = procs.valueAt(ia); + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { + continue; + } + if (!needSep) { + pw.println(" All known processes:"); + needSep = true; + } + pw.print(r.isPersistent() ? " *PERS*" : " *APP*"); + pw.print(" UID "); pw.print(procs.keyAt(ia)); + pw.print(" "); pw.println(r); + r.dump(pw, " "); + if (r.isPersistent()) { + numPers++; + } + } + } + } + + if (mIsolatedProcesses.size() > 0) { + boolean printed = false; + for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) { + ProcessRecord r = mIsolatedProcesses.valueAt(i); + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { + continue; + } + if (!printed) { + if (needSep) { + pw.println(); + } + pw.println(" Isolated process list (sorted by uid):"); + printed = true; + needSep = true; + } + pw.print(" Isolated #"); pw.print(i); pw.print(": "); + pw.println(r); + } + } + + needSep = mService.dumpActiveInstruments(pw, dumpPackage, needSep); + + if (dumpOomLocked(fd, pw, needSep, args, opti, dumpAll, dumpPackage, false)) { + needSep = true; + } + + if (mActiveUids.size() > 0) { + needSep |= mActiveUids.dump(pw, dumpPackage, dumpAppId, + "UID states:", needSep); + } + + if (dumpAll) { + needSep |= mService.mUidObserverController.dumpValidateUids(pw, + dumpPackage, dumpAppId, "UID validation:", needSep); + } + + if (needSep) { + pw.println(); + } + if (dumpLruLocked(pw, dumpPackage, " ")) { + needSep = true; + } + + if (getLruSizeLocked() > 0) { + if (needSep) { + pw.println(); + } + dumpLruListHeaderLocked(pw); + dumpProcessOomList(pw, mService, mLruProcesses, " ", "Proc", "PERS", false, + dumpPackage); + needSep = true; + } + + mService.dumpOtherProcessesInfoLocked(fd, pw, dumpAll, dumpPackage, dumpAppId, numPers, + needSep); + } + + @GuardedBy("this") + void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) { + int numPers = 0; + + final int numOfNames = mProcessNames.getMap().size(); + for (int ip = 0; ip < numOfNames; ip++) { + SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip); + for (int ia = 0, size = procs.size(); ia < size; ia++) { + ProcessRecord r = procs.valueAt(ia); + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { + continue; + } + r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.PROCS, + mLruProcesses.indexOf(r) + ); + if (r.isPersistent()) { + numPers++; + } + } + } + + for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) { + ProcessRecord r = mIsolatedProcesses.valueAt(i); + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { + continue; + } + r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS, + mLruProcesses.indexOf(r) + ); + } + + final int dumpAppId = mService.getAppId(dumpPackage); + mActiveUids.dumpProto(proto, dumpPackage, dumpAppId, + ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS); + + if (getLruSizeLocked() > 0) { + long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS); + int total = getLruSizeLocked(); + proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total); + proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT, + total - mLruProcessActivityStart); + proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT, + total - mLruProcessServiceStart); + writeProcessOomListToProto(proto, + ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, mService, + mLruProcesses, false, dumpPackage); + proto.end(lruToken); + } + + mService.writeOtherProcessesInfoToProtoLocked(proto, dumpPackage, dumpAppId, numPers); + } + + private static ArrayList<Pair<ProcessRecord, Integer>> sortProcessOomList( + List<ProcessRecord> origList, String dumpPackage) { + ArrayList<Pair<ProcessRecord, Integer>> list = + new ArrayList<Pair<ProcessRecord, Integer>>(origList.size()); + for (int i = 0, size = origList.size(); i < size; i++) { + ProcessRecord r = origList.get(i); + if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) { + continue; + } + list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i)); + } + + Comparator<Pair<ProcessRecord, Integer>> comparator = + new Comparator<Pair<ProcessRecord, Integer>>() { + @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; + } + if (object1.first.setProcState != object2.first.setProcState) { + return object1.first.setProcState > object2.first.setProcState ? -1 : 1; + } + if (object1.second.intValue() != object2.second.intValue()) { + return object1.second.intValue() > object2.second.intValue() ? -1 : 1; + } + return 0; + } + }; + + Collections.sort(list, comparator); + return list; + } + + private static boolean writeProcessOomListToProto(ProtoOutputStream proto, long fieldId, + ActivityManagerService service, List<ProcessRecord> origList, + boolean inclDetails, String dumpPackage) { + ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage); + if (list.isEmpty()) return false; + + final long curUptime = SystemClock.uptimeMillis(); + + for (int i = list.size() - 1; i >= 0; i--) { + ProcessRecord r = list.get(i).first; + long token = proto.start(fieldId); + String oomAdj = makeOomAdjString(r.setAdj, 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) { + case SCHED_GROUP_BACKGROUND: + schedGroup = ProcessOomProto.SCHED_GROUP_BACKGROUND; + break; + case SCHED_GROUP_DEFAULT: + schedGroup = ProcessOomProto.SCHED_GROUP_DEFAULT; + break; + case SCHED_GROUP_TOP_APP: + schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP; + break; + case SCHED_GROUP_TOP_APP_BOUND: + schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP_BOUND; + break; + } + if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) { + proto.write(ProcessOomProto.SCHED_GROUP, schedGroup); + } + if (r.hasForegroundActivities()) { + proto.write(ProcessOomProto.ACTIVITIES, true); + } else if (r.hasForegroundServices()) { + proto.write(ProcessOomProto.SERVICES, true); + } + proto.write(ProcessOomProto.STATE, + makeProcStateProtoEnum(r.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; + cn.dumpDebug(proto, ProcessOomProto.ADJ_TARGET_COMPONENT_NAME); + } else if (r.adjTarget != null) { + proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, r.adjTarget.toString()); + } + if (r.adjSource instanceof ProcessRecord) { + ProcessRecord p = (ProcessRecord) r.adjSource; + p.dumpDebug(proto, ProcessOomProto.ADJ_SOURCE_PROC); + } else if (r.adjSource != null) { + proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, r.adjSource.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.CURRENT_STATE, + makeProcStateProtoEnum(r.getCurProcState())); + proto.write(ProcessOomProto.Detail.SET_STATE, + makeProcStateProtoEnum(r.setProcState)); + 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); + + if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) { + long lastCpuTime = r.mProfile.mLastCpuTime.get(); + if (lastCpuTime != 0) { + long uptimeSince = curUptime - service.mLastPowerCheckUptime; + long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime; + long cpuTimeToken = proto.start(ProcessOomProto.Detail.SERVICE_RUN_TIME); + proto.write(ProcessOomProto.Detail.CpuRunTime.OVER_MS, uptimeSince); + proto.write(ProcessOomProto.Detail.CpuRunTime.USED_MS, timeUsed); + proto.write(ProcessOomProto.Detail.CpuRunTime.ULTILIZATION, + (100.0 * timeUsed) / uptimeSince); + proto.end(cpuTimeToken); + } + } + proto.end(detailToken); + } + proto.end(token); + } + + return true; + } + + private static boolean dumpProcessOomList(PrintWriter pw, + ActivityManagerService service, List<ProcessRecord> origList, + String prefix, String normalLabel, String persistentLabel, + boolean inclDetails, String dumpPackage) { + + ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage); + if (list.isEmpty()) return false; + + final long curUptime = SystemClock.uptimeMillis(); + final long uptimeSince = curUptime - service.mLastPowerCheckUptime; + + for (int i = list.size() - 1; i >= 0; i--) { + ProcessRecord r = list.get(i).first; + String oomAdj = makeOomAdjString(r.setAdj, false); + char schedGroup; + switch (r.setSchedGroup) { + case SCHED_GROUP_BACKGROUND: + schedGroup = 'b'; + break; + case SCHED_GROUP_DEFAULT: + schedGroup = 'F'; + break; + case SCHED_GROUP_TOP_APP: + schedGroup = 'T'; + break; + case SCHED_GROUP_RESTRICTED: + schedGroup = 'R'; + break; + case SCHED_GROUP_TOP_APP_BOUND: + schedGroup = 'B'; + break; + default: + schedGroup = '?'; + break; + } + char foreground; + if (r.hasForegroundActivities()) { + foreground = 'A'; + } else if (r.hasForegroundServices()) { + foreground = 'S'; + } else { + foreground = ' '; + } + String procState = makeProcStateString(r.getCurProcState()); + pw.print(prefix); + pw.print(r.isPersistent() ? persistentLabel : normalLabel); + pw.print(" #"); + int num = (origList.size() - 1) - list.get(i).second; + if (num < 10) pw.print(' '); + pw.print(num); + pw.print(": "); + pw.print(oomAdj); + pw.print(' '); + pw.print(schedGroup); + pw.print('/'); + pw.print(foreground); + pw.print('/'); + pw.print(procState); + pw.print(' '); + ActivityManager.printCapabilitiesSummary(pw, r.curCapability); + pw.print(' '); + pw.print(" t:"); + if (r.mProfile.getTrimMemoryLevel() < 10) pw.print(' '); + pw.print(r.mProfile.getTrimMemoryLevel()); + pw.print(' '); + pw.print(r.toShortString()); + pw.print(" ("); + pw.print(r.adjType); + pw.println(')'); + if (r.adjSource != null || r.adjTarget != 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()); + } else { + pw.print("{null}"); + } + pw.print("<="); + if (r.adjSource instanceof ProcessRecord) { + pw.print("Proc{"); + pw.print(((ProcessRecord) r.adjSource).toShortString()); + pw.println("}"); + } else if (r.adjSource != null) { + pw.println(r.adjSource.toString()); + } else { + pw.println("{null}"); + } + } + 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(prefix); + pw.print(" "); + pw.print("state: cur="); pw.print( + makeProcStateString(r.getCurProcState())); + pw.print(" set="); pw.print(makeProcStateString(r.setProcState)); + pw.print(" lastPss="); + DebugUtils.printSizeValue(pw, r.mProfile.getLastPss() * 1024); + pw.print(" lastSwapPss="); + DebugUtils.printSizeValue(pw, r.mProfile.getLastSwapPss() * 1024); + pw.print(" lastCachedPss="); + DebugUtils.printSizeValue(pw, r.mProfile.getLastCachedPss() * 1024); + 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); + + if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) { + long lastCpuTime = r.mProfile.mLastCpuTime.get(); + if (lastCpuTime != 0) { + long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime; + pw.print(prefix); + pw.print(" "); + pw.print("run cpu over "); + TimeUtils.formatDuration(uptimeSince, pw); + pw.print(" used "); + TimeUtils.formatDuration(timeUsed, pw); + pw.print(" ("); + pw.print((timeUsed * 100) / uptimeSince); + pw.println("%)"); + } + } + } + } + return true; + } + + private void printOomLevel(PrintWriter pw, String name, int adj) { + pw.print(" "); + if (adj >= 0) { + pw.print(' '); + if (adj < 10) pw.print(' '); + } else { + if (adj > -10) pw.print(' '); + } + pw.print(adj); + pw.print(": "); + pw.print(name); + pw.print(" ("); + pw.print(ActivityManagerService.stringifySize(getMemLevel(adj), 1024)); + pw.println(")"); + } + + boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String[] args, + int opti, boolean dumpAll, String dumpPackage, boolean inclGc) { + if (getLruSizeLocked() > 0) { + if (needSep) pw.println(); + needSep = true; + pw.println(" OOM levels:"); + printOomLevel(pw, "SYSTEM_ADJ", SYSTEM_ADJ); + printOomLevel(pw, "PERSISTENT_PROC_ADJ", PERSISTENT_PROC_ADJ); + printOomLevel(pw, "PERSISTENT_SERVICE_ADJ", PERSISTENT_SERVICE_ADJ); + printOomLevel(pw, "FOREGROUND_APP_ADJ", FOREGROUND_APP_ADJ); + printOomLevel(pw, "VISIBLE_APP_ADJ", VISIBLE_APP_ADJ); + printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", PERCEPTIBLE_APP_ADJ); + printOomLevel(pw, "PERCEPTIBLE_LOW_APP_ADJ", PERCEPTIBLE_LOW_APP_ADJ); + printOomLevel(pw, "BACKUP_APP_ADJ", BACKUP_APP_ADJ); + printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", HEAVY_WEIGHT_APP_ADJ); + printOomLevel(pw, "SERVICE_ADJ", SERVICE_ADJ); + printOomLevel(pw, "HOME_APP_ADJ", HOME_APP_ADJ); + printOomLevel(pw, "PREVIOUS_APP_ADJ", PREVIOUS_APP_ADJ); + printOomLevel(pw, "SERVICE_B_ADJ", SERVICE_B_ADJ); + printOomLevel(pw, "CACHED_APP_MIN_ADJ", CACHED_APP_MIN_ADJ); + printOomLevel(pw, "CACHED_APP_MAX_ADJ", CACHED_APP_MAX_ADJ); + + if (needSep) pw.println(); + pw.print(" Process OOM control ("); pw.print(getLruSizeLocked()); + pw.print(" total, non-act at "); + pw.print(getLruSizeLocked() - mLruProcessActivityStart); + pw.print(", non-svc at "); + pw.print(getLruSizeLocked() - mLruProcessServiceStart); + pw.println("):"); + dumpProcessOomList(pw, mService, mLruProcesses, " ", "Proc", "PERS", true, + dumpPackage); + needSep = true; + } + + synchronized (mService.mAppProfiler.mProfilerLock) { + mService.mAppProfiler.dumpProcessesToGc(pw, needSep, dumpPackage); + } + + pw.println(); + mService.mAtmInternal.dumpForOom(pw); + + return true; + } + + void registerProcessObserver(IProcessObserver observer) { + mProcessObservers.register(observer); + } + + void unregisterProcessObserver(IProcessObserver observer) { + mProcessObservers.unregister(observer); + } + + void dispatchProcessesChanged() { + int numOfChanges; + synchronized (mProcessChangeLock) { + numOfChanges = mPendingProcessChanges.size(); + if (mActiveProcessChanges.length < numOfChanges) { + mActiveProcessChanges = new ProcessChangeItem[numOfChanges]; + } + mPendingProcessChanges.toArray(mActiveProcessChanges); + mPendingProcessChanges.clear(); + if (DEBUG_PROCESS_OBSERVERS) { + Slog.i(TAG_PROCESS_OBSERVERS, + "*** Delivering " + numOfChanges + " process changes"); + } + } + + int i = mProcessObservers.beginBroadcast(); + while (i > 0) { + i--; + final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); + if (observer != null) { + try { + for (int j = 0; j < numOfChanges; j++) { + ProcessChangeItem item = mActiveProcessChanges[j]; + if ((item.changes & ProcessChangeItem.CHANGE_ACTIVITIES) != 0) { + if (DEBUG_PROCESS_OBSERVERS) { + Slog.i(TAG_PROCESS_OBSERVERS, + "ACTIVITIES CHANGED pid=" + item.pid + " uid=" + + item.uid + ": " + item.foregroundActivities); + } + observer.onForegroundActivitiesChanged(item.pid, item.uid, + item.foregroundActivities); + } + if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES) != 0) { + if (DEBUG_PROCESS_OBSERVERS) { + Slog.i(TAG_PROCESS_OBSERVERS, + "FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid=" + + item.uid + ": " + item.foregroundServiceTypes); + } + observer.onForegroundServicesChanged(item.pid, item.uid, + item.foregroundServiceTypes); + } + } + } catch (RemoteException e) { + } + } + } + mProcessObservers.finishBroadcast(); + + synchronized (mProcessChangeLock) { + for (int j = 0; j < numOfChanges; j++) { + mAvailProcessChanges.add(mActiveProcessChanges[j]); + } + } + } + + @GuardedBy("mService") + ProcessChangeItem enqueueProcessChangeItemLocked(int pid, int uid) { + synchronized (mProcessChangeLock) { + int i = mPendingProcessChanges.size() - 1; + ActivityManagerService.ProcessChangeItem item = null; + while (i >= 0) { + item = mPendingProcessChanges.get(i); + if (item.pid == pid) { + if (DEBUG_PROCESS_OBSERVERS) { + Slog.i(TAG_PROCESS_OBSERVERS, "Re-using existing item: " + item); + } + break; + } + i--; + } + + if (i < 0) { + // No existing item in pending changes; need a new one. + final int num = mAvailProcessChanges.size(); + if (num > 0) { + item = mAvailProcessChanges.remove(num - 1); + if (DEBUG_PROCESS_OBSERVERS) { + Slog.i(TAG_PROCESS_OBSERVERS, "Retrieving available item: " + item); + } + } else { + item = new ActivityManagerService.ProcessChangeItem(); + if (DEBUG_PROCESS_OBSERVERS) { + Slog.i(TAG_PROCESS_OBSERVERS, "Allocating new item: " + item); + } + } + item.changes = 0; + item.pid = pid; + item.uid = uid; + if (mPendingProcessChanges.size() == 0) { + if (DEBUG_PROCESS_OBSERVERS) { + Slog.i(TAG_PROCESS_OBSERVERS, "*** Enqueueing dispatch processes changed!"); + } + mService.mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG) + .sendToTarget(); + } + mPendingProcessChanges.add(item); + } + + return item; + } + } + + @GuardedBy("mService") + void scheduleDispatchProcessDiedLocked(int pid, int uid) { + synchronized (mProcessChangeLock) { + for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) { + ProcessChangeItem item = mPendingProcessChanges.get(i); + if (pid > 0 && item.pid == pid) { + mPendingProcessChanges.remove(i); + mAvailProcessChanges.add(item); + } + } + mService.mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, pid, uid, + null).sendToTarget(); + } + } + + void dispatchProcessDied(int pid, int uid) { + int i = mProcessObservers.beginBroadcast(); + while (i > 0) { + i--; + final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); + if (observer != null) { + try { + observer.onProcessDied(pid, uid); + } catch (RemoteException e) { + } + } + } + mProcessObservers.finishBroadcast(); + } + @GuardedBy("mService") ArrayList<ProcessRecord> collectProcessesLocked(int start, boolean allPkgs, String[] args) { ArrayList<ProcessRecord> procs; @@ -3668,8 +4417,8 @@ public final class ProcessList { ProcessRecord proc = mLruProcesses.get(i); if (proc.pid > 0 && proc.pid == pid) { procs.add(proc); - } else if (allPkgs && proc.pkgList != null - && proc.pkgList.containsKey(args[start])) { + } else if (allPkgs && proc.getPkgList() != null + && proc.getPkgList().containsKey(args[start])) { procs.add(proc); } else if (proc.processName.equals(args[start])) { procs.add(proc); @@ -3697,27 +4446,23 @@ public final class ProcessList { continue; } - final int packageCount = app.pkgList.size(); - for (int j = 0; j < packageCount; j++) { - final String packageName = app.pkgList.keyAt(j); - if (!updateFrameworkRes && !packagesToUpdate.contains(packageName)) { - continue; - } - try { - final ApplicationInfo ai = AppGlobals.getPackageManager() - .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId); - if (ai == null) { - continue; - } - app.thread.scheduleApplicationInfoChanged(ai); - if (ai.packageName.equals(app.info.packageName)) { - app.info = ai; + app.getPkgList().forEachPackage(packageName -> { + if (updateFrameworkRes && packagesToUpdate.contains(packageName)) { + try { + final ApplicationInfo ai = AppGlobals.getPackageManager() + .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId); + if (ai != null) { + app.thread.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)); } - } catch (RemoteException e) { - Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s", - packageName, app)); } - } + }); } } @@ -4103,7 +4848,7 @@ public final class ProcessList { boolean diff = mIdle != idle; mIdle = idle; if (diff && idle) { - synchronized (this) { + synchronized (mService) { if (mWorkItems.size() > 0) { mHandler.sendEmptyMessage(H.MSG_DEVICE_IDLE); } @@ -4176,13 +4921,14 @@ public final class ProcessList { return true; } - final int pkgSize = app.pkgList.size(); - for (int ip = 0; ip < pkgSize; ip++) { - if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.contains( - app.pkgList.keyAt(ip))) { + if (app.getPkgList().forEachPackage(pkgName -> { + if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.contains(pkgName)) { // One of the packages in this process is exempted - return true; + return Boolean.TRUE; } + return null; + }) != null) { + return true; } if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.contains( diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java new file mode 100644 index 000000000000..cf309f4c8e3d --- /dev/null +++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java @@ -0,0 +1,651 @@ +/* + * 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.app.ActivityManager.PROCESS_STATE_NONEXISTENT; +import static android.app.ActivityManager.processStateAmToProto; + +import android.app.IApplicationThread; +import android.content.pm.ApplicationInfo; +import android.os.Debug; +import android.os.SystemClock; +import android.util.DebugUtils; +import android.util.TimeUtils; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.procstats.ProcessState; +import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.am.ProcessList.ProcStateMemTracker; + +import java.io.PrintWriter; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Profiling info of the process, such as PSS, cpu, etc. + */ +final class ProcessProfileRecord { + final ProcessRecord mApp; + + private final ActivityManagerService mService; + + final Object mProfilerLock; + + @GuardedBy("mProfilerLock") + private final ProcessList.ProcStateMemTracker mProcStateMemTracker = + new ProcessList.ProcStateMemTracker(); + + /** + * Stats of pss, cpu, etc. + */ + @GuardedBy("mService.mProcessStats.mLock") + private ProcessState mBaseProcessTracker; + + /** + * Last time we retrieved PSS data. + */ + @GuardedBy("mProfilerLock") + private long mLastPssTime; + + /** + * Next time we want to request PSS data. + */ + @GuardedBy("mProfilerLock") + private long mNextPssTime; + + /** + * Initial memory pss of process for idle maintenance. + */ + @GuardedBy("mProfilerLock") + private long mInitialIdlePss; + + /** + * Last computed memory pss. + */ + @GuardedBy("mProfilerLock") + private long mLastPss; + + /** + * Last computed SwapPss. + */ + @GuardedBy("mProfilerLock") + private long mLastSwapPss; + + /** + * Last computed pss when in cached state. + */ + @GuardedBy("mProfilerLock") + private long mLastCachedPss; + + /** + * Last computed SwapPss when in cached state. + */ + @GuardedBy("mProfilerLock") + private long mLastCachedSwapPss; + + /** + * Last computed memory rss. + */ + @GuardedBy("mProfilerLock") + private long mLastRss; + + /** + * Cache of last retrieve memory info, to throttle how frequently apps can request it. + */ + @GuardedBy("mProfilerLock") + private Debug.MemoryInfo mLastMemInfo; + + /** + * Cache of last retrieve memory uptime, to throttle how frequently apps can request it. + */ + @GuardedBy("mProfilerLock") + private long mLastMemInfoTime; + + /** + * Currently requesting pss for. + */ + @GuardedBy("mProfilerLock") + private int mPssProcState = PROCESS_STATE_NONEXISTENT; + + /** + * The type of stat collection that we are currently requesting. + */ + @GuardedBy("mProfilerLock") + private int mPssStatType; + + /** + * How long proc has run CPU at last check. + */ + final AtomicLong mLastCpuTime = new AtomicLong(0); + + /** + * How long proc has run CPU most recently. + */ + final AtomicLong mCurCpuTime = new AtomicLong(0); + + /** + * Last selected memory trimming level. + */ + @GuardedBy("mService") + private int mTrimMemoryLevel; + + /** + * Want to clean up resources from showing UI? + */ + @GuardedBy("mService") + private boolean mPendingUiClean; + + /** + * Pointer to the battery stats of this process. + */ + private BatteryStatsImpl.Uid.Proc mCurProcBatteryStats; + + /** + * When we last asked the app to do a gc. + */ + @GuardedBy("mProfilerLock") + private long mLastRequestedGc; + + /** + * When we last told the app that memory is low. + */ + @GuardedBy("mService") + private long mLastLowMemory; + + /** + * Set to true when waiting to report low mem. + */ + @GuardedBy("mProfilerLock") + private boolean mReportLowMemory; + + // ======================================================================== + // Local copies of some process info, to avoid holding global AMS lock + @GuardedBy("mProfilerLock") + private int mPid; + + @GuardedBy("mProfilerLock") + private IApplicationThread mThread; + + @GuardedBy("mProfilerLock") + private int mSetProcState; + + @GuardedBy("mProfilerLock") + private int mSetAdj; + + @GuardedBy("mProfilerLock") + private int mCurRawAdj; + + @GuardedBy("mProfilerLock") + private long mLastStateTime; + + ProcessProfileRecord(final ProcessRecord app) { + mApp = app; + mService = app.mService; + mProfilerLock = mService.mAppProfiler.mProfilerLock; + } + + void init(long now) { + mLastPssTime = mNextPssTime = now; + } + + @GuardedBy("mService.mProcessStats.mLock") + ProcessState getBaseProcessTracker() { + return mBaseProcessTracker; + } + + @GuardedBy("mService.mProcessStats.mLock") + void setBaseProcessTracker(ProcessState baseProcessTracker) { + mBaseProcessTracker = baseProcessTracker; + } + + void onProcessActive(IApplicationThread thread, ProcessStatsService tracker) { + if (mThread == null) { + synchronized (mProfilerLock) { + synchronized (tracker.mLock) { + final ProcessState origBase = getBaseProcessTracker(); + final PackageList pkgList = mApp.getPkgList(); + if (origBase != null) { + synchronized (pkgList) { + origBase.setState(ProcessStats.STATE_NOTHING, + tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), + pkgList.getPackageListLocked()); + pkgList.forEachPackage((pkgName, holder) -> + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + mApp.uid, mApp.processName, pkgName, + processStateAmToProto(ProcessStats.STATE_NOTHING), + holder.appVersion) + ); + } + origBase.makeInactive(); + } + final ApplicationInfo info = mApp.info; + final ProcessState baseProcessTracker = tracker.getProcessStateLocked( + info.packageName, info.uid, info.longVersionCode, mApp.processName); + setBaseProcessTracker(baseProcessTracker); + baseProcessTracker.makeActive(); + pkgList.forEachPackage((pkgName, holder) -> { + if (holder.state != null && holder.state != origBase) { + holder.state.makeInactive(); + } + tracker.updateProcessStateHolderLocked(holder, pkgName, mApp.info.uid, + mApp.info.longVersionCode, mApp.processName); + if (holder.state != baseProcessTracker) { + holder.state.makeActive(); + } + }); + mThread = thread; + } + } + } else { + synchronized (mProfilerLock) { + mThread = thread; + } + } + } + + void onProcessInactive(ProcessStatsService tracker) { + synchronized (mProfilerLock) { + synchronized (tracker.mLock) { + final ProcessState origBase = getBaseProcessTracker(); + if (origBase != null) { + final PackageList pkgList = mApp.getPkgList(); + synchronized (pkgList) { + origBase.setState(ProcessStats.STATE_NOTHING, + tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), + pkgList.getPackageListLocked()); + pkgList.forEachPackage((pkgName, holder) -> + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + mApp.uid, mApp.processName, pkgName, + processStateAmToProto(ProcessStats.STATE_NOTHING), + holder.appVersion) + ); + } + origBase.makeInactive(); + setBaseProcessTracker(null); + pkgList.forEachPackageProcessStats(holder -> { + if (holder.state != null && holder.state != origBase) { + holder.state.makeInactive(); + } + holder.pkg = null; + holder.state = null; + }); + } + mThread = null; + } + } + } + + @GuardedBy("mProfilerLock") + long getLastPssTime() { + return mLastPssTime; + } + + @GuardedBy("mProfilerLock") + void setLastPssTime(long lastPssTime) { + mLastPssTime = lastPssTime; + } + + @GuardedBy("mProfilerLock") + long getNextPssTime() { + return mNextPssTime; + } + + @GuardedBy("mProfilerLock") + void setNextPssTime(long nextPssTime) { + mNextPssTime = nextPssTime; + } + + @GuardedBy("mProfilerLock") + long getInitialIdlePss() { + return mInitialIdlePss; + } + + @GuardedBy("mProfilerLock") + void setInitialIdlePss(long initialIdlePss) { + mInitialIdlePss = initialIdlePss; + } + + @GuardedBy("mProfilerLock") + long getLastPss() { + return mLastPss; + } + + @GuardedBy("mProfilerLock") + void setLastPss(long lastPss) { + mLastPss = lastPss; + } + + @GuardedBy("mProfilerLock") + long getLastCachedPss() { + return mLastCachedPss; + } + + @GuardedBy("mProfilerLock") + void setLastCachedPss(long lastCachedPss) { + mLastCachedPss = lastCachedPss; + } + + @GuardedBy("mProfilerLock") + long getLastSwapPss() { + return mLastSwapPss; + } + + @GuardedBy("mProfilerLock") + void setLastSwapPss(long lastSwapPss) { + mLastSwapPss = lastSwapPss; + } + + @GuardedBy("mProfilerLock") + long getLastCachedSwapPss() { + return mLastCachedSwapPss; + } + + @GuardedBy("mProfilerLock") + void setLastCachedSwapPss(long lastCachedSwapPss) { + mLastCachedSwapPss = lastCachedSwapPss; + } + + @GuardedBy("mProfilerLock") + long getLastRss() { + return mLastRss; + } + + @GuardedBy("mProfilerLock") + void setLastRss(long lastRss) { + mLastRss = lastRss; + } + + @GuardedBy("mProfilerLock") + Debug.MemoryInfo getLastMemInfo() { + return mLastMemInfo; + } + + @GuardedBy("mProfilerLock") + void setLastMemInfo(Debug.MemoryInfo lastMemInfo) { + mLastMemInfo = lastMemInfo; + } + + @GuardedBy("mProfilerLock") + long getLastMemInfoTime() { + return mLastMemInfoTime; + } + + @GuardedBy("mProfilerLock") + void setLastMemInfoTime(long lastMemInfoTime) { + mLastMemInfoTime = lastMemInfoTime; + } + + @GuardedBy("mProfilerLock") + int getPssProcState() { + return mPssProcState; + } + + @GuardedBy("mProfilerLock") + void setPssProcState(int pssProcState) { + mPssProcState = pssProcState; + } + + @GuardedBy("mProfilerLock") + int getPssStatType() { + return mPssStatType; + } + + @GuardedBy("mProfilerLock") + void setPssStatType(int pssStatType) { + mPssStatType = pssStatType; + } + + @GuardedBy("mService") + int getTrimMemoryLevel() { + return mTrimMemoryLevel; + } + + @GuardedBy("mService") + void setTrimMemoryLevel(int trimMemoryLevel) { + mTrimMemoryLevel = trimMemoryLevel; + } + + @GuardedBy("mService") + boolean hasPendingUiClean() { + return mPendingUiClean; + } + + @GuardedBy("mService") + void setPendingUiClean(boolean pendingUiClean) { + mPendingUiClean = pendingUiClean; + mApp.getWindowProcessController().setPendingUiClean(pendingUiClean); + } + + BatteryStatsImpl.Uid.Proc getCurProcBatteryStats() { + return mCurProcBatteryStats; + } + + void setCurProcBatteryStats(BatteryStatsImpl.Uid.Proc curProcBatteryStats) { + mCurProcBatteryStats = curProcBatteryStats; + } + + @GuardedBy("mProfilerLock") + long getLastRequestedGc() { + return mLastRequestedGc; + } + + @GuardedBy("mProfilerLock") + void setLastRequestedGc(long lastRequestedGc) { + mLastRequestedGc = lastRequestedGc; + } + + @GuardedBy("mService") + long getLastLowMemory() { + return mLastLowMemory; + } + + @GuardedBy("mService") + void setLastLowMemory(long lastLowMemory) { + mLastLowMemory = lastLowMemory; + } + + @GuardedBy("mProfilerLock") + boolean getReportLowMemory() { + return mReportLowMemory; + } + + @GuardedBy("mProfilerLock") + void setReportLowMemory(boolean reportLowMemory) { + mReportLowMemory = reportLowMemory; + } + + void addPss(long pss, long uss, long rss, boolean always, int type, long duration) { + synchronized (mService.mProcessStats.mLock) { + final ProcessState tracker = mBaseProcessTracker; + if (tracker != null) { + final PackageList pkgList = mApp.getPkgList(); + synchronized (pkgList) { + tracker.addPss(pss, uss, rss, always, type, duration, + pkgList.getPackageListLocked()); + } + } + } + } + + void reportExcessiveCpu() { + synchronized (mService.mProcessStats.mLock) { + final ProcessState tracker = mBaseProcessTracker; + if (tracker != null) { + final PackageList pkgList = mApp.getPkgList(); + synchronized (pkgList) { + tracker.reportExcessiveCpu(pkgList.getPackageListLocked()); + } + } + } + } + + void reportCachedKill() { + synchronized (mService.mProcessStats.mLock) { + final ProcessState tracker = mBaseProcessTracker; + if (tracker != null) { + final PackageList pkgList = mApp.getPkgList(); + synchronized (pkgList) { + tracker.reportCachedKill(pkgList.getPackageListLocked(), mLastCachedPss); + pkgList.forEachPackageProcessStats(holder -> + FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, + mApp.info.uid, + holder.state.getName(), + holder.state.getPackage(), + mLastCachedPss, + holder.appVersion) + ); + } + } + } + } + + void setProcessTrackerState(int procState, int memFactor, long now) { + synchronized (mService.mProcessStats.mLock) { + final ProcessState tracker = mBaseProcessTracker; + if (tracker != null) { + if (procState != PROCESS_STATE_NONEXISTENT) { + final PackageList pkgList = mApp.getPkgList(); + synchronized (pkgList) { + tracker.setState(procState, memFactor, now, + pkgList.getPackageListLocked()); + } + } + } + } + } + + @GuardedBy("mProfilerLock") + void commitNextPssTime() { + commitNextPssTime(mProcStateMemTracker); + } + + @GuardedBy("mProfilerLock") + void abortNextPssTime() { + abortNextPssTime(mProcStateMemTracker); + } + + @GuardedBy("mProfilerLock") + long computeNextPssTime(int procState, boolean test, boolean sleeping, long now) { + return ProcessList.computeNextPssTime(procState, mProcStateMemTracker, test, sleeping, now); + } + + private static void commitNextPssTime(ProcStateMemTracker tracker) { + if (tracker.mPendingMemState >= 0) { + tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState; + tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor; + tracker.mTotalHighestMem = tracker.mPendingHighestMemState; + tracker.mPendingMemState = -1; + } + } + + private static void abortNextPssTime(ProcStateMemTracker tracker) { + tracker.mPendingMemState = -1; + } + + @GuardedBy("mProfilerLock") + int getPid() { + return mPid; + } + + @GuardedBy("mProfilerLock") + void setPid(int pid) { + mPid = pid; + } + + @GuardedBy("mProfilerLock") + IApplicationThread getThread() { + return mThread; + } + + @GuardedBy("mProfilerLock") + int getSetProcState() { + return mSetProcState; + } + + @GuardedBy("mProfilerLock") + int getSetAdj() { + return mSetAdj; + } + + @GuardedBy("mProfilerLock") + int getCurRawAdj() { + return mCurRawAdj; + } + + @GuardedBy("mProfilerLock") + long getLastStateTime() { + return mLastStateTime; + } + + @GuardedBy({"mService", "mProfilerLock"}) + void updateProcState(ProcessRecord app) { + mSetProcState = app.getCurProcState(); + mSetAdj = app.curAdj; + mCurRawAdj = app.getCurRawAdj(); + mLastStateTime = app.lastStateTime; + } + + @GuardedBy("mService") + void dumpPss(PrintWriter pw, String prefix, long nowUptime) { + synchronized (mProfilerLock) { + pw.print(" lastPssTime="); + TimeUtils.formatDuration(mLastPssTime, nowUptime, pw); + pw.print(" pssProcState="); + pw.print(mPssProcState); + pw.print(" pssStatType="); + pw.print(mPssStatType); + pw.print(" nextPssTime="); + TimeUtils.formatDuration(mNextPssTime, nowUptime, pw); + pw.println(); + pw.print(prefix); + pw.print("lastPss="); + DebugUtils.printSizeValue(pw, mLastPss * 1024); + pw.print(" lastSwapPss="); + DebugUtils.printSizeValue(pw, mLastSwapPss * 1024); + pw.print(" lastCachedPss="); + DebugUtils.printSizeValue(pw, mLastCachedPss * 1024); + pw.print(" lastCachedSwapPss="); + DebugUtils.printSizeValue(pw, mLastCachedSwapPss * 1024); + pw.print(" lastRss="); + DebugUtils.printSizeValue(pw, mLastRss * 1024); + pw.print(" trimMemoryLevel="); + pw.println(mTrimMemoryLevel); + pw.println(); + pw.print(prefix); pw.print("procStateMemTracker: "); + mProcStateMemTracker.dumpLine(pw); + pw.print(prefix); + pw.print("lastRequestedGc="); + TimeUtils.formatDuration(mLastRequestedGc, nowUptime, pw); + pw.print(" lastLowMemory="); + TimeUtils.formatDuration(mLastLowMemory, nowUptime, pw); + pw.print(" reportLowMemory="); + pw.println(mReportLowMemory); + } + } + + void dumpCputime(PrintWriter pw, String prefix) { + final long lastCpuTime = mLastCpuTime.get(); + pw.print(prefix); + pw.print("lastCpuTime="); + pw.print(lastCpuTime); + if (lastCpuTime > 0) { + pw.print(" timeUsed="); + TimeUtils.formatDuration(mCurCpuTime.get() - lastCpuTime, pw); + } + } +} diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 63195d35b049..f7e87ef94939 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -52,7 +52,6 @@ import android.content.pm.ServiceInfo; import android.content.pm.VersionedPackage; import android.content.res.CompatibilityInfo; import android.os.Binder; -import android.os.Debug; import android.os.IBinder; import android.os.Message; import android.os.Process; @@ -74,7 +73,6 @@ import android.util.proto.ProtoOutputStream; 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.BatteryStatsImpl; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.Zygote; import com.android.internal.util.FrameworkStatsLog; @@ -96,9 +94,9 @@ import java.util.function.Consumer; * is currently running. */ class ProcessRecord implements WindowProcessListener { - private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM; + static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM; - private final ActivityManagerService mService; // where we came from + final ActivityManagerService mService; // where we came from 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,51 +104,17 @@ class ProcessRecord implements WindowProcessListener { final int uid; // uid of process; may be different from 'info' if isolated final int userId; // user of process. final String processName; // name of the process - // List of packages running in the process - final PackageList pkgList = new PackageList(); - final class PackageList { - final ArrayMap<String, ProcessStats.ProcessStateHolder> mPkgList = new ArrayMap<>(); - ProcessStats.ProcessStateHolder put(String key, ProcessStats.ProcessStateHolder value) { - mWindowProcessController.addPackage(key); - return mPkgList.put(key, value); - } - - void clear() { - mPkgList.clear(); - mWindowProcessController.clearPackageList(); - } - - int size() { - return mPkgList.size(); - } - - String keyAt(int index) { - return mPkgList.keyAt(index); - } - - public ProcessStats.ProcessStateHolder valueAt(int index) { - return mPkgList.valueAt(index); - } - - ProcessStats.ProcessStateHolder get(String pkgName) { - return mPkgList.get(pkgName); - } - - boolean containsKey(Object key) { - return mPkgList.containsKey(key); - } - } + /** + * List of packages running in the process + */ + private final PackageList mPkgList = new PackageList(this); - final ProcessList.ProcStateMemTracker procStateMemTracker - = new ProcessList.ProcStateMemTracker(); 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) - ProcessState baseProcessTracker; - BatteryStatsImpl.Uid.Proc curProcBatteryStats; 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 @@ -158,14 +122,7 @@ class ProcessRecord implements WindowProcessListener { 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 lastPssTime; // Last time we retrieved PSS data - long nextPssTime; // Next time we want to request PSS data long lastStateTime; // Last time setProcState changed - long initialIdlePss; // Initial memory pss of process for idle maintenance. - long lastPss; // Last computed memory pss. - long lastSwapPss; // Last computed SwapPss. - long lastCachedPss; // Last computed pss when in cached state. - long lastCachedSwapPss; // Last computed SwapPss when in cached state. 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 @@ -184,13 +141,10 @@ class ProcessRecord implements WindowProcessListener { 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 - int trimMemoryLevel; // Last selected memory trimming level 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 pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for - int pssStatType; // The type of stat collection that we are currently requesting 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 @@ -226,7 +180,6 @@ class ProcessRecord implements WindowProcessListener { // 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. - private boolean mPendingUiClean; // Want to clean up resources from showing UI? 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 @@ -250,13 +203,8 @@ class ProcessRecord implements WindowProcessListener { 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 lastCpuTime; // How long proc has run CPU at last check - long curCpuTime; // How long proc has run CPU most recently - long lastRequestedGc; // When we last asked the app to do a gc - long lastLowMemory; // When we last told the app that memory is low 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 reportLowMemory; // Set to true when waiting to report low mem boolean empty; // Is this an empty background process? private boolean mCached; // Is this a cached process? String adjType; // Debugging: primary thing impacting oom_adj. @@ -267,11 +215,6 @@ class ProcessRecord implements WindowProcessListener { 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 . - // Cache of last retrieve memory info and uptime, to throttle how frequently - // apps can requyest it. - Debug.MemoryInfo lastMemInfo; - long lastMemInfoTime; - // Controller for error dialogs private final ErrorDialogController mDialogController = new ErrorDialogController(); // Controller for driving the process state on the window manager side. @@ -323,8 +266,8 @@ class ProcessRecord implements WindowProcessListener { // Process is currently hosting a backup agent for backup or restore public boolean inFullBackup; - // App is allowed to manage whitelists such as temporary Power Save mode whitelist. - boolean whitelistManager; + // App is allowed to manage allowlists such as temporary Power Save mode allowlist. + boolean mAllowlistManager; // Params used in starting this process. HostingRecord hostingRecord; @@ -335,8 +278,6 @@ class ProcessRecord implements WindowProcessListener { // set of disabled compat changes for the process (all others are enabled) long[] mDisabledCompatChanges; - long mLastRss; // Last computed memory rss. - // 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. @@ -389,6 +330,11 @@ class ProcessRecord implements WindowProcessListener { // another process through service binding. boolean mAllowStartFgs; + /** + * Profiling info of the process, such as PSS, cpu, etc. + */ + final ProcessProfileRecord mProfile; + void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo, long startTime) { this.startUid = startUid; @@ -439,11 +385,7 @@ class ProcessRecord implements WindowProcessListener { pw.print(prefix); pw.print("dir="); pw.print(info.sourceDir); pw.print(" publicDir="); pw.print(info.publicSourceDir); pw.print(" data="); pw.println(info.dataDir); - pw.print(prefix); pw.print("packageList={"); - for (int i=0; i<pkgList.size(); i++) { - if (i > 0) pw.print(", "); - pw.print(pkgList.keyAt(i)); - } + mPkgList.dump(pw, prefix); pw.println("}"); if (pkgDeps != null) { pw.print(prefix); pw.print("packageDependencies={"); @@ -462,21 +404,7 @@ class ProcessRecord implements WindowProcessListener { pw.println(starting); pw.print(prefix); pw.print("lastActivityTime="); TimeUtils.formatDuration(lastActivityTime, nowUptime, pw); - pw.print(" lastPssTime="); - TimeUtils.formatDuration(lastPssTime, nowUptime, pw); - pw.print(" pssStatType="); pw.print(pssStatType); - pw.print(" nextPssTime="); - TimeUtils.formatDuration(nextPssTime, nowUptime, pw); - pw.println(); - pw.print(prefix); pw.print("lastPss="); DebugUtils.printSizeValue(pw, lastPss * 1024); - pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, lastSwapPss * 1024); - pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, lastCachedPss * 1024); - pw.print(" lastCachedSwapPss="); DebugUtils.printSizeValue(pw, - lastCachedSwapPss * 1024); - pw.print(" lastRss="); DebugUtils.printSizeValue(pw, mLastRss * 1024); pw.println(); - pw.print(prefix); pw.print("procStateMemTracker: "); - procStateMemTracker.dumpLine(pw); 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); @@ -489,10 +417,8 @@ class ProcessRecord implements WindowProcessListener { pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup); pw.print(" setSchedGroup="); pw.print(setSchedGroup); pw.print(" systemNoUi="); pw.print(systemNoUi); - pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); pw.print(prefix); pw.print("curProcState="); pw.print(getCurProcState()); pw.print(" mRepProcState="); pw.print(mRepProcState); - pw.print(" pssProcState="); pw.print(pssProcState); pw.print(" setProcState="); pw.print(setProcState); pw.print(" lastStateTime="); TimeUtils.formatDuration(lastStateTime, nowUptime, pw); @@ -507,11 +433,11 @@ class ProcessRecord implements WindowProcessListener { if (mAllowStartFgs) { pw.print(prefix); pw.print("allowStartFgs="); pw.println(mAllowStartFgs); } - if (hasShownUi || mPendingUiClean || hasAboveClient || treatLikeActivity) { + if (hasShownUi || mProfile.hasPendingUiClean() || hasAboveClient || treatLikeActivity) { pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi); - pw.print(" pendingUiClean="); pw.print(mPendingUiClean); - pw.print(" hasAboveClient="); pw.print(hasAboveClient); - pw.print(" treatLikeActivity="); pw.println(treatLikeActivity); + 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); @@ -521,7 +447,7 @@ class ProcessRecord implements WindowProcessListener { } if (notCachedSinceIdle) { pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(notCachedSinceIdle); - pw.print(" initialIdlePss="); pw.println(initialIdlePss); + pw.print(" initialIdlePss="); pw.println(mProfile.getInitialIdlePss()); } if (connectionService != null || connectionGroup != 0) { pw.print(prefix); pw.print("connectionGroup="); pw.print(connectionGroup); @@ -579,20 +505,12 @@ class ProcessRecord implements WindowProcessListener { pw.print(prefix); pw.print("mountMode="); pw.println( DebugUtils.valueToString(Zygote.class, "MOUNT_EXTERNAL_", mountMode)); if (setProcState > ActivityManager.PROCESS_STATE_SERVICE) { - pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime); - if (lastCpuTime > 0) { - pw.print(" timeUsed="); - TimeUtils.formatDuration(curCpuTime - lastCpuTime, pw); - } - pw.print(" whenUnimportant="); - TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw); - pw.println(); - } - pw.print(prefix); pw.print("lastRequestedGc="); - TimeUtils.formatDuration(lastRequestedGc, nowUptime, pw); - pw.print(" lastLowMemory="); - TimeUtils.formatDuration(lastLowMemory, nowUptime, pw); - pw.print(" reportLowMemory="); pw.println(reportLowMemory); + 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); @@ -614,8 +532,8 @@ class ProcessRecord implements WindowProcessListener { } pw.println(); } - if (whitelistManager) { - pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager); + 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); @@ -699,53 +617,33 @@ class ProcessRecord implements WindowProcessListener { curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ; mPersistent = false; removed = false; - freezeUnfreezeTime = lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis(); + mProfile = new ProcessProfileRecord(this); + final long now = SystemClock.uptimeMillis(); + freezeUnfreezeTime = lastStateTime = now; + mProfile.init(now); mWindowProcessController = new WindowProcessController( mService.mActivityTaskManager, info, processName, uid, userId, this, this); - pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode)); + mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode)); setAllowStartFgsByPermission(); } + PackageList getPkgList() { + return mPkgList; + } + public void setPid(int _pid) { pid = _pid; mWindowProcessController.setPid(pid); procStatFile = null; shortStringName = null; stringName = null; + synchronized (mProfile.mProfilerLock) { + mProfile.setPid(pid); + } } public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) { - if (thread == null) { - synchronized (tracker.mLock) { - final ProcessState origBase = baseProcessTracker; - if (origBase != null) { - origBase.setState(ProcessStats.STATE_NOTHING, - tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), - pkgList.mPkgList); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), - ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), - pkgList.valueAt(ipkg).appVersion); - } - origBase.makeInactive(); - } - baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid, - info.longVersionCode, processName); - baseProcessTracker.makeActive(); - for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) { - ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); - if (holder.state != null && holder.state != origBase) { - holder.state.makeInactive(); - } - tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid, - info.longVersionCode, processName); - if (holder.state != baseProcessTracker) { - holder.state.makeActive(); - } - } - } - } + mProfile.onProcessActive(thread, tracker); thread = _thread; mWindowProcessController.setThread(thread); } @@ -753,32 +651,7 @@ class ProcessRecord implements WindowProcessListener { public void makeInactive(ProcessStatsService tracker) { thread = null; mWindowProcessController.setThread(null); - synchronized (tracker.mLock) { - final ProcessState origBase = baseProcessTracker; - if (origBase != null) { - if (origBase != null) { - origBase.setState(ProcessStats.STATE_NOTHING, - tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), - pkgList.mPkgList); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), - ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), - pkgList.valueAt(ipkg).appVersion); - } - origBase.makeInactive(); - } - baseProcessTracker = null; - for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) { - ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); - if (holder.state != null && holder.state != origBase) { - holder.state.makeInactive(); - } - holder.pkg = null; - holder.state = null; - } - } - } + mProfile.onProcessInactive(tracker); } /** @@ -1080,22 +953,25 @@ class ProcessRecord implements WindowProcessListener { * Return true if package has been added false if not */ public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) { - if (!pkgList.containsKey(pkg)) { - synchronized (tracker.mLock) { - ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( - versionCode); - if (baseProcessTracker != null) { - tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode, - processName); - pkgList.put(pkg, holder); - if (holder.state != baseProcessTracker) { - holder.state.makeActive(); + synchronized (tracker.mLock) { + synchronized (mPkgList) { + if (!mPkgList.containsKey(pkg)) { + ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( + versionCode); + final ProcessState baseProcessTracker = mProfile.getBaseProcessTracker(); + if (baseProcessTracker != null) { + tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode, + processName); + mPkgList.put(pkg, holder); + if (holder.state != baseProcessTracker) { + holder.state.makeActive(); + } + } else { + mPkgList.put(pkg, holder); } - } else { - pkgList.put(pkg, holder); + return true; } } - return true; } return false; } @@ -1114,12 +990,12 @@ class ProcessRecord implements WindowProcessListener { mRepProcState = newState; setCurProcState(newState); setCurRawProcState(newState); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), + getPkgList().forEachPackage((pkgName, holder) -> + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + uid, processName, pkgName, ActivityManager.processStateAmToProto(mRepProcState), - pkgList.valueAt(ipkg).appVersion); - } + holder.appVersion) + ); } } @@ -1127,66 +1003,51 @@ class ProcessRecord implements WindowProcessListener { * Delete all packages from list except the package indicated in info */ public void resetPackageList(ProcessStatsService tracker) { - final int N = pkgList.size(); synchronized (tracker.mLock) { - if (baseProcessTracker != null) { - long now = SystemClock.uptimeMillis(); - baseProcessTracker.setState(ProcessStats.STATE_NOTHING, - tracker.getMemFactorLocked(), now, pkgList.mPkgList); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), - ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), - pkgList.valueAt(ipkg).appVersion); - } - if (N != 1) { - for (int i = 0; i < N; i++) { - ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); - if (holder.state != null && holder.state != baseProcessTracker) { - holder.state.makeInactive(); + final ProcessState baseProcessTracker = mProfile.getBaseProcessTracker(); + synchronized (mPkgList) { + final int numOfPkgs = mPkgList.size(); + if (baseProcessTracker != null) { + long now = SystemClock.uptimeMillis(); + baseProcessTracker.setState(ProcessStats.STATE_NOTHING, + tracker.getMemFactorLocked(), now, mPkgList.getPackageListLocked()); + mPkgList.forEachPackage((pkgName, holder) -> + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + uid, processName, pkgName, + ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), + holder.appVersion) + ); + if (numOfPkgs != 1) { + mPkgList.forEachPackageProcessStats(holder -> { + if (holder.state != null && holder.state != baseProcessTracker) { + holder.state.makeInactive(); + } + }); + mPkgList.clear(); + ProcessStats.ProcessStateHolder holder = + new ProcessStats.ProcessStateHolder(info.longVersionCode); + tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid, + info.longVersionCode, processName); + mPkgList.put(info.packageName, holder); + if (holder.state != baseProcessTracker) { + holder.state.makeActive(); } - - } - pkgList.clear(); - ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( - info.longVersionCode); - tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid, - info.longVersionCode, processName); - pkgList.put(info.packageName, holder); - if (holder.state != baseProcessTracker) { - holder.state.makeActive(); } + } else if (numOfPkgs != 1) { + mPkgList.clear(); + mPkgList.put(info.packageName, + new ProcessStats.ProcessStateHolder(info.longVersionCode)); } - } else if (N != 1) { - pkgList.clear(); - pkgList.put(info.packageName, - new ProcessStats.ProcessStateHolder(info.longVersionCode)); } } } - public String[] getPackageList() { - int size = pkgList.size(); - if (size == 0) { - return null; - } - String list[] = new String[size]; - for (int i=0; i<pkgList.size(); i++) { - list[i] = pkgList.keyAt(i); - } - return list; + String[] getPackageList() { + return mPkgList.getPackageList(); } - public List<VersionedPackage> getPackageListWithVersionCode() { - int size = pkgList.size(); - if (size == 0) { - return null; - } - List<VersionedPackage> list = new ArrayList<>(); - for (int i = 0; i < pkgList.size(); i++) { - list.add(new VersionedPackage(pkgList.keyAt(i), pkgList.valueAt(i).appVersion)); - } - return list; + List<VersionedPackage> getPackageListWithVersionCode() { + return mPkgList.getPackageListWithVersionCode(); } WindowProcessController getWindowProcessController() { @@ -1221,12 +1082,12 @@ class ProcessRecord implements WindowProcessListener { void setReportedProcState(int repProcState) { mRepProcState = repProcState; - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), + getPkgList().forEachPackage((pkgName, holder) -> + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + uid, processName, pkgName, ActivityManager.processStateAmToProto(mRepProcState), - pkgList.valueAt(ipkg).appVersion); - } + holder.appVersion) + ); mWindowProcessController.setReportedProcState(repProcState); } @@ -1469,8 +1330,8 @@ class ProcessRecord implements WindowProcessListener { @Override public void clearProfilerIfNeeded() { - synchronized (mService) { - mService.mAppProfiler.clearProfilerLocked(); + synchronized (mService.mAppProfiler.mProfilerLock) { + mService.mAppProfiler.clearProfilerLPf(); } } @@ -1484,15 +1345,10 @@ class ProcessRecord implements WindowProcessListener { @Override public void setPendingUiClean(boolean pendingUiClean) { synchronized (mService) { - mPendingUiClean = pendingUiClean; - mWindowProcessController.setPendingUiClean(pendingUiClean); + mProfile.setPendingUiClean(pendingUiClean); } } - boolean hasPendingUiClean() { - return mPendingUiClean; - } - @Override public void setPendingUiCleanAndForceProcessStateUpTo(int newState) { synchronized (mService) { @@ -1541,7 +1397,9 @@ class ProcessRecord implements WindowProcessListener { synchronized (mService) { waitingToKill = null; if (setProfileProc) { - mService.mAppProfiler.setProfileProcLocked(this); + synchronized (mService.mAppProfiler.mProfilerLock) { + mService.mAppProfiler.setProfileProcLPf(this); + } } if (packageName != null) { addPackage(packageName, versionCode, mService.mProcessStats); @@ -1698,7 +1556,7 @@ class ProcessRecord implements WindowProcessListener { // Check if package is still being loaded boolean isPackageLoading = false; final PackageManagerInternal packageManagerInternal = - mService.getPackageManagerInternalLocked(); + mService.getPackageManagerInternal(); if (aInfo != null && aInfo.packageName != null) { IncrementalStatesInfo incrementalStatesInfo = packageManagerInternal.getIncrementalStatesInfo( @@ -2104,7 +1962,7 @@ class ProcessRecord implements WindowProcessListener { if (!mAllowStartFgs) { // uid is on DeviceIdleController's user/system allowlist // or AMS's FgsStartTempAllowList. - mAllowStartFgs = mService.isWhitelistedForFgsStartLocked(info.uid); + mAllowStartFgs = mService.isAllowlistedForFgsStartLocked(info.uid); } } diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java index 783f150a5e94..22e7faa4bad9 100644 --- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java +++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java @@ -49,8 +49,8 @@ final class StrictModeViolationDialog extends BaseErrorDialog { mProc = app; mResult = result; CharSequence name; - if ((app.pkgList.size() == 1) && - (name=context.getPackageManager().getApplicationLabel(app.info)) != null) { + if ((app.getPkgList().size() == 1) + && (name = context.getPackageManager().getApplicationLabel(app.info)) != null) { setMessage(res.getString( com.android.internal.R.string.smv_application, name.toString(), app.info.processName)); diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index f1945ede7237..36d22bf0f5da 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -41,8 +41,8 @@ public final class UidRecord { long lastBackgroundTime; boolean ephemeral; boolean foregroundServices; - boolean curWhitelist; - boolean setWhitelist; + boolean mCurAllowlist; + boolean mSetAllowlist; boolean idle; boolean setIdle; int numProcs; @@ -157,7 +157,7 @@ public final class UidRecord { proto.write(UidRecordProto.CURRENT, ProcessList.makeProcStateProtoEnum(mCurProcState)); proto.write(UidRecordProto.EPHEMERAL, ephemeral); proto.write(UidRecordProto.FG_SERVICES, foregroundServices); - proto.write(UidRecordProto.WHILELIST, curWhitelist); + proto.write(UidRecordProto.WHILELIST, mCurAllowlist); ProtoUtils.toDuration(proto, UidRecordProto.LAST_BACKGROUND_TIME, lastBackgroundTime, SystemClock.elapsedRealtime()); proto.write(UidRecordProto.IDLE, idle); @@ -191,8 +191,8 @@ public final class UidRecord { if (foregroundServices) { sb.append(" fgServices"); } - if (curWhitelist) { - sb.append(" whitelist"); + if (mCurAllowlist) { + sb.append(" allowlist"); } if (lastBackgroundTime > 0) { sb.append(" bg:"); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 3bbc8373cef3..6dd78e77aafb 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -118,6 +118,7 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; /** * Helper class for {@link ActivityManagerService} responsible for multi-user functionality. @@ -1458,7 +1459,7 @@ class UserController implements Handler.Callback { t.traceBegin("updateConfigurationAndProfileIds"); if (foreground) { // Make sure the old user is no longer considering the display to be on. - mInjector.reportGlobalUsageEventLocked(UsageEvents.Event.SCREEN_NON_INTERACTIVE); + mInjector.reportGlobalUsageEvent(UsageEvents.Event.SCREEN_NON_INTERACTIVE); boolean userSwitchUiEnabled; synchronized (mLock) { mCurrentUserId = userId; @@ -3045,7 +3046,7 @@ class UserController implements Handler.Callback { d.show(); } - void reportGlobalUsageEventLocked(int event) { + void reportGlobalUsageEvent(int event) { mService.reportGlobalUsageEvent(event); } diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index 508bb01e50a8..fded85cd9126 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -20,6 +20,7 @@ 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.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; @@ -46,6 +47,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -55,6 +57,7 @@ import com.android.server.SystemService; import java.io.FileDescriptor; import java.util.List; import java.util.Map; +import java.util.Set; /** * System service that manages app hibernation state, a state apps can enter that means they are @@ -74,6 +77,8 @@ public final class AppHibernationService extends SystemService { private final UserManager mUserManager; @GuardedBy("mLock") private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>(); + @GuardedBy("mLock") + private final Set<String> mGloballyHibernatedPackages = new ArraySet<>(); /** * Initializes the system service. @@ -138,7 +143,7 @@ public final class AppHibernationService extends SystemService { * @param userId the user to check * @return true if package is hibernating for the user */ - public boolean isHibernating(String packageName, int userId) { + boolean isHibernatingForUser(String packageName, int userId) { userId = handleIncomingUser(userId, "isHibernating"); synchronized (mLock) { final Map<String, UserPackageState> packageStates = mUserStates.get(userId); @@ -151,7 +156,19 @@ public final class AppHibernationService extends SystemService { String.format("Package %s is not installed for user %s", packageName, userId)); } - return pkgState != null ? pkgState.hibernated : null; + return pkgState.hibernated; + } + } + + /** + * Whether a package is hibernated globally. This only occurs when a package is hibernating for + * all users and allows us to make optimizations at the package or APK level. + * + * @param packageName package to check + */ + boolean isHibernatingGlobally(String packageName) { + synchronized (mLock) { + return mGloballyHibernatedPackages.contains(packageName); } } @@ -162,7 +179,7 @@ public final class AppHibernationService extends SystemService { * @param userId user * @param isHibernating new hibernation state */ - public void setHibernating(String packageName, int userId, boolean isHibernating) { + void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { userId = handleIncomingUser(userId, "setHibernating"); synchronized (mLock) { if (!mUserStates.contains(userId)) { @@ -180,32 +197,99 @@ public final class AppHibernationService extends SystemService { return; } + if (isHibernating) { + hibernatePackageForUserL(packageName, userId, pkgState); + } else { + unhibernatePackageForUserL(packageName, userId, pkgState); + } + } + } - final long caller = Binder.clearCallingIdentity(); - try { + /** + * Set whether the package should be hibernated globally at a package level, allowing the + * the system to make optimizations at the package or APK level. + * + * @param packageName package to hibernate globally + * @param isHibernating new hibernation state + */ + void setHibernatingGlobally(String packageName, boolean isHibernating) { + if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) { + synchronized (mLock) { if (isHibernating) { - Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage"); - mIActivityManager.forceStopPackage(packageName, userId); - mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId, - null /* observer */); + hibernatePackageGloballyL(packageName); } else { - Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage"); - mIPackageManager.setPackageStoppedState(packageName, false, userId); + unhibernatePackageGloballyL(packageName); } - pkgState.hibernated = isHibernating; - } catch (RemoteException e) { - throw new IllegalStateException( - "Failed to hibernate due to manager not being available", e); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); - Binder.restoreCallingIdentity(caller); } + } + } - // TODO: Support package level hibernation when package is hibernating for all users + /** + * 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) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage"); + final long caller = Binder.clearCallingIdentity(); + try { + mIActivityManager.forceStopPackage(packageName, userId); + mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId, + null /* observer */); + pkgState.hibernated = true; + } catch (RemoteException e) { + throw new IllegalStateException( + "Failed to hibernate due to manager not being available", e); + } finally { + Binder.restoreCallingIdentity(caller); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } } /** + * Remove a package from hibernation for a given user. The caller should hold {@link #mLock}. + * + * @param pkgState package hibernation state + */ + private void unhibernatePackageForUserL(@NonNull String packageName, int userId, + UserPackageState pkgState) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage"); + final long caller = Binder.clearCallingIdentity(); + try { + mIPackageManager.setPackageStoppedState(packageName, false, userId); + pkgState.hibernated = false; + } catch (RemoteException e) { + throw new IllegalStateException( + "Failed to unhibernate due to manager not being available", e); + } finally { + Binder.restoreCallingIdentity(caller); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + } + + /** + * 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) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally"); + // TODO(175830194): Delete vdex/odex when DexManager API is built out + mGloballyHibernatedPackages.add(packageName); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + /** + * Unhibernate a package from global hibernation. The caller should hold {@link #mLock}. + */ + private void unhibernatePackageGloballyL(@NonNull String packageName) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally"); + mGloballyHibernatedPackages.remove(packageName); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + /** * Populates {@link #mUserStates} with the users installed packages. The caller should hold * {@link #mLock}. * @@ -220,8 +304,8 @@ public final class AppHibernationService extends SystemService { throw new IllegalStateException("Package manager not available.", e); } - for (PackageInfo pkg : packageList) { - packages.put(pkg.packageName, new UserPackageState()); + for (int i = 0, size = packageList.size(); i < size; i++) { + packages.put(packageList.get(i).packageName, new UserPackageState()); } mUserStates.put(userId, packages); } @@ -250,6 +334,12 @@ public final class AppHibernationService extends SystemService { } } + private void onPackageRemovedForAllUsers(@NonNull String packageName) { + synchronized (mLock) { + mGloballyHibernatedPackages.remove(packageName); + } + } + /** * Private helper method to get the real user id and enforce permission checks. * @@ -277,13 +367,23 @@ public final class AppHibernationService extends SystemService { } @Override - public boolean isHibernating(String packageName, int userId) { - return mService.isHibernating(packageName, userId); + public boolean isHibernatingForUser(String packageName, int userId) { + return mService.isHibernatingForUser(packageName, userId); + } + + @Override + public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { + mService.setHibernatingForUser(packageName, userId, isHibernating); + } + + @Override + public void setHibernatingGlobally(String packageName, boolean isHibernating) { + mService.setHibernatingGlobally(packageName, isHibernating); } @Override - public void setHibernating(String packageName, int userId, boolean isHibernating) { - mService.setHibernating(packageName, userId, isHibernating); + public boolean isHibernatingGlobally(String packageName) { + return mService.isHibernatingGlobally(packageName); } @Override @@ -322,6 +422,9 @@ public final class AppHibernationService extends SystemService { onPackageAdded(packageName, userId); } else if (ACTION_PACKAGE_REMOVED.equals(action)) { onPackageRemoved(packageName, userId); + if (intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false)) { + onPackageRemovedForAllUsers(packageName); + } } } } diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java index 869885e28958..7d6eea25541a 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java @@ -18,7 +18,6 @@ package com.android.server.apphibernation; import android.os.ShellCommand; import android.os.UserHandle; -import android.text.TextUtils; import java.io.PrintWriter; @@ -27,6 +26,7 @@ import java.io.PrintWriter; */ final class AppHibernationShellCommand extends ShellCommand { private static final String USER_OPT = "--user"; + private static final String GLOBAL_OPT = "--global"; private static final int SUCCESS = 0; private static final int ERROR = -1; private final AppHibernationService mService; @@ -51,7 +51,21 @@ final class AppHibernationShellCommand extends ShellCommand { } private int runSetState() { - int userId = parseUserOption(); + String opt; + boolean setsGlobal = false; + int userId = UserHandle.USER_CURRENT; + while ((opt = getNextOption()) != null) { + switch (opt) { + case USER_OPT: + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case GLOBAL_OPT: + setsGlobal = true; + break; + default: + getErrPrintWriter().println("Error: Unknown option: " + opt); + } + } String pkg = getNextArgRequired(); if (pkg == null) { @@ -66,32 +80,43 @@ final class AppHibernationShellCommand extends ShellCommand { } boolean newState = Boolean.parseBoolean(newStateRaw); - mService.setHibernating(pkg, userId, newState); + if (setsGlobal) { + mService.setHibernatingGlobally(pkg, newState); + } else { + mService.setHibernatingForUser(pkg, userId, newState); + } return SUCCESS; } private int runGetState() { - int userId = parseUserOption(); + String opt; + boolean requestsGlobal = false; + int userId = UserHandle.USER_CURRENT; + while ((opt = getNextOption()) != null) { + switch (opt) { + case USER_OPT: + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case GLOBAL_OPT: + requestsGlobal = true; + break; + default: + getErrPrintWriter().println("Error: Unknown option: " + opt); + } + } String pkg = getNextArgRequired(); if (pkg == null) { getErrPrintWriter().println("Error: No package specified"); return ERROR; } - boolean isHibernating = mService.isHibernating(pkg, userId); + boolean isHibernating = requestsGlobal + ? mService.isHibernatingGlobally(pkg) : mService.isHibernatingForUser(pkg, userId); final PrintWriter pw = getOutPrintWriter(); pw.println(isHibernating); return SUCCESS; } - private int parseUserOption() { - String option = getNextOption(); - if (TextUtils.equals(option, USER_OPT)) { - return UserHandle.parseUserArg(getNextArgRequired()); - } - return UserHandle.USER_CURRENT; - } - @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -99,11 +124,13 @@ final class AppHibernationShellCommand extends ShellCommand { pw.println(" help"); pw.println(" Print this help text."); pw.println(""); - pw.println(" set-state [--user USER_ID] PACKAGE true|false"); - pw.println(" Sets the hibernation state of the package to value specified"); + pw.println(" set-state [--user USER_ID] [--global] PACKAGE true|false"); + pw.println(" Sets the hibernation state of the package to value specified. Optionally"); + pw.println(" may specify a user id or set global hibernation state."); pw.println(""); - pw.println(" get-state [--user USER_ID] PACKAGE"); - pw.println(" Gets the hibernation state of the package"); + pw.println(" get-state [--user USER_ID] [--global] PACKAGE"); + pw.println(" Gets the hibernation state of the package. Optionally may specify a user"); + pw.println(" id or request global hibernation state."); pw.println(""); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 271537a9876c..c5237ab8c8e7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -635,10 +635,15 @@ public class BiometricScheduler { proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null ? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE); proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled); - Slog.d(getTag(), "Total operations: " + mTotalOperationsHandled); - for (int i = 0; i < mRecentOperations.size(); i++) { - Slog.d(getTag(), "Operation: " + mRecentOperations.get(i)); - proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, mRecentOperations.get(i)); + + if (!mRecentOperations.isEmpty()) { + for (int i = 0; i < mRecentOperations.size(); i++) { + proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, mRecentOperations.get(i)); + } + } else { + // TODO:(b/178828362) Unsure why protobuf has a problem decoding when an empty list + // is returned. So, let's just add a no-op for this case. + proto.write(BiometricSchedulerProto.RECENT_OPERATIONS, BiometricsProto.CM_NONE); } proto.flush(); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 8ce6746bc7cb..b455a3f4169f 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -70,7 +70,7 @@ import android.net.NetworkRequest; import android.net.RouteInfo; import android.net.UidRange; import android.net.UidRangeParcel; -import android.net.VpnInfo; +import android.net.UnderlyingNetworkInfo; import android.net.VpnManager; import android.net.VpnService; import android.net.ipsec.ike.ChildSessionCallback; @@ -426,6 +426,7 @@ public class Vpn { mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); + mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); loadAlwaysOnPackage(keyStore); } @@ -1819,12 +1820,12 @@ public class Vpn { * This method should not be called if underlying interfaces field is needed, because it doesn't * have enough data to fill VpnInfo.underlyingIfaces field. */ - public synchronized VpnInfo getVpnInfo() { + public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() { if (!isRunningLocked()) { return null; } - return new VpnInfo(mOwnerUID, mInterface, null); + return new UnderlyingNetworkInfo(mOwnerUID, mInterface, new ArrayList<>()); } public synchronized boolean appliesToUid(int uid) { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index d9ee9a306f07..13dc0b9be21f 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -710,9 +710,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - private void initialize() { + private void initialize(int displayState) { mPowerState = new DisplayPowerState(mBlanker, - mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId); + mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState); if (mColorFadeEnabled) { mColorFadeOnAnimator = ObjectAnimator.ofFloat( @@ -812,11 +812,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mustNotify = !mDisplayReadyLocked; } - // Initialize things the first time the power state is changed. - if (mustInitialize) { - initialize(); - } - // Compute the basic display state using the policy. // We might override this below based on other factors. // Initialise brightness as invalid. @@ -850,6 +845,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } assert(state != Display.STATE_UNKNOWN); + // Initialize things the first time the power state is changed. + if (mustInitialize) { + initialize(state); + } + // Apply the proximity sensor. if (mProximitySensor != null) { if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) { diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 54f30a954c33..173adce00cd9 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -72,7 +72,8 @@ final class DisplayPowerState { private Runnable mCleanListener; - public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId) { + DisplayPowerState( + DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) { mHandler = new Handler(true /*async*/); mChoreographer = Choreographer.getInstance(); mBlanker = blanker; @@ -81,14 +82,14 @@ final class DisplayPowerState { mPhotonicModulator.start(); mDisplayId = displayId; - // At boot time, we know that the screen is on and the electron beam - // animation is not playing. We don't know the screen's brightness though, + // At boot time, we don't know the screen's brightness, // so prepare to set it to a known state when the state is next applied. - // Although we set the brightness to full on here, the display power controller + // Although we set the brightness here, the display power controller // will reset the brightness to a new level immediately before the changes // actually have a chance to be applied. - mScreenState = Display.STATE_ON; - mScreenBrightness = PowerManager.BRIGHTNESS_MAX; + mScreenState = displayState; + mScreenBrightness = (displayState != Display.STATE_OFF) ? PowerManager.BRIGHTNESS_MAX + : PowerManager.BRIGHTNESS_OFF_FLOAT; scheduleScreenUpdate(); mColorFadePrepared = false; diff --git a/services/core/java/com/android/server/graphics/GameManagerService.java b/services/core/java/com/android/server/graphics/GameManagerService.java new file mode 100644 index 000000000000..876f02f38536 --- /dev/null +++ b/services/core/java/com/android/server/graphics/GameManagerService.java @@ -0,0 +1,215 @@ +/* + * 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.graphics; + +import android.annotation.NonNull; +import android.content.Context; +import android.graphics.GameManager; +import android.graphics.GameManager.GameMode; +import android.graphics.IGameManagerService; +import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.ServiceThread; +import com.android.server.SystemService; + +/** + * Service to manage game related features. + * + * <p>Game service is a core service that monitors, coordinates game related features, + * as well as collect metrics.</p> + * + * @hide + */ +public final class GameManagerService extends IGameManagerService.Stub { + public static final String TAG = "GameManagerService"; + + private static final boolean DEBUG = false; + + static final int WRITE_SETTINGS = 1; + static final int REMOVE_SETTINGS = 2; + static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds + + private final Context mContext; + private final Object mLock = new Object(); + private final Handler mHandler; + @GuardedBy("mLock") + private final ArrayMap<Integer, Settings> mSettings = new ArrayMap<>(); + + public GameManagerService(Context context) { + this(context, createServiceThread().getLooper()); + } + + GameManagerService(Context context, Looper looper) { + mContext = context; + mHandler = new SettingsHandler(looper); + } + + class SettingsHandler extends Handler { + + SettingsHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + doHandleMessage(msg); + } + + void doHandleMessage(Message msg) { + switch (msg.what) { + case WRITE_SETTINGS: { + final int userId = (int) msg.obj; + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + synchronized (mLock) { + removeMessages(WRITE_SETTINGS, msg.obj); + if (mSettings.containsKey(userId)) { + Settings userSettings = mSettings.get(userId); + userSettings.writePersistentDataLocked(); + } + } + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + break; + } + case REMOVE_SETTINGS: { + final int userId = (int) msg.obj; + synchronized (mLock) { + // Since the user was removed, ignore previous write message + // and do write here. + removeMessages(WRITE_SETTINGS, msg.obj); + removeMessages(REMOVE_SETTINGS, msg.obj); + if (mSettings.containsKey(userId)) { + final Settings userSettings = mSettings.get(userId); + mSettings.remove(userId); + userSettings.writePersistentDataLocked(); + } + } + break; + } + } + } + } + + /** + * SystemService lifecycle for GameService. + * @hide + */ + public static class Lifecycle extends SystemService { + private GameManagerService mService; + + public Lifecycle(Context context) { + super(context); + } + + @Override + public void onStart() { + mService = new GameManagerService(getContext()); + publishBinderService(Context.GAME_SERVICE, mService); + } + + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_BOOT_COMPLETED) { + mService.onBootCompleted(); + } + } + + @Override + public void onUserStarting(@NonNull TargetUser user) { + mService.onUserStarting(user.getUserIdentifier()); + } + + @Override + public void onUserStopping(@NonNull TargetUser user) { + mService.onUserStopping(user.getUserIdentifier()); + } + } + + //TODO(b/178111358) Add proper permission check and multi-user handling + @Override + public @GameMode int getGameMode(String packageName, int userId) { + synchronized (mLock) { + if (!mSettings.containsKey(userId)) { + return GameManager.GAME_MODE_UNSUPPORTED; + } + Settings userSettings = mSettings.get(userId); + return userSettings.getGameModeLocked(packageName); + } + } + + //TODO(b/178111358) Add proper permission check and multi-user handling + @Override + public void setGameMode(String packageName, @GameMode int gameMode, int userId) { + synchronized (mLock) { + if (!mSettings.containsKey(userId)) { + return; + } + Settings userSettings = mSettings.get(userId); + userSettings.setGameModeLocked(packageName, gameMode); + final Message msg = mHandler.obtainMessage(WRITE_SETTINGS); + msg.obj = userId; + if (!mHandler.hasEqualMessages(WRITE_SETTINGS, userId)) { + mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY); + } + } + } + + /** + * Notified when boot is completed. + */ + @VisibleForTesting + void onBootCompleted() { + Slog.d(TAG, "onBootCompleted"); + } + + void onUserStarting(int userId) { + synchronized (mLock) { + if (mSettings.containsKey(userId)) { + return; + } + + Settings userSettings = new Settings(Environment.getDataSystemDeDirectory(userId)); + mSettings.put(userId, userSettings); + userSettings.readPersistentDataLocked(); + } + } + + void onUserStopping(int userId) { + synchronized (mLock) { + if (!mSettings.containsKey(userId)) { + return; + } + final Message msg = mHandler.obtainMessage(REMOVE_SETTINGS); + msg.obj = userId; + mHandler.sendMessage(msg); + } + } + + private static ServiceThread createServiceThread() { + ServiceThread handlerThread = new ServiceThread(TAG, + Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); + handlerThread.start(); + return handlerThread; + } +} diff --git a/services/core/java/com/android/server/graphics/Settings.java b/services/core/java/com/android/server/graphics/Settings.java new file mode 100644 index 000000000000..bbd84d037d18 --- /dev/null +++ b/services/core/java/com/android/server/graphics/Settings.java @@ -0,0 +1,193 @@ +/* + * 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.graphics; + +import android.graphics.GameManager; +import android.os.FileUtils; +import android.util.ArrayMap; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import android.util.Xml; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Map; + +/** + * Persists all GameService related settings. + * @hide + */ +public class Settings { + + // The XML file follows the below format: + // <?xml> + // <packages> + // <package></package> + // ... + // </packages> + private static final String GAME_SERVICE_FILE_NAME = "game-manager-service.xml"; + + private static final String TAG_PACKAGE = "package"; + private static final String TAG_PACKAGES = "packages"; + private static final String ATTR_NAME = "name"; + private static final String ATTR_GAME_MODE = "gameMode"; + + private final File mSystemDir; + @VisibleForTesting + final AtomicFile mSettingsFile; + + // PackageName -> GameMode + private final ArrayMap<String, Integer> mGameModes = new ArrayMap<>(); + + Settings(File dataDir) { + mSystemDir = new File(dataDir, "system"); + mSystemDir.mkdirs(); + FileUtils.setPermissions(mSystemDir.toString(), + FileUtils.S_IRWXU | FileUtils.S_IRWXG + | FileUtils.S_IROTH | FileUtils.S_IXOTH, + -1, -1); + mSettingsFile = new AtomicFile(new File(mSystemDir, GAME_SERVICE_FILE_NAME)); + } + + /** + * Return the game mode of a given package. + * This operation must be synced with an external lock. + */ + int getGameModeLocked(String packageName) { + if (mGameModes.containsKey(packageName)) { + return mGameModes.get(packageName); + } + return GameManager.GAME_MODE_UNSUPPORTED; + } + + /** + * Set the game mode of a given package. + * This operation must be synced with an external lock. + */ + void setGameModeLocked(String packageName, int gameMode) { + mGameModes.put(packageName, gameMode); + } + + /** + * Write all current game service settings into disk. + * This operation must be synced with an external lock. + */ + void writePersistentDataLocked() { + FileOutputStream fstr = null; + try { + fstr = mSettingsFile.startWrite(); + + final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, TAG_PACKAGES); + for (Map.Entry<String, Integer> entry : mGameModes.entrySet()) { + serializer.startTag(null, TAG_PACKAGE); + serializer.attribute(null, ATTR_NAME, entry.getKey()); + serializer.attributeInt(null, ATTR_GAME_MODE, entry.getValue()); + serializer.endTag(null, TAG_PACKAGE); + } + serializer.endTag(null, TAG_PACKAGES); + + serializer.endDocument(); + + mSettingsFile.finishWrite(fstr); + + FileUtils.setPermissions(mSettingsFile.toString(), + FileUtils.S_IRUSR | FileUtils.S_IWUSR + | FileUtils.S_IRGRP | FileUtils.S_IWGRP, + -1, -1); + return; + } catch (java.io.IOException e) { + mSettingsFile.failWrite(fstr); + Slog.wtf(GameManagerService.TAG, "Unable to write game manager service settings, " + + "current changes will be lost at reboot", e); + } + } + + /** + * Read game service settings from the disk. + * This operation must be synced with an external lock. + */ + boolean readPersistentDataLocked() { + mGameModes.clear(); + + try { + final FileInputStream str = mSettingsFile.openRead(); + + final TypedXmlPullParser parser = Xml.resolvePullParser(str); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + } + if (type != XmlPullParser.START_TAG) { + Slog.wtf(GameManagerService.TAG, + "No start tag found in package manager settings"); + return false; + } + + int outerDepth = parser.getDepth(); + 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_PACKAGE)) { + readPackage(parser); + } else { + Slog.w(GameManagerService.TAG, "Unknown element: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } catch (XmlPullParserException | java.io.IOException e) { + Slog.wtf(GameManagerService.TAG, "Error reading package manager settings", e); + return false; + } + + return true; + } + + private void readPackage(TypedXmlPullParser parser) throws XmlPullParserException, + IOException { + String name = null; + int gameMode = GameManager.GAME_MODE_UNSUPPORTED; + try { + name = parser.getAttributeValue(null, ATTR_NAME); + gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE); + } catch (XmlPullParserException e) { + Slog.wtf(GameManagerService.TAG, "Error reading game mode", e); + } + if (name != null) { + mGameModes.put(name, gameMode); + } else { + XmlUtils.skipCurrentTag(parser); + } + } +} diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index b5b93d6d8ecd..142f64f0a510 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -74,7 +74,6 @@ import android.os.ICancellationSignal; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; -import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource.WorkChain; @@ -1297,10 +1296,7 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - long currentNanos = SystemClock.elapsedRealtimeNanos(); - long deltaMs = NANOSECONDS.toMillis( - location.getElapsedRealtimeAgeNanos(currentNanos)); - return new LocationTime(location.getTime() + deltaMs, currentNanos); + return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos()); } } diff --git a/services/core/java/com/android/server/location/contexthub/OWNERS b/services/core/java/com/android/server/location/contexthub/OWNERS index d4393d6a83d2..90c233030ed1 100644 --- a/services/core/java/com/android/server/location/contexthub/OWNERS +++ b/services/core/java/com/android/server/location/contexthub/OWNERS @@ -1,2 +1,3 @@ arthuri@google.com bduddie@google.com +stange@google.com diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java index 8399f54764e0..a1e18bd5a6bd 100644 --- a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java +++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java @@ -106,6 +106,8 @@ public class ResumeOnRebootServiceProvider { private final Context mContext; private final ComponentName mComponentName; private IResumeOnRebootService mBinder; + @Nullable + ServiceConnection mServiceConnection; private ResumeOnRebootServiceConnection(Context context, @NonNull ComponentName componentName) { @@ -115,17 +117,9 @@ public class ResumeOnRebootServiceProvider { /** Unbind from the service */ public void unbindService() { - mContext.unbindService(new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - } - - @Override - public void onServiceDisconnected(ComponentName name) { - mBinder = null; - - } - }); + if (mServiceConnection != null) { + mContext.unbindService(mServiceConnection); + } } /** Bind to the service */ @@ -134,17 +128,19 @@ public class ResumeOnRebootServiceProvider { CountDownLatch connectionLatch = new CountDownLatch(1); Intent intent = new Intent(); intent.setComponent(mComponentName); - final boolean success = mContext.bindServiceAsUser(intent, new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - mBinder = IResumeOnRebootService.Stub.asInterface(service); - connectionLatch.countDown(); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - } - }, + mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mBinder = IResumeOnRebootService.Stub.asInterface(service); + connectionLatch.countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mBinder = null; + } + }; + final boolean success = mContext.bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, BackgroundThread.getHandler(), UserHandle.SYSTEM); diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index 4faa7903c630..d042b882fee1 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -27,7 +27,7 @@ import static com.android.server.NetworkManagementSocketTagger.kernelToTag; import android.annotation.Nullable; import android.net.INetd; import android.net.NetworkStats; -import android.net.VpnInfo; +import android.net.UnderlyingNetworkInfo; import android.net.util.NetdService; import android.os.RemoteException; import android.os.StrictMode; @@ -81,7 +81,7 @@ public class NetworkStatsFactory { private final Object mPersistentDataLock = new Object(); /** Set containing info about active VPNs and their underlying networks. */ - private volatile VpnInfo[] mVpnInfos = new VpnInfo[0]; + private volatile UnderlyingNetworkInfo[] mUnderlyingNetworkInfos = new UnderlyingNetworkInfo[0]; // A persistent snapshot of cumulative stats since device start @GuardedBy("mPersistentDataLock") @@ -116,8 +116,8 @@ public class NetworkStatsFactory { * * @param vpnArray The snapshot of the currently-running VPNs. */ - public void updateVpnInfos(VpnInfo[] vpnArray) { - mVpnInfos = vpnArray.clone(); + public void updateUnderlyingNetworkInfos(UnderlyingNetworkInfo[] vpnArray) { + mUnderlyingNetworkInfos = vpnArray.clone(); } /** @@ -319,7 +319,7 @@ public class NetworkStatsFactory { // code that will acquire other locks within the system server. See b/134244752. synchronized (mPersistentDataLock) { // Take a reference. If this gets swapped out, we still have the old reference. - final VpnInfo[] vpnArray = mVpnInfos; + final UnderlyingNetworkInfo[] vpnArray = mUnderlyingNetworkInfos; // Take a defensive copy. mPersistSnapshot is mutated in some cases below final NetworkStats prev = mPersistSnapshot.clone(); @@ -369,8 +369,8 @@ public class NetworkStatsFactory { } @GuardedBy("mPersistentDataLock") - private NetworkStats adjustForTunAnd464Xlat( - NetworkStats uidDetailStats, NetworkStats previousStats, VpnInfo[] vpnArray) { + private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats, + NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray) { // Calculate delta from last snapshot final NetworkStats delta = uidDetailStats.subtract(previousStats); @@ -381,8 +381,9 @@ public class NetworkStatsFactory { delta.apply464xlatAdjustments(mStackedIfaces); // Migrate data usage over a VPN to the TUN network. - for (VpnInfo info : vpnArray) { - delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces); + for (UnderlyingNetworkInfo info : vpnArray) { + delta.migrateTun(info.ownerUid, info.iface, + info.underlyingIfaces.toArray(new String[0])); // Filter out debug entries as that may lead to over counting. delta.filterDebugEntries(); } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 4be7b483af16..0ab35a911025 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -104,8 +104,8 @@ import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; +import android.net.UnderlyingNetworkInfo; import android.net.Uri; -import android.net.VpnInfo; import android.net.netstats.provider.INetworkStatsProvider; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.netstats.provider.NetworkStatsProvider; @@ -973,7 +973,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { Network[] defaultNetworks, NetworkState[] networkStates, String activeIface, - VpnInfo[] vpnInfos) { + UnderlyingNetworkInfo[] underlyingNetworkInfos) { checkNetworkStackPermission(mContext); final long token = Binder.clearCallingIdentity(); @@ -986,7 +986,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // Update the VPN underlying interfaces only after the poll is made and tun data has been // migrated. Otherwise the migration would use the new interfaces instead of the ones that // were current when the polled data was transferred. - mStatsFactory.updateVpnInfos(vpnInfos); + mStatsFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); } @Override diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 4eaac2e44da8..7c425698e507 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -3299,17 +3299,28 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + private void linkFile(String relativePath, String fromBase, String toBase) throws IOException { + try { + // Try + if (mIncrementalFileStorages != null && mIncrementalFileStorages.makeLink(relativePath, + fromBase, toBase)) { + return; + } + mPm.mInstaller.linkFile(relativePath, fromBase, toBase); + } catch (InstallerException | IOException e) { + throw new IOException("failed linkOrCreateDir(" + relativePath + ", " + + fromBase + ", " + toBase + ")", e); + } + } + private void linkFiles(List<File> fromFiles, File toDir, File fromDir) throws IOException { for (File fromFile : fromFiles) { final String relativePath = getRelativePath(fromFile, fromDir); - try { - mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), - toDir.getAbsolutePath()); - } catch (InstallerException e) { - throw new IOException("failed linkOrCreateDir(" + relativePath + ", " - + fromDir + ", " + toDir + ")", e); - } + final String fromBase = fromDir.getAbsolutePath(); + final String toBase = toDir.getAbsolutePath(); + + linkFile(relativePath, fromBase, toBase); } Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir); @@ -3577,12 +3588,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Retrying commit. if (mIncrementalFileStorages != null) { - try { - mIncrementalFileStorages.startLoading(); - } catch (IOException e) { - throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), - e.getCause()); - } return false; } @@ -3757,9 +3762,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { }; try { + final PackageInfo pkgInfo = mPm.getPackageInfo(this.params.appPackageName, 0, + userId); + final File inheritedDir = + (pkgInfo != null && pkgInfo.applicationInfo != null) ? new File( + pkgInfo.applicationInfo.getCodePath()).getParentFile() : null; + mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir, - params, statusListener, healthCheckParams, healthListener, addedFiles, - perUidReadTimeouts); + inheritedDir, params, statusListener, healthCheckParams, healthListener, + addedFiles, perUidReadTimeouts); return false; } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c27e670c4c99..f772f63a08ac 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2153,6 +2153,10 @@ public class PackageManagerService extends IPackageManager.Stub void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, boolean requirePermissionWhenSameUser, String message); + SigningDetails getSigningDetails(@NonNull String packageName); + SigningDetails getSigningDetails(int uid); + boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId); + boolean filterAppAccess(String packageName, int callingUid, int userId); } /** @@ -4578,6 +4582,40 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException(errorMessage); } + public SigningDetails getSigningDetails(@NonNull String packageName) { + AndroidPackage p = mPackages.get(packageName); + if (p == null) { + return null; + } + return p.getSigningDetails(); + } + + public SigningDetails getSigningDetails(int uid) { + final int appId = UserHandle.getAppId(uid); + final Object obj = mSettings.getSettingLPr(appId); + if (obj != null) { + if (obj instanceof SharedUserSetting) { + return ((SharedUserSetting) obj).signatures.mSigningDetails; + } else if (obj instanceof PackageSetting) { + final PackageSetting ps = (PackageSetting) obj; + return ps.signatures.mSigningDetails; + } + } + return SigningDetails.UNKNOWN; + } + + public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { + PackageSetting ps = getPackageSetting(pkg.getPackageName()); + return shouldFilterApplicationLocked(ps, callingUid, + userId); + } + + public boolean filterAppAccess(String packageName, int callingUid, int userId) { + PackageSetting ps = getPackageSetting(packageName); + return shouldFilterApplicationLocked(ps, callingUid, + userId); + } + } /** @@ -4728,6 +4766,26 @@ public class PackageManagerService extends IPackageManager.Stub return super.getPackageUidInternal(packageName, flags, userId, callingUid); } } + public SigningDetails getSigningDetails(@NonNull String packageName) { + synchronized (mLock) { + return super.getSigningDetails(packageName); + } + } + public SigningDetails getSigningDetails(int uid) { + synchronized (mLock) { + return super.getSigningDetails(uid); + } + } + public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { + synchronized (mLock) { + return super.filterAppAccess(pkg, callingUid, userId); + } + } + public boolean filterAppAccess(String packageName, int callingUid, int userId) { + synchronized (mLock) { + return super.filterAppAccess(packageName, callingUid, userId); + } + } } @@ -18105,13 +18163,15 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - String codePath = codeFile.getAbsolutePath(); - if (mIncrementalManager != null && isIncrementalPath(codePath)) { - mIncrementalManager.onPackageRemoved(codePath); - } + final boolean isIncremental = (mIncrementalManager != null && isIncrementalPath( + codeFile.getAbsolutePath())); removeCodePathLI(codeFile); + if (isIncremental) { + mIncrementalManager.onPackageRemoved(codeFile); + } + return true; } @@ -26560,6 +26620,22 @@ public class PackageManagerService extends IPackageManager.Stub return snapshotComputer().getPackage(uid); } + private SigningDetails getSigningDetails(@NonNull String packageName) { + return snapshotComputer().getSigningDetails(packageName); + } + + private SigningDetails getSigningDetails(int uid) { + return snapshotComputer().getSigningDetails(uid); + } + + private boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { + return snapshotComputer().filterAppAccess(pkg, callingUid, userId); + } + + private boolean filterAppAccess(String packageName, int callingUid, int userId) { + return snapshotComputer().filterAppAccess(packageName, callingUid, userId); + } + private class PackageManagerInternalImpl extends PackageManagerInternal { @Override public List<ApplicationInfo> getInstalledApplications(int flags, int userId, @@ -26615,29 +26691,11 @@ public class PackageManagerService extends IPackageManager.Stub } private SigningDetails getSigningDetails(@NonNull String packageName) { - synchronized (mLock) { - AndroidPackage p = mPackages.get(packageName); - if (p == null) { - return null; - } - return p.getSigningDetails(); - } + return PackageManagerService.this.getSigningDetails(packageName); } private SigningDetails getSigningDetails(int uid) { - synchronized (mLock) { - final int appId = UserHandle.getAppId(uid); - final Object obj = mSettings.getSettingLPr(appId); - if (obj != null) { - if (obj instanceof SharedUserSetting) { - return ((SharedUserSetting) obj).signatures.mSigningDetails; - } else if (obj instanceof PackageSetting) { - final PackageSetting ps = (PackageSetting) obj; - return ps.signatures.mSigningDetails; - } - } - return SigningDetails.UNKNOWN; - } + return PackageManagerService.this.getSigningDetails(uid); } @Override @@ -26652,20 +26710,12 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { - synchronized (mLock) { - PackageSetting ps = getPackageSetting(pkg.getPackageName()); - return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid, - userId); - } + return PackageManagerService.this.filterAppAccess(pkg, callingUid, userId); } @Override public boolean filterAppAccess(String packageName, int callingUid, int userId) { - synchronized (mLock) { - PackageSetting ps = getPackageSetting(packageName); - return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid, - userId); - } + return PackageManagerService.this.filterAppAccess(packageName, callingUid, userId); } @Override @@ -28304,6 +28354,13 @@ public class PackageManagerService extends IPackageManager.Stub } continue; } + if (ps.appId < Process.FIRST_APPLICATION_UID) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: package is system, appId=" + ps.appId); + } + continue; + } + final AndroidPackage pkg = ps.getPkg(); if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 8c31d88c6f15..aff871118a34 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -3356,11 +3356,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { // - or its signing certificate was rotated from the source package's certificate // - or its signing certificate is a previous signing certificate of the defining // package, and the defining package still trusts the old certificate for permissions + // - or it shares a common signing certificate in its lineage with the defining package, + // and the defining package still trusts the old certificate for permissions // - or it shares the above relationships with the system package final PackageParser.SigningDetails sourceSigningDetails = getSourcePackageSigningDetails(bp); - return pkg.getSigningDetails().hasAncestorOrSelf(sourceSigningDetails) - || sourceSigningDetails.checkCapability( + return sourceSigningDetails.hasCommonSignerWithCapability( pkg.getSigningDetails(), PackageParser.SigningDetails.CertCapabilities.PERMISSION) || pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails()) diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index c073b430c8df..89e7986fc4bc 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3491,15 +3491,27 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { + final int keyCode = event.getKeyCode(); + final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; + boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 + || event.isWakeKey(); + if (!mSystemBooted) { // If we have not yet booted, don't let key events do anything. + // Exception: Wake and power key events are forwarded to PowerManager to allow it to + // wake from quiescent mode during boot. + if (down && (keyCode == KeyEvent.KEYCODE_POWER + || keyCode == KeyEvent.KEYCODE_TV_POWER)) { + wakeUpFromPowerKey(event.getDownTime()); + } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP) + && isWakeKeyWhenScreenOff(keyCode)) { + wakeUpFromWakeKey(event); + } return 0; } final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0; - final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final boolean canceled = event.isCanceled(); - final int keyCode = event.getKeyCode(); final int displayId = event.getDisplayId(); final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0; @@ -3518,8 +3530,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Basic policy based on interactive state. int result; - boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 - || event.isWakeKey(); if (interactive || (isInjected && !isWakeKey)) { // When the device is interactive or the key is injected pass the // key to the application. @@ -4740,7 +4750,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } startedWakingUp(PowerManager.WAKE_REASON_UNKNOWN); finishedWakingUp(PowerManager.WAKE_REASON_UNKNOWN); - screenTurningOn(DEFAULT_DISPLAY, null); + screenTurningOn(DEFAULT_DISPLAY, mDefaultDisplayPolicy.getScreenOnListener()); screenTurnedOn(DEFAULT_DISPLAY); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 54d05124c114..db4b6d0a3005 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -1036,12 +1036,12 @@ public final class PowerManagerService extends SystemService userActivityNoUpdateLocked( now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + updatePowerStateLocked(); if (sQuiescent) { goToSleepNoUpdateLocked(mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); } - updatePowerStateLocked(); } } } @@ -1679,8 +1679,15 @@ public final class PowerManagerService extends SystemService Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid); } - if (eventTime < mLastSleepTime || getWakefulnessLocked() == WAKEFULNESS_AWAKE - || mForceSuspendActive || !mSystemReady) { + if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) { + return false; + } + + if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) { + if (!mBootCompleted && sQuiescent) { + mDirty |= DIRTY_QUIESCENT; + return true; + } return false; } @@ -2821,7 +2828,7 @@ public final class PowerManagerService extends SystemService * * This function recalculates the display power state each time. * - * @return True if the display became ready. + * @return true if the display became ready. */ private boolean updateDisplayPowerStateLocked(int dirty) { final boolean oldDisplayReady = mDisplayReady; @@ -2830,7 +2837,11 @@ public final class PowerManagerService extends SystemService | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED | DIRTY_QUIESCENT)) != 0) { if ((dirty & DIRTY_QUIESCENT) != 0) { - sQuiescent = false; + if (mDisplayReady) { + sQuiescent = false; + } else { + mDirty |= DIRTY_QUIESCENT; + } } final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( @@ -5605,7 +5616,7 @@ public final class PowerManagerService extends SystemService * ignore the proximity sensor. We don't turn off the proximity sensor because * we still want it to be reenabled if it's state changes. * - * @return True if the proximity sensor was successfully ignored and we should + * @return true if the proximity sensor was successfully ignored and we should * consume the key event. */ private boolean interceptPowerKeyDownInternal(KeyEvent event) { diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index 5bd907fb2654..5fe5db69bc8e 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -69,6 +69,8 @@ public class PowerStatsService extends SystemService { private BatteryTrigger mBatteryTrigger; @Nullable private TimerTrigger mTimerTrigger; + @Nullable + private StatsPullAtomCallbackImpl mPullAtomCallback; @VisibleForTesting static class Injector { @@ -119,6 +121,11 @@ public class PowerStatsService extends SystemService { TimerTrigger createTimerTrigger(Context context, PowerStatsLogger powerStatsLogger) { return new TimerTrigger(context, powerStatsLogger, true /* trigger enabled */); } + + StatsPullAtomCallbackImpl createStatsPullerImpl(Context context, + IPowerStatsHALWrapper powerStatsHALWrapper) { + return new StatsPullAtomCallbackImpl(context, powerStatsHALWrapper); + } } private final class BinderService extends Binder { @@ -156,8 +163,10 @@ public class PowerStatsService extends SystemService { @Override public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_BOOT_COMPLETED) { - onSystemServiceReady(); + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + onSystemServicesReady(); + } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { + onBootCompleted(); } } @@ -170,7 +179,18 @@ public class PowerStatsService extends SystemService { publishBinderService(Context.POWER_STATS_SERVICE, new BinderService()); } - private void onSystemServiceReady() { + private void onSystemServicesReady() { + if (getPowerStatsHal().isInitialized()) { + if (DEBUG) Slog.d(TAG, "Starting PowerStatsService statsd pullers"); + + // Only start statsd pullers if initialization is successful. + mPullAtomCallback = mInjector.createStatsPullerImpl(mContext, getPowerStatsHal()); + } else { + Slog.e(TAG, "Failed to start PowerStatsService statsd pullers"); + } + } + + private void onBootCompleted() { if (getPowerStatsHal().isInitialized()) { if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers"); diff --git a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java new file mode 100644 index 000000000000..f8b9601bfd62 --- /dev/null +++ b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java @@ -0,0 +1,152 @@ +/* + * 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.powerstats; + +import android.app.StatsManager; +import android.content.Context; +import android.hardware.power.stats.Channel; +import android.hardware.power.stats.EnergyMeasurement; +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.util.StatsEvent; + +import com.android.internal.util.ConcurrentUtils; +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * StatsPullAtomCallbackImpl is responsible implementing the stats pullers for + * SUBSYSTEM_SLEEP_STATE and ON_DEVICE_POWER_MEASUREMENT statsd atoms. + */ +public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback { + private Context mContext; + private IPowerStatsHALWrapper mPowerStatsHALWrapper; + private Map<Integer, Channel> mChannels = new HashMap(); + private Map<Integer, String> mEntityNames = new HashMap(); + private Map<Integer, Map<Integer, String>> mStateNames = new HashMap();; + + @Override + public int onPullAtom(int atomTag, List<StatsEvent> data) { + switch (atomTag) { + case FrameworkStatsLog.SUBSYSTEM_SLEEP_STATE: + return pullSubsystemSleepState(atomTag, data); + case FrameworkStatsLog.ON_DEVICE_POWER_MEASUREMENT: + return pullOnDevicePowerMeasurement(atomTag, data); + default: + throw new UnsupportedOperationException("Unknown tagId=" + atomTag); + } + } + + private void initPullOnDevicePowerMeasurement() { + Channel[] channels = mPowerStatsHALWrapper.getEnergyMeterInfo(); + if (channels == null) { + return; + } + + for (int i = 0; i < channels.length; i++) { + final Channel channel = channels[i]; + mChannels.put(channel.id, channel); + } + } + + private int pullOnDevicePowerMeasurement(int atomTag, List<StatsEvent> events) { + EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters(new int[0]); + if (energyMeasurements == null) { + return StatsManager.PULL_SKIP; + } + + for (int i = 0; i < energyMeasurements.length; i++) { + // Only report energy measurements that have been accumulated since boot + final EnergyMeasurement energyMeasurement = energyMeasurements[i]; + if (energyMeasurement.durationMs == energyMeasurement.timestampMs) { + events.add(FrameworkStatsLog.buildStatsEvent( + atomTag, + mChannels.get(energyMeasurement.id).subsystem, + mChannels.get(energyMeasurement.id).name, + energyMeasurement.durationMs, + energyMeasurement.energyUWs)); + } + } + + return StatsManager.PULL_SUCCESS; + } + + private void initSubsystemSleepState() { + PowerEntity[] entities = mPowerStatsHALWrapper.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); + } + } + + private int pullSubsystemSleepState(int atomTag, List<StatsEvent> events) { + StateResidencyResult[] results = mPowerStatsHALWrapper.getStateResidency(new int[0]); + if (results == null) { + return StatsManager.PULL_SKIP; + } + for (int i = 0; i < results.length; i++) { + final StateResidencyResult result = results[i]; + for (int j = 0; j < result.stateResidencyData.length; j++) { + final StateResidency stateResidency = result.stateResidencyData[j]; + events.add(FrameworkStatsLog.buildStatsEvent( + atomTag, + mEntityNames.get(result.id), + mStateNames.get(result.id).get(stateResidency.id), + stateResidency.totalStateEntryCount, + stateResidency.totalTimeInStateMs)); + } + } + + return StatsManager.PULL_SUCCESS; + } + + public StatsPullAtomCallbackImpl(Context context, IPowerStatsHALWrapper powerStatsHALWrapper) { + mContext = context; + mPowerStatsHALWrapper = powerStatsHALWrapper; + initPullOnDevicePowerMeasurement(); + initSubsystemSleepState(); + + StatsManager manager = mContext.getSystemService(StatsManager.class); + manager.setPullAtomCallback( + FrameworkStatsLog.SUBSYSTEM_SLEEP_STATE, + null, // use default PullAtomMetadata values + ConcurrentUtils.DIRECT_EXECUTOR, + this); + manager.setPullAtomCallback( + FrameworkStatsLog.ON_DEVICE_POWER_MEASUREMENT, + null, // use default PullAtomMetadata values + ConcurrentUtils.DIRECT_EXECUTOR, + this); + } +} diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 0cc2a6c52b1d..263776c63db6 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -407,8 +407,6 @@ public class StatsPullAtomService extends SystemService { mContext = context; } - private native void nativeInit(); - /** * Use of this StatsPullAtomCallbackImpl means we avoid one class per tagId, which we would * get if we used lambdas. @@ -681,7 +679,6 @@ public class StatsPullAtomService extends SystemService { super.onBootPhase(phase); if (phase == PHASE_SYSTEM_SERVICES_READY) { BackgroundThread.getHandler().post(() -> { - nativeInit(); initializePullersState(); registerPullers(); registerEventListeners(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 081141c5519c..7d2075cca84d 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -279,12 +279,6 @@ public abstract class ActivityTaskManagerInternal { public abstract void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition); /** - * This enforces {@code func} can only be called if either the caller is Recents activity or - * has {@code permission}. - */ - public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func); - - /** * Returns true if the app can close system dialogs. Otherwise it either throws a {@link * SecurityException} or returns false with a logcat message depending on whether the app * targets SDK level {@link android.os.Build.VERSION_CODES#S} or not. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f0db3f9855df..9e2e58fddd18 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -35,6 +35,7 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; @@ -1577,7 +1578,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void startRecentsActivity(Intent intent, long eventTime, @Nullable IRecentsAnimationRunner recentsAnimationRunner) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "startRecentsActivity()"); + enforceTaskPermission("startRecentsActivity()"); final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); @@ -1605,7 +1606,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public final int startActivityFromRecents(int taskId, Bundle bOptions) { - enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS, + mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, "startActivityFromRecents()"); final int callingPid = Binder.getCallingPid(); @@ -1735,7 +1736,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public RootTaskInfo getFocusedRootTaskInfo() throws RemoteException { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getFocusedRootTaskInfo()"); + enforceTaskPermission("getFocusedRootTaskInfo()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -1796,7 +1797,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean removeTask(int taskId) { - enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()"); + mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { @@ -1821,7 +1822,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void removeAllVisibleRecentTasks() { - enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()"); + mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { @@ -1860,8 +1861,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public ActivityManager.TaskDescription getTaskDescription(int id) { synchronized (mGlobalLock) { - enforceCallerIsRecentsOrHasPermission( - MANAGE_ACTIVITY_TASKS, "getTaskDescription()"); + enforceTaskPermission("getTaskDescription()"); final Task tr = mRootWindowContainer.anyTaskForId(id, MATCH_ATTACHED_TASK_OR_RECENT_TASKS); if (tr != null) { @@ -1873,10 +1873,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "setTaskWindowingMode()"); + enforceTaskPermission("setTaskWindowingMode()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { + if (isInLockTaskMode() && windowingMode != WINDOWING_MODE_FULLSCREEN) { + Slog.w(TAG, "setTaskWindowingMode: Is in lock task mode=" + + getLockTaskModeState()); + return false; + } + if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) { return setTaskWindowingModeSplitScreen(taskId, windowingMode, toTop); } @@ -2103,7 +2109,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "moveTaskToRootTask()"); + enforceTaskPermission("moveTaskToRootTask()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { @@ -2141,11 +2147,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("Calling setTaskWindowingModeSplitScreen with non" + "split-screen mode: " + windowingMode); } - if (isInLockTaskMode()) { - Slog.w(TAG, "setTaskWindowingModeSplitScreen: Is in lock task mode=" - + getLockTaskModeState()); - return false; - } final Task task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_ONLY); @@ -2197,8 +2198,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { */ @Override public void removeRootTasksInWindowingModes(int[] windowingModes) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, - "removeRootTasksInWindowingModes()"); + enforceTaskPermission("removeRootTasksInWindowingModes()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); @@ -2212,8 +2212,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void removeRootTasksWithActivityTypes(int[] activityTypes) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, - "removeRootTasksWithActivityTypes()"); + enforceTaskPermission("removeRootTasksWithActivityTypes()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); @@ -2239,7 +2238,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public List<RootTaskInfo> getAllRootTaskInfos() { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getAllRootTaskInfos()"); + enforceTaskPermission("getAllRootTaskInfos()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -2252,7 +2251,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public RootTaskInfo getRootTaskInfo(int windowingMode, int activityType) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getRootTaskInfo()"); + enforceTaskPermission("getRootTaskInfo()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -2265,8 +2264,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public List<RootTaskInfo> getAllRootTaskInfosOnDisplay(int displayId) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, - "getAllRootTaskInfosOnDisplay()"); + enforceTaskPermission("getAllRootTaskInfosOnDisplay()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -2280,7 +2278,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public RootTaskInfo getRootTaskInfoOnDisplay(int windowingMode, int activityType, int displayId) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getRootTaskInfoOnDisplay()"); + enforceTaskPermission("getRootTaskInfoOnDisplay()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -2293,7 +2291,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "cancelRecentsAnimation()"); + enforceTaskPermission("cancelRecentsAnimation()"); final long callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { @@ -2728,16 +2726,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** Sets the task stack listener that gets callbacks when a task stack changes. */ @Override public void registerTaskStackListener(ITaskStackListener listener) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, - "registerTaskStackListener()"); + enforceTaskPermission("registerTaskStackListener()"); mTaskChangeNotificationController.registerTaskStackListener(listener); } /** Unregister a task stack listener so that it stops receiving callbacks. */ @Override public void unregisterTaskStackListener(ITaskStackListener listener) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, - "unregisterTaskStackListener()"); + enforceTaskPermission("unregisterTaskStackListener()"); mTaskChangeNotificationController.unregisterTaskStackListener(listener); } @@ -2790,19 +2786,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { permission, Binder.getCallingPid(), Binder.getCallingUid()); } - /** This can be called with or without the global lock held. */ - void enforceCallerIsRecentsOrHasPermission(String permission, String func) { - if (getRecentTasks().isCallerRecents(Binder.getCallingUid())) { - return; - } - - if (permission.equals(MANAGE_ACTIVITY_TASKS) || permission.equals(MANAGE_ACTIVITY_STACKS)) { - enforceTaskPermission(func); - } else { - mAmInternal.enforceCallingPermission(permission, func); - } - } - /** * Returns true if the app can close system dialogs. Otherwise it either throws a {@link * SecurityException} or returns false with a logcat message depending on whether the app @@ -3263,7 +3246,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void resizePrimarySplitScreen(Rect dockedBounds, Rect tempDockedTaskBounds, Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "resizePrimarySplitScreen()"); + enforceTaskPermission("resizePrimarySplitScreen()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -3301,7 +3284,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void setSplitScreenResizing(boolean resizing) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "setSplitScreenResizing()"); + enforceTaskPermission("setSplitScreenResizing()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -3371,8 +3354,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void cancelTaskWindowTransition(int taskId) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, - "cancelTaskWindowTransition()"); + enforceTaskPermission("cancelTaskWindowTransition()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -3391,7 +3373,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) { - enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()"); + mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()"); final long ident = Binder.clearCallingIdentity(); try { return getTaskSnapshot(taskId, isLowResolution, true /* restoreFromDisk */); @@ -3526,7 +3508,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void stopAppSwitches() { - enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches"); + mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches"); synchronized (mGlobalLock) { mAppSwitchesAllowed = false; mLastStopAppSwitchesTime = SystemClock.uptimeMillis(); @@ -3535,7 +3517,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void resumeAppSwitches() { - enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches"); + mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches"); synchronized (mGlobalLock) { mAppSwitchesAllowed = true; } @@ -5135,11 +5117,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void enforceCallerIsRecentsOrHasPermission(String permission, String func) { - ActivityTaskManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func); - } - - @Override public boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) { return ActivityTaskManagerService.this.checkCanCloseSystemDialogs(pid, uid, packageName); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index f293fc373bfe..ece101d2e605 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -190,6 +190,7 @@ import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.DisplayCutout; +import android.view.DisplayCutout.CutoutPathParserInfo; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IDisplayWindowInsetsController; @@ -1934,18 +1935,22 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) { return WmDisplayCutout.NO_CUTOUT; } - final Insets waterfallInsets = - RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); if (rotation == ROTATION_0) { return WmDisplayCutout.computeSafeInsets( cutout, mInitialDisplayWidth, mInitialDisplayHeight); } + final Insets waterfallInsets = + RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); final Rect[] newBounds = mRotationUtil.getRotatedBounds( cutout.getBoundingRectsAll(), rotation, mInitialDisplayWidth, mInitialDisplayHeight); + final CutoutPathParserInfo info = cutout.getCutoutPathParserInfo(); + final CutoutPathParserInfo newInfo = new CutoutPathParserInfo( + info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(), + info.getCutoutSpec(), rotation, info.getScale()); return WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets), + DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo), rotated ? mInitialDisplayHeight : mInitialDisplayWidth, rotated ? mInitialDisplayWidth : mInitialDisplayHeight); } @@ -4102,8 +4107,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Callbacks when the given type of {@link WindowContainer} animation finished running in the * hierarchy. */ - void onWindowAnimationFinished(int type) { + void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) { if (type == ANIMATION_TYPE_APP_TRANSITION || type == ANIMATION_TYPE_RECENTS) { + // Unfreeze the insets state of the frozen target when the animation finished if exists. + final Task task = wc.asTask(); + if (task != null) { + task.forAllWindows(w -> { + w.clearFrozenInsetsState(); + }, true /* traverseTopToBottom */); + } removeImeSurfaceImmediately(); } } @@ -4180,12 +4192,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mInsetsStateController.getImeSourceProvider().checkShowImePostLayout(); mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent; - mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId, - mLastHasContent, - mTmpApplySurfaceChangesTransactionState.preferredRefreshRate, - mTmpApplySurfaceChangesTransactionState.preferredModeId, - mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing, - true /* inTraversal, must call performTraversalInTrans... below */); + if (!mWmService.mDisplayFrozen) { + mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId, + mLastHasContent, + mTmpApplySurfaceChangesTransactionState.preferredRefreshRate, + mTmpApplySurfaceChangesTransactionState.preferredModeId, + mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing, + true /* inTraversal, must call performTraversalInTrans... below */); + } final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible(); if (wallpaperVisible != mLastWallpaperVisible) { diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 398049fb97c3..267f67759a24 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -74,7 +74,7 @@ class InsetsStateController { private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>(); private final Consumer<WindowState> mDispatchInsetsChanged = w -> { - if (w.isVisible()) { + if (w.isReadyToDispatchInsetsState()) { w.notifyInsetsChanged(); } }; @@ -117,7 +117,8 @@ class InsetsStateController { final @InternalInsetsType int type = provider != null ? provider.getSource().getType() : ITYPE_INVALID; return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(), - isAboveIme(target)); + isAboveIme(target), + target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : mState); } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { @@ -132,7 +133,7 @@ class InsetsStateController { final @WindowingMode int windowingMode = token != null ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); - return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token)); + return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token), mState); } private boolean isAboveIme(WindowContainer target) { @@ -180,9 +181,8 @@ class InsetsStateController { * @see #getInsetsForWindowMetrics */ private InsetsState getInsetsForTarget(@InternalInsetsType int type, - @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) { - InsetsState state = mState; - + @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme, + @NonNull InsetsState state) { if (type != ITYPE_INVALID) { state = new InsetsState(state); state.removeSource(type); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index b1606c506d5b..3d3e31da469c 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -103,6 +103,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>(); private final DragDropController mDragDropController; final boolean mCanAddInternalSystemWindow; + private final boolean mCanStartTasksFromRecents; + // If non-system overlays from this process can be hidden by the user or app using // HIDE_NON_SYSTEM_OVERLAY_WINDOWS. final boolean mOverlaysCanBeHidden; @@ -134,6 +136,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { mCanCreateSystemApplicationOverlay = service.mContext.checkCallingOrSelfPermission(SYSTEM_APPLICATION_OVERLAY) == PERMISSION_GRANTED; + mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission( + START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED; mOverlaysCanBeHidden = !mCanAddInternalSystemWindow && !mService.mAtmInternal.isCallerRecents(mUid); mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER) @@ -374,8 +378,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } else if (hasShortcut) { // Restrict who can start a shortcut drag since it will start the shortcut as the // target shortcut package - mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS, - "performDrag"); + if (!mCanStartTasksFromRecents) { + throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission"); + } for (int i = 0; i < data.getItemCount(); i++) { final ClipData.Item item = data.getItemAt(i); final Intent intent = item.getIntent(); @@ -403,8 +408,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } } else if (hasTask) { // TODO(b/169894807): Consider opening this up for tasks from the same app as the caller - mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS, - "performDrag"); + if (!mCanStartTasksFromRecents) { + throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission"); + } for (int i = 0; i < data.getItemCount(); i++) { final ClipData.Item item = data.getItemAt(i); final Intent intent = item.getIntent(); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 03fca1137e47..dd4ee877c05b 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2684,6 +2684,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @Nullable ArrayList<WindowContainer> sources) { final Task task = asTask(); if (task != null && !enter && !task.isHomeOrRecentsRootTask()) { + if (AppTransition.isClosingTransitOld(transit)) { + // Freezes the insets state when the window is in app exiting transition, to + // ensure the exiting window won't receive unexpected insets changes from the + // next window. + task.forAllWindows(w -> { + w.freezeInsetsState(); + }, true /* traverseTopToBottom */); + } mDisplayContent.showImeScreenshot(); } final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, @@ -2831,7 +2839,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } mSurfaceAnimationSources.clear(); if (mDisplayContent != null) { - mDisplayContent.onWindowAnimationFinished(type); + mDisplayContent.onWindowAnimationFinished(this, type); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8e6a778c351d..931f52933e2a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3997,8 +3997,10 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) { - mAtmInternal.enforceCallerIsRecentsOrHasPermission( - android.Manifest.permission.SET_ORIENTATION, "setIgnoreOrientationRequest()"); + if (!checkCallingPermission( + android.Manifest.permission.SET_ORIENTATION, "setIgnoreOrientationRequest()")) { + throw new SecurityException("Requires SET_ORIENTATION permission"); + } final long origId = Binder.clearCallingIdentity(); try { @@ -6061,8 +6063,10 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setRecentsVisibility(boolean visible) { - mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR, - "setRecentsVisibility()"); + if (!checkCallingPermission( + android.Manifest.permission.STATUS_BAR, "setRecentsVisibility()")) { + throw new SecurityException("Requires STATUS_BAR permission"); + } synchronized (mGlobalLock) { mPolicy.setRecentsVisibilityLw(visible); } @@ -6070,8 +6074,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void hideTransientBars(int displayId) { - mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR, - "hideTransientBars()"); + if (!checkCallingPermission( + android.Manifest.permission.STATUS_BAR, "hideTransientBars()")) { + throw new SecurityException("Requires STATUS_BAR permission"); + } + synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(displayId); if (displayContent != null) { @@ -8373,8 +8380,11 @@ public class WindowManagerService extends IWindowManager.Stub /** Return whether layer tracing is enabled */ public boolean isLayerTracing() { - mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, - "isLayerTracing"); + if (!checkCallingPermission( + android.Manifest.permission.DUMP, "isLayerTracing()")) { + throw new SecurityException("Requires DUMP permission"); + } + final long token = Binder.clearCallingIdentity(); try { Parcel data = null; @@ -8406,8 +8416,11 @@ public class WindowManagerService extends IWindowManager.Stub /** Enable or disable layer tracing */ public void setLayerTracing(boolean enabled) { - mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, - "setLayerTracing"); + if (!checkCallingPermission( + android.Manifest.permission.DUMP, "setLayerTracing()")) { + throw new SecurityException("Requires DUMP permission"); + } + final long token = Binder.clearCallingIdentity(); try { Parcel data = null; @@ -8433,8 +8446,11 @@ public class WindowManagerService extends IWindowManager.Stub /** Set layer tracing flags. */ public void setLayerTracingFlags(int flags) { - mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, - "setLayerTracingFlags"); + if (!checkCallingPermission( + android.Manifest.permission.DUMP, "setLayerTracingFlags")) { + throw new SecurityException("Requires DUMP permission"); + } + final long token = Binder.clearCallingIdentity(); try { Parcel data = null; diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 043844b2e66b..1b81914bbe7c 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.READ_FRAME_BUFFER; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; @@ -264,57 +265,63 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } // Hierarchy changes final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); - for (int i = 0, n = hops.size(); i < n; ++i) { - final WindowContainerTransaction.HierarchyOp hop = hops.get(i); - switch (hop.getType()) { - case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; - if (task != null) { - task.getDisplayArea().setLaunchRootTask(task, - hop.getWindowingModes(), hop.getActivityTypes()); - } else { - throw new IllegalArgumentException( - "Cannot set non-task as launch root: " + wc); - } - break; - } - case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: - effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); - break; - case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: - effects |= setAdjacentRootsHierarchyOp(hop); - break; - case HIERARCHY_OP_TYPE_REORDER: - case HIERARCHY_OP_TYPE_REPARENT: - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - if (wc == null || !wc.isAttached()) { - Slog.e(TAG, "Attempt to operate on detached container: " + wc); - continue; - } - if (syncId >= 0) { - addToSyncSet(syncId, wc); + if (!hops.isEmpty() && mService.isInLockTaskMode()) { + Slog.w(TAG, "Attempt to perform hierarchy operations while in lock task mode..."); + } else { + for (int i = 0, n = hops.size(); i < n; ++i) { + final WindowContainerTransaction.HierarchyOp hop = hops.get(i); + switch (hop.getType()) { + case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { + final WindowContainer wc = WindowContainer.fromBinder( + hop.getContainer()); + final Task task = wc != null ? wc.asTask() : null; + if (task != null) { + task.getDisplayArea().setLaunchRootTask(task, + hop.getWindowingModes(), hop.getActivityTypes()); + } else { + throw new IllegalArgumentException( + "Cannot set non-task as launch root: " + wc); + } + break; } - if (transition != null) { - transition.collect(wc); - if (hop.isReparent()) { - if (wc.getParent() != null) { - // Collect the current parent. It's visibility may change as - // a result of this reparenting. - transition.collect(wc.getParent()); - } - if (hop.getNewParent() != null) { - final WindowContainer parentWc = - WindowContainer.fromBinder(hop.getNewParent()); - if (parentWc == null) { - Slog.e(TAG, "Can't resolve parent window from token"); - continue; + case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: + effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); + break; + case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: + effects |= setAdjacentRootsHierarchyOp(hop); + break; + case HIERARCHY_OP_TYPE_REORDER: + case HIERARCHY_OP_TYPE_REPARENT: + final WindowContainer wc = WindowContainer.fromBinder( + hop.getContainer()); + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on detached container: " + wc); + continue; + } + if (syncId >= 0) { + addToSyncSet(syncId, wc); + } + if (transition != null) { + transition.collect(wc); + if (hop.isReparent()) { + if (wc.getParent() != null) { + // Collect the current parent. It's visibility may change as + // a result of this reparenting. + transition.collect(wc.getParent()); + } + if (hop.getNewParent() != null) { + final WindowContainer parentWc = + WindowContainer.fromBinder(hop.getNewParent()); + if (parentWc == null) { + Slog.e(TAG, "Can't resolve parent window from token"); + continue; + } + transition.collect(parentWc); } - transition.collect(parentWc); } } - } - effects |= sanitizeAndApplyHierarchyOp(wc, hop); + effects |= sanitizeAndApplyHierarchyOp(wc, hop); + } } } // Queue-up bounds-change transactions for tasks which are now organized. Do @@ -412,6 +419,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (windowingMode > -1) { + if (mService.isInLockTaskMode() && windowingMode != WINDOWING_MODE_FULLSCREEN) { + throw new UnsupportedOperationException("Not supported to set non-fullscreen" + + " windowing mode during locked task mode."); + } container.setWindowingMode(windowingMode); } return effects; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 9f3188be7623..9a7823e35a01 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -713,6 +713,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private @Nullable InsetsSourceProvider mControllableInsetProvider; private final InsetsState mRequestedInsetsState = new InsetsState(); + /** + * Freeze the insets state in some cases that not necessarily keeps up-to-date to the client. + * (e.g app exiting transition) + */ + private InsetsState mFrozenInsetsState; + @Nullable InsetsSourceProvider mPendingPositionChanged; private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; @@ -758,6 +764,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } + /** + * Set a freeze state for the window to ignore dispatching its insets state to the client. + * + * Used to keep the insets state for some use cases. (e.g. app exiting transition) + */ + void freezeInsetsState() { + if (mFrozenInsetsState == null) { + mFrozenInsetsState = new InsetsState(getInsetsState(), true /* copySources */); + } + } + + void clearFrozenInsetsState() { + mFrozenInsetsState = null; + } + + InsetsState getFrozenInsetsState() { + return mFrozenInsetsState; + } + + /** + * Check if the insets state of the window is ready to dispatch to the client when invoking + * {@link InsetsStateController#notifyInsetsChanged}. + */ + boolean isReadyToDispatchInsetsState() { + return isVisible() && mFrozenInsetsState == null; + } + void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation, @Rotation int rotation, boolean requested) { // Invisible windows and the wallpaper do not participate in the seamless rotation animation diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 345b2464d75a..cc7e00a43a6e 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -25,8 +25,6 @@ cc_library_static { "gnss/GnssMeasurement.cpp", "gnss/GnssMeasurementCallback.cpp", "gnss/Utils.cpp", - "stats/PowerStatsPuller.cpp", - "stats/SubsystemSleepStatePuller.cpp", "com_android_server_adb_AdbDebuggingManager.cpp", "com_android_server_am_BatteryStatsService.cpp", "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp", @@ -45,7 +43,6 @@ cc_library_static { "com_android_server_SerialService.cpp", "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp", "com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp", - "com_android_server_stats_pull_StatsPullAtomService.cpp", "com_android_server_storage_AppFuseBridge.cpp", "com_android_server_SystemServer.cpp", "com_android_server_tv_TvUinputBridge.cpp", @@ -152,6 +149,7 @@ cc_defaults { "android.hardware.power@1.1", "android.hardware.power-cpp", "android.hardware.power.stats@1.0", + "android.hardware.power.stats-ndk_platform", "android.hardware.thermal@1.0", "android.hardware.tv.input@1.0", "android.hardware.vibrator-unstable-cpp", diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp index 1208354899d5..3f54529211dd 100644 --- a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp +++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp @@ -335,7 +335,8 @@ static jobjectArray nativeReadEnergyMeters(JNIEnv *env, jclass clazz, jintArray field_EM_timestampMs, energyData[i].timestamp); env->SetLongField(energyMeasurement, - field_EM_durationMs, -1); + field_EM_durationMs, + energyData[i].timestamp); env->SetLongField(energyMeasurement, field_EM_energyUWs, energyData[i].energy); diff --git a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp deleted file mode 100644 index b1fbe6461c44..000000000000 --- a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp +++ /dev/null @@ -1,71 +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. - */ - -#define LOG_TAG "StatsPullAtomService" - -#include <jni.h> -#include <log/log.h> -#include <nativehelper/JNIHelp.h> -#include <stats_event.h> -#include <stats_pull_atom_callback.h> -#include <statslog.h> - -#include "stats/PowerStatsPuller.h" -#include "stats/SubsystemSleepStatePuller.h" - -namespace android { - -static server::stats::PowerStatsPuller gPowerStatsPuller; -static server::stats::SubsystemSleepStatePuller gSubsystemSleepStatePuller; - -static AStatsManager_PullAtomCallbackReturn onDevicePowerMeasurementCallback(int32_t atom_tag, - AStatsEventList* data, - void* cookie) { - return gPowerStatsPuller.Pull(atom_tag, data); -} - -static AStatsManager_PullAtomCallbackReturn subsystemSleepStateCallback(int32_t atom_tag, - AStatsEventList* data, - void* cookie) { - return gSubsystemSleepStatePuller.Pull(atom_tag, data); -} - -static void nativeInit(JNIEnv* env, jobject javaObject) { - // on device power measurement - gPowerStatsPuller = server::stats::PowerStatsPuller(); - AStatsManager_setPullAtomCallback(android::util::ON_DEVICE_POWER_MEASUREMENT, - /* metadata= */ nullptr, onDevicePowerMeasurementCallback, - /* cookie= */ nullptr); - - // subsystem sleep state - gSubsystemSleepStatePuller = server::stats::SubsystemSleepStatePuller(); - AStatsManager_setPullAtomCallback(android::util::SUBSYSTEM_SLEEP_STATE, - /* metadata= */ nullptr, subsystemSleepStateCallback, - /* cookie= */ nullptr); -} - -static const JNINativeMethod sMethods[] = {{"nativeInit", "()V", (void*)nativeInit}}; - -int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, "com/android/server/stats/pull/StatsPullAtomService", - sMethods, NELEM(sMethods)); - if (res < 0) { - ALOGE("failed to register native methods"); - } - return res; -} - -} // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 189332107a5d..c5394f3aba69 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -59,7 +59,6 @@ int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker( JNIEnv* env); int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env); -int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); int register_android_server_AdbDebuggingManager(JNIEnv* env); int register_android_server_FaceService(JNIEnv* env); int register_android_server_GpuService(JNIEnv* env); @@ -115,7 +114,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker( env); register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env); - register_android_server_stats_pull_StatsPullAtomService(env); register_android_server_AdbDebuggingManager(env); register_android_server_FaceService(env); register_android_server_GpuService(env); diff --git a/services/core/jni/stats/OWNERS b/services/core/jni/stats/OWNERS deleted file mode 100644 index a61babf32e58..000000000000 --- a/services/core/jni/stats/OWNERS +++ /dev/null @@ -1,9 +0,0 @@ -jeffreyhuang@google.com -joeo@google.com -jtnguyen@google.com -muhammadq@google.com -ruchirr@google.com -singhtejinder@google.com -tsaichristine@google.com -yaochen@google.com -yro@google.com diff --git a/services/core/jni/stats/PowerStatsPuller.cpp b/services/core/jni/stats/PowerStatsPuller.cpp deleted file mode 100644 index 7f788c226c7b..000000000000 --- a/services/core/jni/stats/PowerStatsPuller.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#define LOG_TAG "PowerStatsPuller" - -#include <android/hardware/power/stats/1.0/IPowerStats.h> -#include <log/log.h> -#include <statslog.h> - -#include <vector> - -#include "PowerStatsPuller.h" - -using android::hardware::hidl_vec; -using android::hardware::Return; -using android::hardware::Void; -using android::hardware::power::stats::V1_0::EnergyData; -using android::hardware::power::stats::V1_0::IPowerStats; -using android::hardware::power::stats::V1_0::RailInfo; -using android::hardware::power::stats::V1_0::Status; - -namespace android { -namespace server { -namespace stats { - -static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr; -static std::mutex gPowerStatsHalMutex; -static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt. -static std::vector<RailInfo> gRailInfo; - -struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient { - virtual void serviceDied(uint64_t cookie, - const wp<android::hidl::base::V1_0::IBase>& who) override { - // The HAL just died. Reset all handles to HAL services. - std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); - gPowerStatsHal = nullptr; - } -}; - -static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient(); - -static bool getPowerStatsHalLocked() { - if (gPowerStatsHal == nullptr && gPowerStatsExist) { - gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService(); - if (gPowerStatsHal == nullptr) { - ALOGW("Couldn't load power.stats HAL service"); - gPowerStatsExist = false; - } else { - // Link death recipient to power.stats service handle - hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0); - if (!linked.isOk()) { - ALOGE("Transaction error in linking to power.stats HAL death: %s", - linked.description().c_str()); - gPowerStatsHal = nullptr; - return false; - } else if (!linked) { - ALOGW("Unable to link to power.stats HAL death notifications"); - // We should still continue even though linking failed - } - } - } - return gPowerStatsHal != nullptr; -} - -PowerStatsPuller::PowerStatsPuller() {} - -AStatsManager_PullAtomCallbackReturn PowerStatsPuller::Pull(int32_t atomTag, - AStatsEventList* data) { - std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); - - if (!getPowerStatsHalLocked()) { - return AStatsManager_PULL_SKIP; - } - - // Pull getRailInfo if necessary - if (gRailInfo.empty()) { - bool resultSuccess = true; - Return<void> ret = gPowerStatsHal->getRailInfo( - [&resultSuccess](const hidl_vec<RailInfo>& list, Status status) { - resultSuccess = (status == Status::SUCCESS || status == Status::NOT_SUPPORTED); - if (status != Status::SUCCESS) return; - gRailInfo.reserve(list.size()); - for (size_t i = 0; i < list.size(); ++i) { - gRailInfo.push_back(list[i]); - } - }); - if (!resultSuccess || !ret.isOk()) { - ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str()); - gPowerStatsHal = nullptr; - return AStatsManager_PULL_SKIP; - } - // If SUCCESS but empty, or if NOT_SUPPORTED, then never try again. - if (gRailInfo.empty()) { - ALOGE("power.stats has no rail information"); - gPowerStatsExist = false; // No rail info, so never try again. - gPowerStatsHal = nullptr; - return AStatsManager_PULL_SKIP; - } - } - - // Pull getEnergyData and write the data out - const hidl_vec<uint32_t> desiredRailIndices; // Empty vector indicates we want all. - bool resultSuccess = true; - Return<void> ret = - gPowerStatsHal - ->getEnergyData(desiredRailIndices, - [&data, &resultSuccess](hidl_vec<EnergyData> energyDataList, - Status status) { - resultSuccess = (status == Status::SUCCESS); - if (!resultSuccess) return; - - for (size_t i = 0; i < energyDataList.size(); i++) { - const EnergyData& energyData = energyDataList[i]; - - if (energyData.index >= gRailInfo.size()) { - ALOGE("power.stats getEnergyData() returned an " - "invalid rail index %u.", - energyData.index); - resultSuccess = false; - return; - } - const RailInfo& rail = gRailInfo[energyData.index]; - - android::util::addAStatsEvent( - data, - android::util::ON_DEVICE_POWER_MEASUREMENT, - rail.subsysName.c_str(), rail.railName.c_str(), - energyData.timestamp, energyData.energy); - - ALOGV("power.stat: %s.%s: %llu, %llu", - rail.subsysName.c_str(), rail.railName.c_str(), - (unsigned long long)energyData.timestamp, - (unsigned long long)energyData.energy); - } - }); - if (!resultSuccess || !ret.isOk()) { - ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str()); - gPowerStatsHal = nullptr; - return AStatsManager_PULL_SKIP; - } - return AStatsManager_PULL_SUCCESS; -} - -} // namespace stats -} // namespace server -} // namespace android diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.cpp b/services/core/jni/stats/SubsystemSleepStatePuller.cpp deleted file mode 100644 index 1ba98ef8dab7..000000000000 --- a/services/core/jni/stats/SubsystemSleepStatePuller.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#define LOG_TAG "SubsystemSleepStatePuller" - -#include <log/log.h> -#include <statslog.h> - -#include <android/hardware/power/1.0/IPower.h> -#include <android/hardware/power/1.1/IPower.h> -#include <android/hardware/power/stats/1.0/IPowerStats.h> - -#include <fcntl.h> -#include <hardware/power.h> -#include <hardware_legacy/power.h> -#include <inttypes.h> -#include <semaphore.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> -#include <unordered_map> -#include "SubsystemSleepStatePuller.h" - -using android::hardware::hidl_vec; -using android::hardware::power::V1_0::IPower; -using android::hardware::power::V1_0::PowerStatePlatformSleepState; -using android::hardware::power::V1_0::PowerStateVoter; -using android::hardware::power::V1_1::PowerStateSubsystem; -using android::hardware::power::V1_1::PowerStateSubsystemSleepState; -using android::hardware::power::stats::V1_0::PowerEntityInfo; -using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult; -using android::hardware::power::stats::V1_0::PowerEntityStateSpace; - -using android::hardware::Return; -using android::hardware::Void; - -namespace android { -namespace server { -namespace stats { - -static std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList* data)> - gPuller = {}; - -static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr; -static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr; -static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr; - -static std::unordered_map<uint32_t, std::string> gEntityNames = {}; -static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {}; - -static std::mutex gPowerHalMutex; - -// The caller must be holding gPowerHalMutex. -static void deinitPowerStatsLocked() { - gPowerHalV1_0 = nullptr; - gPowerHalV1_1 = nullptr; - gPowerStatsHalV1_0 = nullptr; -} - -struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient { - virtual void serviceDied(uint64_t cookie, - const wp<android::hidl::base::V1_0::IBase>& who) override { - - // The HAL just died. Reset all handles to HAL services. - std::lock_guard<std::mutex> lock(gPowerHalMutex); - deinitPowerStatsLocked(); - } -}; - -static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient = - new SubsystemSleepStatePullerDeathRecipient(); - -SubsystemSleepStatePuller::SubsystemSleepStatePuller() {} - -// The caller must be holding gPowerHalMutex. -static bool checkResultLocked(const Return<void> &ret, const char* function) { - if (!ret.isOk()) { - ALOGE("%s failed: requested HAL service not available. Description: %s", - function, ret.description().c_str()); - if (ret.isDeadObject()) { - deinitPowerStatsLocked(); - } - return false; - } - return true; -} - -// The caller must be holding gPowerHalMutex. -// gPowerStatsHalV1_0 must not be null -static bool initializePowerStats() { - using android::hardware::power::stats::V1_0::Status; - - // Clear out previous content if we are re-initializing - gEntityNames.clear(); - gStateNames.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 - for (auto info : infos) { - gEntityNames.emplace(info.powerEntityId, info.powerEntityName); - } - }); - if (!checkResultLocked(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); - } - gStateNames.emplace(stateSpace.powerEntityId, stateNames); - } - }); - if (!checkResultLocked(ret, __func__)) { - return false; - } - - return (!gEntityNames.empty()) && (!gStateNames.empty()); -} - -// The caller must be holding gPowerHalMutex. -static bool getPowerStatsHalLocked() { - if(gPowerStatsHalV1_0 == nullptr) { - gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService(); - if (gPowerStatsHalV1_0 == nullptr) { - ALOGE("Unable to get power.stats HAL service."); - return false; - } - - // Link death recipient to power.stats service handle - hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0); - if (!linked.isOk()) { - ALOGE("Transaction error in linking to power.stats HAL death: %s", - linked.description().c_str()); - deinitPowerStatsLocked(); - return false; - } else if (!linked) { - ALOGW("Unable to link to power.stats HAL death notifications"); - // We should still continue even though linking failed - } - return initializePowerStats(); - } - return true; -} - -// The caller must be holding gPowerHalMutex. -static AStatsManager_PullAtomCallbackReturn getIPowerStatsDataLocked(int32_t atomTag, - AStatsEventList* data) { - using android::hardware::power::stats::V1_0::Status; - - if(!getPowerStatsHalLocked()) { - return AStatsManager_PULL_SKIP; - } - // Get power entity state residency data - bool success = false; - Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData( - {}, [&data, &success](auto results, auto status) { - if (status == Status::NOT_SUPPORTED) { - ALOGW("getPowerEntityStateResidencyData is not supported"); - success = false; - return; - } - for (auto result : results) { - for (auto stateResidency : result.stateResidencyData) { - android::util::addAStatsEvent(data, android::util::SUBSYSTEM_SLEEP_STATE, - gEntityNames.at(result.powerEntityId).c_str(), - gStateNames.at(result.powerEntityId) - .at(stateResidency.powerEntityStateId) - .c_str(), - stateResidency.totalStateEntryCount, - stateResidency.totalTimeInStateMs); - } - } - success = true; - }); - // Intentionally not returning early here. - // bool success determines if this succeeded or not. - checkResultLocked(ret, __func__); - if (!success) { - return AStatsManager_PULL_SKIP; - } - return AStatsManager_PULL_SUCCESS; -} - -// The caller must be holding gPowerHalMutex. -static bool getPowerHalLocked() { - if(gPowerHalV1_0 == nullptr) { - gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService(); - if(gPowerHalV1_0 == nullptr) { - ALOGE("Unable to get power HAL service."); - return false; - } - gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0); - - // Link death recipient to power service handle - hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0); - if (!linked.isOk()) { - ALOGE("Transaction error in linking to power HAL death: %s", - linked.description().c_str()); - gPowerHalV1_0 = nullptr; - return false; - } else if (!linked) { - ALOGW("Unable to link to power. death notifications"); - // We should still continue even though linking failed - } - } - return true; -} - -// The caller must be holding gPowerHalMutex. -static AStatsManager_PullAtomCallbackReturn getIPowerDataLocked(int32_t atomTag, - AStatsEventList* data) { - using android::hardware::power::V1_0::Status; - - if(!getPowerHalLocked()) { - return AStatsManager_PULL_SKIP; - } - - Return<void> ret; - ret = gPowerHalV1_0->getPlatformLowPowerStats( - [&data](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]; - android::util::addAStatsEvent(data, android::util::SUBSYSTEM_SLEEP_STATE, - state.name.c_str(), "", - state.totalTransitions, - state.residencyInMsecSinceBoot); - - ALOGV("powerstate: %s, %lld, %lld, %d", state.name.c_str(), - (long long)state.residencyInMsecSinceBoot, - (long long)state.totalTransitions, - state.supportedOnlyInSuspend ? 1 : 0); - for (const auto& voter : state.voters) { - android::util::addAStatsEvent(data, - android::util::SUBSYSTEM_SLEEP_STATE, - state.name.c_str(), voter.name.c_str(), - voter.totalNumberOfTimesVotedSinceBoot, - voter.totalTimeInMsecVotedForSinceBoot); - - ALOGV("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(), - voter.name.c_str(), - (long long)voter.totalTimeInMsecVotedForSinceBoot, - (long long)voter.totalNumberOfTimesVotedSinceBoot); - } - } - }); - if (!checkResultLocked(ret, __func__)) { - return AStatsManager_PULL_SKIP; - } - - // Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1 - sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 = - android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0); - if (gPowerHal_1_1 != nullptr) { - ret = gPowerHal_1_1->getSubsystemLowPowerStats( - [&data](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]; - for (size_t j = 0; j < subsystem.states.size(); j++) { - const PowerStateSubsystemSleepState& state = - subsystem.states[j]; - android::util:: - addAStatsEvent(data, - android::util::SUBSYSTEM_SLEEP_STATE, - subsystem.name.c_str(), - state.name.c_str(), - state.totalTransitions, - state.residencyInMsecSinceBoot); - - ALOGV("subsystemstate: %s, %s, %lld, %lld, %lld", - subsystem.name.c_str(), state.name.c_str(), - (long long)state.residencyInMsecSinceBoot, - (long long)state.totalTransitions, - (long long)state.lastEntryTimestampMs); - } - } - } - }); - } - return AStatsManager_PULL_SUCCESS; -} - -// The caller must be holding gPowerHalMutex. -std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList* data)> -getPullerLocked() { - std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList * data)> - ret = {}; - - // First see if power.stats HAL is available. Fall back to power HAL if - // power.stats HAL is unavailable. - if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) { - ALOGI("Using power.stats HAL"); - ret = getIPowerStatsDataLocked; - } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) { - ALOGI("Using power HAL"); - ret = getIPowerDataLocked; - } - - return ret; -} - -AStatsManager_PullAtomCallbackReturn SubsystemSleepStatePuller::Pull(int32_t atomTag, - AStatsEventList* data) { - std::lock_guard<std::mutex> lock(gPowerHalMutex); - - if(!gPuller) { - gPuller = getPullerLocked(); - } - - if(gPuller) { - return gPuller(atomTag, data); - } - - ALOGE("Unable to load Power Hal or power.stats HAL"); - return AStatsManager_PULL_SKIP; -} - -} // namespace stats -} // namespace server -} // namespace android diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.h b/services/core/jni/stats/SubsystemSleepStatePuller.h deleted file mode 100644 index da9679c68a64..000000000000 --- a/services/core/jni/stats/SubsystemSleepStatePuller.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <stats_event.h> -#include <stats_pull_atom_callback.h> - -namespace android { -namespace server { -namespace stats { - -/** - * Reads hal for sleep states - */ -class SubsystemSleepStatePuller { -public: - SubsystemSleepStatePuller(); - AStatsManager_PullAtomCallbackReturn Pull(int32_t atomTag, AStatsEventList* data); -}; - -} // namespace stats -} // namespace server -} // namespace android diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index d2244286450b..b2efc71860fb 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -118,18 +118,10 @@ binder::Status BinderIncrementalService::openStorage(const std::string& path, binder::Status BinderIncrementalService::createStorage( const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params, - int32_t createMode, - const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener, - const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams, - const ::android::sp<::android::os::incremental::IStorageHealthListener>& healthListener, - const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts, - int32_t* _aidl_return) { - *_aidl_return = - mImpl.createStorage(path, const_cast<content::pm::DataLoaderParamsParcel&&>(params), - android::incremental::IncrementalService::CreateOptions(createMode), - statusListener, - const_cast<StorageHealthCheckParams&&>(healthCheckParams), - healthListener, perUidReadTimeouts); + int32_t createMode, int32_t* _aidl_return) { + *_aidl_return = mImpl.createStorage(path, params, + android::incremental::IncrementalService::CreateOptions( + createMode)); return ok(); } @@ -144,6 +136,21 @@ binder::Status BinderIncrementalService::createLinkedStorage(const std::string& return ok(); } +binder::Status BinderIncrementalService::startLoading( + int32_t storageId, const ::android::content::pm::DataLoaderParamsParcel& params, + const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener, + const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams, + const ::android::sp<IStorageHealthListener>& healthListener, + const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts, + bool* _aidl_return) { + *_aidl_return = + mImpl.startLoading(storageId, const_cast<content::pm::DataLoaderParamsParcel&&>(params), + statusListener, + const_cast<StorageHealthCheckParams&&>(healthCheckParams), + healthListener, perUidReadTimeouts); + return ok(); +} + binder::Status BinderIncrementalService::makeBindMount(int32_t storageId, const std::string& sourcePath, const std::string& targetFullPath, @@ -253,9 +260,16 @@ binder::Status BinderIncrementalService::isFileFullyLoaded(int32_t storageId, return ok(); } +binder::Status BinderIncrementalService::isFullyLoaded(int32_t storageId, int32_t* _aidl_return) { + *_aidl_return = mImpl.getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/true) + .blocksRemainingOrError(); + return ok(); +} + binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId, float* _aidl_return) { - *_aidl_return = mImpl.getLoadingProgress(storageId).getProgress(); + *_aidl_return = + mImpl.getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false).getProgress(); return ok(); } @@ -291,11 +305,6 @@ binder::Status BinderIncrementalService::makeDirectories(int32_t storageId, cons return ok(); } -binder::Status BinderIncrementalService::startLoading(int32_t storageId, bool* _aidl_return) { - *_aidl_return = mImpl.startLoading(storageId); - return ok(); -} - binder::Status BinderIncrementalService::configureNativeBinaries( int32_t storageId, const std::string& apkFullPath, const std::string& libDirRelativePath, const std::string& abi, bool extractNativeLibs, bool* _aidl_return) { diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h index 9a4537a15f31..740c542f9759 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -39,16 +39,18 @@ public: void onInvalidStorage(int mountId); binder::Status openStorage(const std::string& path, int32_t* _aidl_return) final; - binder::Status createStorage( - const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params, - int32_t createMode, + binder::Status createStorage(const ::std::string& path, + const ::android::content::pm::DataLoaderParamsParcel& params, + int32_t createMode, int32_t* _aidl_return) final; + binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId, + int32_t createMode, int32_t* _aidl_return) final; + binder::Status startLoading( + int32_t storageId, const ::android::content::pm::DataLoaderParamsParcel& params, const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener, const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams, const ::android::sp<IStorageHealthListener>& healthListener, const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts, - int32_t* _aidl_return) final; - binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId, - int32_t createMode, int32_t* _aidl_return) final; + bool* _aidl_return) final; binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath, const std::string& targetFullPath, int32_t bindType, int32_t* _aidl_return) final; @@ -71,12 +73,12 @@ public: binder::Status unlink(int32_t storageId, const std::string& path, int32_t* _aidl_return) final; binder::Status isFileFullyLoaded(int32_t storageId, const std::string& path, int32_t* _aidl_return) final; + binder::Status isFullyLoaded(int32_t storageId, int32_t* _aidl_return) final; binder::Status getLoadingProgress(int32_t storageId, float* _aidl_return) final; binder::Status getMetadataByPath(int32_t storageId, const std::string& path, std::vector<uint8_t>* _aidl_return) final; binder::Status getMetadataById(int32_t storageId, const std::vector<uint8_t>& id, std::vector<uint8_t>* _aidl_return) final; - binder::Status startLoading(int32_t storageId, bool* _aidl_return) final; binder::Status deleteStorage(int32_t storageId) final; binder::Status disallowReadLogs(int32_t storageId) final; binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath, diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index c9c5489a50df..132f973ee83b 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -356,7 +356,9 @@ void IncrementalService::onDump(int fd) { dprintf(fd, " storages (%d): {\n", int(mnt.storages.size())); for (auto&& [storageId, storage] : mnt.storages) { dprintf(fd, " [%d] -> [%s] (%d %% loaded) \n", storageId, storage.name.c_str(), - (int)(getLoadingProgressFromPath(mnt, storage.name.c_str()).getProgress() * + (int)(getLoadingProgressFromPath(mnt, storage.name.c_str(), + /*stopOnFirstIncomplete=*/false) + .getProgress() * 100)); } dprintf(fd, " }\n"); @@ -427,10 +429,8 @@ auto IncrementalService::getStorageSlotLocked() -> MountMap::iterator { } StorageId IncrementalService::createStorage( - std::string_view mountPoint, content::pm::DataLoaderParamsParcel&& dataLoaderParams, - CreateOptions options, const DataLoaderStatusListener& statusListener, - StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener& healthListener, - const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) { + std::string_view mountPoint, const content::pm::DataLoaderParamsParcel& dataLoaderParams, + CreateOptions options) { LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options); if (!path::isAbsolute(mountPoint)) { LOG(ERROR) << "path is not absolute: " << mountPoint; @@ -538,13 +538,10 @@ StorageId IncrementalService::createStorage( metadata::Mount m; m.mutable_storage()->set_id(ifs->mountId); m.mutable_loader()->set_type((int)dataLoaderParams.type); - m.mutable_loader()->set_allocated_package_name(&dataLoaderParams.packageName); - m.mutable_loader()->set_allocated_class_name(&dataLoaderParams.className); - m.mutable_loader()->set_allocated_arguments(&dataLoaderParams.arguments); + m.mutable_loader()->set_package_name(dataLoaderParams.packageName); + m.mutable_loader()->set_class_name(dataLoaderParams.className); + m.mutable_loader()->set_arguments(dataLoaderParams.arguments); const auto metadata = m.SerializeAsString(); - m.mutable_loader()->release_arguments(); - m.mutable_loader()->release_class_name(); - m.mutable_loader()->release_package_name(); if (auto err = mIncFs->makeFile(ifs->control, path::join(ifs->root, constants().mount, @@ -568,26 +565,9 @@ StorageId IncrementalService::createStorage( // Done here as well, all data structures are in good state. secondCleanupOnFailure.release(); - // DataLoader. - auto dataLoaderStub = prepareDataLoader(*ifs, std::move(dataLoaderParams), &statusListener, - std::move(healthCheckParams), &healthListener); - CHECK(dataLoaderStub); - mountIt->second = std::move(ifs); l.unlock(); - // Per Uid timeouts. - if (!perUidReadTimeouts.empty()) { - setUidReadTimeouts(mountId, perUidReadTimeouts); - } - - if (mSystemReady.load(std::memory_order_relaxed) && !dataLoaderStub->requestCreate()) { - // failed to create data loader - LOG(ERROR) << "initializeDataLoader() failed"; - deleteStorage(dataLoaderStub->id()); - return kInvalidStorageId; - } - LOG(INFO) << "created storage " << mountId; return mountId; } @@ -634,6 +614,37 @@ StorageId IncrementalService::createLinkedStorage(std::string_view mountPoint, return storageId; } +bool IncrementalService::startLoading(StorageId storage, + content::pm::DataLoaderParamsParcel&& dataLoaderParams, + const DataLoaderStatusListener& statusListener, + StorageHealthCheckParams&& healthCheckParams, + const StorageHealthListener& healthListener, + const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) { + // Per Uid timeouts. + if (!perUidReadTimeouts.empty()) { + setUidReadTimeouts(storage, perUidReadTimeouts); + } + + // Re-initialize DataLoader. + std::unique_lock l(mLock); + const auto ifs = getIfsLocked(storage); + if (!ifs) { + return false; + } + if (ifs->dataLoaderStub) { + ifs->dataLoaderStub->cleanupResources(); + ifs->dataLoaderStub = {}; + } + l.unlock(); + + // DataLoader. + auto dataLoaderStub = prepareDataLoader(*ifs, std::move(dataLoaderParams), &statusListener, + std::move(healthCheckParams), &healthListener); + CHECK(dataLoaderStub); + + return dataLoaderStub->requestStart(); +} + IncrementalService::BindPathMap::const_iterator IncrementalService::findStorageLocked( std::string_view path) const { return findParentPath(mBindsByPath, path); @@ -960,7 +971,12 @@ int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath LOG(ERROR) << "Invalid paths in link(): " << normOldPath << " | " << normNewPath; return -EINVAL; } - return mIncFs->link(ifsSrc->control, normOldPath, normNewPath); + if (auto err = mIncFs->link(ifsSrc->control, normOldPath, normNewPath); err < 0) { + PLOG(ERROR) << "Failed to link " << oldPath << "[" << normOldPath << "]" + << " to " << newPath << "[" << normNewPath << "]"; + return err; + } + return 0; } int IncrementalService::unlink(StorageId storage, std::string_view path) { @@ -1065,23 +1081,6 @@ RawMetadata IncrementalService::getMetadata(StorageId storage, FileId node) cons return mIncFs->getMetadata(ifs->control, node); } -bool IncrementalService::startLoading(StorageId storage) const { - DataLoaderStubPtr dataLoaderStub; - { - std::unique_lock l(mLock); - const auto& ifs = getIfsLocked(storage); - if (!ifs) { - return false; - } - dataLoaderStub = ifs->dataLoaderStub; - if (!dataLoaderStub) { - return false; - } - } - dataLoaderStub->requestStart(); - return true; -} - void IncrementalService::setUidReadTimeouts( StorageId storage, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) { using microseconds = std::chrono::microseconds; @@ -1092,11 +1091,15 @@ void IncrementalService::setUidReadTimeouts( maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs)); } if (maxPendingTimeUs < Constants::minPerUidTimeout) { + LOG(ERROR) << "Skip setting timeouts: maxPendingTime < Constants::minPerUidTimeout" + << duration_cast<milliseconds>(maxPendingTimeUs).count() << "ms < " + << Constants::minPerUidTimeout.count() << "ms"; return; } const auto ifs = getIfs(storage); if (!ifs) { + LOG(ERROR) << "Setting read timeouts failed: invalid storage id: " << storage; return; } @@ -1126,7 +1129,7 @@ void IncrementalService::updateUidReadTimeouts(StorageId storage, Clock::time_po } // Still loading? - const auto progress = getLoadingProgress(storage); + const auto progress = getLoadingProgress(storage, /*stopOnFirstIncomplete=*/true); if (progress.isError()) { // Something is wrong, abort. return clearUidReadTimeouts(storage); @@ -1840,7 +1843,7 @@ int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs, } IncrementalService::LoadingProgress IncrementalService::getLoadingProgress( - StorageId storage) const { + StorageId storage, bool stopOnFirstIncomplete) const { std::unique_lock l(mLock); const auto ifs = getIfsLocked(storage); if (!ifs) { @@ -1853,11 +1856,11 @@ IncrementalService::LoadingProgress IncrementalService::getLoadingProgress( return {-EINVAL, -EINVAL}; } l.unlock(); - return getLoadingProgressFromPath(*ifs, storageInfo->second.name); + return getLoadingProgressFromPath(*ifs, storageInfo->second.name, stopOnFirstIncomplete); } IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPath( - const IncFsMount& ifs, std::string_view storagePath) const { + const IncFsMount& ifs, std::string_view storagePath, bool stopOnFirstIncomplete) const { ssize_t totalBlocks = 0, filledBlocks = 0; const auto filePaths = mFs->listFilesRecursive(storagePath); for (const auto& filePath : filePaths) { @@ -1870,6 +1873,9 @@ IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPa } totalBlocks += totalBlocksCount; filledBlocks += filledBlocksCount; + if (stopOnFirstIncomplete && filledBlocks < totalBlocks) { + break; + } } return {filledBlocks, totalBlocks}; @@ -1877,7 +1883,7 @@ IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPa bool IncrementalService::updateLoadingProgress( StorageId storage, const StorageLoadingProgressListener& progressListener) { - const auto progress = getLoadingProgress(storage); + const auto progress = getLoadingProgress(storage, /*stopOnFirstIncomplete=*/false); if (progress.isError()) { // Failed to get progress from incfs, abort. return false; diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 306612159412..5d53bac777b5 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -113,6 +113,10 @@ public: bool started() const { return totalBlocks > 0; } bool fullyLoaded() const { return !isError() && (totalBlocks == filledBlocks); } + int blocksRemainingOrError() const { + return totalBlocks <= 0 ? totalBlocks : totalBlocks - filledBlocks; + } + float getProgress() const { return totalBlocks < 0 ? totalBlocks @@ -130,15 +134,18 @@ public: void onSystemReady(); StorageId createStorage(std::string_view mountPoint, - content::pm::DataLoaderParamsParcel&& dataLoaderParams, - CreateOptions options, const DataLoaderStatusListener& statusListener, - StorageHealthCheckParams&& healthCheckParams, - const StorageHealthListener& healthListener, - const std::vector<PerUidReadTimeouts>& perUidReadTimeouts); + const content::pm::DataLoaderParamsParcel& dataLoaderParams, + CreateOptions options); StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage, CreateOptions options = CreateOptions::Default); StorageId openStorage(std::string_view path); + bool startLoading(StorageId storage, content::pm::DataLoaderParamsParcel&& dataLoaderParams, + const DataLoaderStatusListener& statusListener, + StorageHealthCheckParams&& healthCheckParams, + const StorageHealthListener& healthListener, + const std::vector<PerUidReadTimeouts>& perUidReadTimeouts); + int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind); int unbind(StorageId storage, std::string_view target); void deleteStorage(StorageId storage); @@ -156,7 +163,9 @@ public: int unlink(StorageId storage, std::string_view path); int isFileFullyLoaded(StorageId storage, std::string_view filePath) const; - LoadingProgress getLoadingProgress(StorageId storage) const; + + LoadingProgress getLoadingProgress(StorageId storage, bool stopOnFirstIncomplete) const; + bool registerLoadingProgressListener(StorageId storage, const StorageLoadingProgressListener& progressListener); bool unregisterLoadingProgressListener(StorageId storage); @@ -167,8 +176,6 @@ public: RawMetadata getMetadata(StorageId storage, std::string_view path) const; RawMetadata getMetadata(StorageId storage, FileId node) const; - bool startLoading(StorageId storage) const; - bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath, std::string_view libDirRelativePath, std::string_view abi, bool extractNativeLibs); @@ -388,7 +395,8 @@ private: binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const; - LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const; + LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path, + bool stopOnFirstIncomplete) const; int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId, std::string_view debugFilePath, std::span<const uint8_t> data) const; diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp index 6fabc589cf95..3573177af185 100644 --- a/services/incremental/ServiceWrappers.cpp +++ b/services/incremental/ServiceWrappers.cpp @@ -210,7 +210,22 @@ public: ErrorCode setUidReadTimeouts(const Control& control, const std::vector<android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts) const final { - return -ENOTSUP; + std::vector<incfs::UidReadTimeouts> timeouts; + timeouts.resize(perUidReadTimeouts.size()); + for (int i = 0, size = perUidReadTimeouts.size(); i < size; ++i) { + auto&& timeout = timeouts[i]; + const auto& perUidTimeout = perUidReadTimeouts[i]; + timeout.uid = perUidTimeout.uid; + timeout.minTimeUs = perUidTimeout.minTimeUs; + timeout.minPendingTimeUs = perUidTimeout.minPendingTimeUs; + timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs; + } + + LOG(ERROR) << "Set read timeouts: " << timeouts.size() << " [" + << (timeouts.empty() ? -1 : timeouts.front().uid) << "@" + << (timeouts.empty() ? -1 : timeouts.front().minTimeUs / 1000) << "ms]"; + + return incfs::setUidReadTimeouts(control, timeouts); } }; diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index f0deba7db01c..8713f9d3d821 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -678,9 +678,9 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) { mVold->mountIncFsFails(); EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -689,9 +689,9 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel) EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0); EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -702,9 +702,9 @@ TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) { EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0); EXPECT_CALL(*mVold, unmountIncFs(_)); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -716,9 +716,9 @@ TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) { EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0); EXPECT_CALL(*mVold, unmountIncFs(_)); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_LT(storageId, 0); } @@ -734,24 +734,24 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) { EXPECT_CALL(*mDataLoader, destroy(_)).Times(0); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); - ASSERT_LT(storageId, 0); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {}); } TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) { - EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1); - EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1); - EXPECT_CALL(*mDataLoader, start(_)).Times(0); + EXPECT_CALL(*mDataLoader, start(_)).Times(1); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {}); mIncrementalService->deleteStorage(storageId); } @@ -759,14 +759,15 @@ TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) { EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2); EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2); - EXPECT_CALL(*mDataLoader, start(_)).Times(0); + EXPECT_CALL(*mDataLoader, start(_)).Times(2); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {}); // Simulated crash/other connection breakage. mDataLoaderManager->setDataLoaderStatusDestroyed(); } @@ -780,12 +781,13 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) { EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); mDataLoaderManager->setDataLoaderStatusCreated(); - ASSERT_TRUE(mIncrementalService->startLoading(storageId)); mDataLoaderManager->setDataLoaderStatusStarted(); } @@ -793,16 +795,17 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) { mDataLoader->initializeCreateOkNoStatus(); EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1); EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); - EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2); + EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1); EXPECT_CALL(*mDataLoader, start(_)).Times(1); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); - ASSERT_TRUE(mIncrementalService->startLoading(storageId)); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); mDataLoaderManager->setDataLoaderStatusCreated(); } @@ -815,10 +818,12 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) { EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); mDataLoaderManager->setDataLoaderStatusUnavailable(); } @@ -836,10 +841,12 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) { EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(1); EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(1); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); mDataLoaderManager->setDataLoaderStatusUnavailable(); ASSERT_NE(nullptr, mLooper->mCallback); ASSERT_NE(nullptr, mLooper->mCallbackData); @@ -890,10 +897,12 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) { kFirstTimestampUs - std::chrono::duration_cast<MCS>(unhealthyTimeout).count(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, std::move(params), listener, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, + std::move(params), listener, {}); // Healthy state, registered for pending reads. ASSERT_NE(nullptr, mLooper->mCallback); @@ -985,10 +994,12 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) { // Not expecting callback removal. EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); ASSERT_GE(mDataLoader->setStorageParams(true), 0); } @@ -1006,10 +1017,12 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndDisabled) { // Not expecting callback removal. EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); ASSERT_GE(mDataLoader->setStorageParams(true), 0); // Now disable. mIncrementalService->disallowReadLogs(storageId); @@ -1032,10 +1045,12 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChang // After callback is called, disable read logs and remove callback. EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(1); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); ASSERT_GE(mDataLoader->setStorageParams(true), 0); ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get()); mAppOpsManager->mStoredCallback->opChanged(0, {}); @@ -1051,10 +1066,12 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionFails) { EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0); EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); ASSERT_LT(mDataLoader->setStorageParams(true), 0); } @@ -1068,10 +1085,12 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionNoCrossUse EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0); EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); ASSERT_LT(mDataLoader->setStorageParams(true), 0); } @@ -1087,18 +1106,20 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) { EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0); EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); ASSERT_LT(mDataLoader->setStorageParams(true), 0); } TEST_F(IncrementalServiceTest, testMakeDirectory) { TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); std::string dir_path("test"); // Expecting incfs to call makeDir on a path like: @@ -1115,9 +1136,9 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) { TEST_F(IncrementalServiceTest, testMakeDirectories) { TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); auto first = "first"sv; auto second = "second"sv; auto third = "third"sv; @@ -1138,9 +1159,9 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithNoFile) { mFs->hasNoFile(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); } @@ -1149,9 +1170,9 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithFailedRanges) { mFs->hasFiles(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); } @@ -1161,9 +1182,9 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccessWithEmptyRanges) { mFs->hasFiles(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); } @@ -1173,9 +1194,9 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccess) { mFs->hasFiles(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); } @@ -1185,10 +1206,12 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) { mFs->hasNoFile(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); - ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress()); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); + ASSERT_EQ(1, + mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false) + .getProgress()); } TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) { @@ -1196,11 +1219,13 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) { mFs->hasFiles(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); - ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId).getProgress()); + ASSERT_EQ(-1, + mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false) + .getProgress()); } TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) { @@ -1208,11 +1233,13 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) { mFs->hasFiles(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3); - ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress()); + ASSERT_EQ(1, + mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false) + .getProgress()); } TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) { @@ -1220,11 +1247,13 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) { mFs->hasFiles(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3); - ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId).getProgress()); + ASSERT_EQ(0.5, + mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false) + .getProgress()); } TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) { @@ -1232,9 +1261,9 @@ TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) { mFs->hasFiles(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); sp<NiceMock<MockStorageLoadingProgressListener>> listener{ new NiceMock<MockStorageLoadingProgressListener>}; NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get(); @@ -1257,9 +1286,9 @@ TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerFailsToGetProg mFs->hasFiles(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, {}, {}, {}); + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); sp<NiceMock<MockStorageLoadingProgressListener>> listener{ new NiceMock<MockStorageLoadingProgressListener>}; NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get(); @@ -1275,10 +1304,12 @@ TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) { TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, {}, - StorageHealthCheckParams{}, listener, {}); + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, listener, + {}); + StorageHealthCheckParams newParams; newParams.blockedTimeoutMs = 10000; newParams.unhealthyTimeoutMs = 20000; @@ -1378,19 +1409,19 @@ TEST_F(IncrementalServiceTest, testPerUidTimeoutsTooShort) { EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1); EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1); - EXPECT_CALL(*mDataLoader, start(_)).Times(0); + EXPECT_CALL(*mDataLoader, start(_)).Times(1); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0); EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, {}, {}, - {}, - createPerUidTimeouts( - {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}})); + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, + createPerUidTimeouts( + {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}})); } TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) { @@ -1410,13 +1441,12 @@ TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) { TemporaryDir tempDir; int storageId = - mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, {}, {}, - {}, - createPerUidTimeouts({{0, 1, 2, 3}, - {1, 2, 3, 4}, - {2, 3, 4, 100000000}})); + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); + mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, + createPerUidTimeouts( + {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 100000000}})); { // Timed callback present -> 0 progress. diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d28c3cc8e2b8..544eedf99c9e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -360,6 +360,8 @@ public final class SystemServer implements Dumpable { private static final String IP_CONNECTIVITY_METRICS_CLASS = "com.android.server.connectivity.IpConnectivityMetrics"; private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService"; + private static final String GAME_MANAGER_SERVICE_CLASS = + "com.android.server.graphics.GameManagerService$Lifecycle"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -2514,6 +2516,10 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("GameManagerService"); + mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS); + t.traceEnd(); + t.traceBegin("StartBootPhaseDeviceSpecificServicesReady"); mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY); t.traceEnd(); diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 091e688743dd..5453de14a5f1 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -45,7 +45,6 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.people.data.DataManager; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -156,6 +155,13 @@ public class PeopleService extends SystemService { final IBinder mService = new IPeopleManager.Stub() { @Override + public ConversationChannel getConversation( + String packageName, int userId, String shortcutId) { + enforceSystemRootOrSystemUI(getContext(), "get conversation"); + return mDataManager.getConversation(packageName, userId, shortcutId); + } + + @Override public ParceledListSlice<ConversationChannel> getRecentConversations() { enforceSystemRootOrSystemUI(getContext(), "get recent conversations"); return new ParceledListSlice<>( diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index 752141598c9d..9a9a17112245 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -222,33 +222,65 @@ public class DataManager { mContext.getPackageName(), intentFilter, callingUserId); } + /** + * Returns a {@link ConversationChannel} with the associated {@code shortcutId} if existent. + * Otherwise, returns null. + */ + @Nullable + public ConversationChannel getConversation(String packageName, int userId, String shortcutId) { + UserData userData = getUnlockedUserData(userId); + if (userData != null) { + PackageData packageData = userData.getPackageData(packageName); + // App may have been uninstalled. + if (packageData != null) { + return getConversationChannel(packageData, shortcutId); + } + } + return null; + } + + @Nullable + private ConversationChannel getConversationChannel(PackageData packageData, String shortcutId) { + ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId); + if (conversationInfo == null) { + return null; + } + int userId = packageData.getUserId(); + String packageName = packageData.getPackageName(); + ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId); + if (shortcutInfo == null) { + return null; + } + int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); + NotificationChannel parentChannel = + mNotificationManagerInternal.getNotificationChannel(packageName, uid, + conversationInfo.getParentNotificationChannelId()); + NotificationChannelGroup parentChannelGroup = null; + if (parentChannel != null) { + parentChannelGroup = + mNotificationManagerInternal.getNotificationChannelGroup(packageName, + uid, parentChannel.getId()); + } + return new ConversationChannel(shortcutInfo, uid, parentChannel, + parentChannelGroup, + conversationInfo.getLastEventTimestamp(), + hasActiveNotifications(packageName, userId, shortcutId)); + } + /** Returns the cached non-customized recent conversations. */ public List<ConversationChannel> getRecentConversations(@UserIdInt int callingUserId) { List<ConversationChannel> conversationChannels = new ArrayList<>(); forPackagesInProfile(callingUserId, packageData -> { - String packageName = packageData.getPackageName(); - int userId = packageData.getUserId(); packageData.forAllConversations(conversationInfo -> { if (!isCachedRecentConversation(conversationInfo)) { return; } String shortcutId = conversationInfo.getShortcutId(); - ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId); - int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); - NotificationChannel parentChannel = - mNotificationManagerInternal.getNotificationChannel(packageName, uid, - conversationInfo.getParentNotificationChannelId()); - if (shortcutInfo == null || parentChannel == null) { + ConversationChannel channel = getConversationChannel(packageData, shortcutId); + if (channel == null || channel.getParentNotificationChannel() == null) { return; } - NotificationChannelGroup parentChannelGroup = - mNotificationManagerInternal.getNotificationChannelGroup(packageName, - uid, parentChannel.getId()); - conversationChannels.add( - new ConversationChannel(shortcutInfo, uid, parentChannel, - parentChannelGroup, - conversationInfo.getLastEventTimestamp(), - hasActiveNotifications(packageName, userId, shortcutId))); + conversationChannels.add(channel); }); }); return conversationChannels; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java index 17f326fbdbce..a18632b8d1b2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java @@ -118,8 +118,12 @@ public class AppChildProcessTest { @After public void tearDown() { LocalServices.removeServiceForTest(PackageManagerInternal.class); - mMockitoSession.finishMocking(); - mHandlerThread.quit(); + if (mMockitoSession != null) { + mMockitoSession.finishMocking(); + } + if (mHandlerThread != null) { + mHandlerThread.quit(); + } } @Test 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 e9a50b36693a..9441ecf74e83 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java @@ -164,24 +164,24 @@ public class ApplicationExitInfoTest { } private void updateExitInfo(ProcessRecord app) { - ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecordLocked(app); + ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app); mAppExitInfoTracker.handleNoteProcessDiedLocked(raw); - mAppExitInfoTracker.recycleRawRecordLocked(raw); + mAppExitInfoTracker.recycleRawRecord(raw); } private void noteAppKill(ProcessRecord app, int reason, int subReason, String msg) { - ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecordLocked(app); + ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app); raw.setReason(reason); raw.setSubReason(subReason); raw.setDescription(msg); mAppExitInfoTracker.handleNoteAppKillLocked(raw); - mAppExitInfoTracker.recycleRawRecordLocked(raw); + mAppExitInfoTracker.recycleRawRecord(raw); } @Test public void testApplicationExitInfo() throws Exception { mAppExitInfoTracker.clearProcessExitInfo(true); - mAppExitInfoTracker.mAppExitInfoLoaded = true; + mAppExitInfoTracker.mAppExitInfoLoaded.set(true); mAppExitInfoTracker.mProcExitStoreDir = new File(mContext.getFilesDir(), AppExitInfoTracker.APP_EXIT_STORE_DIR); assertTrue(FileUtils.createDir(mAppExitInfoTracker.mProcExitStoreDir)); @@ -1001,9 +1001,9 @@ public class ApplicationExitInfoTest { } app.connectionGroup = connectionGroup; app.setProcState = procState; - app.lastMemInfo = spy(new Debug.MemoryInfo()); - app.lastPss = pss; - app.mLastRss = rss; + app.mProfile.setLastMemInfo(spy(new Debug.MemoryInfo())); + app.mProfile.setLastPss(pss); + app.mProfile.setLastRss(rss); return app; } 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 37103965de3b..c82db7305bd8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java @@ -84,7 +84,7 @@ public class CacheOomRankerTest { private int mNextPackageName = 1; private TestExecutor mExecutor = new TestExecutor(); - private CacheOomRanker mCacheOomRanker = new CacheOomRanker(); + private CacheOomRanker mCacheOomRanker; @Before public void setUp() { @@ -107,6 +107,7 @@ public class CacheOomRankerTest { LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); + mCacheOomRanker = new CacheOomRanker(mAms); mCacheOomRanker.init(mExecutor); } @@ -383,7 +384,7 @@ public class CacheOomRankerTest { app.setProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; app.setAdj = setAdj; app.lastActivityTime = lastActivityTime; - app.mLastRss = lastRss; + app.mProfile.setLastRss(lastRss); app.setCached(false); for (int i = 0; i < returnedToCacheCount; ++i) { app.setCached(false); 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 8011ec2c8fa4..7daf357705bc 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -103,6 +103,7 @@ import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicInteger; /** * Test class for {@link OomAdjuster}. @@ -159,8 +160,9 @@ public class MockingOomAdjusterTests { sContext.getMainThreadHandler()); setFieldValue(ActivityManagerService.class, sService, "mContext", sContext); - ProcessList pr = new ProcessList(); - pr.init(sService, new ActiveUids(sService, false), null); + ProcessList pr = spy(new ProcessList()); + pr.mService = sService; + AppProfiler profiler = mock(AppProfiler.class); setFieldValue(ActivityManagerService.class, sService, "mProcessList", pr); setFieldValue(ActivityManagerService.class, sService, "mHandler", @@ -173,13 +175,14 @@ public class MockingOomAdjusterTests { mock(OomAdjProfiler.class)); setFieldValue(ActivityManagerService.class, sService, "mUserController", mock(UserController.class)); - setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", - mock(AppProfiler.class)); - doReturn(new ActivityManagerService.ProcessChangeItem()).when(sService) + setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); + setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); + doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr) .enqueueProcessChangeItemLocked(anyInt(), anyInt()); sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, mock(ActiveUids.class)); sService.mOomAdjuster.mAdjSeq = 10000; + sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); } @AfterClass @@ -206,9 +209,9 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.maxAdj = PERSISTENT_PROC_ADJ; app.setHasTopUi(true); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_ASLEEP; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERSISTENT_PROC_ADJ, SCHED_GROUP_RESTRICTED); @@ -221,7 +224,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.maxAdj = PERSISTENT_PROC_ADJ; app.setHasTopUi(true); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ, @@ -234,10 +237,10 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.maxAdj = PERSISTENT_PROC_ADJ; - doReturn(app).when(sService).getTopAppLocked(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + doReturn(app).when(sService).getTopApp(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); - doReturn(null).when(sService).getTopAppLocked(); + doReturn(null).when(sService).getTopApp(); assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ, SCHED_GROUP_TOP_APP); @@ -249,10 +252,10 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopAppLocked(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + doReturn(app).when(sService).getTopApp(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); - doReturn(null).when(sService).getTopAppLocked(); + doReturn(null).when(sService).getTopApp(); assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_TOP_APP); } @@ -264,7 +267,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState(); app.runningRemoteAnimation = true; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); @@ -277,7 +280,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); doReturn(mock(ActiveInstrumentation.class)).when(app).getActiveInstrumentation(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); doCallRealMethod().when(app).getActiveInstrumentation(); @@ -292,7 +295,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); doReturn(true).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), any(ArraySet.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); doReturn(false).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), any(ArraySet.class)); @@ -306,7 +309,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.executingServices.add(mock(ServiceRecord.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_SERVICE, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -318,12 +321,12 @@ 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(); - doReturn(app).when(sService).getTopAppLocked(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_ASLEEP; + doReturn(app).when(sService).getTopApp(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); - doReturn(null).when(sService).getTopAppLocked(); + doReturn(null).when(sService).getTopApp(); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -335,8 +338,8 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.setCurRawAdj(CACHED_APP_MIN_ADJ); - doReturn(null).when(sService).getTopAppLocked(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + doReturn(null).when(sService).getTopApp(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, @@ -363,7 +366,7 @@ public class MockingOomAdjusterTests { return 0; })).when(wpc).computeOomAdjFromActivities( any(WindowProcessController.ComputeOomAdjCallback.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); doCallRealMethod().when(app).getWindowProcessController(); @@ -379,7 +382,7 @@ public class MockingOomAdjusterTests { WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).hasRecentTasks(); app.lastTopTime = SystemClock.uptimeMillis(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); doCallRealMethod().when(wpc).hasRecentTasks(); @@ -392,7 +395,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -405,7 +408,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.setHasForegroundServices(true, 0); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -418,7 +421,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.setHasOverlayUi(true); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, PERCEPTIBLE_APP_ADJ, @@ -432,7 +435,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.setHasForegroundServices(true, 0); app.lastTopTime = SystemClock.uptimeMillis(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, @@ -445,7 +448,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.forcingToImportant = new Object(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -460,7 +463,7 @@ public class MockingOomAdjusterTests { doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController(); WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isHeavyWeightProcess(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); doReturn(false).when(wpc).isHeavyWeightProcess(); @@ -476,7 +479,7 @@ public class MockingOomAdjusterTests { doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController(); WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_HOME, HOME_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -491,7 +494,7 @@ public class MockingOomAdjusterTests { WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isPreviousProcess(); doReturn(true).when(wpc).hasActivities(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, @@ -506,7 +509,7 @@ public class MockingOomAdjusterTests { BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = app; doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); doReturn(null).when(sService.mBackupTargets).get(anyInt()); @@ -520,7 +523,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); doReturn(true).when(app).hasClientActivities(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.setProcState); @@ -532,7 +535,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.treatLikeActivity = true; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.setProcState); @@ -549,7 +552,7 @@ public class MockingOomAdjusterTests { s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); app.startService(s); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_B_ADJ, SCHED_GROUP_BACKGROUND); @@ -561,7 +564,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.maxAdj = PERCEPTIBLE_LOW_APP_ADJ; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, PERCEPTIBLE_LOW_APP_ADJ, @@ -575,8 +578,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.setCached(false); app.setCurRawAdj(SERVICE_ADJ); - doReturn(null).when(sService).getTopAppLocked(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + 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); @@ -593,7 +596,7 @@ public class MockingOomAdjusterTests { s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); app.startService(s); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -609,11 +612,11 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, client, null, Context.BIND_WAIVE_PRIORITY, mock(IBinder.class)); s.startRequested = true; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopAppLocked(); + doReturn(client).when(sService).getTopApp(); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); - doReturn(null).when(sService).getTopAppLocked(); + doReturn(null).when(sService).getTopApp(); assertProcStates(app, PROCESS_STATE_SERVICE, UNKNOWN_ADJ, SCHED_GROUP_BACKGROUND); } @@ -627,7 +630,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, Context.BIND_WAIVE_PRIORITY | Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.setProcState); @@ -647,7 +650,7 @@ public class MockingOomAdjusterTests { setFieldValue(ConnectionRecord.class, cr, "activity", mock(ActivityServiceConnectionsHolder.class)); doReturn(true).when(cr.activity).isActivityVisible(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(FOREGROUND_APP_ADJ, app.setAdj); @@ -660,7 +663,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); bindService(app, app, null, 0, mock(IBinder.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, UNKNOWN_ADJ, SCHED_GROUP_BACKGROUND); @@ -675,7 +678,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.treatLikeActivity = true; bindService(app, client, null, 0, mock(IBinder.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PROCESS_STATE_CACHED_EMPTY, app.setProcState); @@ -695,10 +698,10 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, Context.BIND_ALLOW_OOM_MANAGEMENT, mock(IBinder.class)); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopAppLocked(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + doReturn(client).when(sService).getTopApp(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); - doReturn(null).when(sService).getTopAppLocked(); + doReturn(null).when(sService).getTopApp(); assertEquals(PREVIOUS_APP_ADJ, app.setAdj); } @@ -713,7 +716,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.maxAdj = PERSISTENT_PROC_ADJ; client.setHasTopUi(true); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ, @@ -729,7 +732,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, Context.BIND_IMPORTANT, mock(IBinder.class)); client.executingServices.add(mock(ServiceRecord.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(FOREGROUND_APP_ADJ, app.setAdj); @@ -744,10 +747,10 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, 0, mock(IBinder.class)); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopAppLocked(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + doReturn(client).when(sService).getTopApp(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); - doReturn(null).when(sService).getTopAppLocked(); + doReturn(null).when(sService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); } @@ -761,7 +764,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.maxAdj = PERSISTENT_PROC_ADJ; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.setProcState); @@ -776,7 +779,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.maxAdj = PERSISTENT_PROC_ADJ; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.setProcState); @@ -791,7 +794,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, 0, mock(IBinder.class)); client.setHasForegroundServices(true, 0); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, app.setProcState); @@ -808,7 +811,7 @@ public class MockingOomAdjusterTests { BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = client; doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); doReturn(null).when(sService.mBackupTargets).get(anyInt()); @@ -829,7 +832,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); client.runningRemoteAnimation = true; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.setAdj); @@ -844,7 +847,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class)); client.runningRemoteAnimation = true; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PERCEPTIBLE_APP_ADJ, app.setAdj); @@ -859,7 +862,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, 0, mock(IBinder.class)); client.setHasOverlayUi(true); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PERCEPTIBLE_APP_ADJ, app.setAdj); @@ -874,7 +877,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, 0, mock(IBinder.class)); client.runningRemoteAnimation = true; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(VISIBLE_APP_ADJ, app.setAdj); @@ -889,7 +892,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, Context.BIND_IMPORTANT_BACKGROUND, mock(IBinder.class)); client.setHasOverlayUi(true); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.setProcState); @@ -915,7 +918,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, false); client.treatLikeActivity = true; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, UNKNOWN_ADJ, SCHED_GROUP_BACKGROUND); @@ -930,10 +933,10 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, false); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopAppLocked(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + doReturn(client).when(sService).getTopApp(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); - doReturn(null).when(sService).getTopAppLocked(); + doReturn(null).when(sService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT); } @@ -947,7 +950,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.setHasForegroundServices(true, 0); bindProvider(app, client, null, null, false); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -962,7 +965,7 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, true); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, FOREGROUND_APP_ADJ, @@ -975,7 +978,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.lastProviderTime = SystemClock.uptimeMillis(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, @@ -994,10 +997,10 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client, client2, null, 0, mock(IBinder.class)); doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client2).when(sService).getTopAppLocked(); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + doReturn(client2).when(sService).getTopApp(); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); - doReturn(null).when(sService).getTopAppLocked(); + doReturn(null).when(sService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); @@ -1015,7 +1018,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(app, client2, null, 0, mock(IBinder.class)); client2.setHasForegroundServices(true, 0); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1034,7 +1037,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client, client2, null, 0, mock(IBinder.class)); client2.setHasForegroundServices(true, 0); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1059,7 +1062,7 @@ public class MockingOomAdjusterTests { lru.add(app); lru.add(client); lru.add(client2); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, true, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1070,7 +1073,7 @@ public class MockingOomAdjusterTests { SCHED_GROUP_DEFAULT); client2.setHasForegroundServices(false, 0); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(client2, true, OomAdjuster.OOM_ADJ_REASON_NONE); assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.setProcState); @@ -1096,7 +1099,7 @@ public class MockingOomAdjusterTests { lru.add(app); lru.add(client); lru.add(client2); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, true, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1124,7 +1127,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.forcingToImportant = new Object(); bindService(app, client3, null, 0, mock(IBinder.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1150,7 +1153,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.forcingToImportant = new Object(); bindService(app, client3, null, 0, mock(IBinder.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -1178,7 +1181,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.forcingToImportant = new Object(); bindService(app, client4, null, 0, mock(IBinder.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -1208,7 +1211,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.setHasForegroundServices(true, 0); bindService(app, client4, null, 0, mock(IBinder.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1234,7 +1237,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.forcingToImportant = new Object(); bindService(app, client3, null, 0, mock(IBinder.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1253,7 +1256,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); client2.setHasForegroundServices(true, 0); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1273,7 +1276,7 @@ public class MockingOomAdjusterTests { bindProvider(client, client2, null, null, false); client2.setHasForegroundServices(true, 0); bindService(client2, app, null, 0, mock(IBinder.class)); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1292,7 +1295,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); client2.setHasForegroundServices(true, 0); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1312,7 +1315,7 @@ public class MockingOomAdjusterTests { bindProvider(client, client2, null, null, false); client2.setHasForegroundServices(true, 0); bindProvider(client2, app, null, null, false); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1332,7 +1335,7 @@ public class MockingOomAdjusterTests { lru.clear(); lru.add(app); lru.add(app2); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); lru.clear(); @@ -1356,7 +1359,7 @@ public class MockingOomAdjusterTests { lru.clear(); lru.add(app); lru.add(app2); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); lru.clear(); @@ -1384,7 +1387,7 @@ public class MockingOomAdjusterTests { lru.add(app); lru.add(app2); lru.add(app3); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); lru.clear(); @@ -1435,7 +1438,7 @@ public class MockingOomAdjusterTests { lru.add(app3); lru.add(app4); lru.add(app5); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); lru.clear(); @@ -1481,7 +1484,7 @@ public class MockingOomAdjusterTests { lru.add(app3); lru.add(app2); lru.add(app); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); lru.clear(); @@ -1527,7 +1530,7 @@ public class MockingOomAdjusterTests { lru.add(app2); lru.add(app); lru.add(app5); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); lru.clear(); @@ -1573,7 +1576,7 @@ public class MockingOomAdjusterTests { lru.add(app3); lru.add(app4); lru.add(app5); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); lru.clear(); @@ -1618,7 +1621,7 @@ public class MockingOomAdjusterTests { lru.add(app3); lru.add(app2); lru.add(app); - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.mNumServiceProcs = 3; sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); lru.clear(); @@ -1678,7 +1681,7 @@ public class MockingOomAdjusterTests { app2.startService(s2); app2.hasShownUi = false; - sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services"); @@ -1773,11 +1776,12 @@ public class MockingOomAdjusterTests { ai.longVersionCode = versionCode; ai.targetSdkVersion = targetSdkVersion; ProcessRecord app = new ProcessRecord(service, ai, processName, uid); + final ProcessProfileRecord profile = app.mProfile; app.thread = mock(IApplicationThread.class); app.lastActivityTime = lastActivityTime; - app.lastPssTime = lastPssTime; - app.nextPssTime = nextPssTime; - app.lastPss = lastPss; + profile.setLastPssTime(lastPssTime); + profile.setNextPssTime(nextPssTime); + profile.setLastPss(lastPss); app.maxAdj = maxAdj; app.setRawAdj = setRawAdj; app.curAdj = curAdj; diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java index c9fcd0233bef..c5e1ed1f04c6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java @@ -37,6 +37,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -82,6 +83,13 @@ public class UsageStatsServiceTest { mService.onStart(); } + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + } + @Test public void testUsageEventListener() throws Exception { TestUsageEventListener listener = new TestUsageEventListener(); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index df8a720d59b9..110bb21b5851 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -164,9 +164,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { @SmallTest public void testRegisterSystemActionWithoutPermission() throws Exception { doThrow(SecurityException.class).when(mMockSecurityPolicy) - .enforceCallerIsRecentsOrHasPermission( - Manifest.permission.MANAGE_ACCESSIBILITY, - AccessibilityManagerService.FUNCTION_REGISTER_SYSTEM_ACTION); + .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); try { mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID); @@ -185,9 +183,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { @SmallTest public void testUnregisterSystemActionWithoutPermission() throws Exception { doThrow(SecurityException.class).when(mMockSecurityPolicy) - .enforceCallerIsRecentsOrHasPermission( - Manifest.permission.MANAGE_ACCESSIBILITY, - AccessibilityManagerService.FUNCTION_UNREGISTER_SYSTEM_ACTION); + .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); try { mA11yms.unregisterSystemAction(ACTION_ID); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java index cc8ac86d6b59..c7e7c7861370 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java @@ -51,9 +51,6 @@ import android.util.ArraySet; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityWindowInfo; -import com.android.server.LocalServices; -import com.android.server.wm.ActivityTaskManagerInternal; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -123,7 +120,6 @@ public class AccessibilitySecurityPolicyTest { @Mock private AccessibilityWindowManager mMockA11yWindowManager; @Mock private AppWidgetManagerInternal mMockAppWidgetManager; @Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager; - @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal; @Before public void setUp() { @@ -132,10 +128,6 @@ public class AccessibilitySecurityPolicyTest { when(mMockContext.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager); when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager); - LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); - LocalServices.addService( - ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal); - mA11ySecurityPolicy = new AccessibilitySecurityPolicy(mMockContext, mMockA11yUserManager); mA11ySecurityPolicy.setAccessibilityWindowManager(mMockA11yWindowManager); mA11ySecurityPolicy.setAppWidgetManager(mMockAppWidgetManager); @@ -570,10 +562,4 @@ public class AccessibilitySecurityPolicyTest { APP_UID, PACKAGE_NAME); } - @Test - public void testEnforceCallerIsRecentsOrHasPermission() { - mA11ySecurityPolicy.enforceCallerIsRecentsOrHasPermission(PERMISSION, FUNCTION); - verify(mMockActivityTaskManagerInternal).enforceCallerIsRecentsOrHasPermission( - PERMISSION, FUNCTION); - } } 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 4f58c87e61ea..7355b80940a4 100644 --- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java @@ -40,6 +40,9 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + /** * Test class for {@link OomAdjuster}. * @@ -76,6 +79,9 @@ public class OomAdjusterTests { sService.mConstants = new ActivityManagerConstants(sContext, sService, sContext.getMainThreadHandler()); + final AppProfiler profiler = mock(AppProfiler.class); + setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); + setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null); LocalServices.addService(UsageStatsManagerInternal.class, mock(UsageStatsManagerInternal.class)); @@ -83,6 +89,18 @@ public class OomAdjusterTests { }); } + 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) { + } + } + @AfterClass public static void tearDownOnce() { LocalServices.removeServiceForTest(PackageManagerInternal.class); 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 ded14b89d01c..263efa670a67 100644 --- a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java @@ -42,6 +42,8 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.Collections; /** @@ -67,6 +69,11 @@ public class ProcessRecordTests { sService.mActivityTaskManager = new ActivityTaskManagerService(sContext); sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper()); sService.mAtmInternal = sService.mActivityTaskManager.getAtmInternal(); + final AppProfiler profiler = mock(AppProfiler.class); + setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); + setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); + final ProcessList processList = new ProcessList(); + setFieldValue(ActivityManagerService.class, sService, "mProcessList", processList); }); // Avoid NPE when initializing {@link ProcessRecord#mWindowProcessController}. @@ -76,6 +83,18 @@ public class ProcessRecordTests { doReturn(sysUiName).when(packageManagerInternal).getSystemUiServiceComponent(); } + 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) { + } + } + @AfterClass public static void tearDownOnce() { LocalServices.removeServiceForTest(PackageManagerInternal.class); diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 784718b5f51e..9ffb50176f0e 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -770,7 +770,7 @@ public class UserControllerTest { } @Override - void reportGlobalUsageEventLocked(int event) { + void reportGlobalUsageEvent(int event) { } @Override 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 d0370b6c25e9..45bca6829553 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -64,6 +64,8 @@ public final class AppHibernationServiceTest { private static final int USER_ID_1 = 1; private static final int USER_ID_2 = 2; + private final List<UserInfo> mUserInfos = new ArrayList<>(); + private AppHibernationService mAppHibernationService; private BroadcastReceiver mBroadcastReceiver; @Mock @@ -88,47 +90,42 @@ public final class AppHibernationServiceTest { verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any()); mBroadcastReceiver = mReceiverCaptor.getValue(); - List<UserInfo> userList = new ArrayList<>(); - userList.add(new UserInfo(USER_ID_1, "user 1", 0 /* flags */)); - doReturn(userList).when(mUserManager).getUsers(); - - List<PackageInfo> userPackages = new ArrayList<>(); - userPackages.add(makePackageInfo(PACKAGE_NAME_1)); - - doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager) - .getInstalledPackages(anyInt(), eq(USER_ID_1)); + doReturn(mUserInfos).when(mUserManager).getUsers(); doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any(), any()); + addUser(USER_ID_1); mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); } @Test - public void testSetHibernating_packageIsHibernating() { + public void testSetHibernatingForUser_packageIsHibernating() throws RemoteException { // WHEN we hibernate a package for a user - mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true); + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true); // THEN the package is marked hibernating for the user - assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1)); + assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1)); } @Test - public void testSetHibernating_newPackageAdded_packageIsHibernating() { + public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating() + throws RemoteException { // 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 */)); intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1); mBroadcastReceiver.onReceive(mContext, intent); - mAppHibernationService.setHibernating(PACKAGE_NAME_2, USER_ID_1, true); + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_1, true); // THEN the new package is hibernated - assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_2, USER_ID_1)); + assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_2, USER_ID_1)); } @Test - public void testSetHibernating_newUserAdded_packageIsHibernating() throws RemoteException { + public void testSetHibernatingForUser_newUserAdded_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)); @@ -138,16 +135,17 @@ public final class AppHibernationServiceTest { intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2); mBroadcastReceiver.onReceive(mContext, intent); - mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_2, true); + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true); // THEN the new user's package is hibernated - assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_2)); + assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2)); } @Test - public void testIsHibernating_packageReplaced_stillReturnsHibernating() { + public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() + throws RemoteException { // GIVEN a package is currently hibernated - mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true); + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true); // WHEN the package is removed but marked as replacing Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED, @@ -157,7 +155,38 @@ public final class AppHibernationServiceTest { mBroadcastReceiver.onReceive(mContext, intent); // THEN the package is still hibernating - assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1)); + assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1)); + } + + @Test + public void testSetHibernatingGlobally_packageIsHibernatingGlobally() throws RemoteException { + // WHEN we hibernate a package + mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true); + + // THEN the package is marked hibernating for the user + assertTrue(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1)); + } + + /** + * Add a mock user with one package. Must be called before + * {@link AppHibernationService#onBootPhase(int)} to work properly. + */ + private void addUser(int userId) throws RemoteException { + 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 + */ + private void addUser(int userId, String[] packageNames) throws RemoteException { + mUserInfos.add(new UserInfo(userId, "user_" + userId, 0 /* flags */)); + List<PackageInfo> userPackages = new ArrayList<>(); + for (String pkgName : packageNames) { + userPackages.add(makePackageInfo(pkgName)); + } + doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager) + .getInstalledPackages(anyInt(), eq(userId)); } private static PackageInfo makePackageInfo(String packageName) { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 57b0d01b3025..2d457260b8fe 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -196,7 +196,8 @@ public class BiometricSchedulerTest { BiometricSchedulerProto bsp = getDump(true /* clearSchedulerBuffer */); assertEquals(BiometricsProto.CM_NONE, bsp.currentOperation); assertEquals(0, bsp.totalOperations); - assertEquals(0, bsp.recentOperations.length); + // TODO:(b/178828362) See bug and/or commit message :/ + // assertEquals(0, bsp.recentOperations.length); // Pretend the scheduler is busy enrolling, and check the proto dump again. final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken, @@ -207,7 +208,11 @@ public class BiometricSchedulerTest { assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation); // No operations have completed yet assertEquals(0, bsp.totalOperations); - assertEquals(0, bsp.recentOperations.length); + + // TODO:(b/178828362) See bug and/or commit message :/ + assertEquals(1, bsp.recentOperations.length); + assertEquals(BiometricsProto.CM_NONE, bsp.recentOperations[0]); + // Finish this operation, so the next scheduled one can start client.getCallback().onClientFinished(client, true); } @@ -223,7 +228,8 @@ public class BiometricSchedulerTest { assertEquals(BiometricsProto.CM_ENROLL, bsp.currentOperation); // No operations have completed yet assertEquals(0, bsp.totalOperations); - assertEquals(0, bsp.recentOperations.length); + // TODO:(b/178828362) See bug and/or commit message :/ + // assertEquals(0, bsp.recentOperations.length); // Finish this operation, so the next scheduled one can start client.getCallback().onClientFinished(client, true); @@ -265,7 +271,10 @@ public class BiometricSchedulerTest { // RecentOperations queue is cleared (by the previous dump) bsp = getDump(true /* clearSchedulerBuffer */); - assertEquals(0, bsp.recentOperations.length); + + // TODO:(b/178828362) See bug and/or commit message :/ + assertEquals(1, bsp.recentOperations.length); + assertEquals(BiometricsProto.CM_NONE, bsp.recentOperations[0]); } @Test diff --git a/services/tests/servicestests/src/com/android/server/graphics/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/graphics/GameManagerServiceSettingsTests.java new file mode 100644 index 000000000000..6fc6b9eb3da3 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/graphics/GameManagerServiceSettingsTests.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.graphics; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import android.content.Context; +import android.util.AtomicFile; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class GameManagerServiceSettingsTests { + + private static final String TAG = "GameServiceSettingsTests"; + private static final String PACKAGE_NAME_1 = "com.android.app1"; + private static final String PACKAGE_NAME_2 = "com.android.app2"; + private static final String PACKAGE_NAME_3 = "com.android.app3"; + + private void writeFile(File file, byte[] data) { + file.mkdirs(); + try { + AtomicFile aFile = new AtomicFile(file); + FileOutputStream fos = aFile.startWrite(); + fos.write(data); + aFile.finishWrite(fos); + } catch (IOException ioe) { + Log.e(TAG, "Cannot write file " + file.getPath()); + } + } + + private void writeGameServiceXml() { + writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), + "system/game-manager-service.xml"), + ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + + "<packages>\n" + + " <package name=\"com.android.app1\" gameMode=\"1\">\n" + + " </package>\n" + + " <package name=\"com.android.app2\" gameMode=\"2\">\n" + + " </package>\n" + + " <package name=\"com.android.app3\" gameMode=\"3\">\n" + + " </package>\n" + + "</packages>\n").getBytes()); + } + + private void deleteSystemFolder() { + File systemFolder = new File(InstrumentationRegistry.getContext().getFilesDir(), "system"); + deleteFolder(systemFolder); + } + + private static void deleteFolder(File folder) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + deleteFolder(file); + } + } + folder.delete(); + } + + private void writeOldFiles() { + deleteSystemFolder(); + writeGameServiceXml(); + } + + private void verifyGameServiceSettingsData(Settings settings) { + assertThat(settings.getGameModeLocked(PACKAGE_NAME_1), is(1)); + assertThat(settings.getGameModeLocked(PACKAGE_NAME_2), is(2)); + assertThat(settings.getGameModeLocked(PACKAGE_NAME_3), is(3)); + } + + @After + public void tearDown() throws Exception { + deleteFolder(InstrumentationRegistry.getTargetContext().getFilesDir()); + } + + /** read in data and verify */ + @Test + public void testReadGameServiceSettings() { + /* write out files and read */ + writeOldFiles(); + final Context context = InstrumentationRegistry.getContext(); + Settings settings = new Settings(context.getFilesDir()); + assertThat(settings.readPersistentDataLocked(), is(true)); + verifyGameServiceSettingsData(settings); + } + + /** read in data, write it out, and read it back in. Verify same. */ + @Test + public void testWriteGameServiceSettings() { + // write out files and read + writeOldFiles(); + final Context context = InstrumentationRegistry.getContext(); + Settings settings = new Settings(context.getFilesDir()); + assertThat(settings.readPersistentDataLocked(), is(true)); + + // write out, read back in and verify the same + settings.writePersistentDataLocked(); + assertThat(settings.readPersistentDataLocked(), is(true)); + verifyGameServiceSettingsData(settings); + } +} diff --git a/services/tests/servicestests/src/com/android/server/graphics/GameManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/graphics/GameManagerServiceTests.java new file mode 100644 index 000000000000..22df645f7619 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/graphics/GameManagerServiceTests.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.graphics; + +import static org.junit.Assert.assertEquals; + +import android.graphics.GameManager; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class GameManagerServiceTests { + + private static final String TAG = "GameServiceTests"; + private static final String PACKAGE_NAME_0 = "com.android.app0"; + private static final String PACKAGE_NAME_1 = "com.android.app1"; + private static final int USER_ID_1 = 1001; + private static final int USER_ID_2 = 1002; + + /** + * By default game mode is not supported. + */ + @Test + public void testGameModeDefaultValue() { + GameManagerService gameManagerService = + new GameManagerService(InstrumentationRegistry.getContext()); + gameManagerService.onUserStarting(USER_ID_1); + + assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + gameManagerService.getGameMode(PACKAGE_NAME_0, USER_ID_1)); + } + + /** + * Test the default behaviour for a nonexistent user. + */ + @Test + public void testDefaultValueForNonexistentUser() { + GameManagerService gameManagerService = + new GameManagerService(InstrumentationRegistry.getContext()); + gameManagerService.onUserStarting(USER_ID_1); + + gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_2); + assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_2)); + } + + /** + * Test getter and setter of game modes. + */ + @Test + public void testGameMode() { + GameManagerService gameManagerService = + new GameManagerService(InstrumentationRegistry.getContext()); + gameManagerService.onUserStarting(USER_ID_1); + + assertEquals(GameManager.GAME_MODE_UNSUPPORTED, + gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1)); + gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1); + assertEquals(GameManager.GAME_MODE_STANDARD, + gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1)); + gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE, + USER_ID_1); + assertEquals(GameManager.GAME_MODE_PERFORMANCE, + gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java index e5529cb899cc..15a9bcf59b28 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java @@ -16,36 +16,42 @@ package com.android.server.job; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; + import static com.google.common.truth.Truth.assertThat; import android.util.Log; +import android.util.Pair; + +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; -import com.android.server.job.JobConcurrencyManager.JobCountTracker; +import com.android.server.job.JobConcurrencyManager.WorkCountTracker; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; import java.util.Random; -import androidx.test.filters.MediumTest; -import androidx.test.runner.AndroidJUnit4; - /** - * Test for {@link com.android.server.job.JobConcurrencyManager.JobCountTracker}. + * Test for {@link WorkCountTracker}. */ @RunWith(AndroidJUnit4.class) @MediumTest -public class JobCountTrackerTest { - private static final String TAG = "JobCountTrackerTest"; +public class WorkCountTrackerTest { + private static final String TAG = "WorkerCountTrackerTest"; private Random mRandom; - private JobCountTracker mJobCountTracker; + private WorkCountTracker mWorkCountTracker; @Before public void setUp() { mRandom = new Random(1); // Always use the same series of pseudo random values. - mJobCountTracker = new JobCountTracker(); + mWorkCountTracker = new WorkCountTracker(); } /** @@ -83,44 +89,57 @@ public class JobCountTrackerTest { private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) { - mJobCountTracker.reset(totalMax, maxBg, minBg); + mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig("critical", + totalMax, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, totalMax - maxBg), + Pair.create(WORK_TYPE_BG, minBg)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, maxBg)))); + mWorkCountTracker.resetCounts(); for (int i = 0; i < jobs.runningFg; i++) { - mJobCountTracker.incrementRunningJobCount(true); + mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_TOP); } for (int i = 0; i < jobs.runningBg; i++) { - mJobCountTracker.incrementRunningJobCount(false); + mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_BG); } for (int i = 0; i < jobs.pendingFg; i++) { - mJobCountTracker.incrementPendingJobCount(true); + mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_TOP); } for (int i = 0; i < jobs.pendingBg; i++) { - mJobCountTracker.incrementPendingJobCount(false); + mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_BG); } - mJobCountTracker.onCountDone(); + mWorkCountTracker.onCountDone(); - while ((jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) - || (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false))) { + while ((jobs.pendingFg > 0 + && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) + || (jobs.pendingBg > 0 + && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) { final boolean isStartingFg = mRandom.nextBoolean(); if (isStartingFg) { - if (jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) { + if (jobs.pendingFg > 0 + && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) { jobs.pendingFg--; jobs.runningFg++; - mJobCountTracker.onStartingNewJob(true); + mWorkCountTracker.stageJob(WORK_TYPE_TOP); + mWorkCountTracker.onJobStarted(WORK_TYPE_TOP); } } else { - if (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false)) { + if (jobs.pendingBg > 0 + && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) { jobs.pendingBg--; jobs.runningBg++; - mJobCountTracker.onStartingNewJob(false); + mWorkCountTracker.stageJob(WORK_TYPE_BG); + mWorkCountTracker.onJobStarted(WORK_TYPE_BG); } } } - Log.i(TAG, "" + mJobCountTracker); + Log.i(TAG, "" + mWorkCountTracker); } /** @@ -277,6 +296,7 @@ public class JobCountTrackerTest { startPendingJobs(jobs, totalMax, maxBg, minBg); +// fail(mWorkerCountTracker.toString()); assertThat(jobs.runningFg).isEqualTo(resultRunningFg); assertThat(jobs.runningBg).isEqualTo(resultRunningBg); @@ -300,6 +320,8 @@ public class JobCountTrackerTest { checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0); checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1); + checkSimple(8, 6, 2, /*run=*/ 0, 0, /*pen=*/ 0, 49, /*res run/pen=*/ 0, 6, 0, 43); + checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3); } } diff --git a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java index 4c3674767e97..fba36cb402a0 100644 --- a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java +++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java @@ -15,22 +15,34 @@ */ package com.android.server.job; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; + import android.annotation.Nullable; import android.provider.DeviceConfig; +import android.util.Pair; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.server.job.JobSchedulerService.MaxJobCounts; +import com.android.server.job.JobConcurrencyManager.WorkTypeConfig; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + @RunWith(AndroidJUnit4.class) @SmallTest -public class MaxJobCountsTest { +public class WorkTypeConfigTest { + private static final String KEY_MAX_TOTAL = "concurrency_max_total_test"; + private static final String KEY_MAX_TOP = "concurrency_max_top_test"; + private static final String KEY_MAX_BG = "concurrency_max_bg_test"; + private static final String KEY_MIN_TOP = "concurrency_min_top_test"; + private static final String KEY_MIN_BG = "concurrency_min_bg_test"; + @After public void tearDown() throws Exception { resetConfig(); @@ -38,9 +50,11 @@ public class MaxJobCountsTest { private void resetConfig() { // DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually. - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "total", "", false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "maxbg", "", false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "minbg", "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false); } private void check(@Nullable DeviceConfig.Properties config, @@ -51,16 +65,19 @@ public class MaxJobCountsTest { DeviceConfig.setProperties(config); } - final MaxJobCounts counts = new JobSchedulerService.MaxJobCounts( - defaultTotal, "total", - defaultMaxBg, "maxbg", - defaultMinBg, "minbg"); + final WorkTypeConfig counts = new WorkTypeConfig("test", + defaultTotal, + // defaultMin + List.of(Pair.create(WORK_TYPE_TOP, defaultTotal - defaultMaxBg), + Pair.create(WORK_TYPE_BG, defaultMinBg)), + // defaultMax + List.of(Pair.create(WORK_TYPE_BG, defaultMaxBg))); - counts.update(); + counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER)); Assert.assertEquals(expectedTotal, counts.getMaxTotal()); - Assert.assertEquals(expectedMaxBg, counts.getMaxBg()); - Assert.assertEquals(expectedMinBg, counts.getMinBg()); + Assert.assertEquals(expectedMaxBg, counts.getMax(WORK_TYPE_BG)); + Assert.assertEquals(expectedMinBg, counts.getMinReserved(WORK_TYPE_BG)); } @Test @@ -80,19 +97,19 @@ public class MaxJobCountsTest { // Test for overriding with a setting string. check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) - .setInt("total", 5) - .setInt("maxbg", 4) - .setInt("minbg", 3) + .setInt(KEY_MAX_TOTAL, 5) + .setInt(KEY_MAX_BG, 4) + .setInt(KEY_MIN_BG, 3) .build(), /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3); check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) - .setInt("total", 5).build(), + .setInt(KEY_MAX_TOTAL, 5).build(), /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4); check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) - .setInt("maxbg", 4).build(), + .setInt(KEY_MAX_BG, 4).build(), /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4); check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) - .setInt("minbg", 3).build(), + .setInt(KEY_MIN_BG, 3).build(), /*default*/ 9, 9, 9, /*expected*/ 9, 9, 3); } } diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index 63330d518297..161d3163c1cf 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -515,6 +515,85 @@ public final class DataManagerTest { } @Test + public void testGetConversationReturnsCustomizedConversation() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + mDataManager.addOrUpdateConversationInfo(shortcut); + + NotificationListenerService listenerService = + mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); + + listenerService.onNotificationPosted(mStatusBarNotification); + shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS); + mDataManager.addOrUpdateConversationInfo(shortcut); + + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNotNull(); + + listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY), + mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED); + + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNotNull(); + } + + @Test + public void testGetConversation() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNull(); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + shortcut.setCached(ShortcutInfo.FLAG_PINNED); + mDataManager.addOrUpdateConversationInfo(shortcut); + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNotNull(); + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID + "1")).isNull(); + + NotificationListenerService listenerService = + mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); + listenerService.onNotificationPosted(mStatusBarNotification); + + ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID); + assertThat(result).isNotNull(); + assertEquals(shortcut.getId(), result.getShortcutInfo().getId()); + assertEquals(1, result.getShortcutInfo().getPersons().length); + assertEquals(CONTACT_URI, result.getShortcutInfo().getPersons()[0].getUri()); + assertEquals(mParentNotificationChannel.getId(), + result.getParentNotificationChannel().getId()); + assertEquals(mStatusBarNotification.getPostTime(), result.getLastEventTimestamp()); + assertTrue(result.hasActiveNotifications()); + } + + @Test + public void testGetConversationGetsPersonsData() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + shortcut.setCached(ShortcutInfo.FLAG_PINNED); + mDataManager.addOrUpdateConversationInfo(shortcut); + + NotificationListenerService listenerService = + mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); + listenerService.onNotificationPosted(mStatusBarNotification); + + ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID); + + verify(mShortcutServiceInternal).getShortcuts( + anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(), + mQueryFlagsCaptor.capture(), anyInt(), anyInt(), anyInt()); + Integer queryFlags = mQueryFlagsCaptor.getValue(); + assertThat(hasFlag(queryFlags, ShortcutQuery.FLAG_GET_PERSONS_DATA)).isTrue(); + } + + @Test public void testNotificationChannelCreated() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); mDataManager.onUserUnlocked(USER_ID_SECONDARY); diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 533dc1708896..1d0b595d8fc1 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -877,6 +877,22 @@ public class PowerManagerServiceTest { } @Test + public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted() + throws Exception { + when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1"); + createService(); + mService.systemReady(null); + + mService.getBinderServiceInstance().wakeUp(mClock.now(), + PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name"); + + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + DisplayPowerRequest.POLICY_BRIGHT); + } + + @Test public void testIsAmbientDisplayAvailable_available() throws Exception { createService(); when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 78dd4b8119e3..4bea9a2eea45 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -27,7 +27,6 @@ import static android.os.Build.VERSION_CODES.P; import static android.os.Build.VERSION_CODES.Q; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_PRIVATE; -import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -96,6 +95,7 @@ import android.app.ActivityTaskManager; import android.app.WindowConfiguration; import android.app.servertransaction.FixedRotationAdjustmentsItem; import android.content.res.Configuration; +import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; import android.metrics.LogMaker; @@ -707,6 +707,7 @@ public class DisplayContentTests extends WindowTestsBase { // same width and height. final int displayWidth = dc.mInitialDisplayWidth; final int displayHeight = dc.mInitialDisplayHeight; + final float density = dc.mInitialDisplayDensity; final int cutoutWidth = 40; final int cutoutHeight = 10; final int left = (displayWidth - cutoutWidth) / 2; @@ -714,9 +715,13 @@ public class DisplayContentTests extends WindowTestsBase { final int right = (displayWidth + cutoutWidth) / 2; final int bottom = cutoutHeight; - final Rect r1 = new Rect(left, top, right, bottom); + final Rect zeroRect = new Rect(); + final Rect[] bounds = new Rect[]{zeroRect, new Rect(left, top, right, bottom), zeroRect, + zeroRect}; + final DisplayCutout.CutoutPathParserInfo info = new DisplayCutout.CutoutPathParserInfo( + displayWidth, displayHeight, density, "", Surface.ROTATION_0, 1f); final DisplayCutout cutout = new WmDisplayCutout( - fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null) + DisplayCutout.constructDisplayCutout(bounds, Insets.NONE, info), null) .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout(); dc.mInitialDisplayCutout = cutout; @@ -731,9 +736,12 @@ public class DisplayContentTests extends WindowTestsBase { // | | ---o // | | | // | | ------------- - final Rect r = new Rect(top, left, bottom, right); + final Rect[] bounds90 = new Rect[]{new Rect(top, left, bottom, right), zeroRect, zeroRect, + zeroRect}; + final DisplayCutout.CutoutPathParserInfo info90 = new DisplayCutout.CutoutPathParserInfo( + displayWidth, displayHeight, density, "", Surface.ROTATION_90, 1f); assertEquals(new WmDisplayCutout( - fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null) + DisplayCutout.constructDisplayCutout(bounds90, Insets.NONE, info90), null) .computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(), dc.getDisplayInfo().displayCutout); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 70d47a580801..8703c3103607 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -16,11 +16,13 @@ package com.android.server.wm; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.DragEvent.ACTION_DRAG_STARTED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -34,6 +36,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import android.app.PendingIntent; @@ -278,6 +281,8 @@ public class DragDropControllerTests extends WindowTestsBase { @Test public void testValidateAppShortcutArguments() { + doReturn(PERMISSION_GRANTED).when(mWm.mContext) + .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS)); final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) {} @@ -329,6 +334,8 @@ public class DragDropControllerTests extends WindowTestsBase { @Test public void testValidateAppTaskArguments() { + doReturn(PERMISSION_GRANTED).when(mWm.mContext) + .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS)); final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) {} diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 673b00f25824..21fd04ee3ae9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -45,7 +45,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; @@ -58,7 +57,6 @@ import android.app.ActivityManager.RecentTaskInfo; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.ComponentName; -import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.os.Bundle; @@ -1109,28 +1107,6 @@ public class RecentTasksTest extends WindowTestsBase { assertEquals(originalStackCount, mTaskContainer.getRootTaskCount()); } - @Test - public void testNotRecentsComponent_denyApiAccess() throws Exception { - doReturn(PackageManager.PERMISSION_DENIED).when(mAtm) - .checkGetTasksPermission(anyString(), anyInt(), anyInt()); - // Expect the following methods to fail due to recents component not being set - mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION); - doTestRecentTasksApis(false /* expectNoSecurityException */); - // Don't throw for the following tests - mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY); - testGetTasksApis(false /* expectNoSecurityException */); - } - - @Test - public void testRecentsComponent_allowApiAccessWithoutPermissions() { - doReturn(PackageManager.PERMISSION_DENIED).when(mAtm) - .checkGetTasksPermission(anyString(), anyInt(), anyInt()); - // Set the recents component and ensure that the following calls do not fail - mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT); - doTestRecentTasksApis(true /* expectNoSecurityException */); - testGetTasksApis(true /* expectNoSecurityException */); - } - private void doTestRecentTasksApis(boolean expectCallable) { assertSecurityException(expectCallable, () -> mAtm.removeTask(INVALID_STACK_ID)); assertSecurityException(expectCallable, @@ -1295,13 +1271,7 @@ public class RecentTasksTest extends WindowTestsBase { } private static class TestRecentTasks extends RecentTasks { - static final int GRANT = 0; - static final int DENY = 1; - static final int DENY_THROW_SECURITY_EXCEPTION = 2; - - private boolean mOverrideIsCallerRecents; private boolean mIsTrimmableOverride; - private int mIsCallerRecentsPolicy; public boolean mLastAllowed; @@ -1334,26 +1304,6 @@ public class RecentTasksTest extends WindowTestsBase { return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID }; } - @Override - boolean isCallerRecents(int callingUid) { - if (mOverrideIsCallerRecents) { - switch (mIsCallerRecentsPolicy) { - case GRANT: - return true; - case DENY: - return false; - case DENY_THROW_SECURITY_EXCEPTION: - throw new SecurityException(); - } - } - return super.isCallerRecents(callingUid); - } - - void setIsCallerRecentsOverride(int policy) { - mOverrideIsCallerRecents = true; - mIsCallerRecentsPolicy = policy; - } - /** * To simplify the setup for some tests, the caller can request that we only rely on the * visible range test to determine what is trimmable. In this case, we don't try to diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index b0b8afd6c3a4..df5b48a038f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -22,6 +22,8 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; @@ -978,6 +980,31 @@ public class WindowContainerTests extends WindowTestsBase { assertEquals(200, listener.mConfiguration.densityDpi); } + @Test + public void testFreezeInsetsStateWhenAppTransition() { + final Task stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityRecord activity = createActivityRecord(mDisplayContent, task); + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); + task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE); + spyOn(win); + doReturn(true).when(task).okToAnimate(); + ArrayList<WindowContainer> sources = new ArrayList<>(); + sources.add(activity); + + // Simulate the task applying the exit transition, verify the main window of the task + // will be set the frozen insets state. + task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */, + false /* isVoiceInteraction */, sources); + verify(win).freezeInsetsState(); + + // Simulate the task transition finished, verify the frozen insets state of the window + // will be reset. + task.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, + task.mSurfaceAnimator.getAnimation()); + verify(win).clearFrozenInsetsState(); + } + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 263aa194a108..3231f8b6551a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -810,4 +810,27 @@ public class WindowStateTests extends WindowTestsBase { WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); assertFalse(sameTokenWindow.needsRelativeLayeringToIme()); } + + @Test + public void testSetFreezeInsetsState() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + spyOn(app); + doReturn(true).when(app).isVisible(); + + // Set freezing the insets state to make the window ignore to dispatch insets changed. + final InsetsState expectedState = new InsetsState(app.getInsetsState(), + true /* copySources */); + app.freezeInsetsState(); + assertEquals(expectedState, app.getFrozenInsetsState()); + assertFalse(app.isReadyToDispatchInsetsState()); + assertEquals(expectedState, app.getInsetsState()); + mDisplayContent.getInsetsStateController().notifyInsetsChanged(); + verify(app, never()).notifyInsetsChanged(); + + // Unfreeze the insets state to make the window can dispatch insets changed. + app.clearFrozenInsetsState(); + assertTrue(app.isReadyToDispatchInsetsState()); + mDisplayContent.getInsetsStateController().notifyInsetsChanged(); + verify(app).notifyInsetsChanged(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java index 39976a5a2af1..b2646f281eb9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java @@ -150,9 +150,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_waterfall() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT}, - Insets.of(1, 2, 3, 4)), + Insets.of(1, 2, 3, 4), null), 200, 400); assertEquals(new Rect(1, 2, 3, 4), cutout.getDisplayCutout().getSafeInsets()); @@ -161,9 +161,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutTop_greaterThan_waterfallTop() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT}, - Insets.of(0, 20, 0, 0)), + Insets.of(0, 20, 0, 0), null), 200, 400); assertEquals(new Rect(0, 30, 0, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -172,9 +172,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutTop_lessThan_waterfallTop() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT}, - Insets.of(0, 40, 0, 0)), + Insets.of(0, 40, 0, 0), null), 200, 400); assertEquals(new Rect(0, 40, 0, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -183,9 +183,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutLeft_greaterThan_waterfallLeft() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT}, - Insets.of(20, 0, 0, 0)), + Insets.of(20, 0, 0, 0), null), 200, 400); assertEquals(new Rect(30, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -194,9 +194,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutLeft_lessThan_waterfallLeft() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT}, - Insets.of(40, 0, 0, 0)), + Insets.of(40, 0, 0, 0), null), 200, 400); assertEquals(new Rect(40, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -205,9 +205,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutBottom_greaterThan_waterfallBottom() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)}, - Insets.of(0, 0, 0, 20)), + Insets.of(0, 0, 0, 20), null), 200, 400); assertEquals(new Rect(0, 0, 0, 30), cutout.getDisplayCutout().getSafeInsets()); @@ -216,9 +216,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutBottom_lessThan_waterfallBottom() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)}, - Insets.of(0, 0, 0, 40)), + Insets.of(0, 0, 0, 40), null), 200, 400); assertEquals(new Rect(0, 0, 0, 40), cutout.getDisplayCutout().getSafeInsets()); @@ -227,9 +227,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutRight_greaterThan_waterfallRight() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT}, - Insets.of(0, 0, 20, 0)), + Insets.of(0, 0, 20, 0), null), 200, 400); assertEquals(new Rect(0, 0, 30, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -238,9 +238,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutRight_lessThan_waterfallRight() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT}, - Insets.of(0, 0, 40, 0)), + Insets.of(0, 0, 40, 0), null), 200, 400); assertEquals(new Rect(0, 0, 40, 0), cutout.getDisplayCutout().getSafeInsets()); diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index a4f5249c9cb4..339249b33db6 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -1026,6 +1026,8 @@ public class UsageStatsDatabase { writeLocked(fos, stats, version, packagesTokenData); file.finishWrite(fos); fos = null; + } catch (Exception e) { + // Do nothing. Exception has already been handled. } finally { // When fos is null (successful write), this will no-op file.failWrite(fos); @@ -1033,7 +1035,7 @@ public class UsageStatsDatabase { } private static void writeLocked(OutputStream out, IntervalStats stats, int version, - PackagesTokenData packagesTokenData) throws RuntimeException { + PackagesTokenData packagesTokenData) throws Exception { switch (version) { case 1: case 2: @@ -1045,6 +1047,7 @@ public class UsageStatsDatabase { UsageStatsProto.write(out, stats); } catch (Exception e) { Slog.e(TAG, "Unable to write interval stats to proto.", e); + throw e; } break; case 5: @@ -1053,6 +1056,7 @@ public class UsageStatsDatabase { UsageStatsProtoV2.write(out, stats); } catch (Exception e) { Slog.e(TAG, "Unable to write interval stats to proto.", e); + throw e; } break; default: diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java index baa0576cdf13..754814facb71 100644 --- a/telephony/java/android/telephony/ims/ImsUtListener.java +++ b/telephony/java/android/telephony/ims/ImsUtListener.java @@ -178,4 +178,11 @@ public class ImsUtListener { public ImsUtListener(IImsUtListener serviceInterface) { mServiceInterface = serviceInterface; } + + /** + * @hide + */ + public IImsUtListener getListenerInterface() { + return mServiceInterface; + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java index 06c35eaec6dd..2e35d27614d1 100644 --- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java @@ -23,6 +23,8 @@ import android.util.Log; import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsEcbmListener; +import java.util.Objects; + /** * Base implementation of ImsEcbm, which implements stub versions of the methods * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports. @@ -36,11 +38,27 @@ import com.android.ims.internal.IImsEcbmListener; public class ImsEcbmImplBase { private static final String TAG = "ImsEcbmImplBase"; + private final Object mLock = new Object(); private IImsEcbmListener mListener; - private IImsEcbm mImsEcbm = new IImsEcbm.Stub() { + private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() { @Override public void setListener(IImsEcbmListener listener) { - mListener = listener; + synchronized (mLock) { + if (mImsEcbm != null && listener != null && Objects.equals( + mImsEcbm.asBinder(), listener.asBinder())) { + return; + } + if (listener == null) { + mListener = null; + } else if (listener != null && mListener == null) { + mListener = listener; + } else { + // Fail fast here instead of silently overwriting the listener to another + // listener due to another connection connecting. + throw new IllegalStateException("ImsEcbmImplBase: Listener already set by " + + "another connection."); + } + } } @Override @@ -69,9 +87,13 @@ public class ImsEcbmImplBase { */ public final void enteredEcbm() { Log.d(TAG, "Entered ECBM."); - if (mListener != null) { + IImsEcbmListener listener; + synchronized (mLock) { + listener = mListener; + } + if (listener != null) { try { - mListener.enteredECBM(); + listener.enteredECBM(); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -85,9 +107,13 @@ public class ImsEcbmImplBase { */ public final void exitedEcbm() { Log.d(TAG, "Exited ECBM."); - if (mListener != null) { + IImsEcbmListener listener; + synchronized (mLock) { + listener = mListener; + } + if (listener != null) { try { - mListener.exitedECBM(); + listener.exitedECBM(); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java index d002903a11b6..555a47eb8200 100644 --- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java @@ -25,6 +25,7 @@ import com.android.ims.internal.IImsExternalCallStateListener; import com.android.ims.internal.IImsMultiEndpoint; import java.util.List; +import java.util.Objects; /** * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods @@ -41,10 +42,28 @@ public class ImsMultiEndpointImplBase { private static final String TAG = "MultiEndpointImplBase"; private IImsExternalCallStateListener mListener; - private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() { + private final Object mLock = new Object(); + private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() { + @Override public void setListener(IImsExternalCallStateListener listener) throws RemoteException { - mListener = listener; + synchronized (mLock) { + if (mListener != null && listener != null && Objects.equals( + mListener.asBinder(), listener.asBinder())) { + return; + } + + if (listener == null) { + mListener = null; + } else if (listener != null && mListener == null) { + mListener = listener; + } else { + // Fail fast here instead of silently overwriting the listener to another + // listener due to another connection connecting. + throw new IllegalStateException("ImsMultiEndpointImplBase: Listener already" + + " set by another connection."); + } + } } @Override @@ -65,9 +84,13 @@ public class ImsMultiEndpointImplBase { */ public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) { Log.d(TAG, "ims external call state update triggered."); - if (mListener != null) { + IImsExternalCallStateListener listener; + synchronized (mLock) { + listener = mListener; + } + if (listener != null) { try { - mListener.onImsExternalCallStateUpdate(externalCallDialogs); + listener.onImsExternalCallStateUpdate(externalCallDialogs); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index f5219d5b49e8..eef4fcaceeaf 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -29,6 +29,7 @@ import com.android.ims.internal.IImsUtListener; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Base implementation of IMS UT interface, which implements stubs. Override these methods to @@ -116,7 +117,10 @@ public class ImsUtImplBase { */ public static final int INVALID_RESULT = -1; - private IImsUt.Stub mServiceImpl = new IImsUt.Stub() { + private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() { + private final Object mLock = new Object(); + private ImsUtListener mUtListener; + @Override public void close() throws RemoteException { ImsUtImplBase.this.close(); @@ -202,7 +206,26 @@ public class ImsUtImplBase { @Override public void setListener(IImsUtListener listener) throws RemoteException { - ImsUtImplBase.this.setListener(new ImsUtListener(listener)); + synchronized (mLock) { + if (mUtListener != null && listener != null && Objects.equals( + mUtListener.getListenerInterface().asBinder(), listener.asBinder())) { + return; + } + + if (listener == null) { + mUtListener = null; + } else if (listener != null && mUtListener == null) { + mUtListener = new ImsUtListener(listener); + } else { + // This is a limitation of the current API surface, there can only be one + // listener connected. Fail fast instead of silently overwriting the other + // listener. + throw new IllegalStateException("ImsUtImplBase#setListener: listener already " + + "set by another connected interface!"); + } + } + + ImsUtImplBase.this.setListener(mUtListener); } @Override diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index 59e375fa3bd6..f474ec2c389c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -114,7 +114,8 @@ class CloseImeWindowToHomeTest( enabled = !configuration.startRotation.isRotated()) navBarLayerIsAlwaysVisible() statusBarLayerIsAlwaysVisible() - visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE), + enabled = false) imeLayerBecomesInvisible() imeAppLayerBecomesInvisible(testApp) diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt new file mode 100644 index 000000000000..87cfb345e5e0 --- /dev/null +++ b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net + +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.assertParcelSane +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals + +private const val TEST_OWNER_UID = 123 +private const val TEST_IFACE = "test_tun0" +private val TEST_IFACE_LIST = listOf("wlan0", "rmnet_data0", "eth0") + +@SmallTest +@RunWith(DevSdkIgnoreRunner::class) +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) +class UnderlyingNetworkInfoTest { + @Test + fun testParcelUnparcel() { + val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST) + assertEquals(TEST_OWNER_UID, testInfo.ownerUid) + assertEquals(TEST_IFACE, testInfo.iface) + assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces) + assertParcelSane(testInfo, 3) + + val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf()) + assertEquals(0, emptyInfo.ownerUid) + assertEquals(String(), emptyInfo.iface) + assertEquals(listOf(), emptyInfo.underlyingIfaces) + assertParcelSane(emptyInfo, 3) + } +}
\ No newline at end of file diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index f2dd27effe91..c2fddf3d9e82 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -32,6 +32,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; import static android.net.NetworkRequest.Type.REQUEST; import static android.net.NetworkRequest.Type.TRACK_DEFAULT; @@ -368,6 +369,12 @@ public class ConnectivityManagerTest { eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), eq(testPkgName), eq(null)); reset(mService); + + manager.requestBackgroundNetwork(request, null, callback); + verify(mService).requestNetwork(eq(request.networkCapabilities), + eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(testPkgName), eq(null)); + reset(mService); } static Message makeMessage(NetworkRequest req, int messageType) { diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 137543a8147c..14229c5d000f 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -201,8 +201,8 @@ import android.net.RouteInfoParcel; import android.net.SocketKeepalive; import android.net.UidRange; import android.net.UidRangeParcel; +import android.net.UnderlyingNetworkInfo; import android.net.Uri; -import android.net.VpnInfo; import android.net.VpnManager; import android.net.metrics.IpConnectivityLog; import android.net.shared.NetworkMonitorUtils; @@ -1076,7 +1076,7 @@ public class ConnectivityServiceTest { private boolean mAgentRegistered = false; private int mVpnType = VpnManager.TYPE_VPN_SERVICE; - private VpnInfo mVpnInfo; + private UnderlyingNetworkInfo mUnderlyingNetworkInfo; // These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started. // TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the @@ -1250,14 +1250,15 @@ public class ConnectivityServiceTest { } @Override - public synchronized VpnInfo getVpnInfo() { - if (mVpnInfo != null) return mVpnInfo; + public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() { + if (mUnderlyingNetworkInfo != null) return mUnderlyingNetworkInfo; - return super.getVpnInfo(); + return super.getUnderlyingNetworkInfo(); } - private synchronized void setVpnInfo(VpnInfo vpnInfo) { - mVpnInfo = vpnInfo; + private synchronized void setUnderlyingNetworkInfo( + UnderlyingNetworkInfo underlyingNetworkInfo) { + mUnderlyingNetworkInfo = underlyingNetworkInfo; } } @@ -3693,10 +3694,13 @@ public class ConnectivityServiceTest { @Test public void testBackgroundNetworks() throws Exception { - // Create a background request. We can't do this ourselves because ConnectivityService - // doesn't have an API for it. So just turn on mobile data always on. - setAlwaysOnNetworks(true); + // Create a cellular background request. grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); + final TestNetworkCallback cellBgCallback = new TestNetworkCallback(); + mCm.requestBackgroundNetwork(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(), null, cellBgCallback); + + // Make callbacks for monitoring. final NetworkRequest request = new NetworkRequest.Builder().build(); final NetworkRequest fgRequest = new NetworkRequest.Builder() .addCapability(NET_CAPABILITY_FOREGROUND).build(); @@ -3765,6 +3769,7 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(callback); mCm.unregisterNetworkCallback(fgCallback); + mCm.unregisterNetworkCallback(cellBgCallback); } @Ignore // This test has instrinsic chances of spurious failures: ignore for continuous testing. @@ -5195,20 +5200,22 @@ public class ConnectivityServiceTest { private void expectForceUpdateIfaces(Network[] networks, String defaultIface, Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception { ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class); - ArgumentCaptor<VpnInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class); + ArgumentCaptor<UnderlyingNetworkInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass( + UnderlyingNetworkInfo[].class); verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(), any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture()); assertSameElementsNoDuplicates(networksCaptor.getValue(), networks); - VpnInfo[] infos = vpnInfosCaptor.getValue(); + UnderlyingNetworkInfo[] infos = vpnInfosCaptor.getValue(); if (vpnUid != null) { assertEquals("Should have exactly one VPN:", 1, infos.length); - VpnInfo info = infos[0]; + UnderlyingNetworkInfo info = infos[0]; assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid); - assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface); - assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces); + assertEquals("Unexpected VPN interface:", vpnIfname, info.iface); + assertSameElementsNoDuplicates(underlyingIfaces, + info.underlyingIfaces.toArray(new String[0])); } else { assertEquals(0, infos.length); return; @@ -5269,7 +5276,7 @@ public class ConnectivityServiceTest { waitForIdle(); verify(mStatsService, never()) .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + eq(new UnderlyingNetworkInfo[0])); reset(mStatsService); // Roaming change should update ifaces @@ -5352,8 +5359,8 @@ public class ConnectivityServiceTest { // network for the VPN... verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class), any(NetworkState[].class), any() /* anyString() doesn't match null */, - argThat(infos -> infos[0].underlyingIfaces.length == 1 - && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0]))); + argThat(infos -> infos[0].underlyingIfaces.size() == 1 + && WIFI_IFNAME.equals(infos[0].underlyingIfaces.get(0)))); verifyNoMoreInteractions(mStatsService); reset(mStatsService); @@ -5366,8 +5373,8 @@ public class ConnectivityServiceTest { waitForIdle(); verify(mStatsService).forceUpdateIfaces(any(Network[].class), any(NetworkState[].class), any() /* anyString() doesn't match null */, - argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1 - && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0]))); + argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.size() == 1 + && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces.get(0)))); mEthernetNetworkAgent.disconnect(); waitForIdle(); reset(mStatsService); @@ -8347,8 +8354,9 @@ public class ConnectivityServiceTest { assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid); mMockVpn.setVpnType(vpnType); - final VpnInfo vpnInfo = new VpnInfo(vpnOwnerUid, null, null); - mMockVpn.setVpnInfo(vpnInfo); + final UnderlyingNetworkInfo underlyingNetworkInfo = + new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<String>()); + mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo); } private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType) diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java index 1b33930e96a9..a058a466a4ff 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java @@ -33,7 +33,9 @@ import static android.net.NetworkStats.TAG_NONE; import static org.junit.Assert.assertEquals; import android.net.NetworkStats; -import android.net.VpnInfo; +import android.net.UnderlyingNetworkInfo; + +import java.util.Arrays; /** Superclass with utilities for NetworkStats(Service|Factory)Test */ abstract class NetworkStatsBaseTest { @@ -107,11 +109,11 @@ abstract class NetworkStatsBaseTest { assertEquals("unexpected operations", operations, entry.operations); } - static VpnInfo createVpnInfo(String[] underlyingIfaces) { + static UnderlyingNetworkInfo createVpnInfo(String[] underlyingIfaces) { return createVpnInfo(TUN_IFACE, underlyingIfaces); } - static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) { - return new VpnInfo(UID_VPN, vpnIface, underlyingIfaces); + static UnderlyingNetworkInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) { + return new UnderlyingNetworkInfo(UID_VPN, vpnIface, Arrays.asList(underlyingIfaces)); } } diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java index 76647a69de33..f3ae9b051e7c 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java @@ -36,7 +36,7 @@ import static org.junit.Assert.fail; import android.content.res.Resources; import android.net.NetworkStats; import android.net.TrafficStats; -import android.net.VpnInfo; +import android.net.UnderlyingNetworkInfo; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -79,7 +79,7 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { // related to networkStatsFactory is compiled to a minimal native library and loaded here. System.loadLibrary("networkstatsfactorytestjni"); mFactory = new NetworkStatsFactory(mTestProc, false); - mFactory.updateVpnInfos(new VpnInfo[0]); + mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]); } @After @@ -105,8 +105,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnRewriteTrafficThroughItself() throws Exception { - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -134,8 +135,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnWithClat() throws Exception { - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] { + createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); mFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption @@ -167,8 +169,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnWithOneUnderlyingIface() throws Exception { - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -191,8 +194,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception { // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -219,8 +223,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnWithOneUnderlyingIface_withCompression() throws Exception { // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -242,8 +247,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. // Additionally, VPN is duplicating traffic across both WiFi and Cell. - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -267,10 +273,10 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { public void testConcurrentVpns() throws Exception { // Assume two VPNs are connected on two different network interfaces. VPN1 is using // TEST_IFACE and VPN2 is using TEST_IFACE2. - final VpnInfo[] vpnInfos = new VpnInfo[] { + final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] { createVpnInfo(TUN_IFACE, new String[] {TEST_IFACE}), createVpnInfo(TUN_IFACE2, new String[] {TEST_IFACE2})}; - mFactory.updateVpnInfos(vpnInfos); + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -308,8 +314,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell. - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -335,8 +342,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell. - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface: // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. @@ -357,8 +365,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { public void testVpnWithIncorrectUnderlyingIface() throws Exception { // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2), // but has declared only WiFi (TEST_IFACE) in its underlying network set. - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index b4e37de2267f..dde78aa54199 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -86,7 +86,7 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; -import android.net.VpnInfo; +import android.net.UnderlyingNetworkInfo; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.os.ConditionVariable; import android.os.Handler; @@ -286,7 +286,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -328,7 +329,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -401,7 +403,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // modify some number on wifi, and trigger poll event incrementCurrentTime(2 * HOUR_IN_MILLIS); @@ -441,7 +444,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some traffic on first network incrementCurrentTime(HOUR_IN_MILLIS); @@ -475,7 +479,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); forcePollAndWaitForIdle(); @@ -514,7 +519,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -581,7 +587,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new VpnInfo[0]); + new UnderlyingNetworkInfo[0]); // Create some traffic. incrementCurrentTime(MINUTE_IN_MILLIS); @@ -655,7 +661,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some traffic for two apps incrementCurrentTime(HOUR_IN_MILLIS); @@ -713,7 +720,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); NetworkStats.Entry entry1 = new NetworkStats.Entry( TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L); @@ -756,7 +764,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); NetworkStats.Entry uidStats = new NetworkStats.Entry( TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); @@ -810,7 +819,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some initial traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -867,7 +877,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some initial traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -906,7 +917,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // Create some traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -943,7 +955,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some tethering traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -999,7 +1012,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -1104,7 +1118,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mService.registerNetworkStatsProvider("TEST", provider); assertNotNull(cb); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // Verifies that one requestStatsUpdate will be called during iface update. provider.expectOnRequestStatsUpdate(0 /* unused */); @@ -1155,7 +1170,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */, TEST_IFACE)}; - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // Register custom provider and retrieve callback. final TestableNetworkStatsProviderBinder provider = @@ -1204,7 +1220,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // 3G network comes online. setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new VpnInfo[0]); + new UnderlyingNetworkInfo[0]); // Create some traffic. incrementCurrentTime(MINUTE_IN_MILLIS); @@ -1274,7 +1290,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { NetworkState[] states = new NetworkState[]{ buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)}; expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // Create some traffic on mobile network. incrementCurrentTime(HOUR_IN_MILLIS); diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java index 9c6b7194af35..f9db408462b7 100644 --- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -18,14 +18,19 @@ package android.net.vcn; import static androidx.test.InstrumentationRegistry.getContext; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener; import org.junit.Before; @@ -103,4 +108,28 @@ public class VcnManagerTest { public void testRemoveVcnUnderlyingNetworkPolicyListenerNullListener() { mVcnManager.removeVcnUnderlyingNetworkPolicyListener(null); } + + @Test + public void testGetUnderlyingNetworkPolicy() throws Exception { + NetworkCapabilities nc = new NetworkCapabilities(); + LinkProperties lp = new LinkProperties(); + when(mMockVcnManagementService.getUnderlyingNetworkPolicy(eq(nc), eq(lp))) + .thenReturn(new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, nc)); + + VcnUnderlyingNetworkPolicy policy = mVcnManager.getUnderlyingNetworkPolicy(nc, lp); + + assertFalse(policy.isTeardownRequested()); + assertEquals(nc, policy.getMergedNetworkCapabilities()); + verify(mMockVcnManagementService).getUnderlyingNetworkPolicy(eq(nc), eq(lp)); + } + + @Test(expected = NullPointerException.class) + public void testGetUnderlyingNetworkPolicyNullNetworkCapabilities() throws Exception { + mVcnManager.getUnderlyingNetworkPolicy(null, new LinkProperties()); + } + + @Test(expected = NullPointerException.class) + public void testGetUnderlyingNetworkPolicyNullLinkProperties() throws Exception { + mVcnManager.getUnderlyingNetworkPolicy(new NetworkCapabilities(), null); + } } diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index f0cdde33f822..e26bf19488d0 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -21,6 +21,8 @@ import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubsc import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -41,9 +43,12 @@ import static org.mockito.Mockito.verify; import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfigTest; +import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.os.IBinder; import android.os.ParcelUuid; import android.os.PersistableBundle; @@ -63,6 +68,7 @@ import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; import com.android.server.vcn.util.PersistableBundleUtils; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -192,6 +198,14 @@ public class VcnManagementServiceTest { mTestLooper.dispatchAll(); } + @Before + public void setUp() { + doNothing() + .when(mMockContext) + .enforceCallingOrSelfPermission( + eq(android.Manifest.permission.NETWORK_FACTORY), any()); + } + private void setupMockedCarrierPrivilege(boolean isPrivileged) { doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO)) .when(mSubMgr) @@ -455,10 +469,6 @@ public class VcnManagementServiceTest { @Test public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception { - doNothing() - .when(mMockContext) - .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any()); - mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); verify(mMockIBinder).linkToDeath(any(), anyInt()); @@ -468,17 +478,14 @@ public class VcnManagementServiceTest { public void testAddVcnUnderlyingNetworkPolicyListenerInvalidPermission() { doThrow(new SecurityException()) .when(mMockContext) - .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any()); + .enforceCallingOrSelfPermission( + eq(android.Manifest.permission.NETWORK_FACTORY), any()); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); } @Test public void testRemoveVcnUnderlyingNetworkPolicyListener() { - // verify listener added - doNothing() - .when(mMockContext) - .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any()); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); @@ -488,4 +495,24 @@ public class VcnManagementServiceTest { public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() { mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); } + + @Test + public void testGetUnderlyingNetworkPolicy() throws Exception { + VcnUnderlyingNetworkPolicy policy = + mVcnMgmtSvc.getUnderlyingNetworkPolicy( + new NetworkCapabilities(), new LinkProperties()); + + assertFalse(policy.isTeardownRequested()); + assertNotNull(policy.getMergedNetworkCapabilities()); + } + + @Test(expected = SecurityException.class) + public void testGetUnderlyingNetworkPolicyInvalidPermission() { + doThrow(new SecurityException()) + .when(mMockContext) + .enforceCallingOrSelfPermission( + eq(android.Manifest.permission.NETWORK_FACTORY), any()); + + mVcnMgmtSvc.getUnderlyingNetworkPolicy(new NetworkCapabilities(), new LinkProperties()); + } } |