• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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