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