Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 496037de authored by wilsonshih's avatar wilsonshih
Browse files

Create or rename a scramble folder for task snapshots.

Enhance file security by making the file path unpredictable and
difficult to guess.
Create or rename the scramble folder when first access happen.

Flag: com.android.window.flags.scramble_snapshot_file_name
Bug: 293139053
Test: atest TaskSnapshotCacheTest TaskSnapshotLowResDisabledTest
TaskSnapshotPersisterLoaderTest ActivitySnapshotControllerTests

Change-Id: Ie83e381f3a9fe32bdea6378103fd762251a5faa0
parent 7a3c826d
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -494,3 +494,14 @@ flag {
            purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "scramble_snapshot_file_name"
    namespace: "windowing_frontend"
    description: "Scramble the file name of task snapshot."
    bug: "293139053"
    is_fixed_read_only: true
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
 No newline at end of file
+6 −2
Original line number Diff line number Diff line
@@ -107,8 +107,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
                && !ActivityManager.isLowRamDeviceStatic(); // Don't support Android Go
        setSnapshotEnabled(snapshotEnabled);
        mSnapshotPersistQueue = persistQueue;
        mPersistInfoProvider = createPersistInfoProvider(service,
                Environment::getDataSystemCeDirectory);
        mPersistInfoProvider = createPersistInfoProvider(service);
        mPersister = new TaskSnapshotPersister(
                persistQueue,
                mPersistInfoProvider,
@@ -117,6 +116,11 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
        initialize(new ActivitySnapshotCache());
    }

    @VisibleForTesting
    PersistInfoProvider createPersistInfoProvider(WindowManagerService service) {
        return createPersistInfoProvider(service, Environment::getDataSystemCeDirectory);
    }

    @Override
    protected float initSnapshotScale() {
        final float config = mService.mContext.getResources().getFloat(
+84 −0
Original line number Diff line number Diff line
@@ -16,10 +16,20 @@

package com.android.server.wm;

import static com.android.server.wm.AbsAppSnapshotController.TAG;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.window.TaskSnapshot;

import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;

import java.io.File;
import java.util.UUID;

class BaseAppSnapshotPersister {
    static final String LOW_RES_FILE_POSTFIX = "_reduced";
@@ -29,6 +39,7 @@ class BaseAppSnapshotPersister {
    // Shared with SnapshotPersistQueue
    protected final Object mLock;
    protected final SnapshotPersistQueue mSnapshotPersistQueue;
    @VisibleForTesting
    protected final PersistInfoProvider mPersistInfoProvider;

    BaseAppSnapshotPersister(SnapshotPersistQueue persistQueue,
@@ -79,6 +90,8 @@ class BaseAppSnapshotPersister {
        private final boolean mEnableLowResSnapshots;
        private final float mLowResScaleFactor;
        private final boolean mUse16BitFormat;
        private final SparseBooleanArray mInitializedUsers = new SparseBooleanArray();
        private final SparseArray<File> mScrambleDirectories = new SparseArray<>();

        PersistInfoProvider(DirectoryResolver directoryResolver, String dirName,
                boolean enableLowResSnapshots, float lowResScaleFactor, boolean use16BitFormat) {
@@ -91,9 +104,80 @@ class BaseAppSnapshotPersister {

        @NonNull
        File getDirectory(int userId) {
            if (Flags.scrambleSnapshotFileName()) {
                final File directory = getOrInitScrambleDirectory(userId);
                if (directory != null) {
                    return directory;
                }
            }
            return getBaseDirectory(userId);
        }

        @NonNull
        private File getBaseDirectory(int userId) {
            return new File(mDirectoryResolver.getSystemDirectoryForUser(userId), mDirName);
        }

        @Nullable
        private File getOrInitScrambleDirectory(int userId) {
            synchronized (mScrambleDirectories) {
                if (mInitializedUsers.get(userId)) {
                    return mScrambleDirectories.get(userId);
                }
                mInitializedUsers.put(userId, true);
                final File scrambledDirectory = getScrambleDirectory(userId);
                final File baseDir = getBaseDirectory(userId);
                String newName = null;
                // If directory exists, rename
                if (scrambledDirectory.exists()) {
                    newName = UUID.randomUUID().toString();
                    final File scrambleTo = new File(baseDir, newName);
                    if (!scrambledDirectory.renameTo(scrambleTo)) {
                        Slog.w(TAG, "SnapshotPersister rename scramble folder fail.");
                        return null;
                    }
                } else {
                    // If directory not exists, mkDir.
                    if (!baseDir.exists() && !baseDir.mkdir()) {
                        Slog.w(TAG, "SnapshotPersister make base folder fail.");
                        return null;
                    }
                    if (!scrambledDirectory.mkdir()) {
                        Slog.e(TAG, "SnapshotPersister make scramble folder fail");
                        return null;
                    }
                    // Move any existing files to this folder.
                    final String[] files = baseDir.list();
                    if (files != null) {
                        for (String file : files) {
                            final File original = new File(baseDir, file);
                            if (original.isDirectory()) {
                                newName = file;
                            } else {
                                File to = new File(scrambledDirectory, file);
                                original.renameTo(to);
                            }
                        }
                    }
                }
                final File newFolder = new File(baseDir, newName);
                mScrambleDirectories.put(userId, newFolder);
                return newFolder;
            }
        }

        @NonNull
        private File getScrambleDirectory(int userId) {
            final File dir = getBaseDirectory(userId);
            final String[] directories = dir.list(
                    (current, name) -> new File(current, name).isDirectory());
            if (directories != null && directories.length > 0) {
                return new File(dir, directories[0]);
            } else {
                return new File(dir, UUID.randomUUID().toString());
            }
        }

        /**
         * Return if task snapshots are stored in 16 bit pixel format.
         *
+13 −1
Original line number Diff line number Diff line
@@ -63,11 +63,23 @@ public class ActivitySnapshotControllerTests extends TaskSnapshotPersisterTestBa
        super(0.8f /* highResScale */, 0.5f /* lowResScale */);
    }

    private class TestActivitySnapshotController extends ActivitySnapshotController {
        TestActivitySnapshotController(WindowManagerService service,
                SnapshotPersistQueue persistQueue) {
            super(service, persistQueue);
        }
        @Override
        BaseAppSnapshotPersister.PersistInfoProvider createPersistInfoProvider(
                WindowManagerService service) {
            return mPersister.mPersistInfoProvider;
        }
    }
    @Override
    @Before
    public void setUp() {
        super.setUp();
        mActivitySnapshotController = new ActivitySnapshotController(mWm, mSnapshotPersistQueue);
        mActivitySnapshotController = new TestActivitySnapshotController(
                mWm, mSnapshotPersistQueue);
        spyOn(mActivitySnapshotController);
        doReturn(false).when(mActivitySnapshotController).shouldDisableSnapshots();
        mActivitySnapshotController.resetTmpFields();
+7 −21
Original line number Diff line number Diff line
@@ -68,11 +68,8 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
    public void testPersistAndLoadSnapshot() {
        mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
        mSnapshotPersistQueue.waitForQueueEmpty();
        final File[] files = new File[]{
                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
                new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
        final File[] nonExistsFiles = new File[]{
                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
        final File[] files = convertFilePath("1.proto", "1.jpg");
        final File[] nonExistsFiles = convertFilePath("1_reduced.proto");
        assertTrueForFiles(files, File::exists, " must exist");
        assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
        final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
@@ -92,14 +89,9 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
        taskIds.add(1);
        mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
        mSnapshotPersistQueue.waitForQueueEmpty();
        final File[] existsFiles = new File[]{
                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
                new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
        final File[] nonExistsFiles = new File[]{
                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
                new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
        final File[] existsFiles = convertFilePath("1.proto", "1.jpg");
        final File[] nonExistsFiles = convertFilePath("1_reduced.proto", "2.proto", "2.jpg",
                "2_reduced.jpg");
        assertTrueForFiles(existsFiles, File::exists, " must exist");
        assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
    }
@@ -112,14 +104,8 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
        mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
        mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
        mSnapshotPersistQueue.waitForQueueEmpty();
        final File[] existsFiles = new File[]{
                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
                new File(FILES_DIR.getPath() + "/snapshots/2.jpg")};
        final File[] nonExistsFiles = new File[]{
                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
        final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "2.proto", "2.jpg");
        final File[] nonExistsFiles = convertFilePath("1_reduced.jpg", "2_reduced.jpg");
        assertTrueForFiles(existsFiles, File::exists, " must exist");
        assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
    }
Loading