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
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 581aa05..0e07ec0 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -504,8 +504,8 @@
             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 @@
                 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 4b89b40..4ab09c4 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 @@
             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 @@
             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 75ae43b..2bec90d 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.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 @@
             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;
                 }
-                return;
+                if (!pr.whitelistManager) {
+                    if (DEBUG_WHITELISTS) {
+                        Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid
+                                + ": pid " + callerPid + " is not allowed");
+                    }
+                    return;
+                }
             }
         }
 
@@ -8019,65 +8025,42 @@
         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;
         }
-        return ActivityManager.APP_START_MODE_NORMAL;
+        // ...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;
+        }
     }
 
     // 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 @@
         }
 
         // 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 @@
                 // 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 61e555b..ee2467a 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 @@
                     + " (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 @@
                     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 11a3f11..5b539ff 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 @@
         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 f42c5be..a748013 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 @@
             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 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();