OSDN Git Service

Merge "Fix Settings home lifecycle" into nyc-dev
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / recents / model / RecentsTaskLoader.java
1 /*
2  * Copyright (C) 2014 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.systemui.recents.model;
18
19 import android.app.ActivityManager;
20 import android.content.ComponentCallbacks2;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.pm.ActivityInfo;
24 import android.content.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.drawable.BitmapDrawable;
27 import android.graphics.drawable.Drawable;
28 import android.os.Handler;
29 import android.os.HandlerThread;
30 import android.util.Log;
31 import android.util.LruCache;
32
33 import com.android.systemui.R;
34 import com.android.systemui.recents.Recents;
35 import com.android.systemui.recents.RecentsConfiguration;
36 import com.android.systemui.recents.RecentsDebugFlags;
37 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
38 import com.android.systemui.recents.misc.SystemServicesProxy;
39
40 import java.util.Map;
41 import java.util.concurrent.ConcurrentLinkedQueue;
42
43
44 /**
45  * A Task load queue
46  */
47 class TaskResourceLoadQueue {
48     ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
49
50     /** Adds a new task to the load queue */
51     void addTask(Task t) {
52         if (!mQueue.contains(t)) {
53             mQueue.add(t);
54         }
55         synchronized(this) {
56             notifyAll();
57         }
58     }
59
60     /**
61      * Retrieves the next task from the load queue, as well as whether we want that task to be
62      * force reloaded.
63      */
64     Task nextTask() {
65         return mQueue.poll();
66     }
67
68     /** Removes a task from the load queue */
69     void removeTask(Task t) {
70         mQueue.remove(t);
71     }
72
73     /** Clears all the tasks from the load queue */
74     void clearTasks() {
75         mQueue.clear();
76     }
77
78     /** Returns whether the load queue is empty */
79     boolean isEmpty() {
80         return mQueue.isEmpty();
81     }
82 }
83
84 /**
85  * Task resource loader
86  */
87 class BackgroundTaskLoader implements Runnable {
88     static String TAG = "TaskResourceLoader";
89     static boolean DEBUG = false;
90
91     Context mContext;
92     HandlerThread mLoadThread;
93     Handler mLoadThreadHandler;
94     Handler mMainThreadHandler;
95
96     TaskResourceLoadQueue mLoadQueue;
97     TaskKeyLruCache<Drawable> mIconCache;
98     TaskKeyLruCache<Bitmap> mThumbnailCache;
99     Bitmap mDefaultThumbnail;
100     BitmapDrawable mDefaultIcon;
101
102     boolean mCancelled;
103     boolean mWaitingOnLoadQueue;
104
105     /** Constructor, creates a new loading thread that loads task resources in the background */
106     public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
107             TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
108             Bitmap defaultThumbnail, BitmapDrawable defaultIcon) {
109         mLoadQueue = loadQueue;
110         mIconCache = iconCache;
111         mThumbnailCache = thumbnailCache;
112         mDefaultThumbnail = defaultThumbnail;
113         mDefaultIcon = defaultIcon;
114         mMainThreadHandler = new Handler();
115         mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
116                 android.os.Process.THREAD_PRIORITY_BACKGROUND);
117         mLoadThread.start();
118         mLoadThreadHandler = new Handler(mLoadThread.getLooper());
119         mLoadThreadHandler.post(this);
120     }
121
122     /** Restarts the loader thread */
123     void start(Context context) {
124         mContext = context;
125         mCancelled = false;
126         // Notify the load thread to start loading
127         synchronized(mLoadThread) {
128             mLoadThread.notifyAll();
129         }
130     }
131
132     /** Requests the loader thread to stop after the current iteration */
133     void stop() {
134         // Mark as cancelled for the thread to pick up
135         mCancelled = true;
136         // If we are waiting for the load queue for more tasks, then we can just reset the
137         // Context now, since nothing is using it
138         if (mWaitingOnLoadQueue) {
139             mContext = null;
140         }
141     }
142
143     @Override
144     public void run() {
145         while (true) {
146             if (mCancelled) {
147                 // We have to unset the context here, since the background thread may be using it
148                 // when we call stop()
149                 mContext = null;
150                 // If we are cancelled, then wait until we are started again
151                 synchronized(mLoadThread) {
152                     try {
153                         mLoadThread.wait();
154                     } catch (InterruptedException ie) {
155                         ie.printStackTrace();
156                     }
157                 }
158             } else {
159                 RecentsConfiguration config = Recents.getConfiguration();
160                 SystemServicesProxy ssp = Recents.getSystemServices();
161                 // If we've stopped the loader, then fall through to the above logic to wait on
162                 // the load thread
163                 if (ssp != null) {
164                     // Load the next item from the queue
165                     final Task t = mLoadQueue.nextTask();
166                     if (t != null) {
167                         Drawable cachedIcon = mIconCache.get(t.key);
168                         Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
169
170                         // Load the icon if it is stale or we haven't cached one yet
171                         if (cachedIcon == null) {
172                             cachedIcon = ssp.getBadgedTaskDescriptionIcon(t.taskDescription,
173                                     t.key.userId, mContext.getResources());
174
175                             if (cachedIcon == null) {
176                                 ActivityInfo info = ssp.getActivityInfo(
177                                         t.key.getComponent(), t.key.userId);
178                                 if (info != null) {
179                                     if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
180                                     cachedIcon = ssp.getBadgedActivityIcon(info, t.key.userId);
181                                 }
182                             }
183
184                             if (cachedIcon == null) {
185                                 cachedIcon = mDefaultIcon;
186                             }
187
188                             // At this point, even if we can't load the icon, we will set the
189                             // default icon.
190                             mIconCache.put(t.key, cachedIcon);
191                         }
192                         // Load the thumbnail if it is stale or we haven't cached one yet
193                         if (cachedThumbnail == null) {
194                             if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
195                                 if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
196                                 cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
197                             }
198                             if (cachedThumbnail == null) {
199                                 cachedThumbnail = mDefaultThumbnail;
200                             }
201                             // When svelte, we trim the memory to just the visible thumbnails when
202                             // leaving, so don't thrash the cache as the user scrolls (just load
203                             // them from scratch each time)
204                             if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE) {
205                                 mThumbnailCache.put(t.key, cachedThumbnail);
206                             }
207                         }
208                         if (!mCancelled) {
209                             // Notify that the task data has changed
210                             final Drawable newIcon = cachedIcon;
211                             final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail
212                                     ? null : cachedThumbnail;
213                             mMainThreadHandler.post(new Runnable() {
214                                 @Override
215                                 public void run() {
216                                     t.notifyTaskDataLoaded(newThumbnail, newIcon);
217                                 }
218                             });
219                         }
220                     }
221                 }
222
223                 // If there are no other items in the list, then just wait until something is added
224                 if (!mCancelled && mLoadQueue.isEmpty()) {
225                     synchronized(mLoadQueue) {
226                         try {
227                             mWaitingOnLoadQueue = true;
228                             mLoadQueue.wait();
229                             mWaitingOnLoadQueue = false;
230                         } catch (InterruptedException ie) {
231                             ie.printStackTrace();
232                         }
233                     }
234                 }
235             }
236         }
237     }
238 }
239
240 /**
241  * Recents task loader
242  */
243 public class RecentsTaskLoader {
244
245     private static final String TAG = "RecentsTaskLoader";
246     private static final boolean DEBUG = false;
247
248     // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
249     // for many tasks, which we use to get the activity labels and icons.  Unlike the other caches
250     // below, this is per-package so we can't invalidate the items in the cache based on the last
251     // active time.  Instead, we rely on the RecentsPackageMonitor to keep us informed whenever a
252     // package in the cache has been updated, so that we may remove it.
253     private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
254     private final TaskKeyLruCache<Drawable> mIconCache;
255     private final TaskKeyLruCache<Bitmap> mThumbnailCache;
256     private final TaskKeyLruCache<String> mActivityLabelCache;
257     private final TaskKeyLruCache<String> mContentDescriptionCache;
258     private final TaskResourceLoadQueue mLoadQueue;
259     private final BackgroundTaskLoader mLoader;
260
261     private final int mMaxThumbnailCacheSize;
262     private final int mMaxIconCacheSize;
263     private int mNumVisibleTasksLoaded;
264     private int mNumVisibleThumbnailsLoaded;
265
266     int mDefaultTaskBarBackgroundColor;
267     int mDefaultTaskViewBackgroundColor;
268     BitmapDrawable mDefaultIcon;
269     Bitmap mDefaultThumbnail;
270
271     private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction =
272             new TaskKeyLruCache.EvictionCallback() {
273         @Override
274         public void onEntryEvicted(Task.TaskKey key) {
275             mActivityInfoCache.remove(key.getComponent());
276         }
277     };
278
279     public RecentsTaskLoader(Context context) {
280         Resources res = context.getResources();
281         mDefaultTaskBarBackgroundColor =
282                 context.getColor(R.color.recents_task_bar_default_background_color);
283         mDefaultTaskViewBackgroundColor =
284                 context.getColor(R.color.recents_task_view_default_background_color);
285         mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count);
286         mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count);
287         int iconCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
288                 mMaxIconCacheSize;
289         int thumbnailCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
290                 mMaxThumbnailCacheSize;
291
292         // Create the default assets
293         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
294         icon.eraseColor(0);
295         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
296         mDefaultThumbnail.setHasAlpha(false);
297         mDefaultThumbnail.eraseColor(0xFFffffff);
298         mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
299
300         // Initialize the proxy, cache and loaders
301         int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
302         mLoadQueue = new TaskResourceLoadQueue();
303         mIconCache = new TaskKeyLruCache<>(iconCacheSize, mClearActivityInfoOnEviction);
304         mThumbnailCache = new TaskKeyLruCache<>(thumbnailCacheSize);
305         mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction);
306         mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks,
307                 mClearActivityInfoOnEviction);
308         mActivityInfoCache = new LruCache(numRecentTasks);
309         mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mThumbnailCache,
310                 mDefaultThumbnail, mDefaultIcon);
311     }
312
313     /** Returns the size of the app icon cache. */
314     public int getIconCacheSize() {
315         return mMaxIconCacheSize;
316     }
317
318     /** Returns the size of the thumbnail cache. */
319     public int getThumbnailCacheSize() {
320         return mMaxThumbnailCacheSize;
321     }
322
323     /** Creates a new plan for loading the recent tasks. */
324     public RecentsTaskLoadPlan createLoadPlan(Context context) {
325         RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context);
326         return plan;
327     }
328
329     /** Preloads recents tasks using the specified plan to store the output. */
330     public void preloadTasks(RecentsTaskLoadPlan plan, int topTaskId, boolean isTopTaskHome) {
331         plan.preloadPlan(this, topTaskId, isTopTaskHome);
332     }
333
334     /** Begins loading the heavy task data according to the specified options. */
335     public void loadTasks(Context context, RecentsTaskLoadPlan plan,
336             RecentsTaskLoadPlan.Options opts) {
337         if (opts == null) {
338             throw new RuntimeException("Requires load options");
339         }
340         plan.executePlan(opts, this, mLoadQueue);
341         if (!opts.onlyLoadForCache) {
342             mNumVisibleTasksLoaded = opts.numVisibleTasks;
343             mNumVisibleThumbnailsLoaded = opts.numVisibleTaskThumbnails;
344
345             // Start the loader
346             mLoader.start(context);
347         }
348     }
349
350     /**
351      * Acquires the task resource data directly from the cache, loading if necessary.
352      *
353      * @param fetchAndInvalidateThumbnails If set, will try loading thumbnails, invalidating them
354      *                                     in the cache and loading if necessary. Otherwise, do not
355      *                                     load the thumbnail unless the icon also has to be loaded.
356      */
357     public void loadTaskData(Task t, boolean fetchAndInvalidateThumbnails) {
358         Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
359         Bitmap thumbnail = mDefaultThumbnail;
360         if (fetchAndInvalidateThumbnails) {
361             thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
362         }
363
364         // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
365         // use the default assets in their place until they load
366         boolean requiresLoad = (icon == null) || (thumbnail == null);
367         icon = icon != null ? icon : mDefaultIcon;
368         if (requiresLoad) {
369             mLoadQueue.addTask(t);
370         }
371         t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon);
372     }
373
374     /** Releases the task resource data back into the pool. */
375     public void unloadTaskData(Task t) {
376         mLoadQueue.removeTask(t);
377         t.notifyTaskDataUnloaded(null, mDefaultIcon);
378     }
379
380     /** Completely removes the resource data from the pool. */
381     public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
382         mLoadQueue.removeTask(t);
383         mThumbnailCache.remove(t.key);
384         mIconCache.remove(t.key);
385         mActivityLabelCache.remove(t.key);
386         mContentDescriptionCache.remove(t.key);
387         if (notifyTaskDataUnloaded) {
388             t.notifyTaskDataUnloaded(null, mDefaultIcon);
389         }
390     }
391
392     /**
393      * Handles signals from the system, trimming memory when requested to prevent us from running
394      * out of memory.
395      */
396     public void onTrimMemory(int level) {
397         RecentsConfiguration config = Recents.getConfiguration();
398         switch (level) {
399             case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
400                 // Stop the loader immediately when the UI is no longer visible
401                 stopLoader();
402                 if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
403                     mThumbnailCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
404                             mMaxThumbnailCacheSize / 2));
405                 } else if (config.svelteLevel == RecentsConfiguration.SVELTE_LIMIT_CACHE) {
406                     mThumbnailCache.trimToSize(mNumVisibleThumbnailsLoaded);
407                 } else if (config.svelteLevel >= RecentsConfiguration.SVELTE_DISABLE_CACHE) {
408                     mThumbnailCache.evictAll();
409                 }
410                 mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
411                         mMaxIconCacheSize / 2));
412                 break;
413             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
414             case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
415                 // We are leaving recents, so trim the data a bit
416                 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 2));
417                 mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
418                 mActivityInfoCache.trimToSize(Math.max(1,
419                         ActivityManager.getMaxRecentTasksStatic() / 2));
420                 break;
421             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
422             case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
423                 // We are going to be low on memory
424                 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 4));
425                 mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
426                 mActivityInfoCache.trimToSize(Math.max(1,
427                         ActivityManager.getMaxRecentTasksStatic() / 4));
428                 break;
429             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
430             case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
431                 // We are low on memory, so release everything
432                 mThumbnailCache.evictAll();
433                 mIconCache.evictAll();
434                 mActivityInfoCache.evictAll();
435                 // The cache is small, only clear the label cache when we are critical
436                 mActivityLabelCache.evictAll();
437                 mContentDescriptionCache.evictAll();
438                 break;
439             default:
440                 break;
441         }
442     }
443
444     /**
445      * Returns the cached task label if the task key is not expired, updating the cache if it is.
446      */
447     String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) {
448         SystemServicesProxy ssp = Recents.getSystemServices();
449
450         // Return the task description label if it exists
451         if (td != null && td.getLabel() != null) {
452             return td.getLabel();
453         }
454         // Return the cached activity label if it exists
455         String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
456         if (label != null) {
457             return label;
458         }
459         // All short paths failed, load the label from the activity info and cache it
460         ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
461         if (activityInfo != null) {
462             label = ssp.getBadgedActivityLabel(activityInfo, taskKey.userId);
463             mActivityLabelCache.put(taskKey, label);
464             return label;
465         }
466         // If the activity info does not exist or fails to load, return an empty label for now,
467         // but do not cache it
468         return "";
469     }
470
471     /**
472      * Returns the cached task content description if the task key is not expired, updating the
473      * cache if it is.
474      */
475     String getAndUpdateContentDescription(Task.TaskKey taskKey, Resources res) {
476         SystemServicesProxy ssp = Recents.getSystemServices();
477
478         // Return the cached content description if it exists
479         String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
480         if (label != null) {
481             return label;
482         }
483
484         // All short paths failed, load the label from the activity info and cache it
485         ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
486         if (activityInfo != null) {
487             label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, res);
488             mContentDescriptionCache.put(taskKey, label);
489             return label;
490         }
491         // If the content description does not exist, return an empty label for now, but do not
492         // cache it
493         return "";
494     }
495
496     /**
497      * Returns the cached task icon if the task key is not expired, updating the cache if it is.
498      */
499     Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
500             Resources res, boolean loadIfNotCached) {
501         SystemServicesProxy ssp = Recents.getSystemServices();
502
503         // Return the cached activity icon if it exists
504         Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
505         if (icon != null) {
506             return icon;
507         }
508
509         if (loadIfNotCached) {
510             // Return and cache the task description icon if it exists
511             icon = ssp.getBadgedTaskDescriptionIcon(td, taskKey.userId, res);
512             if (icon != null) {
513                 mIconCache.put(taskKey, icon);
514                 return icon;
515             }
516
517             // Load the icon from the activity info and cache it
518             ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
519             if (activityInfo != null) {
520                 icon = ssp.getBadgedActivityIcon(activityInfo, taskKey.userId);
521                 if (icon != null) {
522                     mIconCache.put(taskKey, icon);
523                     return icon;
524                 }
525             }
526         }
527         // We couldn't load any icon
528         return null;
529     }
530
531     /**
532      * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
533      */
534     Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached) {
535         SystemServicesProxy ssp = Recents.getSystemServices();
536
537         // Return the cached thumbnail if it exists
538         Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
539         if (thumbnail != null) {
540             return thumbnail;
541         }
542
543         if (loadIfNotCached) {
544             RecentsConfiguration config = Recents.getConfiguration();
545             if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
546                 // Load the thumbnail from the system
547                 thumbnail = ssp.getTaskThumbnail(taskKey.id);
548                 if (thumbnail != null) {
549                     mThumbnailCache.put(taskKey, thumbnail);
550                     return thumbnail;
551                 }
552             }
553         }
554         // We couldn't load any thumbnail
555         return null;
556     }
557
558     /**
559      * Returns the task's primary color if possible, defaulting to the default color if there is
560      * no specified primary color.
561      */
562     int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
563         if (td != null && td.getPrimaryColor() != 0) {
564             return td.getPrimaryColor();
565         }
566         return mDefaultTaskBarBackgroundColor;
567     }
568
569     /**
570      * Returns the task's background color if possible.
571      */
572     int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
573         if (td != null && td.getBackgroundColor() != 0) {
574             return td.getBackgroundColor();
575         }
576         return mDefaultTaskViewBackgroundColor;
577     }
578
579     /**
580      * Returns the activity info for the given task key, retrieving one from the system if the
581      * task key is expired.
582      */
583     ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
584         SystemServicesProxy ssp = Recents.getSystemServices();
585         ComponentName cn = taskKey.getComponent();
586         ActivityInfo activityInfo = mActivityInfoCache.get(cn);
587         if (activityInfo == null) {
588             activityInfo = ssp.getActivityInfo(cn, taskKey.userId);
589             if (cn == null || activityInfo == null) {
590                 Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " +
591                         activityInfo);
592                 return null;
593             }
594             mActivityInfoCache.put(cn, activityInfo);
595         }
596         return activityInfo;
597     }
598
599     /**
600      * Stops the task loader and clears all queued, pending task loads.
601      */
602     private void stopLoader() {
603         mLoader.stop();
604         mLoadQueue.clearTasks();
605     }
606
607     /**** Event Bus Events ****/
608
609     public final void onBusEvent(PackagesChangedEvent event) {
610         // Remove all the cached activity infos for this package.  The other caches do not need to
611         // be pruned at this time, as the TaskKey expiration checks will flush them next time their
612         // cached contents are requested
613         Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
614         for (ComponentName cn : activityInfoCache.keySet()) {
615             if (cn.getPackageName().equals(event.packageName)) {
616                 if (DEBUG) {
617                     Log.d(TAG, "Removing activity info from cache: " + cn);
618                 }
619                 mActivityInfoCache.remove(cn);
620             }
621         }
622     }
623 }