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