Add new BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS binding flag
and the mechanism to use it

Since both starts and bindings might be contributing to the service
being whitelisted, track the state in ServiceRecord and update
the parent process accordingly when changes happen.

While we're at it, add that flag for bindings to accessibility
services, voice interaction services and QSTiles.

Also, make the timeout constant configurable for start-based
whitelisting.

Bug: 124298522
Bug: 123495976 (accessibility services)
Bug: 123337181 (paying via NFC, like Google Pay)
Bug: 122887611 (binding to InCallService)
Bug: 127774234 (binding to QSTile)
Bug: 124288681 (binding to voice interaction services)
Test: atest WmTests:ActivityStarterTests
Test: atest AccessibilityServiceConnectionTest
Test: manual with:
1) notifications from calendar and dynamite to test service starts
whitelisting
2) enabling talkback and livetranscribe to test service bindings
whitelisting
3) adding the LastPass QSTile and clicking on it
Change-Id: I8b6f23f999683920411ca40ab5b25c688a4f7055
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 85ec2dd..6270106 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -131,9 +131,6 @@
     // calling startForeground() before we ANR + stop it.
     static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;
 
-    // For how long after a whitelisted service's start its process can start a background activity
-    private static final int SERVICE_BG_ACTIVITY_START_TIMEOUT_MS = 10*1000;
-
     final ActivityManagerService mAm;
 
     // Maximum number of services that we allow to start in the background
@@ -333,8 +330,8 @@
                             + " delayedStop=" + r.delayedStop);
                 } else {
                     try {
-                        startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true,
-                                false);
+                        startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false,
+                                true);
                     } catch (TransactionTooLargeException e) {
                         // Ignore, nobody upstack cares.
                     }
@@ -637,26 +634,26 @@
         }
 
         if (allowBackgroundActivityStarts) {
-            ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
-            if (proc != null) {
-                proc.addAllowBackgroundActivityStartsToken(r);
-                // schedule removal of the whitelisting token after the timeout
-                removeAllowBackgroundActivityStartsServiceToken(proc, r,
-                        SERVICE_BG_ACTIVITY_START_TIMEOUT_MS);
-            }
+            r.hasStartedWhitelistingBgActivityStarts = true;
+            scheduleCleanUpHasStartedWhitelistingBgActivityStartsLocked(r);
         }
-        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting,
-                allowBackgroundActivityStarts);
+
+        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
         return cmp;
     }
 
-    private void removeAllowBackgroundActivityStartsServiceToken(ProcessRecord proc,
-            ServiceRecord r, int delayMillis) {
-        mAm.mHandler.postDelayed(() -> {
-            if (proc != null) {
-                proc.removeAllowBackgroundActivityStartsToken(r);
-            }
-        }, delayMillis);
+    private void scheduleCleanUpHasStartedWhitelistingBgActivityStartsLocked(ServiceRecord r) {
+        // if there's a request pending from the past, drop it before scheduling a new one
+        if (r.startedWhitelistingBgActivityStartsCleanUp == null) {
+            r.startedWhitelistingBgActivityStartsCleanUp = () -> {
+                synchronized(mAm) {
+                    r.setHasStartedWhitelistingBgActivityStarts(false);
+                }
+            };
+        }
+        mAm.mHandler.removeCallbacks(r.startedWhitelistingBgActivityStartsCleanUp);
+        mAm.mHandler.postDelayed(r.startedWhitelistingBgActivityStartsCleanUp,
+                mAm.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
     }
 
     private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
@@ -705,8 +702,7 @@
     }
 
     ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
-            boolean callerFg, boolean addToStarting, boolean allowBackgroundActivityStarts)
-            throws TransactionTooLargeException {
+            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
         ServiceState stracker = r.getTracker();
         if (stracker != null) {
             stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
@@ -717,8 +713,7 @@
         synchronized (r.stats.getBatteryStats()) {
             r.stats.startRunningLocked();
         }
-        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false,
-                allowBackgroundActivityStarts);
+        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
         if (error != null) {
             return new ComponentName("!!", error);
         }
@@ -765,6 +760,12 @@
                     SystemClock.uptimeMillis());
         }
         service.callStart = false;
+
+        // the service will not necessarily be brought down, so only clear the whitelisting state
+        // for start-based bg activity starts now, and drop any existing future cleanup callback
+        service.setHasStartedWhitelistingBgActivityStarts(false);
+        mAm.mHandler.removeCallbacks(service.startedWhitelistingBgActivityStartsCleanUp);
+
         bringDownServiceIfNeededLocked(service, false, false);
     }
 
@@ -788,9 +789,6 @@
             if (r.record != null) {
                 final long origId = Binder.clearCallingIdentity();
                 try {
-                    // immediately remove bg activity whitelisting token if there was one
-                    removeAllowBackgroundActivityStartsServiceToken(callerApp, r.record,
-                            0 /* delayMillis */);
                     stopServiceLocked(r.record);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
@@ -1614,6 +1612,12 @@
                             + ") set BIND_ALLOW_INSTANT when binding service " + service);
         }
 
+        if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
+            mAm.enforceCallingPermission(
+                    android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+                    "BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS");
+        }
+
         final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
         final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
         final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
@@ -1672,7 +1676,7 @@
                                 try {
                                     bringUpServiceLocked(serviceRecord,
                                             serviceIntent.getFlags(),
-                                            callerFg, false, false, false);
+                                            callerFg, false, false);
                                 } catch (RemoteException e) {
                                     /* ignore - local call */
                                 }
@@ -1762,6 +1766,9 @@
             if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
                 s.whitelistManager = true;
             }
+            if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
+                s.setHasBindingWhitelistingBgActivityStarts(true);
+            }
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app, c, true);
             }
@@ -1775,7 +1782,7 @@
             if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                 s.lastActivity = SystemClock.uptimeMillis();
                 if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
-                        permissionsReviewRequired, false) != null) {
+                        permissionsReviewRequired) != null) {
                     return 0;
                 }
             }
@@ -2445,8 +2452,7 @@
             return;
         }
         try {
-            bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false,
-                    false);
+            bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false);
         } catch (TransactionTooLargeException e) {
             // Ignore, it's been logged and nothing upstack cares.
         }
@@ -2491,11 +2497,8 @@
     }
 
     private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
-            boolean whileRestarting, boolean permissionsReviewRequired,
-            boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
-        //Slog.i(TAG, "Bring up service:");
-        //r.dump("  ");
-
+            boolean whileRestarting, boolean permissionsReviewRequired)
+            throws TransactionTooLargeException {
         if (r.app != null && r.app.thread != null) {
             sendServiceArgsLocked(r, execInFg, false);
             return null;
@@ -2603,13 +2606,6 @@
             }
         }
 
-        if (app != null && allowBackgroundActivityStarts) {
-            app.addAllowBackgroundActivityStartsToken(r);
-            // schedule removal of the whitelisting token after the timeout
-            removeAllowBackgroundActivityStartsServiceToken(app, r,
-                    SERVICE_BG_ACTIVITY_START_TIMEOUT_MS);
-        }
-
         if (r.fgRequired) {
             if (DEBUG_FOREGROUND_SERVICE) {
                 Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid)
@@ -2646,6 +2642,11 @@
         }
     }
 
+    /**
+     * Note the name of this method should not be confused with the started services concept.
+     * The "start" here means bring up the instance in the client, and this method is called
+     * from bindService() as well.
+     */
     private final void realStartServiceLocked(ServiceRecord r,
             ProcessRecord app, boolean execInFg) throws RemoteException {
         if (app.thread == null) {
@@ -3085,6 +3086,10 @@
                     updateWhitelistManagerLocked(s.app);
                 }
             }
+            // And do the same for bg activity starts whitelisting.
+            if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
+                s.updateHasBindingWhitelistingBgActivityStarts();
+            }
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app, c, true);
             }