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