Moving recent tasks and images to /data/system_ce
Moved user sensitive recents data to the credential encrypted directory
/data/system_ce. Since this directory is not available before the user
is unlocked, and unavailable task ids are needed to be known before
assigning an id to a new task, this change adds a file in /data/system_de
for each user, to store the ids of the tasks present in /data/system_ce.
Bug: b/24569398
Change-Id: I340d1abe23e2594e2cee4bff89a697bd8dc61fc7
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ccaa1d2..150ea7c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1892,7 +1892,7 @@
case SYSTEM_USER_UNLOCK_MSG: {
final int userId = msg.arg1;
mSystemServiceManager.unlockUser(userId);
- mRecentTasks.cleanupLocked(userId);
+ mRecentTasks.loadUserRecentsLocked(userId);
installEncryptionUnawareProviders(userId);
break;
}
@@ -2539,7 +2539,7 @@
}
void onUserStoppedLocked(int userId) {
- mRecentTasks.unloadUserRecentsLocked(userId);
+ mRecentTasks.unloadUserDataFromMemoryLocked(userId);
}
public void initPowerManagement() {
@@ -8725,6 +8725,10 @@
android.Manifest.permission.GET_DETAILED_TASKS)
== PackageManager.PERMISSION_GRANTED;
+ if (!isUserRunning(userId, ActivityManager.FLAG_AND_UNLOCKED)) {
+ Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
+ return Collections.emptyList();
+ }
mRecentTasks.loadUserRecentsLocked(userId);
final int recentsCount = mRecentTasks.size();
@@ -12519,7 +12523,7 @@
// Make sure we have the current profile info, since it is needed for security checks.
mUserController.onSystemReady();
- mRecentTasks.onSystemReady();
+ mRecentTasks.onSystemReadyLocked();
// Check to see if there are any update receivers to run.
if (!mDidUpdate) {
if (mWaitingUpdate) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 662c51e..f3f0561 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -719,14 +719,14 @@
}
int getNextTaskIdForUserLocked(int userId) {
- mRecentTasks.loadUserRecentsLocked(userId);
final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER);
// for a userId u, a taskId can only be in the range
// [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
// was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
int candidateTaskId = currentTaskId;
- while (anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS,
- INVALID_STACK_ID) != null) {
+ while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId)
+ || anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS,
+ INVALID_STACK_ID) != null) {
candidateTaskId++;
if (candidateTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
// Wrap around as there will be smaller task ids that are available now.
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 9c139d5..7209814 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -37,8 +37,12 @@
import android.graphics.Bitmap;
import android.os.Environment;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings.System;
+import android.util.ArraySet;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import java.io.File;
@@ -59,12 +63,22 @@
// Maximum number recent bitmaps to keep in memory.
private static final int MAX_RECENT_BITMAPS = 3;
+ private static final int DEFAULT_INITIAL_CAPACITY = 5;
/**
* Save recent tasks information across reboots.
*/
private final TaskPersister mTaskPersister;
- private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(5);
+ private final ActivityManagerService mService;
+ private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
+ DEFAULT_INITIAL_CAPACITY);
+
+ /**
+ * Stores for each user task ids that are taken by tasks residing in persistent storage. These
+ * tasks may or may not currently be in memory.
+ */
+ final SparseArray<SparseBooleanArray> mPersistedTaskIds = new SparseArray<>(
+ DEFAULT_INITIAL_CAPACITY);
// Mainly to avoid object recreation on multiple calls.
private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>();
@@ -75,6 +89,7 @@
RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) {
File systemDir = Environment.getDataSystemDirectory();
+ mService = service;
mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this);
mStackSupervisor.setRecentTasks(this);
}
@@ -87,6 +102,8 @@
*/
void loadUserRecentsLocked(int userId) {
if (!mUsersWithRecentsLoaded.get(userId)) {
+ // Load the task ids if not loaded.
+ loadPersistedTaskIdsForUserLocked(userId);
Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
addAll(mTaskPersister.restoreTasksForUserLocked(userId));
cleanupLocked(userId);
@@ -94,21 +111,49 @@
}
}
+ private void loadPersistedTaskIdsForUserLocked(int userId) {
+ // An empty instead of a null set here means that no persistent taskIds were present
+ // on file when we loaded them.
+ if (mPersistedTaskIds.get(userId) == null) {
+ mPersistedTaskIds.put(userId, mTaskPersister.loadPersistedTaskIdsForUser(userId));
+ }
+ }
+
+ boolean taskIdTakenForUserLocked(int taskId, int userId) {
+ loadPersistedTaskIdsForUserLocked(userId);
+ return mPersistedTaskIds.get(userId).get(taskId);
+ }
+
void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
if (task != null && task.stack != null && task.stack.isHomeStack()) {
// Never persist the home stack.
return;
}
+ syncPersistentTaskIdsLocked();
mTaskPersister.wakeup(task, flush);
}
- void onSystemReady() {
- clear();
- loadUserRecentsLocked(UserHandle.USER_SYSTEM);
- startPersisting();
+ private void syncPersistentTaskIdsLocked() {
+ for (int i = mPersistedTaskIds.size() - 1; i >= 0; i--) {
+ int userId = mPersistedTaskIds.keyAt(i);
+ if (mUsersWithRecentsLoaded.get(userId)) {
+ // Recents are loaded only after task ids are loaded. Therefore, the set of taskids
+ // referenced here should not be null.
+ mPersistedTaskIds.valueAt(i).clear();
+ }
+ }
+ for (int i = size() - 1; i >= 0; i--) {
+ TaskRecord task = get(i);
+ if (task.isPersistable && (task.stack == null || !task.stack.isHomeStack())) {
+ // Set of persisted taskIds for task.userId should not be null here
+ mPersistedTaskIds.get(task.userId).put(task.taskId, true);
+ }
+ }
}
- void startPersisting() {
+
+ void onSystemReadyLocked() {
+ clear();
mTaskPersister.startPersisting();
}
@@ -125,11 +170,14 @@
}
void flush() {
+ synchronized (mService) {
+ syncPersistentTaskIdsLocked();
+ }
mTaskPersister.flush();
}
/**
- * Returns all userIds for which recents from storage are loaded
+ * Returns all userIds for which recents from persistent storage are loaded into this list.
*
* @return an array of userIds.
*/
@@ -149,12 +197,7 @@
return usersWithRecentsLoaded;
}
- /**
- * Removes recent tasks for this user if they are loaded, does not do anything otherwise.
- *
- * @param userId the user id.
- */
- void unloadUserRecentsLocked(int userId) {
+ private void unloadUserRecentsLocked(int userId) {
if (mUsersWithRecentsLoaded.get(userId)) {
Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
mUsersWithRecentsLoaded.delete(userId);
@@ -162,6 +205,18 @@
}
}
+ /**
+ * Removes recent tasks and any other state kept in memory for the passed in user. Does not
+ * touch the information present on persistent storage.
+ *
+ * @param userId the id of the user
+ */
+ void unloadUserDataFromMemoryLocked(int userId) {
+ unloadUserRecentsLocked(userId);
+ mPersistedTaskIds.delete(userId);
+ mTaskPersister.unloadUserDataFromMemory(userId);
+ }
+
TaskRecord taskForIdLocked(int id) {
final int recentsCount = size();
for (int i = 0; i < recentsCount; i++) {
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 283939e..11fd3bc 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Debug;
@@ -26,11 +29,13 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.Xml;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
-
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -38,9 +43,12 @@
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -71,6 +79,7 @@
private static final String TASKS_DIRNAME = "recent_tasks";
private static final String TASK_EXTENSION = ".xml";
private static final String IMAGES_DIRNAME = "recent_images";
+ private static final String PERSISTED_TASK_IDS_FILENAME = "persisted_taskIds.txt";
static final String IMAGE_EXTENSION = ".png";
private static final String TAG_TASK = "task";
@@ -78,6 +87,7 @@
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private final RecentTasks mRecentTasks;
+ private final SparseArray<SparseBooleanArray> mTaskIdsInFile = new SparseArray<>();
/**
* Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
@@ -170,6 +180,64 @@
}
}
+ @NonNull
+ SparseBooleanArray loadPersistedTaskIdsForUser(int userId) {
+ if (mTaskIdsInFile.get(userId) != null) {
+ return mTaskIdsInFile.get(userId).clone();
+ }
+ final SparseBooleanArray persistedTaskIds = new SparseBooleanArray();
+ BufferedReader reader = null;
+ String line;
+ try {
+ reader = new BufferedReader(new FileReader(getUserPersistedTaskIdsFile(userId)));
+ while ((line = reader.readLine()) != null) {
+ for (String taskIdString : line.split("\\s+")) {
+ int id = Integer.parseInt(taskIdString);
+ persistedTaskIds.put(id, true);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // File doesn't exist. Ignore.
+ } catch (Exception e) {
+ Slog.e(TAG, "Error while reading taskIds file for user " + userId, e);
+ } finally {
+ IoUtils.closeQuietly(reader);
+ }
+ mTaskIdsInFile.put(userId, persistedTaskIds);
+ return persistedTaskIds.clone();
+ }
+
+ private void maybeWritePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds,
+ int userId) {
+ if (userId < 0) {
+ return;
+ }
+ SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
+ if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIds)) {
+ return;
+ }
+ final File persistedTaskIdsFile = getUserPersistedTaskIdsFile(userId);
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(persistedTaskIdsFile));
+ for (int i = 0; i < taskIds.size(); i++) {
+ if (taskIds.valueAt(i)) {
+ writer.write(String.valueOf(taskIds.keyAt(i)));
+ writer.newLine();
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error while writing taskIds file for user " + userId, e);
+ } finally {
+ IoUtils.closeQuietly(writer);
+ }
+ mTaskIdsInFile.put(userId, taskIds.clone());
+ }
+
+ void unloadUserDataFromMemory(int userId) {
+ mTaskIdsInFile.delete(userId);
+ }
+
void wakeup(TaskRecord task, boolean flush) {
synchronized (this) {
if (task != null) {
@@ -336,14 +404,16 @@
File[] recentFiles = userTasksDir.listFiles();
if (recentFiles == null) {
- Slog.e(TAG, "restoreTasksForUser: Unable to list files from " + userTasksDir);
+ Slog.e(TAG, "restoreTasksForUserLocked: Unable to list files from " + userTasksDir);
return tasks;
}
for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
File taskFile = recentFiles[taskNdx];
- if (DEBUG) Slog.d(TAG, "restoreTasksForUser: userId=" + userId
- + ", taskFile=" + taskFile.getName());
+ if (DEBUG) {
+ Slog.d(TAG, "restoreTasksForUserLocked: userId=" + userId
+ + ", taskFile=" + taskFile.getName());
+ }
BufferedReader reader = null;
boolean deleteFile = false;
try {
@@ -366,20 +436,29 @@
// out the stuff we just read, if we don't write it we will
// read the same thing again.
// mWriteQueue.add(new TaskWriteQueueItem(task));
+
final int taskId = task.taskId;
- mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId);
- // Check if it's a valid user id. Don't add tasks for removed users.
- if (userId == task.userId) {
+ if (mStackSupervisor.anyTaskForIdLocked(taskId,
+ /* restoreFromRecents= */ false, 0) != null) {
+ // Should not happen.
+ Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
+ } else if (userId != task.userId) {
+ // Should not happen.
+ Slog.wtf(TAG, "Task with userId " + task.userId + " found in "
+ + userTasksDir.getAbsolutePath());
+ } else {
+ // Looks fine.
+ mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId);
task.isPersistable = true;
tasks.add(task);
recoveredTaskIds.add(taskId);
}
} else {
- Slog.e(TAG, "restoreTasksForUser: Unable to restore taskFile="
+ Slog.e(TAG, "restoreTasksForUserLocked: Unable to restore taskFile="
+ taskFile + ": " + fileToString(taskFile));
}
} else {
- Slog.wtf(TAG, "restoreTasksForUser: Unknown xml event=" + event
+ Slog.wtf(TAG, "restoreTasksForUserLocked: Unknown xml event=" + event
+ " name=" + name);
}
}
@@ -454,6 +533,20 @@
}
}
+ private void writeTaskIdsFiles() {
+ int candidateUserIds[];
+ synchronized (mService) {
+ candidateUserIds = mRecentTasks.usersWithRecentsLoadedLocked();
+ }
+ SparseBooleanArray taskIdsToSave;
+ for (int userId : candidateUserIds) {
+ synchronized (mService) {
+ taskIdsToSave = mRecentTasks.mPersistedTaskIds.get(userId).clone();
+ }
+ maybeWritePersistedTaskIdsForUser(taskIdsToSave, userId);
+ }
+ }
+
private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
int[] candidateUserIds;
synchronized (mService) {
@@ -472,8 +565,12 @@
return BitmapFactory.decodeFile(filename);
}
+ static File getUserPersistedTaskIdsFile(int userId) {
+ return new File(Environment.getDataSystemDeDirectory(userId), PERSISTED_TASK_IDS_FILENAME);
+ }
+
static File getUserTasksDir(int userId) {
- File userTasksDir = new File(Environment.getUserSystemDirectory(userId), TASKS_DIRNAME);
+ File userTasksDir = new File(Environment.getDataSystemCeDirectory(userId), TASKS_DIRNAME);
if (!userTasksDir.exists()) {
if (!userTasksDir.mkdir()) {
@@ -485,7 +582,7 @@
}
static File getUserImagesDir(int userId) {
- File userImagesDir = new File(Environment.getUserSystemDirectory(userId), IMAGES_DIRNAME);
+ File userImagesDir = new File(Environment.getDataSystemCeDirectory(userId), IMAGES_DIRNAME);
if (!userImagesDir.exists()) {
if (!userImagesDir.mkdir()) {
@@ -535,6 +632,7 @@
}
removeObsoleteFiles(persistentTaskIds);
}
+ writeTaskIdsFiles();
// If mNextWriteTime, then don't delay between each call to saveToXml().
final WriteQueueItem item;