summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsPersister.java263
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java15
2 files changed, 178 insertions, 100 deletions
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 2394da91684d..4aa4f22ec148 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -22,6 +22,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManagerInternal;
import android.graphics.Rect;
import android.os.Environment;
+import android.os.Process;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -50,6 +51,9 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
import java.util.function.IntFunction;
/**
@@ -84,6 +88,12 @@ class LaunchParamsPersister {
private PackageList mPackageList;
/**
+ * A map from user ID to the active {@link LoadingTask} when we're loading the launch params for
+ * that user.
+ */
+ private final SparseArray<LoadingTask> mLoadingTaskMap = new SparseArray<>();
+
+ /**
* A dual layer map that first maps user ID to a secondary map, which maps component name (the
* launching activity of tasks) to {@link PersistableLaunchParams} that stores launch metadata
* that are stable across reboots.
@@ -117,113 +127,33 @@ class LaunchParamsPersister {
}
void onUnlockUser(int userId) {
- loadLaunchParams(userId);
+ if (mLoadingTaskMap.contains(userId)) {
+ Slog.e(TAG, "Duplicated onUnlockUser " + userId);
+ return;
+ }
+
+ final LoadingTask task = new LoadingTask(userId);
+ mLoadingTaskMap.put(userId, task);
+ task.execute();
}
void onCleanupUser(int userId) {
+ // There is no need to abort the task itself. Just let the loading task finish silently
+ // without modifying any state.
+ mLoadingTaskMap.remove(userId);
mLaunchParamsMap.remove(userId);
}
- private void loadLaunchParams(int userId) {
- final List<File> filesToDelete = new ArrayList<>();
- final File launchParamsFolder = getLaunchParamFolder(userId);
- if (!launchParamsFolder.isDirectory()) {
- Slog.i(TAG, "Didn't find launch param folder for user " + userId);
+ private void waitAndMoveResultIfLoading(int userId) {
+ final LoadingTask task = mLoadingTaskMap.removeReturnOld(userId);
+ if (task == null) {
return;
}
-
- final Set<String> packages = new ArraySet<>(mPackageList.getPackageNames());
-
- final File[] paramsFiles = launchParamsFolder.listFiles();
- final ArrayMap<ComponentName, PersistableLaunchParams> map =
- new ArrayMap<>(paramsFiles.length);
- mLaunchParamsMap.put(userId, map);
-
- for (File paramsFile : paramsFiles) {
- if (!paramsFile.isFile()) {
- Slog.w(TAG, paramsFile.getAbsolutePath() + " is not a file.");
- continue;
- }
- if (!paramsFile.getName().endsWith(LAUNCH_PARAMS_FILE_SUFFIX)) {
- Slog.w(TAG, "Unexpected params file name: " + paramsFile.getName());
- filesToDelete.add(paramsFile);
- continue;
- }
- String paramsFileName = paramsFile.getName();
- // Migrate all records from old separator to new separator.
- final int oldSeparatorIndex =
- paramsFileName.indexOf(OLD_ESCAPED_COMPONENT_SEPARATOR);
- if (oldSeparatorIndex != -1) {
- if (paramsFileName.indexOf(
- OLD_ESCAPED_COMPONENT_SEPARATOR, oldSeparatorIndex + 1) != -1) {
- // Rare case. We have more than one old escaped component separator probably
- // because this app uses underscore in their package name. We can't distinguish
- // which one is the real separator so let's skip it.
- filesToDelete.add(paramsFile);
- continue;
- }
- paramsFileName = paramsFileName.replace(
- OLD_ESCAPED_COMPONENT_SEPARATOR, ESCAPED_COMPONENT_SEPARATOR);
- final File newFile = new File(launchParamsFolder, paramsFileName);
- if (paramsFile.renameTo(newFile)) {
- paramsFile = newFile;
- } else {
- // Rare case. For some reason we can't rename the file. Let's drop this record
- // instead.
- filesToDelete.add(paramsFile);
- continue;
- }
- }
- final String componentNameString = paramsFileName.substring(
- 0 /* beginIndex */,
- paramsFileName.length() - LAUNCH_PARAMS_FILE_SUFFIX.length())
- .replace(ESCAPED_COMPONENT_SEPARATOR, ORIGINAL_COMPONENT_SEPARATOR);
- final ComponentName name = ComponentName.unflattenFromString(
- componentNameString);
- if (name == null) {
- Slog.w(TAG, "Unexpected file name: " + paramsFileName);
- filesToDelete.add(paramsFile);
- continue;
- }
-
- if (!packages.contains(name.getPackageName())) {
- // Rare case. PersisterQueue doesn't have a chance to remove files for removed
- // packages last time.
- filesToDelete.add(paramsFile);
- continue;
- }
-
- try (InputStream in = new FileInputStream(paramsFile)) {
- final PersistableLaunchParams params = new PersistableLaunchParams();
- final TypedXmlPullParser parser = Xml.resolvePullParser(in);
- int event;
- while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
- && event != XmlPullParser.END_TAG) {
- if (event != XmlPullParser.START_TAG) {
- continue;
- }
-
- final String tagName = parser.getName();
- if (!TAG_LAUNCH_PARAMS.equals(tagName)) {
- Slog.w(TAG, "Unexpected tag name: " + tagName);
- continue;
- }
-
- params.restore(paramsFile, parser);
- }
-
- map.put(name, params);
- addComponentNameToLaunchParamAffinityMapIfNotNull(
- name, params.mWindowLayoutAffinity);
- } catch (Exception e) {
- Slog.w(TAG, "Failed to restore launch params for " + name, e);
- filesToDelete.add(paramsFile);
- }
- }
-
- if (!filesToDelete.isEmpty()) {
- mPersisterQueue.addItem(new CleanUpComponentQueueItem(filesToDelete), true);
+ final ArrayMap<ComponentName, PersistableLaunchParams> map = task.get();
+ if (map == null) {
+ return;
}
+ mLaunchParamsMap.put(userId, map);
}
void saveTask(Task task) {
@@ -236,6 +166,7 @@ class LaunchParamsPersister {
return;
}
final int userId = task.mUserId;
+ waitAndMoveResultIfLoading(userId);
PersistableLaunchParams params;
ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId);
if (map == null) {
@@ -297,6 +228,7 @@ class LaunchParamsPersister {
void getLaunchParams(Task task, ActivityRecord activity, LaunchParams outParams) {
final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent;
final int userId = task != null ? task.mUserId : activity.mUserId;
+ waitAndMoveResultIfLoading(userId);
final String windowLayoutAffinity;
if (task != null) {
windowLayoutAffinity = task.mWindowLayoutAffinity;
@@ -394,6 +326,137 @@ class LaunchParamsPersister {
}
}
+ private class LoadingTask
+ implements Callable<ArrayMap<ComponentName, PersistableLaunchParams>> {
+ private final int mUserId;
+ private final FutureTask<ArrayMap<ComponentName, PersistableLaunchParams>> mFutureTask;
+
+ private LoadingTask(int userId) {
+ mUserId = userId;
+ mFutureTask = new FutureTask<>(this);
+ }
+
+ private void execute() {
+ new Thread(mFutureTask).start();
+ }
+
+ private ArrayMap<ComponentName, PersistableLaunchParams> get() {
+ try {
+ return mFutureTask.get();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ Slog.e(TAG, "Failed to load launch params for user#" + mUserId, e);
+ return null;
+ }
+ }
+
+ @Override
+ public ArrayMap<ComponentName, PersistableLaunchParams> call() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+ final List<File> filesToDelete = new ArrayList<>();
+ final File launchParamsFolder = getLaunchParamFolder(mUserId);
+ if (!launchParamsFolder.isDirectory()) {
+ Slog.i(TAG, "Didn't find launch param folder for user " + mUserId);
+ return null;
+ }
+
+ final Set<String> packages = new ArraySet<>(mPackageList.getPackageNames());
+
+ final File[] paramsFiles = launchParamsFolder.listFiles();
+ final ArrayMap<ComponentName, PersistableLaunchParams> map =
+ new ArrayMap<>(paramsFiles.length);
+
+ for (File paramsFile : paramsFiles) {
+ if (!paramsFile.isFile()) {
+ Slog.w(TAG, paramsFile.getAbsolutePath() + " is not a file.");
+ continue;
+ }
+ if (!paramsFile.getName().endsWith(LAUNCH_PARAMS_FILE_SUFFIX)) {
+ Slog.w(TAG, "Unexpected params file name: " + paramsFile.getName());
+ filesToDelete.add(paramsFile);
+ continue;
+ }
+ String paramsFileName = paramsFile.getName();
+ // Migrate all records from old separator to new separator.
+ final int oldSeparatorIndex =
+ paramsFileName.indexOf(OLD_ESCAPED_COMPONENT_SEPARATOR);
+ if (oldSeparatorIndex != -1) {
+ if (paramsFileName.indexOf(
+ OLD_ESCAPED_COMPONENT_SEPARATOR, oldSeparatorIndex + 1) != -1) {
+ // Rare case. We have more than one old escaped component separator probably
+ // because this app uses underscore in their package name. We can't
+ // distinguish which one is the real separator so let's skip it.
+ filesToDelete.add(paramsFile);
+ continue;
+ }
+ paramsFileName = paramsFileName.replace(
+ OLD_ESCAPED_COMPONENT_SEPARATOR, ESCAPED_COMPONENT_SEPARATOR);
+ final File newFile = new File(launchParamsFolder, paramsFileName);
+ if (paramsFile.renameTo(newFile)) {
+ paramsFile = newFile;
+ } else {
+ // Rare case. For some reason we can't rename the file. Let's drop this
+ // record instead.
+ filesToDelete.add(paramsFile);
+ continue;
+ }
+ }
+ final String componentNameString = paramsFileName.substring(
+ 0 /* beginIndex */,
+ paramsFileName.length() - LAUNCH_PARAMS_FILE_SUFFIX.length())
+ .replace(ESCAPED_COMPONENT_SEPARATOR, ORIGINAL_COMPONENT_SEPARATOR);
+ final ComponentName name = ComponentName.unflattenFromString(
+ componentNameString);
+ if (name == null) {
+ Slog.w(TAG, "Unexpected file name: " + paramsFileName);
+ filesToDelete.add(paramsFile);
+ continue;
+ }
+
+ if (!packages.contains(name.getPackageName())) {
+ // Rare case. PersisterQueue doesn't have a chance to remove files for removed
+ // packages last time.
+ filesToDelete.add(paramsFile);
+ continue;
+ }
+
+ try (InputStream in = new FileInputStream(paramsFile)) {
+ final PersistableLaunchParams params = new PersistableLaunchParams();
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ int event;
+ while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
+ && event != XmlPullParser.END_TAG) {
+ if (event != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final String tagName = parser.getName();
+ if (!TAG_LAUNCH_PARAMS.equals(tagName)) {
+ Slog.w(TAG, "Unexpected tag name: " + tagName);
+ continue;
+ }
+
+ params.restore(paramsFile, parser);
+ }
+
+ map.put(name, params);
+ addComponentNameToLaunchParamAffinityMapIfNotNull(
+ name, params.mWindowLayoutAffinity);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to restore launch params for " + name, e);
+ filesToDelete.add(paramsFile);
+ }
+ }
+
+ if (!filesToDelete.isEmpty()) {
+ mPersisterQueue.addItem(new CleanUpComponentQueueItem(filesToDelete), true);
+ }
+ return map;
+ }
+ }
+
private class LaunchParamsWriteQueueItem
implements PersisterQueue.WriteQueueItem<LaunchParamsWriteQueueItem> {
private final int mUserId;
@@ -466,7 +529,7 @@ class LaunchParamsPersister {
}
}
- private class CleanUpComponentQueueItem implements PersisterQueue.WriteQueueItem {
+ private static class CleanUpComponentQueueItem implements PersisterQueue.WriteQueueItem {
private final List<File> mComponentFiles;
private CleanUpComponentQueueItem(List<File> componentFiles) {
@@ -483,7 +546,7 @@ class LaunchParamsPersister {
}
}
- private class PersistableLaunchParams {
+ private static class PersistableLaunchParams {
private static final String ATTR_WINDOWING_MODE = "windowing_mode";
private static final String ATTR_DISPLAY_UNIQUE_ID = "display_unique_id";
private static final String ATTR_BOUNDS = "bounds";
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 1be61c36f272..66d7963946b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -494,6 +494,21 @@ public class LaunchParamsPersisterTests extends WindowTestsBase {
assertTrue("Result should be empty.", mResult.isEmpty());
}
+ @Test
+ public void testAbortsLoadingWhenUserCleansUpBeforeLoadingFinishes() {
+ mTarget.saveTask(mTestTask);
+ mPersisterQueue.flush();
+
+ final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+ mUserFolderGetter);
+ target.onSystemReady();
+ target.onUnlockUser(TEST_USER_ID);
+ target.onCleanupUser(TEST_USER_ID);
+
+ target.getLaunchParams(mTestTask, null, mResult);
+ assertTrue("Result should be empty.", mResult.isEmpty());
+ }
+
private static boolean deleteRecursively(File file) {
boolean result = true;
if (file.isDirectory()) {