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