Merge "Block service bound by foreground app to launch background activity by default"
diff --git a/core/api/current.txt b/core/api/current.txt
index 847e1c3..38f0481 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10071,6 +10071,7 @@
     field public static final String BATTERY_SERVICE = "batterymanager";
     field public static final int BIND_ABOVE_CLIENT = 8; // 0x8
     field public static final int BIND_ADJUST_WITH_ACTIVITY = 128; // 0x80
+    field public static final int BIND_ALLOW_ACTIVITY_STARTS = 512; // 0x200
     field public static final int BIND_ALLOW_OOM_MANAGEMENT = 16; // 0x10
     field public static final int BIND_AUTO_CREATE = 1; // 0x1
     field public static final int BIND_DEBUG_UNBIND = 2; // 0x2
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c8d48c1..a58cac3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -278,6 +278,7 @@
             BIND_IMPORTANT,
             BIND_ADJUST_WITH_ACTIVITY,
             BIND_NOT_PERCEPTIBLE,
+            BIND_ALLOW_ACTIVITY_STARTS,
             BIND_INCLUDE_CAPABILITIES,
             BIND_SHARED_ISOLATED_PROCESS
     })
@@ -382,6 +383,15 @@
     public static final int BIND_NOT_PERCEPTIBLE = 0x00000100;
 
     /**
+     * Flag for {@link #bindService}: If binding from an app that is visible, the bound service is
+     * allowed to start an activity from background. This was the default behavior before SDK
+     * version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}. Since then, the default
+     * behavior changed to disallow the bound service to start a background activity even if the app
+     * bound to it is in foreground, unless this flag is specified when binding.
+     */
+    public static final int BIND_ALLOW_ACTIVITY_STARTS = 0X000000200;
+
+    /**
      * Flag for {@link #bindService}: If binding from an app that has specific capabilities
      * due to its foreground state such as an activity or foreground service, then this flag will
      * allow the bound app to get the same capabilities, as long as it has the required permissions
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 18d84d5..ab7b0ab 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -543,6 +543,7 @@
         DEAD = 15;
         NOT_PERCEPTIBLE = 16;
         INCLUDE_CAPABILITIES = 17;
+        ALLOW_ACTIVITY_STARTS = 18;
     }
     repeated Flag flags = 3;
     optional string service_name = 4;
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 17fff91..f9c0c49 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -77,6 +77,7 @@
             Context.BIND_NOT_VISIBLE,
             Context.BIND_NOT_PERCEPTIBLE,
             Context.BIND_INCLUDE_CAPABILITIES,
+            Context.BIND_ALLOW_ACTIVITY_STARTS,
     };
     private static final int[] BIND_PROTO_ENUMS = new int[] {
             ConnectionRecordProto.AUTO_CREATE,
@@ -96,6 +97,7 @@
             ConnectionRecordProto.NOT_VISIBLE,
             ConnectionRecordProto.NOT_PERCEPTIBLE,
             ConnectionRecordProto.INCLUDE_CAPABILITIES,
+            ConnectionRecordProto.ALLOW_ACTIVITY_STARTS,
     };
 
     void dump(PrintWriter pw, String prefix) {
@@ -237,6 +239,9 @@
         if ((flags & Context.BIND_NOT_PERCEPTIBLE) != 0) {
             sb.append("!PRCP ");
         }
+        if ((flags & Context.BIND_ALLOW_ACTIVITY_STARTS) != 0) {
+            sb.append("BALF ");
+        }
         if ((flags & Context.BIND_INCLUDE_CAPABILITIES) != 0) {
             sb.append("CAPS ");
         }
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index df442e8..bd7f96a 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -28,6 +28,7 @@
 import android.util.ArraySet;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.wm.WindowProcessController;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -506,19 +507,21 @@
         return mConnections.size();
     }
 
-    void addBoundClientUid(int clientUid) {
+    void addBoundClientUid(int clientUid, String clientPackageName, int bindFlags) {
         mBoundClientUids.add(clientUid);
-        mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+        mApp.getWindowProcessController()
+                .addBoundClientUid(clientUid, clientPackageName, bindFlags);
     }
 
     void updateBoundClientUids() {
+        clearBoundClientUids();
         if (mServices.isEmpty()) {
-            clearBoundClientUids();
             return;
         }
         // grab a set of clientUids of all mConnections of all services
         final ArraySet<Integer> boundClientUids = new ArraySet<>();
         final int serviceCount = mServices.size();
+        WindowProcessController controller = mApp.getWindowProcessController();
         for (int j = 0; j < serviceCount; j++) {
             final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns =
                     mServices.valueAt(j).getConnections();
@@ -526,12 +529,13 @@
             for (int conni = 0; conni < size; conni++) {
                 ArrayList<ConnectionRecord> c = conns.valueAt(conni);
                 for (int i = 0; i < c.size(); i++) {
-                    boundClientUids.add(c.get(i).clientUid);
+                    ConnectionRecord cr = c.get(i);
+                    boundClientUids.add(cr.clientUid);
+                    controller.addBoundClientUid(cr.clientUid, cr.clientPackageName, cr.flags);
                 }
             }
         }
         mBoundClientUids = boundClientUids;
-        mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
     }
 
     void addBoundClientUidsOfNewService(ServiceRecord sr) {
@@ -542,15 +546,18 @@
         for (int conni = conns.size() - 1; conni >= 0; conni--) {
             ArrayList<ConnectionRecord> c = conns.valueAt(conni);
             for (int i = 0; i < c.size(); i++) {
-                mBoundClientUids.add(c.get(i).clientUid);
+                ConnectionRecord cr = c.get(i);
+                mBoundClientUids.add(cr.clientUid);
+                mApp.getWindowProcessController()
+                        .addBoundClientUid(cr.clientUid, cr.clientPackageName, cr.flags);
+
             }
         }
-        mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
     }
 
     void clearBoundClientUids() {
         mBoundClientUids.clear();
-        mApp.getWindowProcessController().setBoundClientUids(mBoundClientUids);
+        mApp.getWindowProcessController().clearBoundClientUids();
     }
 
     @GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index f667e33..2a7f181 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -894,7 +894,7 @@
 
         // if we have a process attached, add bound client uid of this connection to it
         if (app != null) {
-            app.mServices.addBoundClientUid(c.clientUid);
+            app.mServices.addBoundClientUid(c.clientUid, c.clientPackageName, c.flags);
             app.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_BOUND_SERVICE);
         }
     }
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index ecc8aad..bdb06a97 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -33,11 +33,17 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.BackgroundStartPrivileges;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
+import android.content.Context;
 import android.os.Binder;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Slog;
 
@@ -58,6 +64,12 @@
     private static final String TAG =
             TAG_WITH_CLASS_NAME ? "BackgroundLaunchProcessController" : TAG_ATM;
 
+    /** If enabled BAL are prevented by default in applications targeting U and later. */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @Overridable
+    private static final long DEFAULT_RESCIND_BAL_FG_PRIVILEGES_BOUND_SERVICE = 261072174;
+
     /** It is {@link ActivityTaskManagerService#hasActiveVisibleWindow(int)}. */
     private final IntPredicate mUidHasActiveVisibleWindowPredicate;
 
@@ -71,9 +83,11 @@
     @GuardedBy("this")
     private @Nullable ArrayMap<Binder, BackgroundStartPrivileges> mBackgroundStartPrivileges;
 
-    /** Set of UIDs of clients currently bound to this process. */
+    /** Set of UIDs of clients currently bound to this process and opt in to allow this process to
+     * launch background activity.
+     */
     @GuardedBy("this")
-    private @Nullable IntArray mBoundClientUids;
+    private @Nullable IntArray mBalOptInBoundClientUids;
 
     BackgroundLaunchProcessController(@NonNull IntPredicate uidHasActiveVisibleWindowPredicate,
             @Nullable BackgroundActivityStartCallback callback) {
@@ -204,9 +218,9 @@
 
     private boolean isBoundByForegroundUid() {
         synchronized (this) {
-            if (mBoundClientUids != null) {
-                for (int i = mBoundClientUids.size() - 1; i >= 0; i--) {
-                    if (mUidHasActiveVisibleWindowPredicate.test(mBoundClientUids.get(i))) {
+            if (mBalOptInBoundClientUids != null) {
+                for (int i = mBalOptInBoundClientUids.size() - 1; i >= 0; i--) {
+                    if (mUidHasActiveVisibleWindowPredicate.test(mBalOptInBoundClientUids.get(i))) {
                         return true;
                     }
                 }
@@ -215,19 +229,27 @@
         return false;
     }
 
-    void setBoundClientUids(ArraySet<Integer> boundClientUids) {
+    void clearBalOptInBoundClientUids() {
         synchronized (this) {
-            if (boundClientUids == null || boundClientUids.isEmpty()) {
-                mBoundClientUids = null;
-                return;
-            }
-            if (mBoundClientUids == null) {
-                mBoundClientUids = new IntArray();
+            if (mBalOptInBoundClientUids == null) {
+                mBalOptInBoundClientUids = new IntArray();
             } else {
-                mBoundClientUids.clear();
+                mBalOptInBoundClientUids.clear();
             }
-            for (int i = boundClientUids.size() - 1; i >= 0; i--) {
-                mBoundClientUids.add(boundClientUids.valueAt(i));
+        }
+    }
+
+    void addBoundClientUid(int clientUid, String clientPackageName, int bindFlags) {
+        if (!CompatChanges.isChangeEnabled(
+                DEFAULT_RESCIND_BAL_FG_PRIVILEGES_BOUND_SERVICE,
+                clientPackageName,
+                UserHandle.getUserHandleForUid(clientUid))
+                || (bindFlags & Context.BIND_ALLOW_ACTIVITY_STARTS) != 0) {
+            if (mBalOptInBoundClientUids == null) {
+                mBalOptInBoundClientUids = new IntArray();
+            }
+            if (mBalOptInBoundClientUids.indexOf(clientUid) == -1) {
+                mBalOptInBoundClientUids.add(clientUid);
             }
         }
     }
@@ -298,10 +320,10 @@
                     pw.println(mBackgroundStartPrivileges.valueAt(i));
                 }
             }
-            if (mBoundClientUids != null && mBoundClientUids.size() > 0) {
+            if (mBalOptInBoundClientUids != null && mBalOptInBoundClientUids.size() > 0) {
                 pw.print(prefix);
                 pw.print("BoundClientUids:");
-                pw.println(Arrays.toString(mBoundClientUids.toArray()));
+                pw.println(Arrays.toString(mBalOptInBoundClientUids.toArray()));
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 8f6ac15..2980c76 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -70,7 +70,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -594,8 +593,18 @@
         return mBgLaunchController.canCloseSystemDialogsByToken(mUid);
     }
 
-    public void setBoundClientUids(ArraySet<Integer> boundClientUids) {
-        mBgLaunchController.setBoundClientUids(boundClientUids);
+    /**
+     * Clear all bound client Uids.
+     */
+    public void clearBoundClientUids() {
+        mBgLaunchController.clearBalOptInBoundClientUids();
+    }
+
+    /**
+     * Add bound client Uid.
+     */
+    public void addBoundClientUid(int clientUid, String clientPackageName, int bindFlags) {
+        mBgLaunchController.addBoundClientUid(clientUid, clientPackageName, bindFlags);
     }
 
     /**