From c3af19a87dc70c321ffcc1e90453bb6f0545aef2 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 20 Jan 2017 17:00:44 -0800 Subject: [PATCH] Optimize bg check flow. No longer need to look up the application info, target SDK is explicitly passed in to the check. For the external method, we change this to just checked to see if background is completely disabled, which doesn't need a target SDK check (and is the only thing any of the current clients care about). Now allow SystemUI to put targets of notification pending intents on the temporary whitelist when they fire, so developers can avoid dealing with background restrictions in this case (if the user interacts with their notification, they will temporarily be considered in the foreground). Remove any thoughts of enforing restrictions on registerReceiver(), so we don't need to deal with target SDK versions there (which can't be done all that efficiently). Also bring back the old "allow starts coming from foreground apps" only for the MODE_IGNORE app op, since it should provide some better compatibility. Test: ran them. Change-Id: Id4ea7f992d12ce4bd8e54f1dbaeb4a460a3dee59 --- core/java/android/app/ActivityManager.java | 8 +- core/java/android/app/ContextImpl.java | 4 +- core/java/android/app/IActivityManager.aidl | 2 +- packages/SystemUI/AndroidManifest.xml | 3 + .../android/server/AlarmManagerService.java | 7 +- .../com/android/server/am/ActiveServices.java | 9 +- .../server/am/ActivityManagerService.java | 145 +++++++++--------- .../com/android/server/am/BroadcastQueue.java | 23 +-- .../android/server/content/SyncManager.java | 3 +- .../server/job/JobSchedulerService.java | 9 +- 10 files changed, 96 insertions(+), 117 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 1d4b038f2d825..c1a888d28428c 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -520,17 +520,17 @@ public class ActivityManager { /** @hide Flag for registerUidObserver: report uid has become active. */ public static final int UID_OBSERVER_ACTIVE = 1<<3; - /** @hide Mode for {@link IActivityManager#getAppStartMode}: normal free-to-run operation. */ + /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: normal free-to-run operation. */ public static final int APP_START_MODE_NORMAL = 0; - /** @hide Mode for {@link IActivityManager#getAppStartMode}: delay running until later. */ + /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: delay running until later. */ public static final int APP_START_MODE_DELAYED = 1; - /** @hide Mode for {@link IActivityManager#getAppStartMode}: delay running until later, with + /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: delay running until later, with * rigid errors (throwing exception). */ public static final int APP_START_MODE_DELAYED_RIGID = 2; - /** @hide Mode for {@link IActivityManager#getAppStartMode}: disable/cancel pending + /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: disable/cancel pending * launches; this is the mode for ephemeral apps. */ public static final int APP_START_MODE_DISABLED = 3; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5e7d46fe05aa4..1b31cc5ea41f0 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1342,8 +1342,8 @@ class ContextImpl extends Context { } try { final Intent intent = ActivityManager.getService().registerReceiver( - mMainThread.getApplicationThread(), mBasePackageName, - rd, filter, broadcastPermission, userId); + mMainThread.getApplicationThread(), mBasePackageName, rd, filter, + broadcastPermission, userId); if (intent != null) { intent.setExtrasClassLoader(getClassLoader()); intent.prepareToEnterProcess(); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 135c2a4eef915..ec7c1709b3c5b 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -475,7 +475,7 @@ interface IActivityManager { void suppressResizeConfigChanges(boolean suppress); void moveTasksToFullscreenStack(int fromStackId, boolean onTop); boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds); - int getAppStartMode(int uid, in String packageName); + boolean isAppStartModeDisabled(int uid, in String packageName); boolean unlockUser(int userid, in byte[] token, in byte[] secret, in IProgressListener listener); boolean isInMultiWindowMode(in IBinder token); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index fede34d2ced37..f72d091607ac1 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -131,6 +131,9 @@ + + + diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 581aa05d7d329..0e07ec0e27f4c 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -504,8 +504,8 @@ class AlarmManagerService extends SystemService { for (int i = alarms.size()-1; i >= 0; i--) { Alarm alarm = alarms.get(i); try { - if (alarm.uid == uid && ActivityManager.getService().getAppStartMode( - uid, alarm.packageName) == ActivityManager.APP_START_MODE_DISABLED) { + if (alarm.uid == uid && ActivityManager.getService().isAppStartModeDisabled( + uid, alarm.packageName)) { alarms.remove(i); didRemove = true; if (alarm.alarmClock != null) { @@ -1089,8 +1089,7 @@ class AlarmManagerService extends SystemService { operation, directReceiver, listenerTag, workSource, flags, alarmClock, callingUid, callingPackage); try { - if (ActivityManager.getService().getAppStartMode(callingUid, callingPackage) - == ActivityManager.APP_START_MODE_DISABLED) { + if (ActivityManager.getService().isAppStartModeDisabled(callingUid, callingPackage)) { Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a + " -- package not allowed to start"); return; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 4b89b404f9b6b..4ab09c4d8ae30 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -349,8 +349,8 @@ public final class ActiveServices { try { // Before going further -- if this app is not allowed to start services in the // background, then at this point we aren't going to let it period. - final int allowed = mAm.checkAllowBackgroundLocked( - r.appInfo.uid, r.packageName, callingPid, false); + final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName, + r.appInfo.targetSdkVersion, callingPid, false, false); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { Slog.w(TAG, "Background start not allowed: service " + service + " to " + r.name.flattenToShortString() @@ -607,8 +607,9 @@ public final class ActiveServices { for (int i=services.mServicesByName.size()-1; i>=0; i--) { ServiceRecord service = services.mServicesByName.valueAt(i); if (service.appInfo.uid == uid && service.startRequested) { - if (mAm.checkAllowBackgroundLocked(service.appInfo.uid, service.packageName, - -1, false) != ActivityManager.APP_START_MODE_NORMAL) { + if (mAm.getAppStartModeLocked(service.appInfo.uid, service.packageName, + service.appInfo.targetSdkVersion, -1, false, false) + != ActivityManager.APP_START_MODE_NORMAL) { if (stopping == null) { stopping = new ArrayList<>(); stopping.add(service); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 75ae43b02329a..2bec90d7313b7 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; @@ -7283,18 +7284,23 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.d(TAG, "tempWhitelistAppForPowerSave(" + callerPid + ", " + callerUid + ", " + targetUid + ", " + duration + ")"); } - synchronized (mPidsSelfLocked) { - final ProcessRecord pr = mPidsSelfLocked.get(callerPid); - if (pr == null) { - Slog.w(TAG, "tempWhitelistAppForPowerSave() no ProcessRecord for pid " + callerPid); - return; - } - if (!pr.whitelistManager) { - if (DEBUG_WHITELISTS) { - Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid + ": pid " - + callerPid + " is not allowed"); + + if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid) + != PackageManager.PERMISSION_GRANTED) { + synchronized (mPidsSelfLocked) { + final ProcessRecord pr = mPidsSelfLocked.get(callerPid); + if (pr == null) { + Slog.w(TAG, "tempWhitelistAppForPowerSave() no ProcessRecord for pid " + + callerPid); + return; + } + if (!pr.whitelistManager) { + if (DEBUG_WHITELISTS) { + Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid + + ": pid " + callerPid + " is not allowed"); + } + return; } - return; } } @@ -8019,65 +8025,42 @@ public class ActivityManagerService extends IActivityManager.Stub return readMet && writeMet; } - public int getAppStartMode(int uid, String packageName) { + public boolean isAppStartModeDisabled(int uid, String packageName) { synchronized (this) { - return checkAllowBackgroundLocked(uid, packageName, -1, false); + return getAppStartModeLocked(uid, packageName, 0, -1, false, true) + == ActivityManager.APP_START_MODE_DISABLED; } } // Unified app-op and target sdk check - int appRestrictedInBackgroundLocked(int uid, String packageName) { - if (packageName == null) { - packageName = mPackageManagerInt.getNameForUid(uid); - if (packageName == null) { - Slog.w(TAG, "No package known for uid " + uid); - return ActivityManager.APP_START_MODE_NORMAL; - } - } - - // !!! TODO: cache the package/versionCode lookups to fast path this - ApplicationInfo app = getPackageManagerInternalLocked().getApplicationInfo(packageName, - UserHandle.getUserId(uid)); - if (app != null) { - // Apps that target O+ are always subject to background check - if (mEnforceBackgroundCheck && app.targetSdkVersion >= Build.VERSION_CODES.O) { - if (DEBUG_BACKGROUND_CHECK) { - Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted"); - } - return ActivityManager.APP_START_MODE_DELAYED_RIGID; - } - // ...and legacy apps get an AppOp check - int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, - uid, packageName); + int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) { + // Apps that target O+ are always subject to background check + if (mEnforceBackgroundCheck && packageTargetSdk >= Build.VERSION_CODES.O) { if (DEBUG_BACKGROUND_CHECK) { - Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop); + Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted"); } - switch (appop) { - case AppOpsManager.MODE_ALLOWED: - return ActivityManager.APP_START_MODE_NORMAL; - case AppOpsManager.MODE_IGNORED: - return ActivityManager.APP_START_MODE_DELAYED; - default: - return ActivityManager.APP_START_MODE_DELAYED_RIGID; - } - } else { - Slog.w(TAG, "Unknown app " + packageName + " / " + uid); + return ActivityManager.APP_START_MODE_DELAYED_RIGID; + } + // ...and legacy apps get an AppOp check + int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, + uid, packageName); + if (DEBUG_BACKGROUND_CHECK) { + Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop); + } + switch (appop) { + case AppOpsManager.MODE_ALLOWED: + return ActivityManager.APP_START_MODE_NORMAL; + case AppOpsManager.MODE_IGNORED: + return ActivityManager.APP_START_MODE_DELAYED; + default: + return ActivityManager.APP_START_MODE_DELAYED_RIGID; } - return ActivityManager.APP_START_MODE_NORMAL; } // Service launch is available to apps with run-in-background exemptions but // some other background operations are not. If we're doing a check // of service-launch policy, allow those callers to proceed unrestricted. - int appServicesRestrictedInBackgroundLocked(int uid, String packageName) { - if (packageName == null) { - packageName = mPackageManagerInt.getNameForUid(uid); - if (packageName == null) { - Slog.w(TAG, "No package known for uid " + uid); - return ActivityManager.APP_START_MODE_NORMAL; - } - } - + int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) { // Persistent app? NB: expects that persistent uids are always active. final UidRecord uidRec = mActiveUids.get(uid); if (uidRec != null && uidRec.persistent) { @@ -8107,11 +8090,11 @@ public class ActivityManagerService extends IActivityManager.Stub } // None of the service-policy criteria apply, so we apply the common criteria - return appRestrictedInBackgroundLocked(uid, packageName); + return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk); } - int checkAllowBackgroundLocked(int uid, String packageName, int callingPid, - boolean alwaysRestrict) { + int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk, + int callingPid, boolean alwaysRestrict, boolean disabledOnly) { UidRecord uidRec = mActiveUids.get(uid); if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg=" + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle=" @@ -8129,27 +8112,37 @@ public class ActivityManagerService extends IActivityManager.Stub // We are hard-core about ephemeral apps not running in the background. return ActivityManager.APP_START_MODE_DISABLED; } else { - /** Don't want to allow this exception in the final background check impl? - if (callingPid >= 0) { - ProcessRecord proc; - synchronized (mPidsSelfLocked) { - proc = mPidsSelfLocked.get(callingPid); - } - if (proc != null && proc.curProcState - < ActivityManager.PROCESS_STATE_RECEIVER) { - // Whoever is instigating this is in the foreground, so we will allow it - // to go through. - return ActivityManager.APP_START_MODE_NORMAL; - } + if (disabledOnly) { + // The caller is only interested in whether app starts are completely + // disabled for the given package (that is, it is an instant app). So + // we don't need to go further, which is all just seeing if we should + // apply a "delayed" mode for a regular app. + return ActivityManager.APP_START_MODE_NORMAL; } - */ - final int startMode = (alwaysRestrict) - ? appRestrictedInBackgroundLocked(uid, packageName) - : appServicesRestrictedInBackgroundLocked(uid, packageName); + ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk) + : appServicesRestrictedInBackgroundLocked(uid, packageName, + packageTargetSdk); if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg=" + packageName + " startMode=" + startMode + " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid)); + if (startMode == ActivityManager.APP_START_MODE_DELAYED) { + // This is an old app that has been forced into a "compatible as possible" + // mode of background check. To increase compatibility, we will allow other + // foreground apps to cause its services to start. + if (callingPid >= 0) { + ProcessRecord proc; + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(callingPid); + } + if (proc != null && proc.curProcState + < ActivityManager.PROCESS_STATE_RECEIVER) { + // Whoever is instigating this is in the foreground, so we will allow it + // to go through. + return ActivityManager.APP_START_MODE_NORMAL; + } + } + } return startMode; } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 61e555bd8b210..ee2467a925c17 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -592,22 +592,6 @@ public final class BroadcastQueue { + " (uid " + r.callingUid + ")"); skip = true; } - if (!skip) { - final int allowed = mService.checkAllowBackgroundLocked(filter.receiverList.uid, - filter.packageName, -1, false); - if (false && allowed == ActivityManager.APP_START_MODE_DISABLED) { - // XXX should we really not allow this? It means that while we are - // keeping an ephemeral app cached, its registered receivers will stop - // receiving broadcasts after it goes idle... so if it comes back to - // the foreground, it won't know what the current state of those broadcasts is. - Slog.w(TAG, "Background execution not allowed: receiving " - + r.intent - + " to " + filter.receiverList.app - + " (pid=" + filter.receiverList.pid - + ", uid=" + filter.receiverList.uid + ")"); - skip = true; - } - } if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid, r.callingPid, r.resolvedType, filter.receiverList.uid)) { @@ -1156,13 +1140,14 @@ public final class BroadcastQueue { info.activityInfo.applicationInfo.uid, false); if (!skip) { - final int allowed = mService.checkAllowBackgroundLocked( - info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1, true); + final int allowed = mService.getAppStartModeLocked( + info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, + info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { // We won't allow this receiver to be launched if the app has been // completely disabled from launches, or it was not explicitly sent // to it and the app is in a state that should not receive it - // (depending on how checkAllowBackgroundLocked has determined that). + // (depending on how getAppStartModeLocked has determined that). if (allowed == ActivityManager.APP_START_MODE_DISABLED) { Slog.w(TAG, "Background execution disabled: receiving " + r.intent + " to " diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 11a3f1189129d..5b539ff1976d1 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -1019,8 +1019,7 @@ public class SyncManager { final int owningUid = syncAdapterInfo.uid; final String owningPackage = syncAdapterInfo.componentName.getPackageName(); try { - if (ActivityManager.getService().getAppStartMode(owningUid, - owningPackage) == ActivityManager.APP_START_MODE_DISABLED) { + if (ActivityManager.getService().isAppStartModeDisabled(owningUid, owningPackage)) { Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":" + syncAdapterInfo.componentName + " -- package not allowed to start"); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index f42c5be0ebf94..a7480133972ad 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -566,8 +566,8 @@ public final class JobSchedulerService extends com.android.server.SystemService String tag) { JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); try { - if (ActivityManager.getService().getAppStartMode(uId, - job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) { + if (ActivityManager.getService().isAppStartModeDisabled(uId, + job.getService().getPackageName())) { Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString() + " -- package not allowed to start"); return JobScheduler.RESULT_FAILURE; @@ -1201,9 +1201,8 @@ public final class JobSchedulerService extends com.android.server.SystemService public void process(JobStatus job) { if (isReadyToBeExecutedLocked(job)) { try { - if (ActivityManager.getService().getAppStartMode(job.getUid(), - job.getJob().getService().getPackageName()) - == ActivityManager.APP_START_MODE_DISABLED) { + if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(), + job.getJob().getService().getPackageName())) { Slog.w(TAG, "Aborting job " + job.getUid() + ":" + job.getJob().toString() + " -- package not allowed to start"); mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();