Home | History | Annotate | Download | only in model
      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.Context;
     22 import android.content.pm.ActivityInfo;
     23 import android.content.res.Resources;
     24 import android.graphics.Bitmap;
     25 import android.graphics.drawable.BitmapDrawable;
     26 import android.graphics.drawable.Drawable;
     27 import android.os.Handler;
     28 import android.os.HandlerThread;
     29 import android.os.UserHandle;
     30 import android.util.Log;
     31 
     32 import com.android.systemui.R;
     33 import com.android.systemui.recents.Constants;
     34 import com.android.systemui.recents.RecentsConfiguration;
     35 import com.android.systemui.recents.misc.SystemServicesProxy;
     36 
     37 import java.util.ArrayList;
     38 import java.util.Collection;
     39 import java.util.Collections;
     40 import java.util.HashMap;
     41 import java.util.List;
     42 import java.util.concurrent.ConcurrentLinkedQueue;
     43 
     44 
     45 /** Handle to an ActivityInfo */
     46 class ActivityInfoHandle {
     47     ActivityInfo info;
     48 }
     49 
     50 /** A bitmap load queue */
     51 class TaskResourceLoadQueue {
     52     ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
     53 
     54     /** Adds a new task to the load queue */
     55     void addTasks(Collection<Task> tasks) {
     56         for (Task t : tasks) {
     57             if (!mQueue.contains(t)) {
     58                 mQueue.add(t);
     59             }
     60         }
     61         synchronized(this) {
     62             notifyAll();
     63         }
     64     }
     65 
     66     /** Adds a new task to the load queue */
     67     void addTask(Task t) {
     68         if (!mQueue.contains(t)) {
     69             mQueue.add(t);
     70         }
     71         synchronized(this) {
     72             notifyAll();
     73         }
     74     }
     75 
     76     /**
     77      * Retrieves the next task from the load queue, as well as whether we want that task to be
     78      * force reloaded.
     79      */
     80     Task nextTask() {
     81         return mQueue.poll();
     82     }
     83 
     84     /** Removes a task from the load queue */
     85     void removeTask(Task t) {
     86         mQueue.remove(t);
     87     }
     88 
     89     /** Clears all the tasks from the load queue */
     90     void clearTasks() {
     91         mQueue.clear();
     92     }
     93 
     94     /** Returns whether the load queue is empty */
     95     boolean isEmpty() {
     96         return mQueue.isEmpty();
     97     }
     98 }
     99 
    100 /* Task resource loader */
    101 class TaskResourceLoader implements Runnable {
    102     Context mContext;
    103     HandlerThread mLoadThread;
    104     Handler mLoadThreadHandler;
    105     Handler mMainThreadHandler;
    106 
    107     SystemServicesProxy mSystemServicesProxy;
    108     TaskResourceLoadQueue mLoadQueue;
    109     DrawableLruCache mApplicationIconCache;
    110     BitmapLruCache mThumbnailCache;
    111     Bitmap mDefaultThumbnail;
    112     BitmapDrawable mDefaultApplicationIcon;
    113 
    114     boolean mCancelled;
    115     boolean mWaitingOnLoadQueue;
    116 
    117     /** Constructor, creates a new loading thread that loads task resources in the background */
    118     public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache applicationIconCache,
    119                               BitmapLruCache thumbnailCache, Bitmap defaultThumbnail,
    120                               BitmapDrawable defaultApplicationIcon) {
    121         mLoadQueue = loadQueue;
    122         mApplicationIconCache = applicationIconCache;
    123         mThumbnailCache = thumbnailCache;
    124         mDefaultThumbnail = defaultThumbnail;
    125         mDefaultApplicationIcon = defaultApplicationIcon;
    126         mMainThreadHandler = new Handler();
    127         mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
    128                 android.os.Process.THREAD_PRIORITY_BACKGROUND);
    129         mLoadThread.start();
    130         mLoadThreadHandler = new Handler(mLoadThread.getLooper());
    131         mLoadThreadHandler.post(this);
    132     }
    133 
    134     /** Restarts the loader thread */
    135     void start(Context context) {
    136         mContext = context;
    137         mCancelled = false;
    138         mSystemServicesProxy = new SystemServicesProxy(context);
    139         // Notify the load thread to start loading
    140         synchronized(mLoadThread) {
    141             mLoadThread.notifyAll();
    142         }
    143     }
    144 
    145     /** Requests the loader thread to stop after the current iteration */
    146     void stop() {
    147         // Mark as cancelled for the thread to pick up
    148         mCancelled = true;
    149         mSystemServicesProxy = null;
    150         // If we are waiting for the load queue for more tasks, then we can just reset the
    151         // Context now, since nothing is using it
    152         if (mWaitingOnLoadQueue) {
    153             mContext = null;
    154         }
    155     }
    156 
    157     @Override
    158     public void run() {
    159         while (true) {
    160             if (mCancelled) {
    161                 // We have to unset the context here, since the background thread may be using it
    162                 // when we call stop()
    163                 mContext = null;
    164                 // If we are cancelled, then wait until we are started again
    165                 synchronized(mLoadThread) {
    166                     try {
    167                         mLoadThread.wait();
    168                     } catch (InterruptedException ie) {
    169                         ie.printStackTrace();
    170                     }
    171                 }
    172             } else {
    173                 SystemServicesProxy ssp = mSystemServicesProxy;
    174 
    175                 // Load the next item from the queue
    176                 final Task t = mLoadQueue.nextTask();
    177                 if (t != null) {
    178                     Drawable cachedIcon = mApplicationIconCache.get(t.key);
    179                     Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
    180 
    181                     // Load the application icon if it is stale or we haven't cached one yet
    182                     if (cachedIcon == null) {
    183                         cachedIcon = getTaskDescriptionIcon(t.key, t.icon, t.iconFilename, ssp,
    184                                 mContext.getResources());
    185 
    186                         if (cachedIcon == null) {
    187                             ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
    188                                     t.key.userId);
    189                             if (info != null) {
    190                                 cachedIcon = ssp.getActivityIcon(info, t.key.userId);
    191                             }
    192                         }
    193 
    194                         if (cachedIcon == null) {
    195                             cachedIcon = mDefaultApplicationIcon;
    196                         }
    197 
    198                         // At this point, even if we can't load the icon, we will set the default
    199                         // icon.
    200                         mApplicationIconCache.put(t.key, cachedIcon);
    201                     }
    202                     // Load the thumbnail if it is stale or we haven't cached one yet
    203                     if (cachedThumbnail == null) {
    204                         cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
    205                         if (cachedThumbnail != null) {
    206                             cachedThumbnail.setHasAlpha(false);
    207                         } else {
    208                             cachedThumbnail = mDefaultThumbnail;
    209                         }
    210                         mThumbnailCache.put(t.key, cachedThumbnail);
    211                     }
    212                     if (!mCancelled) {
    213                         // Notify that the task data has changed
    214                         final Drawable newIcon = cachedIcon;
    215                         final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail
    216                                 ? null : cachedThumbnail;
    217                         mMainThreadHandler.post(new Runnable() {
    218                             @Override
    219                             public void run() {
    220                                 t.notifyTaskDataLoaded(newThumbnail, newIcon);
    221                             }
    222                         });
    223                     }
    224                 }
    225 
    226                 // If there are no other items in the list, then just wait until something is added
    227                 if (!mCancelled && mLoadQueue.isEmpty()) {
    228                     synchronized(mLoadQueue) {
    229                         try {
    230                             mWaitingOnLoadQueue = true;
    231                             mLoadQueue.wait();
    232                             mWaitingOnLoadQueue = false;
    233                         } catch (InterruptedException ie) {
    234                             ie.printStackTrace();
    235                         }
    236                     }
    237                 }
    238             }
    239         }
    240     }
    241 
    242     Drawable getTaskDescriptionIcon(Task.TaskKey taskKey, Bitmap iconBitmap, String iconFilename,
    243             SystemServicesProxy ssp, Resources res) {
    244         Bitmap tdIcon = iconBitmap != null
    245                 ? iconBitmap
    246                 : ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename);
    247         if (tdIcon != null) {
    248             return ssp.getBadgedIcon(new BitmapDrawable(res, tdIcon), taskKey.userId);
    249         }
    250         return null;
    251     }
    252 }
    253 
    254 /* Recents task loader
    255  * NOTE: We should not hold any references to a Context from a static instance */
    256 public class RecentsTaskLoader {
    257     private static final String TAG = "RecentsTaskLoader";
    258 
    259     static RecentsTaskLoader sInstance;
    260 
    261     SystemServicesProxy mSystemServicesProxy;
    262     DrawableLruCache mApplicationIconCache;
    263     BitmapLruCache mThumbnailCache;
    264     StringLruCache mActivityLabelCache;
    265     TaskResourceLoadQueue mLoadQueue;
    266     TaskResourceLoader mLoader;
    267 
    268     RecentsPackageMonitor mPackageMonitor;
    269 
    270     int mMaxThumbnailCacheSize;
    271     int mMaxIconCacheSize;
    272 
    273     BitmapDrawable mDefaultApplicationIcon;
    274     Bitmap mDefaultThumbnail;
    275 
    276     /** Private Constructor */
    277     private RecentsTaskLoader(Context context) {
    278         mMaxThumbnailCacheSize = context.getResources().getInteger(
    279                 R.integer.config_recents_max_thumbnail_count);
    280         mMaxIconCacheSize = context.getResources().getInteger(
    281                 R.integer.config_recents_max_icon_count);
    282         int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
    283                 mMaxIconCacheSize;
    284         int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
    285                 mMaxThumbnailCacheSize;
    286 
    287         // Create the default assets
    288         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
    289         icon.eraseColor(0x00000000);
    290         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
    291         mDefaultThumbnail.setHasAlpha(false);
    292         mDefaultThumbnail.eraseColor(0xFFffffff);
    293         mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
    294 
    295         // Initialize the proxy, cache and loaders
    296         mSystemServicesProxy = new SystemServicesProxy(context);
    297         mPackageMonitor = new RecentsPackageMonitor();
    298         mLoadQueue = new TaskResourceLoadQueue();
    299         mApplicationIconCache = new DrawableLruCache(iconCacheSize);
    300         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
    301         mActivityLabelCache = new StringLruCache(100);
    302         mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
    303                 mDefaultThumbnail, mDefaultApplicationIcon);
    304     }
    305 
    306     /** Initializes the recents task loader */
    307     public static RecentsTaskLoader initialize(Context context) {
    308         if (sInstance == null) {
    309             sInstance = new RecentsTaskLoader(context);
    310         }
    311         return sInstance;
    312     }
    313 
    314     /** Returns the current recents task loader */
    315     public static RecentsTaskLoader getInstance() {
    316         return sInstance;
    317     }
    318 
    319     /** Returns the system services proxy */
    320     public SystemServicesProxy getSystemServicesProxy() {
    321         return mSystemServicesProxy;
    322     }
    323 
    324     /** Gets the list of recent tasks, ordered from back to front. */
    325     private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp,
    326             boolean isTopTaskHome) {
    327         RecentsConfiguration config = RecentsConfiguration.getInstance();
    328         List<ActivityManager.RecentTaskInfo> tasks =
    329                 ssp.getRecentTasks(config.maxNumTasksToLoad, UserHandle.CURRENT.getIdentifier(),
    330                         isTopTaskHome);
    331         Collections.reverse(tasks);
    332         return tasks;
    333     }
    334 
    335     /** Returns the activity icon using as many cached values as we can. */
    336     public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey,
    337              ActivityManager.TaskDescription td, SystemServicesProxy ssp,
    338              Resources res, ActivityInfoHandle infoHandle, boolean preloadTask) {
    339         // Return the cached activity icon if it exists
    340         Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
    341         if (icon != null) {
    342             return icon;
    343         }
    344 
    345         // If we are preloading this task, continue to load the task description icon or the
    346         // activity icon
    347         if (preloadTask) {
    348 
    349             // Return and cache the task description icon if it exists
    350             Drawable tdDrawable = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
    351                     td.getIconFilename(), ssp, res);
    352             if (tdDrawable != null) {
    353                 mApplicationIconCache.put(taskKey, tdDrawable);
    354                 return tdDrawable;
    355             }
    356 
    357             // Load the icon from the activity info and cache it
    358             if (infoHandle.info == null) {
    359                 infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
    360                         taskKey.userId);
    361             }
    362             if (infoHandle.info != null) {
    363                 icon = ssp.getActivityIcon(infoHandle.info, taskKey.userId);
    364                 if (icon != null) {
    365                     mApplicationIconCache.put(taskKey, icon);
    366                     return icon;
    367                 }
    368             }
    369         }
    370         // If we couldn't load any icon, return null
    371         return null;
    372     }
    373 
    374     /** Returns the activity label using as many cached values as we can. */
    375     public String getAndUpdateActivityLabel(Task.TaskKey taskKey,
    376             ActivityManager.TaskDescription td, SystemServicesProxy ssp,
    377             ActivityInfoHandle infoHandle) {
    378         // Return the task description label if it exists
    379         if (td != null && td.getLabel() != null) {
    380             return td.getLabel();
    381         }
    382         // Return the cached activity label if it exists
    383         String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
    384         if (label != null) {
    385             return label;
    386         }
    387         // All short paths failed, load the label from the activity info and cache it
    388         if (infoHandle.info == null) {
    389             infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
    390                     taskKey.userId);
    391         }
    392         if (infoHandle.info != null) {
    393             label = ssp.getActivityLabel(infoHandle.info);
    394             mActivityLabelCache.put(taskKey, label);
    395         } else {
    396             Log.w(TAG, "Missing ActivityInfo for " + taskKey.baseIntent.getComponent()
    397                     + " u=" + taskKey.userId);
    398         }
    399         return label;
    400     }
    401 
    402     /** Returns the activity's primary color. */
    403     public int getActivityPrimaryColor(ActivityManager.TaskDescription td,
    404             RecentsConfiguration config) {
    405         if (td != null && td.getPrimaryColor() != 0) {
    406             return td.getPrimaryColor();
    407         }
    408         return config.taskBarViewDefaultBackgroundColor;
    409     }
    410 
    411     /** Reload the set of recent tasks */
    412     public SpaceNode reload(Context context, int preloadCount, boolean isTopTaskHome) {
    413         ArrayList<Task.TaskKey> taskKeys = new ArrayList<Task.TaskKey>();
    414         ArrayList<Task> tasksToLoad = new ArrayList<Task>();
    415         TaskStack stack = getTaskStack(mSystemServicesProxy, context.getResources(),
    416                 -1, preloadCount, true, isTopTaskHome, taskKeys, tasksToLoad);
    417         SpaceNode root = new SpaceNode();
    418         root.setStack(stack);
    419 
    420         // Start the task loader and add all the tasks we need to load
    421         mLoader.start(context);
    422         mLoadQueue.addTasks(tasksToLoad);
    423 
    424         // Update the package monitor with the list of packages to listen for
    425         mPackageMonitor.setTasks(taskKeys);
    426 
    427         return root;
    428     }
    429 
    430     /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
    431     public TaskStack getTaskStack(SystemServicesProxy ssp, Resources res,
    432             int preloadTaskId, int preloadTaskCount,
    433             boolean loadTaskThumbnails, boolean isTopTaskHome,
    434             List<Task.TaskKey> taskKeysOut, List<Task> tasksToLoadOut) {
    435         RecentsConfiguration config = RecentsConfiguration.getInstance();
    436         List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp, isTopTaskHome);
    437         HashMap<Task.ComponentNameKey, ActivityInfoHandle> activityInfoCache =
    438                 new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
    439         ArrayList<Task> tasksToAdd = new ArrayList<Task>();
    440         TaskStack stack = new TaskStack();
    441 
    442         int taskCount = tasks.size();
    443         for (int i = 0; i < taskCount; i++) {
    444             ActivityManager.RecentTaskInfo t = tasks.get(i);
    445 
    446             // Compose the task key
    447             Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
    448                     t.firstActiveTime, t.lastActiveTime);
    449 
    450             // Get an existing activity info handle if possible
    451             Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
    452             ActivityInfoHandle infoHandle;
    453             boolean hasCachedActivityInfo = false;
    454             if (activityInfoCache.containsKey(cnKey)) {
    455                 infoHandle = activityInfoCache.get(cnKey);
    456                 hasCachedActivityInfo = true;
    457             } else {
    458                 infoHandle = new ActivityInfoHandle();
    459             }
    460 
    461             // Determine whether to preload this task
    462             boolean preloadTask = false;
    463             if (preloadTaskId > 0) {
    464                 preloadTask = (t.id == preloadTaskId);
    465             } else if (preloadTaskCount > 0) {
    466                 preloadTask = (i >= (taskCount - preloadTaskCount));
    467             }
    468 
    469             // Load the label, icon, and color
    470             String activityLabel  = getAndUpdateActivityLabel(taskKey, t.taskDescription,
    471                     ssp, infoHandle);
    472             Drawable activityIcon = getAndUpdateActivityIcon(taskKey, t.taskDescription,
    473                     ssp, res, infoHandle, preloadTask);
    474             int activityColor = getActivityPrimaryColor(t.taskDescription, config);
    475 
    476             // Update the activity info cache
    477             if (!hasCachedActivityInfo && infoHandle.info != null) {
    478                 activityInfoCache.put(cnKey, infoHandle);
    479             }
    480 
    481             Bitmap icon = t.taskDescription != null
    482                     ? t.taskDescription.getInMemoryIcon()
    483                     : null;
    484             String iconFilename = t.taskDescription != null
    485                     ? t.taskDescription.getIconFilename()
    486                     : null;
    487 
    488             // Add the task to the stack
    489             Task task = new Task(taskKey, (t.id > -1), t.affiliatedTaskId, t.affiliatedTaskColor,
    490                     activityLabel, activityIcon, activityColor, (i == (taskCount - 1)),
    491                     config.lockToAppEnabled, icon, iconFilename);
    492 
    493             if (preloadTask && loadTaskThumbnails) {
    494                 // Load the thumbnail from the cache if possible
    495                 task.thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
    496                 if (task.thumbnail == null) {
    497                     // Load the thumbnail from the system
    498                     task.thumbnail = ssp.getTaskThumbnail(taskKey.id);
    499                     if (task.thumbnail != null) {
    500                         task.thumbnail.setHasAlpha(false);
    501                         mThumbnailCache.put(taskKey, task.thumbnail);
    502                     }
    503                 }
    504                 if (task.thumbnail == null && tasksToLoadOut != null) {
    505                     // Either the task has changed since the last active time, or it was not
    506                     // previously cached, so try and load the task anew.
    507                     tasksToLoadOut.add(task);
    508                 }
    509             }
    510 
    511             // Add to the list of task keys
    512             if (taskKeysOut != null) {
    513                 taskKeysOut.add(taskKey);
    514             }
    515             // Add the task to the stack
    516             tasksToAdd.add(task);
    517         }
    518         stack.setTasks(tasksToAdd);
    519         stack.createAffiliatedGroupings(config);
    520         return stack;
    521     }
    522 
    523     /** Acquires the task resource data directly from the pool. */
    524     public void loadTaskData(Task t) {
    525         Drawable applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(t.key);
    526         Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
    527 
    528         // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
    529         // use the default assets in their place until they load
    530         boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
    531         applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
    532         if (requiresLoad) {
    533             mLoadQueue.addTask(t);
    534         }
    535         t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, applicationIcon);
    536     }
    537 
    538     /** Releases the task resource data back into the pool. */
    539     public void unloadTaskData(Task t) {
    540         mLoadQueue.removeTask(t);
    541         t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
    542     }
    543 
    544     /** Completely removes the resource data from the pool. */
    545     public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
    546         mLoadQueue.removeTask(t);
    547         mThumbnailCache.remove(t.key);
    548         mApplicationIconCache.remove(t.key);
    549         if (notifyTaskDataUnloaded) {
    550             t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
    551         }
    552     }
    553 
    554     /** Stops the task loader and clears all pending tasks */
    555     void stopLoader() {
    556         mLoader.stop();
    557         mLoadQueue.clearTasks();
    558     }
    559 
    560     /** Registers any broadcast receivers. */
    561     public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) {
    562         // Register the broadcast receiver to handle messages related to packages being added/removed
    563         mPackageMonitor.register(context, cb);
    564     }
    565 
    566     /** Unregisters any broadcast receivers. */
    567     public void unregisterReceivers() {
    568         mPackageMonitor.unregister();
    569     }
    570 
    571     /**
    572      * Handles signals from the system, trimming memory when requested to prevent us from running
    573      * out of memory.
    574      */
    575     public void onTrimMemory(int level) {
    576         switch (level) {
    577             case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
    578                 // Stop the loader immediately when the UI is no longer visible
    579                 stopLoader();
    580                 mThumbnailCache.trimToSize(Math.max(
    581                         Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
    582                         mMaxThumbnailCacheSize / 2));
    583                 mApplicationIconCache.trimToSize(Math.max(
    584                         Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
    585                         mMaxIconCacheSize / 2));
    586                 break;
    587             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
    588             case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
    589                 // We are leaving recents, so trim the data a bit
    590                 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
    591                 mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2);
    592                 break;
    593             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
    594             case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
    595                 // We are going to be low on memory
    596                 mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
    597                 mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4);
    598                 break;
    599             case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
    600             case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
    601                 // We are low on memory, so release everything
    602                 mThumbnailCache.evictAll();
    603                 mApplicationIconCache.evictAll();
    604                 // The cache is small, only clear the label cache when we are critical
    605                 mActivityLabelCache.evictAll();
    606                 break;
    607             default:
    608                 break;
    609         }
    610     }
    611 }
    612