1 /* 2 * Copyright (C) 2018 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.am; 18 19 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; 20 21 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; 22 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; 23 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 24 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; 25 26 import android.annotation.Nullable; 27 import android.app.Activity; 28 import android.app.ActivityManagerInternal; 29 import android.app.ActivityOptions; 30 import android.app.AppGlobals; 31 import android.app.PendingIntent; 32 import android.app.PendingIntentStats; 33 import android.content.IIntentSender; 34 import android.content.Intent; 35 import android.os.Binder; 36 import android.os.Bundle; 37 import android.os.Handler; 38 import android.os.IBinder; 39 import android.os.Looper; 40 import android.os.Message; 41 import android.os.PowerWhitelistManager; 42 import android.os.RemoteCallbackList; 43 import android.os.RemoteException; 44 import android.os.UserHandle; 45 import android.util.ArrayMap; 46 import android.util.Slog; 47 import android.util.SparseArray; 48 import android.util.SparseIntArray; 49 50 import com.android.internal.annotations.GuardedBy; 51 import com.android.internal.os.IResultReceiver; 52 import com.android.internal.util.RingBuffer; 53 import com.android.internal.util.function.pooled.PooledLambda; 54 import com.android.server.AlarmManagerInternal; 55 import com.android.server.LocalServices; 56 import com.android.server.wm.ActivityTaskManagerInternal; 57 import com.android.server.wm.SafeActivityOptions; 58 59 import java.io.PrintWriter; 60 import java.lang.ref.WeakReference; 61 import java.util.ArrayList; 62 import java.util.Arrays; 63 import java.util.HashMap; 64 import java.util.Iterator; 65 import java.util.List; 66 67 /** 68 * Helper class for {@link ActivityManagerService} responsible for managing pending intents. 69 * 70 * <p>This class uses {@link #mLock} to synchronize access to internal state and doesn't make use of 71 * {@link ActivityManagerService} lock since there can be direct calls into this class from outside 72 * AM. This helps avoid deadlocks. 73 */ 74 public class PendingIntentController { 75 private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentController" : TAG_AM; 76 private static final String TAG_MU = TAG + POSTFIX_MU; 77 78 /** @see {@link #mRecentIntentsPerUid}. */ 79 private static final int RECENT_N = 10; 80 81 /** Lock for internal state. */ 82 final Object mLock = new Object(); 83 final Handler mH; 84 ActivityManagerInternal mAmInternal; 85 final UserController mUserController; 86 final ActivityTaskManagerInternal mAtmInternal; 87 88 /** Set of IntentSenderRecord objects that are currently active. */ 89 final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords 90 = new HashMap<>(); 91 92 /** The number of PendingIntentRecord per uid */ 93 @GuardedBy("mLock") 94 private final SparseIntArray mIntentsPerUid = new SparseIntArray(); 95 96 /** The recent PendingIntentRecord, up to {@link #RECENT_N} per uid */ 97 @GuardedBy("mLock") 98 private final SparseArray<RingBuffer<String>> mRecentIntentsPerUid = new SparseArray<>(); 99 100 private final ActivityManagerConstants mConstants; 101 PendingIntentController(Looper looper, UserController userController, ActivityManagerConstants constants)102 PendingIntentController(Looper looper, UserController userController, 103 ActivityManagerConstants constants) { 104 mH = new Handler(looper); 105 mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); 106 mUserController = userController; 107 mConstants = constants; 108 } 109 onActivityManagerInternalAdded()110 void onActivityManagerInternalAdded() { 111 synchronized (mLock) { 112 mAmInternal = LocalServices.getService(ActivityManagerInternal.class); 113 } 114 } 115 getIntentSender(int type, String packageName, @Nullable String featureId, int callingUid, int userId, IBinder token, String resultWho, int requestCode, Intent[] intents, String[] resolvedTypes, int flags, Bundle bOptions)116 public PendingIntentRecord getIntentSender(int type, String packageName, 117 @Nullable String featureId, int callingUid, int userId, IBinder token, String resultWho, 118 int requestCode, Intent[] intents, String[] resolvedTypes, int flags, Bundle bOptions) { 119 synchronized (mLock) { 120 if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid); 121 122 // We're going to be splicing together extras before sending, so we're 123 // okay poking into any contained extras. 124 if (intents != null) { 125 for (int i = 0; i < intents.length; i++) { 126 intents[i].setDefusable(true); 127 } 128 } 129 Bundle.setDefusable(bOptions, true); 130 ActivityOptions opts = ActivityOptions.fromBundle(bOptions); 131 if (opts != null && opts.getPendingIntentBackgroundActivityStartMode() 132 != ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) { 133 Slog.wtf(TAG, "Resetting option setPendingIntentBackgroundActivityStartMode(" 134 + opts.getPendingIntentBackgroundActivityStartMode() 135 + ") to SYSTEM_DEFINED from the options provided by the pending " 136 + "intent creator (" 137 + packageName 138 + ") because this option is meant for the pending intent sender"); 139 opts.setPendingIntentBackgroundActivityStartMode( 140 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED); 141 } 142 143 final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0; 144 final boolean cancelCurrent = (flags & PendingIntent.FLAG_CANCEL_CURRENT) != 0; 145 final boolean updateCurrent = (flags & PendingIntent.FLAG_UPDATE_CURRENT) != 0; 146 flags &= ~(PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_CANCEL_CURRENT 147 | PendingIntent.FLAG_UPDATE_CURRENT); 148 149 PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, featureId, 150 token, resultWho, requestCode, intents, resolvedTypes, flags, 151 new SafeActivityOptions(opts), userId); 152 WeakReference<PendingIntentRecord> ref; 153 ref = mIntentSenderRecords.get(key); 154 PendingIntentRecord rec = ref != null ? ref.get() : null; 155 if (rec != null) { 156 if (!cancelCurrent) { 157 if (updateCurrent) { 158 if (rec.key.requestIntent != null) { 159 rec.key.requestIntent.replaceExtras(intents != null ? 160 intents[intents.length - 1] : null); 161 } 162 if (intents != null) { 163 intents[intents.length - 1] = rec.key.requestIntent; 164 rec.key.allIntents = intents; 165 rec.key.allResolvedTypes = resolvedTypes; 166 } else { 167 rec.key.allIntents = null; 168 rec.key.allResolvedTypes = null; 169 } 170 } 171 return rec; 172 } 173 makeIntentSenderCanceled(rec); 174 mIntentSenderRecords.remove(key); 175 decrementUidStatLocked(rec); 176 } 177 if (noCreate) { 178 return rec; 179 } 180 rec = new PendingIntentRecord(this, key, callingUid); 181 mIntentSenderRecords.put(key, rec.ref); 182 incrementUidStatLocked(rec); 183 return rec; 184 } 185 } 186 removePendingIntentsForPackage(String packageName, int userId, int appId, boolean doIt)187 boolean removePendingIntentsForPackage(String packageName, int userId, int appId, 188 boolean doIt) { 189 190 boolean didSomething = false; 191 synchronized (mLock) { 192 193 // Remove pending intents. For now we only do this when force stopping users, because 194 // we have some problems when doing this for packages -- app widgets are not currently 195 // cleaned up for such packages, so they can be left with bad pending intents. 196 if (mIntentSenderRecords.size() <= 0) { 197 return false; 198 } 199 200 Iterator<WeakReference<PendingIntentRecord>> it 201 = mIntentSenderRecords.values().iterator(); 202 while (it.hasNext()) { 203 WeakReference<PendingIntentRecord> wpir = it.next(); 204 if (wpir == null) { 205 it.remove(); 206 continue; 207 } 208 PendingIntentRecord pir = wpir.get(); 209 if (pir == null) { 210 it.remove(); 211 continue; 212 } 213 if (packageName == null) { 214 // Stopping user, remove all objects for the user. 215 if (pir.key.userId != userId) { 216 // Not the same user, skip it. 217 continue; 218 } 219 } else { 220 if (UserHandle.getAppId(pir.uid) != appId) { 221 // Different app id, skip it. 222 continue; 223 } 224 if (userId != UserHandle.USER_ALL && pir.key.userId != userId) { 225 // Different user, skip it. 226 continue; 227 } 228 if (!pir.key.packageName.equals(packageName)) { 229 // Different package, skip it. 230 continue; 231 } 232 } 233 if (!doIt) { 234 return true; 235 } 236 didSomething = true; 237 it.remove(); 238 makeIntentSenderCanceled(pir); 239 decrementUidStatLocked(pir); 240 if (pir.key.activity != null) { 241 final Message m = PooledLambda.obtainMessage( 242 PendingIntentController::clearPendingResultForActivity, this, 243 pir.key.activity, pir.ref); 244 mH.sendMessage(m); 245 } 246 } 247 } 248 249 return didSomething; 250 } 251 cancelIntentSender(IIntentSender sender)252 public void cancelIntentSender(IIntentSender sender) { 253 if (!(sender instanceof PendingIntentRecord)) { 254 return; 255 } 256 synchronized (mLock) { 257 final PendingIntentRecord rec = (PendingIntentRecord) sender; 258 try { 259 final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName, 260 MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId()); 261 if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) { 262 String msg = "Permission Denial: cancelIntentSender() from pid=" 263 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 264 + " is not allowed to cancel package " + rec.key.packageName; 265 Slog.w(TAG, msg); 266 throw new SecurityException(msg); 267 } 268 } catch (RemoteException e) { 269 throw new SecurityException(e); 270 } 271 cancelIntentSender(rec, true); 272 } 273 } 274 cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity)275 public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity) { 276 synchronized (mLock) { 277 makeIntentSenderCanceled(rec); 278 mIntentSenderRecords.remove(rec.key); 279 decrementUidStatLocked(rec); 280 if (cleanActivity && rec.key.activity != null) { 281 final Message m = PooledLambda.obtainMessage( 282 PendingIntentController::clearPendingResultForActivity, this, 283 rec.key.activity, rec.ref); 284 mH.sendMessage(m); 285 } 286 } 287 } 288 registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver)289 boolean registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) { 290 if (!(sender instanceof PendingIntentRecord)) { 291 Slog.w(TAG, "registerIntentSenderCancelListener called on non-PendingIntentRecord"); 292 // In this case, it's not "success", but we don't know if it's canceld either. 293 return true; 294 } 295 boolean isCancelled; 296 synchronized (mLock) { 297 PendingIntentRecord pendingIntent = (PendingIntentRecord) sender; 298 isCancelled = pendingIntent.canceled; 299 if (!isCancelled) { 300 pendingIntent.registerCancelListenerLocked(receiver); 301 return true; 302 } else { 303 return false; 304 } 305 } 306 } 307 unregisterIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver)308 void unregisterIntentSenderCancelListener(IIntentSender sender, 309 IResultReceiver receiver) { 310 if (!(sender instanceof PendingIntentRecord)) { 311 return; 312 } 313 synchronized (mLock) { 314 ((PendingIntentRecord) sender).unregisterCancelListenerLocked(receiver); 315 } 316 } 317 setPendingIntentAllowlistDuration(IIntentSender target, IBinder allowlistToken, long duration, int type, @PowerWhitelistManager.ReasonCode int reasonCode, @Nullable String reason)318 void setPendingIntentAllowlistDuration(IIntentSender target, IBinder allowlistToken, 319 long duration, int type, @PowerWhitelistManager.ReasonCode int reasonCode, 320 @Nullable String reason) { 321 if (!(target instanceof PendingIntentRecord)) { 322 Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target); 323 return; 324 } 325 synchronized (mLock) { 326 ((PendingIntentRecord) target).setAllowlistDurationLocked(allowlistToken, duration, 327 type, reasonCode, reason); 328 } 329 } 330 getPendingIntentFlags(IIntentSender target)331 int getPendingIntentFlags(IIntentSender target) { 332 if (!(target instanceof PendingIntentRecord)) { 333 Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target); 334 return 0; 335 } 336 synchronized (mLock) { 337 return ((PendingIntentRecord) target).key.flags; 338 } 339 } 340 makeIntentSenderCanceled(PendingIntentRecord rec)341 private void makeIntentSenderCanceled(PendingIntentRecord rec) { 342 rec.canceled = true; 343 final RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked(); 344 if (callbacks != null) { 345 final Message m = PooledLambda.obtainMessage( 346 PendingIntentController::handlePendingIntentCancelled, this, callbacks); 347 mH.sendMessage(m); 348 } 349 final AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class); 350 ami.remove(new PendingIntent(rec)); 351 } 352 handlePendingIntentCancelled(RemoteCallbackList<IResultReceiver> callbacks)353 private void handlePendingIntentCancelled(RemoteCallbackList<IResultReceiver> callbacks) { 354 int N = callbacks.beginBroadcast(); 355 for (int i = 0; i < N; i++) { 356 try { 357 callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null); 358 } catch (RemoteException e) { 359 // Process is not longer running...whatever. 360 } 361 } 362 callbacks.finishBroadcast(); 363 // We have to clean up the RemoteCallbackList here, because otherwise it will 364 // needlessly hold the enclosed callbacks until the remote process dies. 365 callbacks.kill(); 366 } 367 clearPendingResultForActivity(IBinder activityToken, WeakReference<PendingIntentRecord> pir)368 private void clearPendingResultForActivity(IBinder activityToken, 369 WeakReference<PendingIntentRecord> pir) { 370 mAtmInternal.clearPendingResultForActivity(activityToken, pir); 371 } 372 dumpPendingIntents(PrintWriter pw, boolean dumpAll, String dumpPackage)373 void dumpPendingIntents(PrintWriter pw, boolean dumpAll, String dumpPackage) { 374 synchronized (mLock) { 375 boolean printed = false; 376 377 pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)"); 378 379 if (mIntentSenderRecords.size() > 0) { 380 // Organize these by package name, so they are easier to read. 381 final ArrayMap<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>(); 382 final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>(); 383 final Iterator<WeakReference<PendingIntentRecord>> it 384 = mIntentSenderRecords.values().iterator(); 385 while (it.hasNext()) { 386 WeakReference<PendingIntentRecord> ref = it.next(); 387 PendingIntentRecord rec = ref != null ? ref.get() : null; 388 if (rec == null) { 389 weakRefs.add(ref); 390 continue; 391 } 392 if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) { 393 continue; 394 } 395 ArrayList<PendingIntentRecord> list = byPackage.get(rec.key.packageName); 396 if (list == null) { 397 list = new ArrayList<>(); 398 byPackage.put(rec.key.packageName, list); 399 } 400 list.add(rec); 401 } 402 for (int i = 0; i < byPackage.size(); i++) { 403 ArrayList<PendingIntentRecord> intents = byPackage.valueAt(i); 404 printed = true; 405 pw.print(" * "); pw.print(byPackage.keyAt(i)); 406 pw.print(": "); pw.print(intents.size()); pw.println(" items"); 407 for (int j = 0; j < intents.size(); j++) { 408 pw.print(" #"); pw.print(j); pw.print(": "); pw.println(intents.get(j)); 409 if (dumpAll) { 410 intents.get(j).dump(pw, " "); 411 } 412 } 413 } 414 if (weakRefs.size() > 0) { 415 printed = true; 416 pw.println(" * WEAK REFS:"); 417 for (int i = 0; i < weakRefs.size(); i++) { 418 pw.print(" #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i)); 419 } 420 } 421 } 422 423 final int sizeOfIntentsPerUid = mIntentsPerUid.size(); 424 if (sizeOfIntentsPerUid > 0) { 425 for (int i = 0; i < sizeOfIntentsPerUid; i++) { 426 pw.print(" * UID: "); 427 pw.print(mIntentsPerUid.keyAt(i)); 428 pw.print(" total: "); 429 pw.println(mIntentsPerUid.valueAt(i)); 430 } 431 } 432 433 if (!printed) { 434 pw.println(" (nothing)"); 435 } 436 } 437 } 438 439 /** 440 * Provides some stats tracking of the current state of the PendingIntent queue. 441 * 442 * Data about the pending intent queue is intended to be used for memory impact tracking. 443 * Returned data (one per uid) will consist of instances of PendingIntentStats containing 444 * (I) number of PendingIntents and (II) total size of all bundled extras in the PIs. 445 * 446 * @hide 447 */ dumpPendingIntentStatsForStatsd()448 public List<PendingIntentStats> dumpPendingIntentStatsForStatsd() { 449 List<PendingIntentStats> pendingIntentStats = new ArrayList<>(); 450 451 synchronized (mLock) { 452 if (mIntentSenderRecords.size() > 0) { 453 // First, aggregate PendingIntent data by package uid. 454 final SparseIntArray countsByUid = new SparseIntArray(); 455 final SparseIntArray bundleSizesByUid = new SparseIntArray(); 456 457 for (WeakReference<PendingIntentRecord> reference : mIntentSenderRecords.values()) { 458 if (reference == null || reference.get() == null) { 459 continue; 460 } 461 PendingIntentRecord record = reference.get(); 462 int index = countsByUid.indexOfKey(record.uid); 463 464 if (index < 0) { // ie. the key was not found 465 countsByUid.put(record.uid, 1); 466 bundleSizesByUid.put(record.uid, 467 record.key.requestIntent.getExtrasTotalSize()); 468 } else { 469 countsByUid.put(record.uid, countsByUid.valueAt(index) + 1); 470 bundleSizesByUid.put(record.uid, 471 bundleSizesByUid.valueAt(index) 472 + record.key.requestIntent.getExtrasTotalSize()); 473 } 474 } 475 476 // Now generate the output. 477 for (int i = 0, size = countsByUid.size(); i < size; i++) { 478 pendingIntentStats.add(new PendingIntentStats( 479 countsByUid.keyAt(i), 480 countsByUid.valueAt(i), 481 /* NB: int conversion here */ bundleSizesByUid.valueAt(i) / 1024)); 482 } 483 } 484 } 485 return pendingIntentStats; 486 } 487 488 /** 489 * Increment the number of the PendingIntentRecord for the given uid, log a warning 490 * if there are too many for this uid already. 491 */ 492 @GuardedBy("mLock") incrementUidStatLocked(final PendingIntentRecord pir)493 void incrementUidStatLocked(final PendingIntentRecord pir) { 494 final int uid = pir.uid; 495 final int idx = mIntentsPerUid.indexOfKey(uid); 496 int newCount = 1; 497 if (idx >= 0) { 498 newCount = mIntentsPerUid.valueAt(idx) + 1; 499 mIntentsPerUid.setValueAt(idx, newCount); 500 } else { 501 mIntentsPerUid.put(uid, newCount); 502 } 503 504 // If the number is within the range [threshold - N + 1, threshold], log it into buffer 505 final int lowBound = mConstants.PENDINGINTENT_WARNING_THRESHOLD - RECENT_N + 1; 506 RingBuffer<String> recentHistory = null; 507 if (newCount == lowBound) { 508 recentHistory = new RingBuffer(String.class, RECENT_N); 509 mRecentIntentsPerUid.put(uid, recentHistory); 510 } else if (newCount > lowBound && newCount <= mConstants.PENDINGINTENT_WARNING_THRESHOLD) { 511 recentHistory = mRecentIntentsPerUid.get(uid); 512 } 513 if (recentHistory == null) { 514 return; 515 } 516 517 recentHistory.append(pir.key.toString()); 518 519 // Output the log if we are hitting the threshold 520 if (newCount == mConstants.PENDINGINTENT_WARNING_THRESHOLD) { 521 Slog.wtf(TAG, "Too many PendingIntent created for uid " + uid 522 + ", recent " + RECENT_N + ": " + Arrays.toString(recentHistory.toArray())); 523 // Clear the buffer, as we don't want to spam the log when the numbers 524 // are jumping up and down around the threshold. 525 mRecentIntentsPerUid.remove(uid); 526 } 527 } 528 529 /** 530 * Decrement the number of the PendingIntentRecord for the given uid. 531 */ 532 @GuardedBy("mLock") decrementUidStatLocked(final PendingIntentRecord pir)533 void decrementUidStatLocked(final PendingIntentRecord pir) { 534 final int uid = pir.uid; 535 final int idx = mIntentsPerUid.indexOfKey(uid); 536 if (idx >= 0) { 537 final int newCount = mIntentsPerUid.valueAt(idx) - 1; 538 // If we are going below the low threshold, no need to keep logs. 539 if (newCount == mConstants.PENDINGINTENT_WARNING_THRESHOLD - RECENT_N) { 540 mRecentIntentsPerUid.delete(uid); 541 } 542 if (newCount == 0) { 543 mIntentsPerUid.removeAt(idx); 544 } else { 545 mIntentsPerUid.setValueAt(idx, newCount); 546 } 547 } 548 } 549 } 550