diff options
| -rw-r--r-- | services/core/java/com/android/server/wm/LaunchParamsPersister.java | 263 | ||||
| -rw-r--r-- | services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java | 15 |
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()) { |