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