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