1 /* 2 * Copyright (C) 2016 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.server.wm; 18 19 import android.annotation.Nullable; 20 import android.app.ActivityManager.TaskSnapshot; 21 import android.util.ArrayMap; 22 23 import java.io.PrintWriter; 24 25 /** 26 * Caches snapshots. See {@link TaskSnapshotController}. 27 * <p> 28 * Access to this class should be guarded by the global window manager lock. 29 */ 30 class TaskSnapshotCache { 31 32 private final WindowManagerService mService; 33 private final TaskSnapshotLoader mLoader; 34 private final ArrayMap<ActivityRecord, Integer> mAppTaskMap = new ArrayMap<>(); 35 private final ArrayMap<Integer, CacheEntry> mRunningCache = new ArrayMap<>(); 36 TaskSnapshotCache(WindowManagerService service, TaskSnapshotLoader loader)37 TaskSnapshotCache(WindowManagerService service, TaskSnapshotLoader loader) { 38 mService = service; 39 mLoader = loader; 40 } 41 clearRunningCache()42 void clearRunningCache() { 43 mRunningCache.clear(); 44 } 45 putSnapshot(Task task, TaskSnapshot snapshot)46 void putSnapshot(Task task, TaskSnapshot snapshot) { 47 final CacheEntry entry = mRunningCache.get(task.mTaskId); 48 if (entry != null) { 49 mAppTaskMap.remove(entry.topApp); 50 } 51 final ActivityRecord top = task.getTopMostActivity(); 52 mAppTaskMap.put(top, task.mTaskId); 53 mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, top)); 54 } 55 56 /** 57 * If {@param restoreFromDisk} equals {@code true}, DO NOT HOLD THE WINDOW MANAGER LOCK! 58 */ getSnapshot(int taskId, int userId, boolean restoreFromDisk, boolean isLowResolution)59 @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk, 60 boolean isLowResolution) { 61 62 synchronized (mService.mGlobalLock) { 63 // Try the running cache. 64 final CacheEntry entry = mRunningCache.get(taskId); 65 if (entry != null) { 66 return entry.snapshot; 67 } 68 } 69 70 // Try to restore from disk if asked. 71 if (!restoreFromDisk) { 72 return null; 73 } 74 return tryRestoreFromDisk(taskId, userId, isLowResolution); 75 } 76 77 /** 78 * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING THIS METHOD! 79 */ tryRestoreFromDisk(int taskId, int userId, boolean isLowResolution)80 private TaskSnapshot tryRestoreFromDisk(int taskId, int userId, boolean isLowResolution) { 81 final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId, isLowResolution); 82 if (snapshot == null) { 83 return null; 84 } 85 return snapshot; 86 } 87 88 /** 89 * Called when an app token has been removed 90 */ onAppRemoved(ActivityRecord activity)91 void onAppRemoved(ActivityRecord activity) { 92 final Integer taskId = mAppTaskMap.get(activity); 93 if (taskId != null) { 94 removeRunningEntry(taskId); 95 } 96 } 97 98 /** 99 * Callend when an app window token's process died. 100 */ onAppDied(ActivityRecord activity)101 void onAppDied(ActivityRecord activity) { 102 final Integer taskId = mAppTaskMap.get(activity); 103 if (taskId != null) { 104 removeRunningEntry(taskId); 105 } 106 } 107 onTaskRemoved(int taskId)108 void onTaskRemoved(int taskId) { 109 removeRunningEntry(taskId); 110 } 111 removeRunningEntry(int taskId)112 void removeRunningEntry(int taskId) { 113 final CacheEntry entry = mRunningCache.get(taskId); 114 if (entry != null) { 115 mAppTaskMap.remove(entry.topApp); 116 mRunningCache.remove(taskId); 117 } 118 } 119 dump(PrintWriter pw, String prefix)120 void dump(PrintWriter pw, String prefix) { 121 final String doublePrefix = prefix + " "; 122 final String triplePrefix = doublePrefix + " "; 123 pw.println(prefix + "SnapshotCache"); 124 for (int i = mRunningCache.size() - 1; i >= 0; i--) { 125 final CacheEntry entry = mRunningCache.valueAt(i); 126 pw.println(doublePrefix + "Entry taskId=" + mRunningCache.keyAt(i)); 127 pw.println(triplePrefix + "topApp=" + entry.topApp); 128 pw.println(triplePrefix + "snapshot=" + entry.snapshot); 129 } 130 } 131 132 private static final class CacheEntry { 133 134 /** The snapshot. */ 135 final TaskSnapshot snapshot; 136 137 /** The app token that was on top of the task when the snapshot was taken */ 138 final ActivityRecord topApp; 139 CacheEntry(TaskSnapshot snapshot, ActivityRecord topApp)140 CacheEntry(TaskSnapshot snapshot, ActivityRecord topApp) { 141 this.snapshot = snapshot; 142 this.topApp = topApp; 143 } 144 } 145 } 146