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.TAG_ATM; 21 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; 22 import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS; 23 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; 24 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_DISALLOW; 25 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_BOUND_BY_FOREGROUND; 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_TOKEN; 30 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW; 31 import static com.android.window.flags.Flags.balImprovedMetrics; 32 33 import static java.util.Objects.requireNonNull; 34 35 import android.annotation.NonNull; 36 import android.annotation.Nullable; 37 import android.app.BackgroundStartPrivileges; 38 import android.app.compat.CompatChanges; 39 import android.compat.annotation.ChangeId; 40 import android.compat.annotation.EnabledSince; 41 import android.compat.annotation.Overridable; 42 import android.content.Context; 43 import android.os.Binder; 44 import android.os.Build; 45 import android.os.IBinder; 46 import android.os.SystemClock; 47 import android.os.UserHandle; 48 import android.util.ArrayMap; 49 import android.util.IntArray; 50 51 import com.android.internal.annotations.GuardedBy; 52 import com.android.server.wm.BackgroundActivityStartController.BalVerdict; 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 BalCheckConfiguration( boolean isCheckingForFgsStart, boolean checkVisibility, boolean checkOtherExemptions, long gracePeriod )100 record BalCheckConfiguration( 101 boolean isCheckingForFgsStart, 102 boolean checkVisibility, 103 boolean checkOtherExemptions, 104 long gracePeriod 105 ) { 106 } 107 108 /** 109 * Check configuration for foreground service starts. 110 * 111 * The check executes all parts of the BAL checks and uses the same grace period, 112 * so FGS is allowed whenever BAL is allowed. 113 */ 114 static final BalCheckConfiguration CHECK_FOR_FGS_START = new BalCheckConfiguration( 115 /* isCheckingForFgsStarts */ true, 116 /* checkVisibility */ true, 117 /* checkOtherExemptions */ true, 118 ACTIVITY_BG_START_GRACE_PERIOD_MS); 119 areBackgroundActivityStartsAllowed( int pid, int uid, String packageName, int appSwitchState, BalCheckConfiguration checkConfiguration, boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges, long lastStopAppSwitchesTime, long lastActivityLaunchTime, long lastActivityFinishTime)120 BalVerdict areBackgroundActivityStartsAllowed( 121 int pid, int uid, String packageName, 122 int appSwitchState, BalCheckConfiguration checkConfiguration, 123 boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges, 124 long lastStopAppSwitchesTime, long lastActivityLaunchTime, 125 long lastActivityFinishTime) { 126 // Allow if the proc is instrumenting with background activity starts privs. 127 if (checkConfiguration.checkOtherExemptions && hasBackgroundActivityStartPrivileges) { 128 return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ 129 "process instrumenting with background activity starts privileges"); 130 } 131 // Allow if the token is explicitly allowed. 132 if (checkConfiguration.checkOtherExemptions) { 133 BalVerdict tokenVerdict = isBackgroundStartAllowedByToken(uid, 134 packageName, checkConfiguration.isCheckingForFgsStart); 135 if (tokenVerdict.allows()) { 136 if (!balImprovedMetrics()) { 137 return new BalVerdict(BAL_ALLOW_PERMISSION, tokenVerdict.toString()); 138 } 139 return tokenVerdict; 140 } 141 } 142 // Allow if the caller is bound by a UID that's currently foreground. 143 // But still respect the appSwitchState. 144 if (checkConfiguration.checkVisibility && appSwitchState != APP_SWITCH_DISALLOW 145 && isBoundByForegroundUid()) { 146 return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_BOUND_BY_FOREGROUND 147 : BAL_ALLOW_VISIBLE_WINDOW, /*background*/ 148 "process bound by foreground uid"); 149 } 150 // Allow if the caller has an activity in any foreground task. 151 if (checkConfiguration.checkOtherExemptions && hasActivityInVisibleTask 152 && appSwitchState != APP_SWITCH_DISALLOW) { 153 return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/ 154 "process has activity in foreground task"); 155 } 156 157 // If app switching is not allowed, we ignore all the start activity grace period 158 // exception so apps cannot start itself in onPause() after pressing home button. 159 if (checkConfiguration.checkOtherExemptions && appSwitchState == APP_SWITCH_ALLOW) { 160 // Allow if any activity in the caller has either started or finished very recently, and 161 // it must be started or finished after last stop app switches time. 162 if (lastActivityLaunchTime > lastStopAppSwitchesTime 163 || lastActivityFinishTime > lastStopAppSwitchesTime) { 164 final long now = SystemClock.uptimeMillis(); 165 long timeSinceLastStartOrFinish = now - Math.max(lastActivityLaunchTime, 166 lastActivityFinishTime); 167 if (timeSinceLastStartOrFinish < checkConfiguration.gracePeriod) { 168 return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/ 169 "within " + checkConfiguration.gracePeriod + "ms grace period (" 170 + timeSinceLastStartOrFinish + "ms)"); 171 } 172 } 173 } 174 return BalVerdict.BLOCK; 175 } 176 177 /** 178 * If there are no tokens, we don't allow *by token*. If there are tokens and 179 * isCheckingForFgsStart is false, we ask the callback if the start is allowed for these tokens, 180 * otherwise if there is no callback we allow. 181 */ isBackgroundStartAllowedByToken(int uid, String packageName, boolean isCheckingForFgsStart)182 private BalVerdict isBackgroundStartAllowedByToken(int uid, String packageName, 183 boolean isCheckingForFgsStart) { 184 synchronized (this) { 185 if (mBackgroundStartPrivileges == null 186 || mBackgroundStartPrivileges.isEmpty()) { 187 // no tokens to allow anything 188 return BalVerdict.BLOCK; 189 } 190 if (isCheckingForFgsStart) { 191 // check if any token allows foreground service starts 192 for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) { 193 if (mBackgroundStartPrivileges.valueAt(i).allowsBackgroundFgsStarts()) { 194 return new BalVerdict(BAL_ALLOW_TOKEN, "process allowed by token"); 195 } 196 } 197 return BalVerdict.BLOCK; 198 } 199 if (mBackgroundActivityStartCallback == null) { 200 // without a callback just check if any token allows background activity starts 201 for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) { 202 if (mBackgroundStartPrivileges.valueAt(i) 203 .allowsBackgroundActivityStarts()) { 204 return new BalVerdict(BAL_ALLOW_TOKEN, "process allowed by token"); 205 } 206 } 207 return BalVerdict.BLOCK; 208 } 209 List<IBinder> binderTokens = getOriginatingTokensThatAllowBal(); 210 if (binderTokens.isEmpty()) { 211 // no tokens to allow anything 212 return BalVerdict.BLOCK; 213 } 214 215 // The callback will decide. 216 BackgroundActivityStartCallback.BackgroundActivityStartCallbackResult 217 activityStartAllowed = mBackgroundActivityStartCallback.isActivityStartAllowed( 218 binderTokens, uid, packageName); 219 if (!activityStartAllowed.allowed()) { 220 return BalVerdict.BLOCK; 221 } 222 if (activityStartAllowed.token() == null) { 223 return new BalVerdict(BAL_ALLOW_TOKEN, 224 "process allowed by callback (token ignored) tokens: " + binderTokens); 225 } 226 return new BalVerdict(BAL_ALLOW_TOKEN, 227 "process allowed by callback (token: " + activityStartAllowed.token() 228 + ") tokens: " + binderTokens); 229 } 230 } 231 getOriginatingTokensThatAllowBal()232 private List<IBinder> getOriginatingTokensThatAllowBal() { 233 List<IBinder> originatingTokens = new ArrayList<>(); 234 for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) { 235 BackgroundStartPrivileges privilege = 236 mBackgroundStartPrivileges.valueAt(i); 237 if (privilege.allowsBackgroundActivityStarts()) { 238 originatingTokens.add(privilege.getOriginatingToken()); 239 } 240 } 241 return originatingTokens; 242 } 243 isBoundByForegroundUid()244 private boolean isBoundByForegroundUid() { 245 synchronized (this) { 246 if (mBalOptInBoundClientUids != null) { 247 for (int i = mBalOptInBoundClientUids.size() - 1; i >= 0; i--) { 248 if (mUidHasActiveVisibleWindowPredicate.test(mBalOptInBoundClientUids.get(i))) { 249 return true; 250 } 251 } 252 } 253 } 254 return false; 255 } 256 clearBalOptInBoundClientUids()257 void clearBalOptInBoundClientUids() { 258 synchronized (this) { 259 if (mBalOptInBoundClientUids == null) { 260 mBalOptInBoundClientUids = new IntArray(); 261 } else { 262 mBalOptInBoundClientUids.clear(); 263 } 264 } 265 } 266 addBoundClientUid(int clientUid, String clientPackageName, long bindFlags)267 void addBoundClientUid(int clientUid, String clientPackageName, long bindFlags) { 268 if (!CompatChanges.isChangeEnabled( 269 DEFAULT_RESCIND_BAL_FG_PRIVILEGES_BOUND_SERVICE, 270 clientPackageName, 271 UserHandle.getUserHandleForUid(clientUid)) 272 || (bindFlags & Context.BIND_ALLOW_ACTIVITY_STARTS) != 0) { 273 if (mBalOptInBoundClientUids == null) { 274 mBalOptInBoundClientUids = new IntArray(); 275 } 276 if (mBalOptInBoundClientUids.indexOf(clientUid) == -1) { 277 mBalOptInBoundClientUids.add(clientUid); 278 } 279 } 280 } 281 282 /** 283 * Allows background activity starts using token {@code entity}. Optionally, you can provide 284 * {@code originatingToken} in the {@link BackgroundStartPrivileges} if you have one such 285 * originating token, this is useful for tracing back the grant in the case of the notification 286 * token. 287 * 288 * If {@code entity} is already added, this method will update its {@code originatingToken}. 289 */ addOrUpdateAllowBackgroundStartPrivileges(@onNull Binder entity, @NonNull BackgroundStartPrivileges backgroundStartPrivileges)290 void addOrUpdateAllowBackgroundStartPrivileges(@NonNull Binder entity, 291 @NonNull BackgroundStartPrivileges backgroundStartPrivileges) { 292 requireNonNull(entity, "entity"); 293 requireNonNull(backgroundStartPrivileges, "backgroundStartPrivileges"); 294 checkArgument(backgroundStartPrivileges.allowsAny(), 295 "backgroundStartPrivileges does not allow anything"); 296 synchronized (this) { 297 if (mBackgroundStartPrivileges == null) { 298 mBackgroundStartPrivileges = new ArrayMap<>(); 299 } 300 mBackgroundStartPrivileges.put(entity, backgroundStartPrivileges); 301 } 302 } 303 304 /** 305 * Removes token {@code entity} that allowed background activity starts added via {@link 306 * #addOrUpdateAllowBackgroundStartPrivileges(Binder, BackgroundStartPrivileges)}. 307 */ removeAllowBackgroundStartPrivileges(@onNull Binder entity)308 void removeAllowBackgroundStartPrivileges(@NonNull Binder entity) { 309 requireNonNull(entity, "entity"); 310 synchronized (this) { 311 if (mBackgroundStartPrivileges != null) { 312 mBackgroundStartPrivileges.remove(entity); 313 } 314 } 315 } 316 317 /** 318 * Returns whether this process is allowed to close system dialogs via a background activity 319 * start token that allows the close system dialogs operation (eg. notification). 320 */ canCloseSystemDialogsByToken(int uid)321 boolean canCloseSystemDialogsByToken(int uid) { 322 if (mBackgroundActivityStartCallback == null) { 323 return false; 324 } 325 synchronized (this) { 326 if (mBackgroundStartPrivileges == null 327 || mBackgroundStartPrivileges.isEmpty()) { 328 return false; 329 } 330 return mBackgroundActivityStartCallback.canCloseSystemDialogs( 331 getOriginatingTokensThatAllowBal(), uid); 332 } 333 } 334 dump(PrintWriter pw, String prefix)335 void dump(PrintWriter pw, String prefix) { 336 synchronized (this) { 337 if (mBackgroundStartPrivileges != null 338 && !mBackgroundStartPrivileges.isEmpty()) { 339 pw.print(prefix); 340 pw.println("Background activity start tokens (token: originating token):"); 341 for (int i = mBackgroundStartPrivileges.size() - 1; i >= 0; i--) { 342 pw.print(prefix); 343 pw.print(" - "); 344 pw.print(mBackgroundStartPrivileges.keyAt(i)); 345 pw.print(": "); 346 pw.println(mBackgroundStartPrivileges.valueAt(i)); 347 } 348 } 349 if (mBalOptInBoundClientUids != null && mBalOptInBoundClientUids.size() > 0) { 350 pw.print(prefix); 351 pw.print("BoundClientUids:"); 352 pw.println(Arrays.toString(mBalOptInBoundClientUids.toArray())); 353 } 354 } 355 } 356 } 357