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

Commit 07694ed4 authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

Inherited installation support for Incremental.

Bug: 162345970
Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest PackageManagerServiceTest ChecksumsTest
Change-Id: I360f44bc52e05553eacc448faa26f603d9eaae59
Merged-In: I360f44bc52e05553eacc448faa26f603d9eaae59
parent dd56fa93
Loading
Loading
Loading
Loading
+19 −10
Original line number Original line Diff line number Diff line
@@ -38,12 +38,18 @@ interface IIncrementalService {
     * Opens or creates a storage given a target path and data loader params. Returns the storage ID.
     * Opens or creates a storage given a target path and data loader params. Returns the storage ID.
     */
     */
    int openStorage(in @utf8InCpp String path);
    int openStorage(in @utf8InCpp String path);
    int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode,
    int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode);
    int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);

    /**
     * Loops DataLoader through bind/create/start with params.
     */
    boolean startLoading(int storageId,
                         in DataLoaderParamsParcel params,
                         in IDataLoaderStatusListener statusListener,
                         in IDataLoaderStatusListener statusListener,
                         in StorageHealthCheckParams healthCheckParams,
                         in StorageHealthCheckParams healthCheckParams,
                         in IStorageHealthListener healthListener,
                         in IStorageHealthListener healthListener,
                         in PerUidReadTimeouts[] perUidReadTimeouts);
                         in PerUidReadTimeouts[] perUidReadTimeouts);
    int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);


    /**
    /**
     * Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
     * Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
@@ -100,6 +106,14 @@ interface IIncrementalService {
     */
     */
    int isFileFullyLoaded(int storageId, in @utf8InCpp String path);
    int isFileFullyLoaded(int storageId, in @utf8InCpp String path);


    /**
     * Checks if all files in the storage are fully loaded.
     * 0 - fully loaded
     * >0 - certain pages missing
     * <0 - -errcode
     */
    int isFullyLoaded(int storageId);

    /**
    /**
     * Returns overall loading progress of all the files on a storage, progress value between [0,1].
     * Returns overall loading progress of all the files on a storage, progress value between [0,1].
     * Returns a negative value on error.
     * Returns a negative value on error.
@@ -112,11 +126,6 @@ interface IIncrementalService {
    byte[] getMetadataByPath(int storageId, in @utf8InCpp String path);
    byte[] getMetadataByPath(int storageId, in @utf8InCpp String path);
    byte[] getMetadataById(int storageId, in byte[] fileId);
    byte[] getMetadataById(int storageId, in byte[] fileId);


    /**
     * Starts loading data for a storage.
     */
    boolean startLoading(int storageId);

    /**
    /**
     * Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader.
     * Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader.
     */
     */
+58 −33
Original line number Original line Diff line number Diff line
@@ -37,7 +37,6 @@ import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.InstallationFileParcel;
import android.content.pm.InstallationFileParcel;
import android.text.TextUtils;


import java.io.File;
import java.io.File;
import java.io.IOException;
import java.io.IOException;
@@ -53,6 +52,7 @@ public final class IncrementalFileStorages {


    private @NonNull final IncrementalManager mIncrementalManager;
    private @NonNull final IncrementalManager mIncrementalManager;
    private @NonNull final File mStageDir;
    private @NonNull final File mStageDir;
    private @Nullable IncrementalStorage mInheritedStorage;
    private @Nullable IncrementalStorage mDefaultStorage;
    private @Nullable IncrementalStorage mDefaultStorage;


    /**
    /**
@@ -65,6 +65,7 @@ public final class IncrementalFileStorages {
     */
     */
    public static IncrementalFileStorages initialize(Context context,
    public static IncrementalFileStorages initialize(Context context,
            @NonNull File stageDir,
            @NonNull File stageDir,
            @Nullable File inheritedDir,
            @NonNull DataLoaderParams dataLoaderParams,
            @NonNull DataLoaderParams dataLoaderParams,
            @Nullable IDataLoaderStatusListener statusListener,
            @Nullable IDataLoaderStatusListener statusListener,
            @Nullable StorageHealthCheckParams healthCheckParams,
            @Nullable StorageHealthCheckParams healthCheckParams,
@@ -79,9 +80,8 @@ public final class IncrementalFileStorages {
            throw new IOException("Failed to obtain incrementalManager.");
            throw new IOException("Failed to obtain incrementalManager.");
        }
        }


        final IncrementalFileStorages result = new IncrementalFileStorages(stageDir,
        final IncrementalFileStorages result = new IncrementalFileStorages(stageDir, inheritedDir,
                incrementalManager, dataLoaderParams, statusListener, healthCheckParams,
                incrementalManager, dataLoaderParams);
                healthListener, perUidReadTimeouts);
        for (InstallationFileParcel file : addedFiles) {
        for (InstallationFileParcel file : addedFiles) {
            if (file.location == LOCATION_DATA_APP) {
            if (file.location == LOCATION_DATA_APP) {
                try {
                try {
@@ -95,43 +95,46 @@ public final class IncrementalFileStorages {
                throw new IOException("Unknown file location: " + file.location);
                throw new IOException("Unknown file location: " + file.location);
            }
            }
        }
        }

        result.startLoading(dataLoaderParams, statusListener, healthCheckParams, healthListener,
        result.startLoading();
                perUidReadTimeouts);


        return result;
        return result;
    }
    }


    private IncrementalFileStorages(@NonNull File stageDir,
    private IncrementalFileStorages(@NonNull File stageDir,
            @Nullable File inheritedDir,
            @NonNull IncrementalManager incrementalManager,
            @NonNull IncrementalManager incrementalManager,
            @NonNull DataLoaderParams dataLoaderParams,
            @NonNull DataLoaderParams dataLoaderParams) throws IOException {
            @Nullable IDataLoaderStatusListener statusListener,
            @Nullable StorageHealthCheckParams healthCheckParams,
            @Nullable IStorageHealthListener healthListener,
            @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
        try {
        try {
            mStageDir = stageDir;
            mStageDir = stageDir;
            mIncrementalManager = incrementalManager;
            mIncrementalManager = incrementalManager;
            if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
            if (inheritedDir != null && IncrementalManager.isIncrementalPath(
                final String incrementalPath = dataLoaderParams.getArguments();
                    inheritedDir.getAbsolutePath())) {
                if (TextUtils.isEmpty(incrementalPath)) {
                mInheritedStorage = mIncrementalManager.openStorage(
                    throw new IOException("Failed to create storage: incrementalPath is empty");
                        inheritedDir.getAbsolutePath());
                if (mInheritedStorage != null) {
                    if (!mInheritedStorage.isFullyLoaded()) {
                        throw new IOException("Inherited storage has missing pages.");
                    }
                    }
                mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);

                    mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
                            mInheritedStorage, IncrementalManager.CREATE_MODE_CREATE
                                    | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
                    if (mDefaultStorage == null) {
                    if (mDefaultStorage == null) {
                        throw new IOException(
                        throw new IOException(
                            "Couldn't open incremental storage at " + incrementalPath);
                                "Couldn't create linked incremental storage at " + stageDir);
                    }
                    }
                mDefaultStorage.bind(stageDir.getAbsolutePath());
                    return;
            } else {
                }
            }

            mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
            mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
                    dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
                    dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
                                | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false,
                            | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
                        statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
            if (mDefaultStorage == null) {
            if (mDefaultStorage == null) {
                throw new IOException(
                throw new IOException(
                        "Couldn't create incremental storage at " + stageDir);
                        "Couldn't create incremental storage at " + stageDir);
            }
            }
            }
        } catch (IOException e) {
        } catch (IOException e) {
            cleanUp();
            cleanUp();
            throw e;
            throw e;
@@ -149,9 +152,16 @@ public final class IncrementalFileStorages {
    /**
    /**
     * Starts or re-starts loading of data.
     * Starts or re-starts loading of data.
     */
     */
    public void startLoading() throws IOException {
    void startLoading(
        if (!mDefaultStorage.startLoading()) {
            @NonNull DataLoaderParams dataLoaderParams,
            throw new IOException("Failed to start loading data for Incremental installation.");
            @Nullable IDataLoaderStatusListener statusListener,
            @Nullable StorageHealthCheckParams healthCheckParams,
            @Nullable IStorageHealthListener healthListener,
            @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
        if (!mDefaultStorage.startLoading(dataLoaderParams, statusListener, healthCheckParams,
                healthListener, perUidReadTimeouts)) {
            throw new IOException(
                    "Failed to start or restart loading data for Incremental installation.");
        }
        }
    }
    }


@@ -162,6 +172,21 @@ public final class IncrementalFileStorages {
        mDefaultStorage.makeFile(name, content.length, UUID.randomUUID(), null, null, content);
        mDefaultStorage.makeFile(name, content.length, UUID.randomUUID(), null, null, content);
    }
    }


    /**
     * Creates a hardlink from inherited storage to default.
     */
    public boolean makeLink(@NonNull String relativePath, @NonNull String fromBase,
            @NonNull String toBase) throws IOException {
        if (mInheritedStorage == null) {
            return false;
        }
        final File sourcePath = new File(fromBase, relativePath);
        final File destPath = new File(toBase, relativePath);
        mInheritedStorage.makeLink(sourcePath.getAbsolutePath(), mDefaultStorage,
                destPath.getAbsolutePath());
        return true;
    }

    /**
    /**
     * Permanently disables readlogs.
     * Permanently disables readlogs.
     */
     */
+8 −18
Original line number Original line Diff line number Diff line
@@ -22,7 +22,6 @@ import android.annotation.Nullable;
import android.annotation.SystemService;
import android.annotation.SystemService;
import android.content.Context;
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.IPackageLoadingProgressCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.RemoteException;
@@ -95,32 +94,20 @@ public final class IncrementalManager {
     * @param params              IncrementalDataLoaderParams object to configure data loading.
     * @param params              IncrementalDataLoaderParams object to configure data loading.
     * @param createMode          Mode for opening an old Incremental File System mount or creating
     * @param createMode          Mode for opening an old Incremental File System mount or creating
     *                            a new mount.
     *                            a new mount.
     * @param autoStartDataLoader Set true to immediately start data loader after creating storage.
     * @return IncrementalStorage object corresponding to the mounted directory.
     * @return IncrementalStorage object corresponding to the mounted directory.
     */
     */
    @Nullable
    @Nullable
    public IncrementalStorage createStorage(@NonNull String path,
    public IncrementalStorage createStorage(@NonNull String path,
            @NonNull DataLoaderParams params,
            @NonNull DataLoaderParams params,
            @CreateMode int createMode,
            @CreateMode int createMode) {
            boolean autoStartDataLoader,
            @Nullable IDataLoaderStatusListener statusListener,
            @Nullable StorageHealthCheckParams healthCheckParams,
            @Nullable IStorageHealthListener healthListener,
            @NonNull PerUidReadTimeouts[] perUidReadTimeouts) {
        Objects.requireNonNull(path);
        Objects.requireNonNull(path);
        Objects.requireNonNull(params);
        Objects.requireNonNull(params);
        Objects.requireNonNull(perUidReadTimeouts);
        try {
        try {
            final int id = mService.createStorage(path, params.getData(), createMode,
            final int id = mService.createStorage(path, params.getData(), createMode);
                    statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
            if (id < 0) {
            if (id < 0) {
                return null;
                return null;
            }
            }
            final IncrementalStorage storage = new IncrementalStorage(mService, id);
            return new IncrementalStorage(mService, id);
            if (autoStartDataLoader) {
                storage.startLoading();
            }
            return storage;
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            throw e.rethrowFromSystemServer();
        }
        }
@@ -281,15 +268,18 @@ public final class IncrementalManager {
     * Unbinds the target dir and deletes the corresponding storage instance.
     * Unbinds the target dir and deletes the corresponding storage instance.
     * Deletes the package name and associated storage id from maps.
     * Deletes the package name and associated storage id from maps.
     */
     */
    public void onPackageRemoved(@NonNull String codePath) {
    public void onPackageRemoved(@NonNull File codeFile) {
        try {
        try {
            final String codePath = codeFile.getAbsolutePath();
            final IncrementalStorage storage = openStorage(codePath);
            final IncrementalStorage storage = openStorage(codePath);
            if (storage == null) {
            if (storage == null) {
                return;
                return;
            }
            }
            mLoadingProgressCallbacks.cleanUpCallbacks(storage);
            mLoadingProgressCallbacks.cleanUpCallbacks(storage);
            unregisterHealthListener(codePath);
            unregisterHealthListener(codePath);
            mService.deleteStorage(storage.getId());

            // Parent since we bind-mount a folder one level above.
            mService.deleteBindMount(storage.getId(), codeFile.getParent());
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            throw e.rethrowFromSystemServer();
        }
        }
+34 −5
Original line number Original line Diff line number Diff line
@@ -18,11 +18,14 @@ package android.os.incremental;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
import android.os.RemoteException;
import android.os.RemoteException;


import java.io.File;
import java.io.File;
import java.io.IOException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.UUID;
import java.util.UUID;


/**
/**
@@ -323,6 +326,24 @@ public final class IncrementalStorage {
        }
        }
    }
    }



    /**
     * Checks if all files in the storage are fully loaded.
     */
    public boolean isFullyLoaded() throws IOException {
        try {
            final int res = mService.isFullyLoaded(mId);
            if (res < 0) {
                throw new IOException(
                        "isFullyLoaded() failed at querying loading progress, errno " + -res);
            }
            return res == 0;
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
            return false;
        }
    }

    /**
    /**
     * Returns the loading progress of a storage
     * Returns the loading progress of a storage
     *
     *
@@ -376,13 +397,21 @@ public final class IncrementalStorage {
    }
    }


    /**
    /**
     * Informs the data loader service associated with the current storage to start data loader
     * Iinitializes and starts the DataLoader.
     *
     * This makes sure all install-time parameters are applied.
     * @return True if data loader is successfully started.
     * Does not affect persistent DataLoader params.
     * @return True if start request was successfully queued.
     */
     */
    public boolean startLoading() {
    public boolean startLoading(
            @NonNull DataLoaderParams dataLoaderParams,
            @Nullable IDataLoaderStatusListener statusListener,
            @Nullable StorageHealthCheckParams healthCheckParams,
            @Nullable IStorageHealthListener healthListener,
            @NonNull PerUidReadTimeouts[] perUidReadTimeouts) {
        Objects.requireNonNull(perUidReadTimeouts);
        try {
        try {
            return mService.startLoading(mId);
            return mService.startLoading(mId, dataLoaderParams.getData(), statusListener,
                    healthCheckParams, healthListener, perUidReadTimeouts);
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
            e.rethrowFromSystemServer();
            return false;
            return false;
+26 −15
Original line number Original line Diff line number Diff line
@@ -3299,17 +3299,28 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        }
        }
    }
    }


    private void linkFile(String relativePath, String fromBase, String toBase) throws IOException {
        try {
            // Try
            if (mIncrementalFileStorages != null && mIncrementalFileStorages.makeLink(relativePath,
                    fromBase, toBase)) {
                return;
            }
            mPm.mInstaller.linkFile(relativePath, fromBase, toBase);
        } catch (InstallerException | IOException e) {
            throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
                    + fromBase + ", " + toBase + ")", e);
        }
    }

    private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
    private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
            throws IOException {
            throws IOException {
        for (File fromFile : fromFiles) {
        for (File fromFile : fromFiles) {
            final String relativePath = getRelativePath(fromFile, fromDir);
            final String relativePath = getRelativePath(fromFile, fromDir);
            try {
            final String fromBase = fromDir.getAbsolutePath();
                mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
            final String toBase = toDir.getAbsolutePath();
                        toDir.getAbsolutePath());

            } catch (InstallerException e) {
            linkFile(relativePath, fromBase, toBase);
                throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
                        + fromDir + ", " + toDir + ")", e);
            }
        }
        }


        Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
        Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
@@ -3577,12 +3588,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {


        // Retrying commit.
        // Retrying commit.
        if (mIncrementalFileStorages != null) {
        if (mIncrementalFileStorages != null) {
            try {
                mIncrementalFileStorages.startLoading();
            } catch (IOException e) {
                throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
                        e.getCause());
            }
            return false;
            return false;
        }
        }


@@ -3757,9 +3762,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            };
            };


            try {
            try {
                final PackageInfo pkgInfo = mPm.getPackageInfo(this.params.appPackageName, 0,
                        userId);
                final File inheritedDir =
                        (pkgInfo != null && pkgInfo.applicationInfo != null) ? new File(
                                pkgInfo.applicationInfo.getCodePath()).getParentFile() : null;

                mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
                mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
                        params, statusListener, healthCheckParams, healthListener, addedFiles,
                        inheritedDir, params, statusListener, healthCheckParams, healthListener,
                        perUidReadTimeouts);
                        addedFiles, perUidReadTimeouts);
                return false;
                return false;
            } catch (IOException e) {
            } catch (IOException e) {
                throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
                throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
Loading