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