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

Commit 030536c8 authored by Alex Buynytskyy's avatar Alex Buynytskyy Committed by Android (Google) Code Review
Browse files

Merge "Set incremental apk directories"

parents 59c6c384 476bcf76
Loading
Loading
Loading
Loading
+39 −103
Original line number Diff line number Diff line
@@ -23,8 +23,6 @@ import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.os.RemoteException;
import android.system.ErrnoException;
import android.system.Os;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
@@ -193,116 +191,54 @@ public final class IncrementalManager {
    }

    /**
     * Renames an Incremental path to a new path. If source path is a file, make a link from the old
     * Incremental file to the new one. If source path is a dir, unbind old dir from Incremental
     * Storage and bind the new one.
     * <ol>
     *     <li> For renaming a dir, dest dir will be created if not exists, and does not need to
     *          be on the same Incremental storage as the source. </li>
     *     <li> For renaming a file, dest file must be on the same Incremental storage as source.
     *     </li>
     * </ol>
     * Set up an app's code path. The expected outcome of this method is:
     * 1) The actual apk directory under /data/incremental is bind-mounted to the parent directory
     * of {@code afterCodeFile}.
     * 2) All the files under {@code beforeCodeFile} will show up under {@code afterCodeFile}.
     *
     * @param sourcePath Absolute path to the source. Should be the same type as the destPath (file
     *                   or dir). Expected to already exist and is an Incremental path.
     * @param destPath   Absolute path to the destination.
     * @throws IllegalArgumentException when 1) source does not exist, or 2) source and dest type
     *                                  mismatch (one is file and the other is dir), or 3) source
     *                                  path is not on Incremental File System,
     * @throws IOException              when 1) cannot find the root path of the Incremental storage
     *                                  of source, or 2) cannot retrieve the Incremental storage
     *                                  instance of the source, or 3) renaming a file, but dest is
     *                                  not on the same Incremental Storage, or 4) renaming a dir,
     *                                  dest dir does not exist but fails to be created.
     *                                  <p>
     *                                  TODO(b/136132412): add unit tests
     * @param beforeCodeFile Path that is currently bind-mounted and have APKs under it.
     *                       Should no longer have any APKs after this method is called.
     *                       Example: /data/app/vmdl*tmp
     * @param afterCodeFile Path that should will have APKs after this method is called. Its parent
     *                      directory should be bind-mounted to a directory under /data/incremental.
     *                      Example: /data/app/~~[randomStringA]/[packageName]-[randomStringB]
     * @throws IllegalArgumentException
     * @throws IOException
     * TODO(b/147371381): add unit tests
     */
    public void rename(@NonNull String sourcePath, @NonNull String destPath) throws IOException {
        final File source = new File(sourcePath);
        final File dest = new File(destPath);
        if (!source.exists()) {
            throw new IllegalArgumentException("Path not exist: " + sourcePath);
        }
        if (dest.exists()) {
            throw new IllegalArgumentException("Target path already exists: " + destPath);
        }
        if (source.isDirectory() && dest.exists() && dest.isFile()) {
            throw new IllegalArgumentException(
                    "Trying to rename a dir but destination is a file: " + destPath);
        }
        if (source.isFile() && dest.exists() && dest.isDirectory()) {
            throw new IllegalArgumentException(
                    "Trying to rename a file but destination is a dir: " + destPath);
        }
        if (!isIncrementalPath(sourcePath)) {
            throw new IllegalArgumentException("Not an Incremental path: " + sourcePath);
        }

        Path storagePath = Paths.get(sourcePath);
        if (source.isFile()) {
            storagePath = getStoragePathForFile(source);
        }
        if (storagePath == null || storagePath.toAbsolutePath() == null) {
            throw new IOException("Invalid source storage path for: " + sourcePath);
        }
        final IncrementalStorage storage = openStorage(storagePath.toAbsolutePath().toString());
        if (storage == null) {
    public void renameCodePath(File beforeCodeFile, File afterCodeFile)
            throws IllegalArgumentException, IOException {
        final String beforeCodePath = beforeCodeFile.getAbsolutePath();
        final String afterCodePathParent = afterCodeFile.getParentFile().getAbsolutePath();
        if (!isIncrementalPath(beforeCodePath)) {
            throw new IllegalArgumentException("Not an Incremental path: " + beforeCodePath);
        }
        final String afterCodePathName = afterCodeFile.getName();
        final Path apkStoragePath = Paths.get(beforeCodePath);
        if (apkStoragePath == null || apkStoragePath.toAbsolutePath() == null) {
            throw new IOException("Invalid source storage path for: " + beforeCodePath);
        }
        final IncrementalStorage apkStorage =
                openStorage(apkStoragePath.toAbsolutePath().toString());
        if (apkStorage == null) {
            throw new IOException("Failed to retrieve storage from Incremental Service.");
        }

        if (source.isFile()) {
            renameFile(storage, storagePath, source, dest);
        } else {
            renameDir(storage, storagePath, source, dest);
        }
    }

    private void renameFile(IncrementalStorage storage, Path storagePath,
            File source, File dest) throws IOException {
        Path sourcePath = source.toPath();
        Path destPath = dest.toPath();
        if (!sourcePath.startsWith(storagePath)) {
            throw new IOException("Path: " + source.getAbsolutePath() + " is not on storage at: "
                    + storagePath.toString());
        }
        if (!destPath.startsWith(storagePath)) {
            throw new IOException("Path: " + dest.getAbsolutePath() + " is not on storage at: "
                    + storagePath.toString());
        }
        final Path sourceRelativePath = storagePath.relativize(sourcePath);
        final Path destRelativePath = storagePath.relativize(destPath);
        storage.moveFile(sourceRelativePath.toString(), destRelativePath.toString());

    }

    private void renameDir(IncrementalStorage storage, Path storagePath,
            File source, File dest) throws IOException {
        Path destPath = dest.toPath();
        boolean usedMkdir = false;
        try {
            Os.mkdir(dest.getAbsolutePath(), 0755);
            usedMkdir = true;
        } catch (ErrnoException e) {
            // Traditional mkdir fails but maybe we can create it on Incremental File System if
            // the dest path is on the same Incremental storage as the source.
            if (destPath.startsWith(storagePath)) {
                storage.makeDirectories(storagePath.relativize(destPath).toString());
            } else {
                throw new IOException("Failed to create directory: " + dest.getAbsolutePath(), e);
            }
        }
        try {
            storage.moveDir(source.getAbsolutePath(), dest.getAbsolutePath());
        } catch (Exception ex) {
            if (usedMkdir) {
                try {
                    Os.remove(dest.getAbsolutePath());
                } catch (ErrnoException ignored) {
        final IncrementalStorage linkedApkStorage = createStorage(afterCodePathParent, apkStorage,
                IncrementalManager.CREATE_MODE_CREATE
                        | IncrementalManager.CREATE_MODE_PERMANENT_BIND);
        if (linkedApkStorage == null) {
            throw new IOException("Failed to create linked storage at dir: " + afterCodePathParent);
        }
        linkedApkStorage.makeDirectory(afterCodePathName);
        File[] files = beforeCodeFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].isFile()) {
                String fileName = files[i].getName();
                apkStorage.makeLink(
                        fileName, linkedApkStorage, afterCodePathName + "/" + fileName);
            }
            throw new IOException(
                    "Failed to move " + source.getAbsolutePath() + " to " + dest.getAbsolutePath());
        }
        apkStorage.unBind(beforeCodePath);
    }

    /**
+1 −3
Original line number Diff line number Diff line
@@ -14999,9 +14999,7 @@ public class PackageManagerService extends IPackageManager.Stub
            try {
                makeDirRecursive(afterCodeFile.getParentFile(), 0775);
                if (onIncremental) {
                    // TODO(b/147371381): fix incremental installation
                    mIncrementalManager.rename(beforeCodeFile.getAbsolutePath(),
                            afterCodeFile.getAbsolutePath());
                    mIncrementalManager.renameCodePath(beforeCodeFile, afterCodeFile);
                } else {
                    Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
                }