1 /* 2 * Copyright (C) 2017 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 static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 20 21 import android.os.Trace; 22 import android.util.ArraySet; 23 import android.window.TaskSnapshot; 24 25 import com.android.internal.annotations.GuardedBy; 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.io.File; 29 import java.util.Arrays; 30 31 /** 32 * Persists {@link TaskSnapshot}s to disk. 33 * <p> 34 * Test class: {@link TaskSnapshotPersisterLoaderTest} 35 */ 36 class TaskSnapshotPersister extends BaseAppSnapshotPersister { 37 38 /** 39 * The list of ids of the tasks that have been persisted since {@link #removeObsoleteFiles} was 40 * called. 41 */ 42 @GuardedBy("mLock") 43 private final ArraySet<Integer> mPersistedTaskIdsSinceLastRemoveObsolete = new ArraySet<>(); 44 TaskSnapshotPersister(SnapshotPersistQueue persistQueue, PersistInfoProvider persistInfoProvider)45 TaskSnapshotPersister(SnapshotPersistQueue persistQueue, 46 PersistInfoProvider persistInfoProvider) { 47 super(persistQueue, persistInfoProvider); 48 } 49 50 /** 51 * Persists a snapshot of a task to disk. 52 * 53 * @param taskId The id of the task that needs to be persisted. 54 * @param userId The id of the user this tasks belongs to. 55 * @param snapshot The snapshot to persist. 56 */ persistSnapshot(int taskId, int userId, TaskSnapshot snapshot)57 void persistSnapshot(int taskId, int userId, TaskSnapshot snapshot) { 58 synchronized (mLock) { 59 mPersistedTaskIdsSinceLastRemoveObsolete.add(taskId); 60 super.persistSnapshot(taskId, userId, snapshot); 61 } 62 } 63 64 /** 65 * Callend when a task has been removed. 66 * 67 * @param taskId The id of task that has been removed. 68 * @param userId The id of the user the task belonged to. 69 */ onTaskRemovedFromRecents(int taskId, int userId)70 void onTaskRemovedFromRecents(int taskId, int userId) { 71 synchronized (mLock) { 72 mPersistedTaskIdsSinceLastRemoveObsolete.remove(taskId); 73 super.removeSnap(taskId, userId); 74 } 75 } 76 77 /** 78 * In case a write/delete operation was lost because the system crashed, this makes sure to 79 * clean up the directory to remove obsolete files. 80 * 81 * @param persistentTaskIds A set of task ids that exist in our in-memory model. 82 * @param runningUserIds The ids of the list of users that have tasks loaded in our in-memory 83 * model. 84 */ removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds)85 void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) { 86 synchronized (mLock) { 87 mPersistedTaskIdsSinceLastRemoveObsolete.clear(); 88 mSnapshotPersistQueue.sendToQueueLocked(new RemoveObsoleteFilesQueueItem( 89 persistentTaskIds, runningUserIds, mPersistInfoProvider)); 90 } 91 } 92 93 @VisibleForTesting 94 class RemoveObsoleteFilesQueueItem extends SnapshotPersistQueue.WriteQueueItem { 95 private final ArraySet<Integer> mPersistentTaskIds; 96 private final int[] mRunningUserIds; 97 98 @VisibleForTesting RemoveObsoleteFilesQueueItem(ArraySet<Integer> persistentTaskIds, int[] runningUserIds, PersistInfoProvider provider)99 RemoveObsoleteFilesQueueItem(ArraySet<Integer> persistentTaskIds, 100 int[] runningUserIds, PersistInfoProvider provider) { 101 super(provider); 102 mPersistentTaskIds = new ArraySet<>(persistentTaskIds); 103 mRunningUserIds = Arrays.copyOf(runningUserIds, runningUserIds.length); 104 } 105 106 @Override write()107 void write() { 108 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RemoveObsoleteFilesQueueItem"); 109 final ArraySet<Integer> newPersistedTaskIds; 110 synchronized (mLock) { 111 newPersistedTaskIds = new ArraySet<>(mPersistedTaskIdsSinceLastRemoveObsolete); 112 } 113 for (int userId : mRunningUserIds) { 114 final File dir = mPersistInfoProvider.getDirectory(userId); 115 final String[] files = dir.list(); 116 if (files == null) { 117 continue; 118 } 119 for (String file : files) { 120 final int taskId = getTaskId(file); 121 if (!mPersistentTaskIds.contains(taskId) 122 && !newPersistedTaskIds.contains(taskId)) { 123 new File(dir, file).delete(); 124 } 125 } 126 } 127 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 128 } 129 130 @VisibleForTesting getTaskId(String fileName)131 int getTaskId(String fileName) { 132 if (!fileName.endsWith(PROTO_EXTENSION) && !fileName.endsWith(BITMAP_EXTENSION)) { 133 return -1; 134 } 135 final int end = fileName.lastIndexOf('.'); 136 if (end == -1) { 137 return -1; 138 } 139 String name = fileName.substring(0, end); 140 if (name.endsWith(LOW_RES_FILE_POSTFIX)) { 141 name = name.substring(0, name.length() - LOW_RES_FILE_POSTFIX.length()); 142 } 143 try { 144 return Integer.parseInt(name); 145 } catch (NumberFormatException e) { 146 return -1; 147 } 148 } 149 } 150 } 151