1 /* 2 * Copyright (C) 2022 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 package com.android.server.wm; 17 18 import android.annotation.Nullable; 19 import android.util.ArrayMap; 20 import android.window.TaskSnapshot; 21 22 import com.android.internal.annotations.GuardedBy; 23 24 import java.io.PrintWriter; 25 26 /** 27 * Base class for an app snapshot cache 28 * @param <TYPE> The basic type, either Task or ActivityRecord 29 */ 30 abstract class SnapshotCache<TYPE extends WindowContainer> { 31 protected final Object mLock = new Object(); 32 33 protected final String mName; 34 35 @GuardedBy("mLock") 36 protected final ArrayMap<ActivityRecord, Integer> mAppIdMap = new ArrayMap<>(); 37 38 @GuardedBy("mLock") 39 protected final ArrayMap<Integer, CacheEntry> mRunningCache = new ArrayMap<>(); 40 SnapshotCache(String name)41 SnapshotCache(String name) { 42 mName = name; 43 } 44 putSnapshot(TYPE window, TaskSnapshot snapshot)45 abstract void putSnapshot(TYPE window, TaskSnapshot snapshot); 46 clearRunningCache()47 void clearRunningCache() { 48 synchronized (mLock) { 49 mRunningCache.clear(); 50 } 51 } 52 53 @Nullable getSnapshot(Integer id)54 final TaskSnapshot getSnapshot(Integer id) { 55 synchronized (mLock) { 56 // Try the running cache. 57 final CacheEntry entry = mRunningCache.get(id); 58 if (entry != null) { 59 return entry.snapshot; 60 } 61 } 62 return null; 63 } 64 65 /** Called when an app token has been removed. */ onAppRemoved(ActivityRecord activity)66 void onAppRemoved(ActivityRecord activity) { 67 synchronized (mLock) { 68 final Integer id = mAppIdMap.get(activity); 69 if (id != null) { 70 removeRunningEntry(id); 71 } 72 } 73 } 74 75 /** Called when an app window token's process died. */ onAppDied(ActivityRecord activity)76 void onAppDied(ActivityRecord activity) { 77 synchronized (mLock) { 78 final Integer id = mAppIdMap.get(activity); 79 if (id != null) { 80 removeRunningEntry(id); 81 } 82 } 83 } 84 onIdRemoved(Integer index)85 void onIdRemoved(Integer index) { 86 removeRunningEntry(index); 87 } 88 removeRunningEntry(Integer id)89 void removeRunningEntry(Integer id) { 90 synchronized (mLock) { 91 final CacheEntry entry = mRunningCache.get(id); 92 if (entry != null) { 93 mAppIdMap.remove(entry.topApp); 94 mRunningCache.remove(id); 95 entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE); 96 } 97 } 98 } 99 dump(PrintWriter pw, String prefix)100 void dump(PrintWriter pw, String prefix) { 101 final String doublePrefix = prefix + " "; 102 final String triplePrefix = doublePrefix + " "; 103 pw.println(prefix + "SnapshotCache " + mName); 104 105 synchronized (mLock) { 106 for (int i = mRunningCache.size() - 1; i >= 0; i--) { 107 final CacheEntry entry = mRunningCache.valueAt(i); 108 pw.println(doublePrefix + "Entry token=" + mRunningCache.keyAt(i)); 109 pw.println(triplePrefix + "topApp=" + entry.topApp); 110 pw.println(triplePrefix + "snapshot=" + entry.snapshot); 111 } 112 } 113 } 114 115 static final class CacheEntry { 116 /** The snapshot. */ 117 final TaskSnapshot snapshot; 118 /** The app token that was on top of the task when the snapshot was taken */ 119 final ActivityRecord topApp; CacheEntry(TaskSnapshot snapshot, ActivityRecord topApp)120 CacheEntry(TaskSnapshot snapshot, ActivityRecord topApp) { 121 this.snapshot = snapshot; 122 this.topApp = topApp; 123 } 124 } 125 } 126