• 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.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