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