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