OSDN Git Service

Rename related users to profiles.
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / recents / 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;
18
19 import android.app.ActivityManager;
20 import android.content.ComponentCallbacks2;
21 import android.content.Context;
22 import android.content.pm.ActivityInfo;
23 import android.content.pm.PackageManager;
24 import android.content.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.Canvas;
27 import android.graphics.drawable.BitmapDrawable;
28 import android.graphics.drawable.Drawable;
29 import android.os.Handler;
30 import android.os.HandlerThread;
31 import android.os.UserHandle;
32 import android.util.LruCache;
33 import android.util.Pair;
34 import com.android.systemui.recents.model.SpaceNode;
35 import com.android.systemui.recents.model.Task;
36 import com.android.systemui.recents.model.TaskStack;
37
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.concurrent.ConcurrentHashMap;
43 import java.util.concurrent.ConcurrentLinkedQueue;
44
45
46 /** A bitmap load queue */
47 class TaskResourceLoadQueue {
48     ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
49     ConcurrentHashMap<Task.TaskKey, Boolean> mForceLoadSet =
50             new ConcurrentHashMap<Task.TaskKey, Boolean>();
51
52     static final Boolean sFalse = new Boolean(false);
53
54     /** Adds a new task to the load queue */
55     void addTask(Task t, boolean forceLoad) {
56         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|addTask]");
57         if (!mQueue.contains(t)) {
58             mQueue.add(t);
59         }
60         if (forceLoad) {
61             mForceLoadSet.put(t.key, new Boolean(true));
62         }
63         synchronized(this) {
64             notifyAll();
65         }
66     }
67
68     /**
69      * Retrieves the next task from the load queue, as well as whether we want that task to be
70      * force reloaded.
71      */
72     Pair<Task, Boolean> nextTask() {
73         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
74         Task task = mQueue.poll();
75         Boolean forceLoadTask = null;
76         if (task != null) {
77             forceLoadTask = mForceLoadSet.remove(task.key);
78         }
79         if (forceLoadTask == null) {
80             forceLoadTask = sFalse;
81         }
82         return new Pair<Task, Boolean>(task, forceLoadTask);
83     }
84
85     /** Removes a task from the load queue */
86     void removeTask(Task t) {
87         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|removeTask]");
88         mQueue.remove(t);
89         mForceLoadSet.remove(t.key);
90     }
91
92     /** Clears all the tasks from the load queue */
93     void clearTasks() {
94         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|clearTasks]");
95         mQueue.clear();
96         mForceLoadSet.clear();
97     }
98
99     /** Returns whether the load queue is empty */
100     boolean isEmpty() {
101         return mQueue.isEmpty();
102     }
103 }
104
105 /* Task resource loader */
106 class TaskResourceLoader implements Runnable {
107     Context mContext;
108     HandlerThread mLoadThread;
109     Handler mLoadThreadHandler;
110     Handler mMainThreadHandler;
111
112     TaskResourceLoadQueue mLoadQueue;
113     DrawableLruCache mIconCache;
114     BitmapLruCache mThumbnailCache;
115
116     boolean mCancelled;
117     boolean mWaitingOnLoadQueue;
118
119     /** Constructor, creates a new loading thread that loads task resources in the background */
120     public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache iconCache,
121                               BitmapLruCache thumbnailCache) {
122         mLoadQueue = loadQueue;
123         mIconCache = iconCache;
124         mThumbnailCache = thumbnailCache;
125         mMainThreadHandler = new Handler();
126         mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
127         mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
128         mLoadThread.start();
129         mLoadThreadHandler = new Handler(mLoadThread.getLooper());
130         mLoadThreadHandler.post(this);
131     }
132
133     /** Restarts the loader thread */
134     void start(Context context) {
135         Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|start]");
136         mContext = context;
137         mCancelled = false;
138         // Notify the load thread to start loading
139         synchronized(mLoadThread) {
140             mLoadThread.notifyAll();
141         }
142     }
143
144     /** Requests the loader thread to stop after the current iteration */
145     void stop() {
146         Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|stop]");
147         // Mark as cancelled for the thread to pick up
148         mCancelled = true;
149         // If we are waiting for the load queue for more tasks, then we can just reset the
150         // Context now, since nothing is using it
151         if (mWaitingOnLoadQueue) {
152             mContext = null;
153         }
154     }
155
156     @Override
157     public void run() {
158         while (true) {
159             Console.log(Constants.DebugFlags.App.TaskDataLoader,
160                     "[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]");
161             if (mCancelled) {
162                 Console.log(Constants.DebugFlags.App.TaskDataLoader,
163                         "[TaskResourceLoader|cancel|" + Thread.currentThread().getId() + "]");
164                 // We have to unset the context here, since the background thread may be using it
165                 // when we call stop()
166                 mContext = null;
167                 // If we are cancelled, then wait until we are started again
168                 synchronized(mLoadThread) {
169                     try {
170                         Console.log(Constants.DebugFlags.App.TaskDataLoader,
171                                 "[TaskResourceLoader|waitOnLoadThreadCancelled]");
172                         mLoadThread.wait();
173                     } catch (InterruptedException ie) {
174                         ie.printStackTrace();
175                     }
176                 }
177             } else {
178                 // Load the next item from the queue
179                 Pair<Task, Boolean> nextTaskData = mLoadQueue.nextTask();
180                 final Task t = nextTaskData.first;
181                 final boolean forceLoadTask = nextTaskData.second;
182                 if (t != null) {
183                     try {
184                         Drawable loadIcon = mIconCache.get(t.key);
185                         Bitmap loadThumbnail = mThumbnailCache.get(t.key);
186                         Console.log(Constants.DebugFlags.App.TaskDataLoader,
187                                 "  [TaskResourceLoader|load]",
188                                 t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail +
189                                         " forceLoad: " + forceLoadTask);
190                         // Load the icon
191                         if (loadIcon == null || forceLoadTask) {
192                             PackageManager pm = mContext.getPackageManager();
193                             ActivityInfo info = pm.getActivityInfo(t.key.intent.getComponent(),
194                                     PackageManager.GET_META_DATA);
195                             Drawable icon = info.loadIcon(pm);
196                             if (!mCancelled) {
197                                 if (icon != null) {
198                                     Console.log(Constants.DebugFlags.App.TaskDataLoader,
199                                             "    [TaskResourceLoader|loadIcon]",
200                                             icon);
201                                     loadIcon = icon;
202                                     mIconCache.put(t.key, icon);
203                                 }
204                             }
205                         }
206                         // Load the thumbnail
207                         if (loadThumbnail == null || forceLoadTask) {
208                             ActivityManager am = (ActivityManager)
209                                     mContext.getSystemService(Context.ACTIVITY_SERVICE);
210                             Bitmap thumbnail = am.getTaskTopThumbnail(t.key.id);
211                             if (!mCancelled) {
212                                 if (thumbnail != null) {
213                                     Console.log(Constants.DebugFlags.App.TaskDataLoader,
214                                             "    [TaskResourceLoader|loadThumbnail]",
215                                             thumbnail);
216                                     loadThumbnail = thumbnail;
217                                     mThumbnailCache.put(t.key, thumbnail);
218                                 } else {
219                                     Console.logError(mContext,
220                                             "Failed to load task top thumbnail for: " +
221                                                     t.key.intent.getComponent().getPackageName());
222                                 }
223                             }
224                         }
225                         if (!mCancelled) {
226                             // Notify that the task data has changed
227                             final Drawable newIcon = loadIcon;
228                             final Bitmap newThumbnail = loadThumbnail;
229                             mMainThreadHandler.post(new Runnable() {
230                                 @Override
231                                 public void run() {
232                                     t.notifyTaskDataLoaded(newThumbnail, newIcon, forceLoadTask);
233                                 }
234                             });
235                         }
236                     } catch (PackageManager.NameNotFoundException ne) {
237                         ne.printStackTrace();
238                     }
239                 }
240
241                 // If there are no other items in the list, then just wait until something is added
242                 if (!mCancelled && mLoadQueue.isEmpty()) {
243                     synchronized(mLoadQueue) {
244                         try {
245                             Console.log(Constants.DebugFlags.App.TaskDataLoader,
246                                     "[TaskResourceLoader|waitOnLoadQueue]");
247                             mWaitingOnLoadQueue = true;
248                             mLoadQueue.wait();
249                             mWaitingOnLoadQueue = false;
250                         } catch (InterruptedException ie) {
251                             ie.printStackTrace();
252                         }
253                     }
254                 }
255             }
256         }
257     }
258 }
259
260 /**
261  * The drawable cache.  By using the Task's key, we can prevent holding onto a reference to the Task
262  * resource data, while keeping the cache data in memory where necessary.
263  */
264 class DrawableLruCache extends LruCache<Task.TaskKey, Drawable> {
265     public DrawableLruCache(int cacheSize) {
266         super(cacheSize);
267     }
268
269     @Override
270     protected int sizeOf(Task.TaskKey t, Drawable d) {
271         // The cache size will be measured in kilobytes rather than number of items
272         // NOTE: this isn't actually correct, as the icon may be smaller
273         int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4);
274         return maxBytes / 1024;
275     }
276 }
277
278 /**
279  * The bitmap cache.  By using the Task's key, we can prevent holding onto a reference to the Task
280  * resource data, while keeping the cache data in memory where necessary.
281  */
282 class BitmapLruCache extends LruCache<Task.TaskKey, Bitmap> {
283     public BitmapLruCache(int cacheSize) {
284         super(cacheSize);
285     }
286
287     @Override
288     protected int sizeOf(Task.TaskKey t, Bitmap bitmap) {
289         // The cache size will be measured in kilobytes rather than number of items
290         return bitmap.getAllocationByteCount() / 1024;
291     }
292 }
293
294 /* Recents task loader
295  * NOTE: We should not hold any references to a Context from a static instance */
296 public class RecentsTaskLoader {
297     static RecentsTaskLoader sInstance;
298
299     DrawableLruCache mIconCache;
300     BitmapLruCache mThumbnailCache;
301     TaskResourceLoadQueue mLoadQueue;
302     TaskResourceLoader mLoader;
303
304     int mMaxThumbnailCacheSize;
305     int mMaxIconCacheSize;
306
307     BitmapDrawable mDefaultIcon;
308     Bitmap mDefaultThumbnail;
309
310     /** Private Constructor */
311     private RecentsTaskLoader(Context context) {
312         // Calculate the cache sizes, we just use a reasonable number here similar to those
313         // suggested in the Android docs, 1/8th for the thumbnail cache and 1/32 of the max memory
314         // for icons.
315         int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
316         mMaxThumbnailCacheSize = maxMemory / 8;
317         mMaxIconCacheSize = mMaxThumbnailCacheSize / 4;
318         int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
319                 mMaxIconCacheSize;
320         int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
321                 mMaxThumbnailCacheSize;
322
323         Console.log(Constants.DebugFlags.App.TaskDataLoader,
324                 "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
325                 " iconCache: " + iconCacheSize);
326
327         // Initialize the cache and loaders
328         mLoadQueue = new TaskResourceLoadQueue();
329         mIconCache = new DrawableLruCache(iconCacheSize);
330         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
331         mLoader = new TaskResourceLoader(mLoadQueue, mIconCache, mThumbnailCache);
332
333         // Create the default assets
334         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
335         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
336         Canvas c = new Canvas();
337         c.setBitmap(icon);
338         c.drawColor(0x00000000);
339         c.setBitmap(mDefaultThumbnail);
340         c.drawColor(0x00000000);
341         c.setBitmap(null);
342         mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
343         Console.log(Constants.DebugFlags.App.TaskDataLoader,
344                 "[RecentsTaskLoader|defaultBitmaps]",
345                 "icon: " + mDefaultIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
346     }
347
348     /** Initializes the recents task loader */
349     public static RecentsTaskLoader initialize(Context context) {
350         if (sInstance == null) {
351             sInstance = new RecentsTaskLoader(context);
352         }
353         return sInstance;
354     }
355
356     /** Returns the current recents task loader */
357     public static RecentsTaskLoader getInstance() {
358         return sInstance;
359     }
360
361     /** Reload the set of recent tasks */
362     SpaceNode reload(Context context, int preloadCount) {
363         Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
364         Resources res = context.getResources();
365         ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
366         TaskStack stack = new TaskStack(context);
367         SpaceNode root = new SpaceNode(context);
368         root.setStack(stack);
369
370         try {
371             long t1 = System.currentTimeMillis();
372
373             PackageManager pm = context.getPackageManager();
374             ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
375
376             // Get the recent tasks
377             List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasksForUser(25,
378                     ActivityManager.RECENT_IGNORE_UNAVAILABLE |
379                     ActivityManager.RECENT_INCLUDE_PROFILES, UserHandle.CURRENT.getIdentifier());
380             Collections.reverse(tasks);
381             Console.log(Constants.DebugFlags.App.TimeSystemCalls,
382                     "[RecentsTaskLoader|getRecentTasks]",
383                     "" + (System.currentTimeMillis() - t1) + "ms");
384             Console.log(Constants.DebugFlags.App.TaskDataLoader,
385                     "[RecentsTaskLoader|tasks]", "" + tasks.size());
386
387             // Remove home/recents tasks
388             Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
389             while (iter.hasNext()) {
390                 ActivityManager.RecentTaskInfo t = iter.next();
391
392                 // Skip tasks in the home stack
393                 if (am.isInHomeStack(t.persistentId)) {
394                     iter.remove();
395                     continue;
396                 }
397                 // Skip tasks from this Recents package
398                 if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) {
399                     iter.remove();
400                     continue;
401                 }
402             }
403
404             // Add each task to the task stack
405             t1 = System.currentTimeMillis();
406             int taskCount = tasks.size();
407             for (int i = 0; i < taskCount; i++) {
408                 ActivityManager.RecentTaskInfo t = tasks.get(i);
409                 ActivityInfo info = pm.getActivityInfo(t.baseIntent.getComponent(),
410                         PackageManager.GET_META_DATA);
411                 String title = info.loadLabel(pm).toString();
412                 boolean isForemostTask = (i == (taskCount - 1));
413
414                 // Preload the specified number of apps
415                 if (i >= (taskCount - preloadCount)) {
416                     Console.log(Constants.DebugFlags.App.TaskDataLoader,
417                             "[RecentsTaskLoader|preloadTask]",
418                             "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
419
420                     String label = (t.activityLabel == null ? title : t.activityLabel.toString());
421                     BitmapDrawable bd = null;
422                     if (t.activityIcon != null) {
423                         bd = new BitmapDrawable(res, t.activityIcon);
424                     }
425                     Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, label, bd);
426
427                     // Load the icon (if possible and not the foremost task, from the cache)
428                     if (task.icon != null) {
429                         mIconCache.put(task.key, task.icon);
430                     } else {
431                         if (!isForemostTask) {
432                             task.icon = mIconCache.get(task.key);
433                             if (task.icon != null) {
434                                 // Even though we get things from the cache, we should update them
435                                 // if they've changed in the bg
436                                 tasksToForceLoad.add(task);
437                             }
438                         }
439                         if (task.icon == null) {
440                             task.icon = info.loadIcon(pm);
441                             if (task.icon != null) {
442                                 mIconCache.put(task.key, task.icon);
443                             } else {
444                                 task.icon = mDefaultIcon;
445                             }
446                         }
447                     }
448
449                     // Load the thumbnail (if possible and not the foremost task, from the cache)
450                     if (!isForemostTask) {
451                         task.thumbnail = mThumbnailCache.get(task.key);
452                         if (task.thumbnail != null) {
453                             // Even though we get things from the cache, we should update them if
454                             // they've changed in the bg
455                             tasksToForceLoad.add(task);
456                         }
457                     }
458                     if (task.thumbnail == null) {
459                         Console.log(Constants.DebugFlags.App.TaskDataLoader,
460                                 "[RecentsTaskLoader|loadingTaskThumbnail]");
461                         task.thumbnail = am.getTaskTopThumbnail(t.id);
462                         if (task.thumbnail != null) {
463                             mThumbnailCache.put(task.key, task.thumbnail);
464                         } else {
465                             task.thumbnail = mDefaultThumbnail;
466                         }
467                     }
468
469                     // Create as many tasks a we want to multiply by
470                     for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
471                         Console.log(Constants.DebugFlags.App.TaskDataLoader,
472                                 "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
473                         stack.addTask(task);
474                     }
475                 } else {
476                     // Create as many tasks a we want to multiply by
477                     for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
478                         Console.log(Constants.DebugFlags.App.TaskDataLoader,
479                                 "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
480                         stack.addTask(new Task(t.persistentId, (t.id > -1), t.baseIntent, title,
481                                 null, null));
482                     }
483                 }
484             }
485             Console.log(Constants.DebugFlags.App.TimeSystemCalls,
486                     "[RecentsTaskLoader|getAllTaskTopThumbnail]",
487                     "" + (System.currentTimeMillis() - t1) + "ms");
488
489             /*
490             // Get all the stacks
491             t1 = System.currentTimeMillis();
492             List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos();
493             Console.log(Constants.DebugFlags.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
494             Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size());
495             for (ActivityManager.StackInfo s : stackInfos) {
496                 Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [RecentsTaskLoader|stack]", s.toString());
497                 if (stacks.containsKey(s.stackId)) {
498                     stacks.get(s.stackId).setRect(s.bounds);
499                 }
500             }
501             */
502         } catch (Exception e) {
503             e.printStackTrace();
504         }
505
506         // Start the task loader
507         mLoader.start(context);
508
509         // Add all the tasks that we are force/re-loading
510         for (Task t : tasksToForceLoad) {
511             mLoadQueue.addTask(t, true);
512         }
513
514         return root;
515     }
516
517     /** Acquires the task resource data from the pool. */
518     public void loadTaskData(Task t) {
519         Drawable icon = mIconCache.get(t.key);
520         Bitmap thumbnail = mThumbnailCache.get(t.key);
521
522         Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
523                 t + " icon: " + icon + " thumbnail: " + thumbnail +
524                         " thumbnailCacheSize: " + mThumbnailCache.size());
525
526         boolean requiresLoad = false;
527         if (icon == null) {
528             icon = mDefaultIcon;
529             requiresLoad = true;
530         }
531         if (thumbnail == null) {
532             thumbnail = mDefaultThumbnail;
533             requiresLoad = true;
534         }
535         if (requiresLoad) {
536             mLoadQueue.addTask(t, false);
537         }
538         t.notifyTaskDataLoaded(thumbnail, icon, false);
539     }
540
541     /** Releases the task resource data back into the pool. */
542     public void unloadTaskData(Task t) {
543         Console.log(Constants.DebugFlags.App.TaskDataLoader,
544                 "[RecentsTaskLoader|unloadTask]", t +
545                 " thumbnailCacheSize: " + mThumbnailCache.size());
546
547         mLoadQueue.removeTask(t);
548         t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
549     }
550
551     /** Completely removes the resource data from the pool. */
552     public void deleteTaskData(Task t) {
553         Console.log(Constants.DebugFlags.App.TaskDataLoader,
554                 "[RecentsTaskLoader|deleteTask]", t);
555
556         mLoadQueue.removeTask(t);
557         mThumbnailCache.remove(t.key);
558         mIconCache.remove(t.key);
559         t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
560     }
561
562     /** Stops the task loader and clears all pending tasks */
563     void stopLoader() {
564         Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]");
565         mLoader.stop();
566         mLoadQueue.clearTasks();
567     }
568
569     void onTrimMemory(int level) {
570         Console.log(Constants.DebugFlags.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
571                 Console.trimMemoryLevelToString(level));
572
573         switch (level) {
574             case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
575                 // Stop the loader immediately when the UI is no longer visible
576                 stopLoader();
577                 break;
578             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
579             case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
580                 // We are leaving recents, so trim the data a bit
581                 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
582                 mIconCache.trimToSize(mMaxIconCacheSize / 2);
583                 break;
584             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
585             case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
586                 // We are going to be low on memory
587                 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
588                 mIconCache.trimToSize(mMaxIconCacheSize / 4);
589                 break;
590             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
591             case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
592                 // We are low on memory, so release everything
593                 mThumbnailCache.evictAll();
594                 mIconCache.evictAll();
595                 break;
596             default:
597                 break;
598         }
599     }
600 }