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