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 } else { 204 // Kick off an early upload of the bitmap to GL so 205 // that this won't jank the first frame it's drawn in. 206 cachedThumbnailData.thumbnail.prepareToDraw(); 207 } 208 209 // When svelte, we trim the memory to just the visible thumbnails when 210 // leaving, so don't thrash the cache as the user scrolls (just load 211 // them from scratch each time) 212 if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE) { 213 mThumbnailCache.put(t.key, cachedThumbnailData); 214 } 215 } 216 if (!mCancelled) { 217 // Notify that the task data has changed 218 final Drawable newIcon = cachedIcon; 219 final ThumbnailData newThumbnailData = cachedThumbnailData; 220 mMainThreadHandler.post(new Runnable() { 221 @Override 222 public void run() { 223 t.notifyTaskDataLoaded(newThumbnailData.thumbnail, newIcon, 224 newThumbnailData.thumbnailInfo); 225 } 226 }); 227 } 228 } 229 } 230 231 // If there are no other items in the list, then just wait until something is added 232 if (!mCancelled && mLoadQueue.isEmpty()) { 233 synchronized(mLoadQueue) { 234 try { 235 mWaitingOnLoadQueue = true; 236 mLoadQueue.wait(); 237 mWaitingOnLoadQueue = false; 238 } catch (InterruptedException ie) { 239 ie.printStackTrace(); 240 } 241 } 242 } 243 } 244 } 245 } 246 } 247 248 /** 249 * Recents task loader 250 */ 251 public class RecentsTaskLoader { 252 253 private static final String TAG = "RecentsTaskLoader"; 254 private static final boolean DEBUG = false; 255 256 // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos 257 // for many tasks, which we use to get the activity labels and icons. Unlike the other caches 258 // below, this is per-package so we can't invalidate the items in the cache based on the last 259 // active time. Instead, we rely on the RecentsPackageMonitor to keep us informed whenever a 260 // package in the cache has been updated, so that we may remove it. 261 private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache; 262 private final TaskKeyLruCache<Drawable> mIconCache; 263 private final TaskKeyLruCache<ThumbnailData> mThumbnailCache; 264 private final TaskKeyLruCache<String> mActivityLabelCache; 265 private final TaskKeyLruCache<String> mContentDescriptionCache; 266 private final TaskResourceLoadQueue mLoadQueue; 267 private final BackgroundTaskLoader mLoader; 268 269 private final int mMaxThumbnailCacheSize; 270 private final int mMaxIconCacheSize; 271 private int mNumVisibleTasksLoaded; 272 private int mNumVisibleThumbnailsLoaded; 273 274 int mDefaultTaskBarBackgroundColor; 275 int mDefaultTaskViewBackgroundColor; 276 BitmapDrawable mDefaultIcon; 277 Bitmap mDefaultThumbnail; 278 279 private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction = 280 new TaskKeyLruCache.EvictionCallback() { 281 @Override 282 public void onEntryEvicted(Task.TaskKey key) { 283 if (key != null) { 284 mActivityInfoCache.remove(key.getComponent()); 285 } 286 } 287 }; 288 RecentsTaskLoader(Context context)289 public RecentsTaskLoader(Context context) { 290 Resources res = context.getResources(); 291 mDefaultTaskBarBackgroundColor = 292 context.getColor(R.color.recents_task_bar_default_background_color); 293 mDefaultTaskViewBackgroundColor = 294 context.getColor(R.color.recents_task_view_default_background_color); 295 mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count); 296 mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count); 297 int iconCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 : 298 mMaxIconCacheSize; 299 int thumbnailCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 : 300 mMaxThumbnailCacheSize; 301 302 // Create the default assets 303 Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); 304 icon.eraseColor(0); 305 mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 306 mDefaultThumbnail.setHasAlpha(false); 307 mDefaultThumbnail.eraseColor(0xFFffffff); 308 mDefaultIcon = new BitmapDrawable(context.getResources(), icon); 309 310 // Initialize the proxy, cache and loaders 311 int numRecentTasks = ActivityManager.getMaxRecentTasksStatic(); 312 mLoadQueue = new TaskResourceLoadQueue(); 313 mIconCache = new TaskKeyLruCache<>(iconCacheSize, mClearActivityInfoOnEviction); 314 mThumbnailCache = new TaskKeyLruCache<>(thumbnailCacheSize); 315 mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction); 316 mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks, 317 mClearActivityInfoOnEviction); 318 mActivityInfoCache = new LruCache(numRecentTasks); 319 mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mThumbnailCache, 320 mDefaultThumbnail, mDefaultIcon); 321 } 322 323 /** Returns the size of the app icon cache. */ getIconCacheSize()324 public int getIconCacheSize() { 325 return mMaxIconCacheSize; 326 } 327 328 /** Returns the size of the thumbnail cache. */ getThumbnailCacheSize()329 public int getThumbnailCacheSize() { 330 return mMaxThumbnailCacheSize; 331 } 332 333 /** Creates a new plan for loading the recent tasks. */ createLoadPlan(Context context)334 public RecentsTaskLoadPlan createLoadPlan(Context context) { 335 RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context); 336 return plan; 337 } 338 339 /** Preloads recents tasks using the specified plan to store the output. */ preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId, boolean includeFrontMostExcludedTask)340 public void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId, 341 boolean includeFrontMostExcludedTask) { 342 plan.preloadPlan(this, runningTaskId, includeFrontMostExcludedTask); 343 } 344 345 /** Begins loading the heavy task data according to the specified options. */ loadTasks(Context context, RecentsTaskLoadPlan plan, RecentsTaskLoadPlan.Options opts)346 public void loadTasks(Context context, RecentsTaskLoadPlan plan, 347 RecentsTaskLoadPlan.Options opts) { 348 if (opts == null) { 349 throw new RuntimeException("Requires load options"); 350 } 351 plan.executePlan(opts, this, mLoadQueue); 352 if (!opts.onlyLoadForCache) { 353 mNumVisibleTasksLoaded = opts.numVisibleTasks; 354 mNumVisibleThumbnailsLoaded = opts.numVisibleTaskThumbnails; 355 356 // Start the loader 357 mLoader.start(context); 358 } 359 } 360 361 /** 362 * Acquires the task resource data directly from the cache, loading if necessary. 363 */ loadTaskData(Task t)364 public void loadTaskData(Task t) { 365 Drawable icon = mIconCache.getAndInvalidateIfModified(t.key); 366 Bitmap thumbnail = null; 367 ActivityManager.TaskThumbnailInfo thumbnailInfo = null; 368 ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key); 369 if (thumbnailData != null) { 370 thumbnail = thumbnailData.thumbnail; 371 thumbnailInfo = thumbnailData.thumbnailInfo; 372 } 373 374 // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and 375 // use the default assets in their place until they load 376 boolean requiresLoad = (icon == null) || (thumbnail == null); 377 icon = icon != null ? icon : mDefaultIcon; 378 if (requiresLoad) { 379 mLoadQueue.addTask(t); 380 } 381 t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon, 382 thumbnailInfo); 383 } 384 385 /** Releases the task resource data back into the pool. */ unloadTaskData(Task t)386 public void unloadTaskData(Task t) { 387 mLoadQueue.removeTask(t); 388 t.notifyTaskDataUnloaded(null, mDefaultIcon); 389 } 390 391 /** Completely removes the resource data from the pool. */ deleteTaskData(Task t, boolean notifyTaskDataUnloaded)392 public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) { 393 mLoadQueue.removeTask(t); 394 mThumbnailCache.remove(t.key); 395 mIconCache.remove(t.key); 396 mActivityLabelCache.remove(t.key); 397 mContentDescriptionCache.remove(t.key); 398 if (notifyTaskDataUnloaded) { 399 t.notifyTaskDataUnloaded(null, mDefaultIcon); 400 } 401 } 402 403 /** 404 * Handles signals from the system, trimming memory when requested to prevent us from running 405 * out of memory. 406 */ onTrimMemory(int level)407 public void onTrimMemory(int level) { 408 RecentsConfiguration config = Recents.getConfiguration(); 409 switch (level) { 410 case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN: 411 // Stop the loader immediately when the UI is no longer visible 412 stopLoader(); 413 if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) { 414 mThumbnailCache.trimToSize(Math.max(mNumVisibleTasksLoaded, 415 mMaxThumbnailCacheSize / 2)); 416 } else if (config.svelteLevel == RecentsConfiguration.SVELTE_LIMIT_CACHE) { 417 mThumbnailCache.trimToSize(mNumVisibleThumbnailsLoaded); 418 } else if (config.svelteLevel >= RecentsConfiguration.SVELTE_DISABLE_CACHE) { 419 mThumbnailCache.evictAll(); 420 } 421 mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded, 422 mMaxIconCacheSize / 2)); 423 break; 424 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE: 425 case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND: 426 // We are leaving recents, so trim the data a bit 427 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 2)); 428 mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2)); 429 mActivityInfoCache.trimToSize(Math.max(1, 430 ActivityManager.getMaxRecentTasksStatic() / 2)); 431 break; 432 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW: 433 case ComponentCallbacks2.TRIM_MEMORY_MODERATE: 434 // We are going to be low on memory 435 mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 4)); 436 mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4)); 437 mActivityInfoCache.trimToSize(Math.max(1, 438 ActivityManager.getMaxRecentTasksStatic() / 4)); 439 break; 440 case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL: 441 case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: 442 // We are low on memory, so release everything 443 mThumbnailCache.evictAll(); 444 mIconCache.evictAll(); 445 mActivityInfoCache.evictAll(); 446 // The cache is small, only clear the label cache when we are critical 447 mActivityLabelCache.evictAll(); 448 mContentDescriptionCache.evictAll(); 449 break; 450 default: 451 break; 452 } 453 } 454 455 /** 456 * Returns the cached task label if the task key is not expired, updating the cache if it is. 457 */ getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td)458 String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) { 459 SystemServicesProxy ssp = Recents.getSystemServices(); 460 461 // Return the task description label if it exists 462 if (td != null && td.getLabel() != null) { 463 return td.getLabel(); 464 } 465 // Return the cached activity label if it exists 466 String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey); 467 if (label != null) { 468 return label; 469 } 470 // All short paths failed, load the label from the activity info and cache it 471 ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey); 472 if (activityInfo != null) { 473 label = ssp.getBadgedActivityLabel(activityInfo, taskKey.userId); 474 mActivityLabelCache.put(taskKey, label); 475 return label; 476 } 477 // If the activity info does not exist or fails to load, return an empty label for now, 478 // but do not cache it 479 return ""; 480 } 481 482 /** 483 * Returns the cached task content description if the task key is not expired, updating the 484 * cache if it is. 485 */ getAndUpdateContentDescription(Task.TaskKey taskKey, Resources res)486 String getAndUpdateContentDescription(Task.TaskKey taskKey, Resources res) { 487 SystemServicesProxy ssp = Recents.getSystemServices(); 488 489 // Return the cached content description if it exists 490 String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey); 491 if (label != null) { 492 return label; 493 } 494 495 // All short paths failed, load the label from the activity info and cache it 496 ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey); 497 if (activityInfo != null) { 498 label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, res); 499 mContentDescriptionCache.put(taskKey, label); 500 return label; 501 } 502 // If the content description does not exist, return an empty label for now, but do not 503 // cache it 504 return ""; 505 } 506 507 /** 508 * Returns the cached task icon if the task key is not expired, updating the cache if it is. 509 */ getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td, Resources res, boolean loadIfNotCached)510 Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td, 511 Resources res, boolean loadIfNotCached) { 512 SystemServicesProxy ssp = Recents.getSystemServices(); 513 514 // Return the cached activity icon if it exists 515 Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey); 516 if (icon != null) { 517 return icon; 518 } 519 520 if (loadIfNotCached) { 521 // Return and cache the task description icon if it exists 522 icon = ssp.getBadgedTaskDescriptionIcon(td, taskKey.userId, res); 523 if (icon != null) { 524 mIconCache.put(taskKey, icon); 525 return icon; 526 } 527 528 // Load the icon from the activity info and cache it 529 ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey); 530 if (activityInfo != null) { 531 icon = ssp.getBadgedActivityIcon(activityInfo, taskKey.userId); 532 if (icon != null) { 533 mIconCache.put(taskKey, icon); 534 return icon; 535 } 536 } 537 } 538 // We couldn't load any icon 539 return null; 540 } 541 542 /** 543 * Returns the cached thumbnail if the task key is not expired, updating the cache if it is. 544 */ getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached)545 Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached) { 546 SystemServicesProxy ssp = Recents.getSystemServices(); 547 548 // Return the cached thumbnail if it exists 549 ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(taskKey); 550 if (thumbnailData != null) { 551 return thumbnailData.thumbnail; 552 } 553 554 if (loadIfNotCached) { 555 RecentsConfiguration config = Recents.getConfiguration(); 556 if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) { 557 // Load the thumbnail from the system 558 thumbnailData = ssp.getTaskThumbnail(taskKey.id); 559 if (thumbnailData.thumbnail != null) { 560 mThumbnailCache.put(taskKey, thumbnailData); 561 return thumbnailData.thumbnail; 562 } 563 } 564 } 565 // We couldn't load any thumbnail 566 return null; 567 } 568 569 /** 570 * Returns the task's primary color if possible, defaulting to the default color if there is 571 * no specified primary color. 572 */ getActivityPrimaryColor(ActivityManager.TaskDescription td)573 int getActivityPrimaryColor(ActivityManager.TaskDescription td) { 574 if (td != null && td.getPrimaryColor() != 0) { 575 return td.getPrimaryColor(); 576 } 577 return mDefaultTaskBarBackgroundColor; 578 } 579 580 /** 581 * Returns the task's background color if possible. 582 */ getActivityBackgroundColor(ActivityManager.TaskDescription td)583 int getActivityBackgroundColor(ActivityManager.TaskDescription td) { 584 if (td != null && td.getBackgroundColor() != 0) { 585 return td.getBackgroundColor(); 586 } 587 return mDefaultTaskViewBackgroundColor; 588 } 589 590 /** 591 * Returns the activity info for the given task key, retrieving one from the system if the 592 * task key is expired. 593 */ getAndUpdateActivityInfo(Task.TaskKey taskKey)594 ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) { 595 SystemServicesProxy ssp = Recents.getSystemServices(); 596 ComponentName cn = taskKey.getComponent(); 597 ActivityInfo activityInfo = mActivityInfoCache.get(cn); 598 if (activityInfo == null) { 599 activityInfo = ssp.getActivityInfo(cn, taskKey.userId); 600 if (cn == null || activityInfo == null) { 601 Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " + 602 activityInfo); 603 return null; 604 } 605 mActivityInfoCache.put(cn, activityInfo); 606 } 607 return activityInfo; 608 } 609 610 /** 611 * Stops the task loader and clears all queued, pending task loads. 612 */ stopLoader()613 private void stopLoader() { 614 mLoader.stop(); 615 mLoadQueue.clearTasks(); 616 } 617 618 /**** Event Bus Events ****/ 619 onBusEvent(PackagesChangedEvent event)620 public final void onBusEvent(PackagesChangedEvent event) { 621 // Remove all the cached activity infos for this package. The other caches do not need to 622 // be pruned at this time, as the TaskKey expiration checks will flush them next time their 623 // cached contents are requested 624 Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot(); 625 for (ComponentName cn : activityInfoCache.keySet()) { 626 if (cn.getPackageName().equals(event.packageName)) { 627 if (DEBUG) { 628 Log.d(TAG, "Removing activity info from cache: " + cn); 629 } 630 mActivityInfoCache.remove(cn); 631 } 632 } 633 } 634 dump(String prefix, PrintWriter writer)635 public void dump(String prefix, PrintWriter writer) { 636 String innerPrefix = prefix + " "; 637 638 writer.print(prefix); writer.println(TAG); 639 writer.print(prefix); writer.println("Icon Cache"); 640 mIconCache.dump(innerPrefix, writer); 641 writer.print(prefix); writer.println("Thumbnail Cache"); 642 mThumbnailCache.dump(innerPrefix, writer); 643 } 644 } 645