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

Commit 7bf70c97 authored by Songchun Fan's avatar Songchun Fan Committed by Android (Google) Code Review
Browse files

Merge "make adb install --incremental work"

parents bd3506c8 54c6aed5
Loading
Loading
Loading
Loading
+11 −134
Original line number Diff line number Diff line
@@ -31,17 +31,10 @@ package android.os.incremental;
 * @throws IllegalStateException the session is not an Incremental installation session.
 */

import static dalvik.system.VMRuntime.getInstructionSet;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.DataLoaderParams;
import android.content.pm.InstallationFile;
import android.os.IVold;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArraySet;
import android.util.Slog;

import java.io.File;
@@ -58,26 +51,14 @@ import java.util.Random;
public final class IncrementalFileStorages {
    private static final String TAG = "IncrementalFileStorages";
    private @Nullable IncrementalStorage mDefaultStorage;
    private @Nullable IncrementalStorage mApkStorage;
    private @Nullable IncrementalStorage mObbStorage;
    private @Nullable String mDefaultDir;
    private @Nullable String mObbDir;
    private @NonNull IncrementalManager mIncrementalManager;
    private @Nullable ArraySet<String> mLibDirs;
    private @NonNull String mPackageName;
    private @NonNull File mStageDir;

    /**
     * Set up files and directories used in an installation session.
     * Currently only used by Incremental Installation.
     * For Incremental installation, the expected outcome of this function is:
     * 0) All the files are in defaultStorage
     * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the
     * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage.
     * 2) All lib files are in the sub directories as their names suggest, and in the same parent
     * directory as the APK files. The files are linked from mApkStorage to defaultStorage.
     * 3) OBB files are in another directory that is different from APK files and lib files, bound
     * to mObbStorage. The files are linked from mObbStorage to defaultStorage.
     * Set up files and directories used in an installation session. Only used by Incremental.
     * All the files will be created in defaultStorage.
     * TODO(b/133435829): code clean up
     *
     * @throws IllegalStateException the session is not an Incremental installation session.
     */
@@ -85,7 +66,6 @@ public final class IncrementalFileStorages {
            @NonNull File stageDir,
            @NonNull IncrementalManager incrementalManager,
            @NonNull DataLoaderParams dataLoaderParams) {
        mPackageName = packageName;
        mStageDir = stageDir;
        mIncrementalManager = incrementalManager;
        if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
@@ -114,83 +94,23 @@ public final class IncrementalFileStorages {
        }
        if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
            addApkFile(file);
        } else if (file.getFileType() == InstallationFile.FILE_TYPE_OBB) {
            addObbFile(file);
        } else if (file.getFileType() == InstallationFile.FILE_TYPE_LIB) {
            addLibFile(file);
        } else {
            throw new IOException("Unknown file type: " + file.getFileType());
        }
    }

    private void addApkFile(@NonNull InstallationFile apk) throws IOException {
        // Create a storage for APK files and lib files
        final String stageDirPath = mStageDir.getAbsolutePath();
        if (mApkStorage == null) {
            mApkStorage = mIncrementalManager.createStorage(stageDirPath, mDefaultStorage,
                    IncrementalManager.CREATE_MODE_CREATE
                            | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
            mApkStorage.bind(stageDirPath);
        }

        if (!new File(mDefaultDir, apk.getName()).exists()) {
            mDefaultStorage.makeFile(apk.getName(), apk.getSize(), null,
        mDefaultStorage.bind(stageDirPath);
        String apkName = apk.getName();
        File targetFile = Paths.get(stageDirPath, apkName).toFile();
        if (!targetFile.exists()) {
            mDefaultStorage.makeFile(apkName, apk.getSize(), null,
                    apk.getMetadata(), 0, null, null, null);
        }
        // Assuming APK files are already named properly, e.g., "base.apk"
        mDefaultStorage.makeLink(apk.getName(), mApkStorage, apk.getName());
    }

    private void addLibFile(@NonNull InstallationFile lib) throws IOException {
        // TODO(b/136132412): remove this after we have incfs support for lib file mapping
        if (mApkStorage == null) {
            throw new IOException("Cannot add lib file without adding an apk file first");
        }
        if (mLibDirs == null) {
            mLibDirs = new ArraySet<>();
        }
        String current = "";
        final Path libDirPath = Paths.get(lib.getName()).getParent();
        final int numDirComponents = libDirPath.getNameCount();
        for (int i = 0; i < numDirComponents; i++) {
            String dirName = libDirPath.getName(i).toString();
            try {
                dirName = getInstructionSet(dirName);
            } catch (IllegalArgumentException ignored) {
            }
            current += dirName;
            if (!mLibDirs.contains(current)) {
                mDefaultStorage.makeDirectory(current);
                mApkStorage.makeDirectory(current);
                mLibDirs.add(current);
            }
            current += '/';
        if (targetFile.exists()) {
            Slog.i(TAG, "!!! created: " + targetFile.getAbsolutePath());
        }
        String libFilePath = current + Paths.get(lib.getName()).getFileName();
        mDefaultStorage.makeFile(libFilePath, lib.getSize(), null, lib.getMetadata(), 0, null, null,
                                 null);
        mDefaultStorage.makeLink(libFilePath, mApkStorage, libFilePath);
    }

    private void addObbFile(@NonNull InstallationFile obb) throws IOException {
        if (mObbStorage == null) {
            // Create a storage for OBB files
            mObbDir = getTempDir();
            if (mObbDir == null) {
                throw new IOException("Failed to create obb storage directory.");
            }
            mObbStorage = mIncrementalManager.createStorage(
                    mObbDir, mDefaultStorage,
                    IncrementalManager.CREATE_MODE_CREATE
                            | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
        }
        mDefaultStorage.makeFile(obb.getName(), obb.getSize(), null, obb.getMetadata(), 0, null,
                                 null, null);
        mDefaultStorage.makeLink(obb.getName(), mObbStorage, obb.getName());
    }

    private boolean hasObb() {
        return (mObbStorage != null && mObbDir != null);
    }

    /**
@@ -208,35 +128,6 @@ public final class IncrementalFileStorages {
     * Sets up obb storage directory and create bindings.
     */
    public void finishSetUp() {
        if (!hasObb()) {
            return;
        }
        final String obbDir = "/storage/emulated/0/Android/obb";
        final String packageObbDir = String.format("%s/%s", obbDir, mPackageName);
        final String packageObbDirRoot =
                String.format("/mnt/runtime/%s/emulated/0/Android/obb/", mPackageName);
        final String[] obbDirs = {
                packageObbDirRoot + "read",
                packageObbDirRoot + "write",
                packageObbDirRoot + "full",
                packageObbDirRoot + "default",
                String.format("/data/media/0/Android/obb/%s", mPackageName),
                packageObbDir,
        };
        try {
            Slog.i(TAG, "Creating obb directory '" + packageObbDir + "'");
            final IVold vold = IVold.Stub.asInterface(ServiceManager.getServiceOrThrow("vold"));
            vold.setupAppDir(packageObbDir, obbDir, Process.ROOT_UID);
            for (String d : obbDirs) {
                mObbStorage.bindPermanent(d);
            }
        } catch (ServiceManager.ServiceNotFoundException ex) {
            Slog.e(TAG, "vold service is not found.");
            cleanUp();
        } catch (IOException | RemoteException ex) {
            Slog.e(TAG, "Failed to create obb dir at: " + packageObbDir, ex);
            cleanUp();
        }
    }

    /**
@@ -247,26 +138,12 @@ public final class IncrementalFileStorages {
        if (mDefaultStorage != null && mDefaultDir != null) {
            try {
                mDefaultStorage.unBind(mDefaultDir);
                mDefaultStorage.unBind(mStageDir.getAbsolutePath());
            } catch (IOException ignored) {
            }
            mDefaultDir = null;
            mDefaultStorage = null;
        }
        if (mApkStorage != null && mStageDir != null) {
            try {
                mApkStorage.unBind(mStageDir.getAbsolutePath());
            } catch (IOException ignored) {
            }
            mApkStorage = null;
        }
        if (mObbStorage != null && mObbDir != null) {
            try {
                mObbStorage.unBind(mObbDir);
            } catch (IOException ignored) {
            }
            mObbDir = null;
            mObbStorage = null;
        }
    }

    private String getTempDir() {
+9 −4
Original line number Diff line number Diff line
@@ -174,9 +174,12 @@ public final class IncrementalStorage {
            @Nullable byte[] metadata, int hashAlgorithm, @Nullable byte[] rootHash,
            @Nullable byte[] additionalData, @Nullable byte[] signature) throws IOException {
        try {
            if (id == null && metadata == null) {
                throw new IOException("File ID and metadata cannot both be null");
            }
            final IncrementalNewFileParams params = new IncrementalNewFileParams();
            params.size = size;
            params.metadata = metadata;
            params.metadata = (metadata == null ? new byte[0] : metadata);
            params.fileId = idToBytes(id);
            if (hashAlgorithm != 0 || signature != null) {
                params.signature = new IncrementalSignature();
@@ -354,9 +357,10 @@ public final class IncrementalStorage {
     * @param id The id to convert
     * @return Byte array that contains the same ID.
     */
    public static byte[] idToBytes(UUID id) {
    @NonNull
    public static byte[] idToBytes(@Nullable UUID id) {
        if (id == null) {
            return null;
            return new byte[0];
        }
        final ByteBuffer buf = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
        buf.putLong(id.getMostSignificantBits());
@@ -370,7 +374,8 @@ public final class IncrementalStorage {
     * @param bytes The id in byte array format, 16 bytes long
     * @return UUID constructed from the byte array.
     */
    public static UUID bytesToId(byte[] bytes) {
    @NonNull
    public static UUID bytesToId(byte[] bytes) throws IllegalArgumentException {
        if (bytes.length != UUID_BYTE_SIZE) {
            throw new IllegalArgumentException("Expected array of size " + UUID_BYTE_SIZE
                                               + ", got " + bytes.length);
+4 −5
Original line number Diff line number Diff line
@@ -111,8 +111,7 @@ binder::Status BinderIncrementalService::openStorage(const std::string& path,
binder::Status BinderIncrementalService::createStorage(const std::string& path,
                                                       const DataLoaderParamsParcel& params,
                                                       int32_t createMode, int32_t* _aidl_return) {
    *_aidl_return =
            mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params),
    *_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params),
                                        android::incremental::IncrementalService::CreateOptions(
                                                createMode));
    return ok();
@@ -195,7 +194,7 @@ binder::Status BinderIncrementalService::makeFile(
        return ok();
    }

    *_aidl_return = mImpl.makeFile(storageId, path, 0555, fileId, nfp);
    *_aidl_return = mImpl.makeFile(storageId, path, 0777, fileId, nfp);
    return ok();
}
binder::Status BinderIncrementalService::makeFileFromRange(int32_t storageId,
+20 −3
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@
#include <binder/BinderService.h>
#include <binder/ParcelFileDescriptor.h>
#include <binder/Status.h>

#include <openssl/sha.h>
#include <sys/stat.h>
#include <uuid/uuid.h>
@@ -612,13 +611,18 @@ int IncrementalService::bind(StorageId storage, std::string_view source, std::st
    if (!ifs) {
        return -EINVAL;
    }
    auto normSource = path::normalize(source);

    std::unique_lock l(ifs->lock);
    const auto storageInfo = ifs->storages.find(storage);
    if (storageInfo == ifs->storages.end()) {
        return -EINVAL;
    }
    std::string normSource;
    if (path::isAbsolute(source)) {
        normSource = path::normalize(source);
    } else {
        normSource = path::normalize(path::join(storageInfo->second.name, source));
    }
    if (!path::startsWith(normSource, storageInfo->second.name)) {
        return -EINVAL;
    }
@@ -673,7 +677,20 @@ int IncrementalService::unbind(StorageId storage, std::string_view target) {
int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
                                 incfs::NewFileParams params) {
    if (auto ifs = getIfs(storage)) {
        auto err = mIncFs->makeFile(ifs->control, path, mode, id, params);
        const auto storageInfo = ifs->storages.find(storage);
        if (storageInfo == ifs->storages.end()) {
            return -EINVAL;
        }
        std::string normPath;
        if (path::isAbsolute(path)) {
            normPath = path::normalize(path);
        } else {
            normPath = path::normalize(path::join(storageInfo->second.name, path));
        }
        if (!path::startsWith(normPath, storageInfo->second.name)) {
            return -EINVAL;
        }
        auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
        if (err) {
            return err;
        }