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