1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static com.android.internal.util.Preconditions.checkArgument;
20 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
21 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
22 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
23 import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
24 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
25 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_DISALLOW;
26 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_FOREGROUND;
27 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD;
28 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
29 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
30 
31 import static java.util.Objects.requireNonNull;
32 
33 import android.annotation.NonNull;
34 import android.annotation.Nullable;
35 import android.app.BackgroundStartPrivileges;
36 import android.app.compat.CompatChanges;
37 import android.compat.annotation.ChangeId;
38 import android.compat.annotation.EnabledSince;
39 import android.compat.annotation.Overridable;
40 import android.content.Context;
41 import android.os.Binder;
42 import android.os.Build;
43 import android.os.IBinder;
44 import android.os.SystemClock;
45 import android.os.UserHandle;
46 import android.util.ArrayMap;
47 import android.util.IntArray;
48 import android.util.Slog;
49 
50 import com.android.internal.annotations.GuardedBy;
51 import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
52 import com.android.window.flags.Flags;
53 
54 import java.io.PrintWriter;
55 import java.util.ArrayList;
56 import java.util.Arrays;
57 import java.util.List;
58 import java.util.function.IntPredicate;
59 
60 /**
61  * A per-process controller to decide whether the process can start activity or foreground service
62  * (especially from background). All methods of this class must be thread safe. The caller does not
63  * need to hold WM lock, e.g. lock contention of WM lock shouldn't happen when starting service.
64  */
65 class BackgroundLaunchProcessController {
66     private static final String TAG =
67             TAG_WITH_CLASS_NAME ? "BackgroundLaunchProcessController" : TAG_ATM;
68 
69     /** If enabled BAL are prevented by default in applications targeting U and later. */
70     @ChangeId
71     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
72     @Overridable
73     private static final long DEFAULT_RESCIND_BAL_FG_PRIVILEGES_BOUND_SERVICE = 261072174;
74 
75     /** It is {@link ActivityTaskManagerService#hasActiveVisibleWindow(int)}. */
76     private final IntPredicate mUidHasActiveVisibleWindowPredicate;
77 
78     private final @Nullable BackgroundActivityStartCallback mBackgroundActivityStartCallback;
79 
80     /**
81      * A set of tokens that currently contribute to this process being temporarily allowed
82      * to start activities even if it's not in the foreground. The values of this map are optional
83      * (can be null) and are used to trace back the grant to the notification token mechanism.
84      */
85     @GuardedBy("this")
86     private @Nullable ArrayMap<Binder, BackgroundStartPrivileges> mBackgroundStartPrivileges;
87 
88     /** Set of UIDs of clients currently bound to this process and opt in to allow this process to
89      * launch background activity.
90      */
91     @GuardedBy("this")
92     private @Nullable IntArray mBalOptInBoundClientUids;
93 
BackgroundLaunchProcessController(@onNull IntPredicate uidHasActiveVisibleWindowPredicate, @Nullable BackgroundActivityStartCallback callback)94     BackgroundLaunchProcessController(@NonNull IntPredicate uidHasActiveVisibleWindowPredicate,
95             @Nullable BackgroundActivityStartCallback callback) {
96         mUidHasActiveVisibleWindowPredicate = uidHasActiveVisibleWindowPredicate;
97         mBackgroundActivityStartCallback = callback;
98     }
99 
areBackgroundActivityStartsAllowed( int pid, int uid, String packageName, int appSwitchState, boolean isCheckingForFgsStart, boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges, long lastStopAppSwitchesTime, long lastActivityLaunchTime, long lastActivityFinishTime)100     BalVerdict areBackgroundActivityStartsAllowed(
101             int pid, int uid, String packageName,
102             int appSwitchState, boolean isCheckingForFgsStart,
103             boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
104             long lastStopAppSwitchesTime, long lastActivityLaunchTime,
105             long lastActivityFinishTime) {
106         // Allow if the proc is instrumenting with background activity starts privs.
107         if (hasBackgroundActivityStartPrivileges) {
108             return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
109                     "process instrumenting with background activity starts privileges");
110         }
111         // Allow if the flag was explicitly set.
112         if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
113             return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
114                     "process allowed by token");
115         }
116         // Allow if the caller is bound by a UID that's currently foreground.
117         // But still respect the appSwitchState.
118         boolean allowBoundByForegroundUid =
119                 Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid()
120                 ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid()
121                 : isBoundByForegroundUid();
122         if (allowBoundByForegroundUid) {
123             return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false,
124                     "process bound by foreground uid");
125         }
126         // Allow if the caller has an activity in any foreground task.
127         if (hasActivityInVisibleTask && appSwitchState != APP_SWITCH_DISALLOW) {
128             return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/ false,
129                     "process has activity in foreground task");
130         }
131 
132         // If app switching is not allowed, we ignore all the start activity grace period
133         // exception so apps cannot start itself in onPause() after pressing home button.
134         if (appSwitchState == APP_SWITCH_ALLOW) {
135             // Allow if any activity in the caller has either started or finished very recently, and
136             // it must be started or finished after last stop app switches time.
137             final long now = SystemClock.uptimeMillis();
138             if (now - lastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
139                     || now - lastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
140                 // If activity is started and finished before stop app switch time, we should not
141                 // let app to be able to start background activity even it's in grace period.
142                 if (lastActivityLaunchTime > lastStopAppSwitchesTime
143                         || lastActivityFinishTime > lastStopAppSwitchesTime) {
144                     return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/ true,
145                             "within " + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
146                 }
147                 if (DEBUG_ACTIVITY_STARTS) {
148                     Slog.d(TAG, "[Process(" + pid + ")] Activity start within "
149                             + ACTIVITY_BG_START_GRACE_PERIOD_MS
150                             + "ms grace period but also within stop app switch window");
151                 }
152 
153             }
154         }
155         return BalVerdict.BLOCK;
156     }
157 
158     /**
159      * If there are no tokens, we don't allow *by token*. If there are tokens and
160      * isCheckingForFgsStart is false, we ask the callback if the start is allowed for these tokens,
161      * otherwise if there is no callback we allow.
162      */
isBackgroundStartAllowedByToken(int uid, String packageName, boolean isCheckingForFgsStart)163     private boolean isBackgroundStartAllowedByToken(int uid, String packageName,
164             boolean isCheckingForFgsStart) {
165         synchronized (this) {
166             if (mBackgroundStartPrivileges == null
167                     || mBackgroundStartPrivileges.isEmpty()) {
168                 // no tokens to allow anything
169                 return false;
170             }
171             if (isCheckingForFgsStart) {
172                 // check if any token allows foreground service starts
173                 for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) {
174                     if (mBackgroundStartPrivileges.valueAt(i).allowsBackgroundFgsStarts()) {
175                         return true;
176                     }
177                 }
178                 return false;
179             }
180             if (mBackgroundActivityStartCallback == null) {
181                 // without a callback just check if any token allows background activity starts
182                 for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) {
183                     if (mBackgroundStartPrivileges.valueAt(i)
184                             .allowsBackgroundActivityStarts()) {
185                         return true;
186                     }
187                 }
188                 return false;
189             }
190             List<IBinder> binderTokens = getOriginatingTokensThatAllowBal();
191             if (binderTokens.isEmpty()) {
192                 // no tokens to allow anything
193                 return false;
194             }
195 
196             // The callback will decide.
197             return mBackgroundActivityStartCallback.isActivityStartAllowed(
198                     binderTokens, uid, packageName);
199         }
200     }
201 
getOriginatingTokensThatAllowBal()202     private List<IBinder> getOriginatingTokensThatAllowBal() {
203         List<IBinder> originatingTokens = new ArrayList<>();
204         for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) {
205             BackgroundStartPrivileges privilege =
206                     mBackgroundStartPrivileges.valueAt(i);
207             if (privilege.allowsBackgroundActivityStarts()) {
208                 originatingTokens.add(privilege.getOriginatingToken());
209             }
210         }
211         return originatingTokens;
212     }
213 
isBoundByForegroundUid()214     private boolean isBoundByForegroundUid() {
215         synchronized (this) {
216             if (mBalOptInBoundClientUids != null) {
217                 for (int i = mBalOptInBoundClientUids.size() - 1; i >= 0; i--) {
218                     if (mUidHasActiveVisibleWindowPredicate.test(mBalOptInBoundClientUids.get(i))) {
219                         return true;
220                     }
221                 }
222             }
223         }
224         return false;
225     }
226 
clearBalOptInBoundClientUids()227     void clearBalOptInBoundClientUids() {
228         synchronized (this) {
229             if (mBalOptInBoundClientUids == null) {
230                 mBalOptInBoundClientUids = new IntArray();
231             } else {
232                 mBalOptInBoundClientUids.clear();
233             }
234         }
235     }
236 
addBoundClientUid(int clientUid, String clientPackageName, long bindFlags)237     void addBoundClientUid(int clientUid, String clientPackageName, long bindFlags) {
238         if (!CompatChanges.isChangeEnabled(
239                 DEFAULT_RESCIND_BAL_FG_PRIVILEGES_BOUND_SERVICE,
240                 clientPackageName,
241                 UserHandle.getUserHandleForUid(clientUid))
242                 || (bindFlags & Context.BIND_ALLOW_ACTIVITY_STARTS) != 0) {
243             if (mBalOptInBoundClientUids == null) {
244                 mBalOptInBoundClientUids = new IntArray();
245             }
246             if (mBalOptInBoundClientUids.indexOf(clientUid) == -1) {
247                 mBalOptInBoundClientUids.add(clientUid);
248             }
249         }
250     }
251 
252     /**
253      * Allows background activity starts using token {@code entity}. Optionally, you can provide
254      * {@code originatingToken} in the {@link BackgroundStartPrivileges} if you have one such
255      * originating token, this is useful for tracing back the grant in the case of the notification
256      * token.
257      *
258      * If {@code entity} is already added, this method will update its {@code originatingToken}.
259      */
addOrUpdateAllowBackgroundStartPrivileges(@onNull Binder entity, @NonNull BackgroundStartPrivileges backgroundStartPrivileges)260     void addOrUpdateAllowBackgroundStartPrivileges(@NonNull Binder entity,
261             @NonNull BackgroundStartPrivileges backgroundStartPrivileges) {
262         requireNonNull(entity, "entity");
263         requireNonNull(backgroundStartPrivileges, "backgroundStartPrivileges");
264         checkArgument(backgroundStartPrivileges.allowsAny(),
265                 "backgroundStartPrivileges does not allow anything");
266         synchronized (this) {
267             if (mBackgroundStartPrivileges == null) {
268                 mBackgroundStartPrivileges = new ArrayMap<>();
269             }
270             mBackgroundStartPrivileges.put(entity, backgroundStartPrivileges);
271         }
272     }
273 
274     /**
275      * Removes token {@code entity} that allowed background activity starts added via {@link
276      * #addOrUpdateAllowBackgroundStartPrivileges(Binder, BackgroundStartPrivileges)}.
277      */
removeAllowBackgroundStartPrivileges(@onNull Binder entity)278     void removeAllowBackgroundStartPrivileges(@NonNull Binder entity) {
279         requireNonNull(entity, "entity");
280         synchronized (this) {
281             if (mBackgroundStartPrivileges != null) {
282                 mBackgroundStartPrivileges.remove(entity);
283             }
284         }
285     }
286 
287     /**
288      * Returns whether this process is allowed to close system dialogs via a background activity
289      * start token that allows the close system dialogs operation (eg. notification).
290      */
canCloseSystemDialogsByToken(int uid)291     boolean canCloseSystemDialogsByToken(int uid) {
292         if (mBackgroundActivityStartCallback == null) {
293             return false;
294         }
295         synchronized (this) {
296             if (mBackgroundStartPrivileges == null
297                     || mBackgroundStartPrivileges.isEmpty()) {
298                 return false;
299             }
300             return mBackgroundActivityStartCallback.canCloseSystemDialogs(
301                     getOriginatingTokensThatAllowBal(), uid);
302         }
303     }
304 
dump(PrintWriter pw, String prefix)305     void dump(PrintWriter pw, String prefix) {
306         synchronized (this) {
307             if (mBackgroundStartPrivileges != null
308                     && !mBackgroundStartPrivileges.isEmpty()) {
309                 pw.print(prefix);
310                 pw.println("Background activity start tokens (token: originating token):");
311                 for (int i = mBackgroundStartPrivileges.size() - 1; i >= 0; i--) {
312                     pw.print(prefix);
313                     pw.print("  - ");
314                     pw.print(mBackgroundStartPrivileges.keyAt(i));
315                     pw.print(": ");
316                     pw.println(mBackgroundStartPrivileges.valueAt(i));
317                 }
318             }
319             if (mBalOptInBoundClientUids != null && mBalOptInBoundClientUids.size() > 0) {
320                 pw.print(prefix);
321                 pw.print("BoundClientUids:");
322                 pw.println(Arrays.toString(mBalOptInBoundClientUids.toArray()));
323             }
324         }
325     }
326 }
327