• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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