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