Inherited installation support for Incremental.

Bug: 162345970
Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest PackageManagerServiceTest ChecksumsTest
Change-Id: I360f44bc52e05553eacc448faa26f603d9eaae59
Merged-In: I360f44bc52e05553eacc448faa26f603d9eaae59
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 7db5a80..3fbc284 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -38,14 +38,20 @@
      * Opens or creates a storage given a target path and data loader params. Returns the storage ID.
      */
     int openStorage(in @utf8InCpp String path);
-    int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode,
-                      in IDataLoaderStatusListener statusListener,
-                      in StorageHealthCheckParams healthCheckParams,
-                      in IStorageHealthListener healthListener,
-                      in PerUidReadTimeouts[] perUidReadTimeouts);
+    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 StorageHealthCheckParams healthCheckParams,
+                         in IStorageHealthListener healthListener,
+                         in PerUidReadTimeouts[] perUidReadTimeouts);
+
+    /**
      * Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
      */
     const int BIND_TEMPORARY = 0;
@@ -101,6 +107,14 @@
     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 a negative value on error.
      */
@@ -113,11 +127,6 @@
     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.
      */
     void deleteStorage(int storageId);
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 59292baa..f2fe719 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -37,7 +37,6 @@
 import android.content.pm.DataLoaderParams;
 import android.content.pm.IDataLoaderStatusListener;
 import android.content.pm.InstallationFileParcel;
-import android.text.TextUtils;
 
 import java.io.File;
 import java.io.IOException;
@@ -53,6 +52,7 @@
 
     private @NonNull final IncrementalManager mIncrementalManager;
     private @NonNull final File mStageDir;
+    private @Nullable IncrementalStorage mInheritedStorage;
     private @Nullable IncrementalStorage mDefaultStorage;
 
     /**
@@ -65,6 +65,7 @@
      */
     public static IncrementalFileStorages initialize(Context context,
             @NonNull File stageDir,
+            @Nullable File inheritedDir,
             @NonNull DataLoaderParams dataLoaderParams,
             @Nullable IDataLoaderStatusListener statusListener,
             @Nullable StorageHealthCheckParams healthCheckParams,
@@ -79,9 +80,8 @@
             throw new IOException("Failed to obtain incrementalManager.");
         }
 
-        final IncrementalFileStorages result = new IncrementalFileStorages(stageDir,
-                incrementalManager, dataLoaderParams, statusListener, healthCheckParams,
-                healthListener, perUidReadTimeouts);
+        final IncrementalFileStorages result = new IncrementalFileStorages(stageDir, inheritedDir,
+                incrementalManager, dataLoaderParams);
         for (InstallationFileParcel file : addedFiles) {
             if (file.location == LOCATION_DATA_APP) {
                 try {
@@ -95,42 +95,45 @@
                 throw new IOException("Unknown file location: " + file.location);
             }
         }
-
-        result.startLoading();
+        result.startLoading(dataLoaderParams, statusListener, healthCheckParams, healthListener,
+                perUidReadTimeouts);
 
         return result;
     }
 
     private IncrementalFileStorages(@NonNull File stageDir,
+            @Nullable File inheritedDir,
             @NonNull IncrementalManager incrementalManager,
-            @NonNull DataLoaderParams dataLoaderParams,
-            @Nullable IDataLoaderStatusListener statusListener,
-            @Nullable StorageHealthCheckParams healthCheckParams,
-            @Nullable IStorageHealthListener healthListener,
-            @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
+            @NonNull DataLoaderParams dataLoaderParams) throws IOException {
         try {
             mStageDir = stageDir;
             mIncrementalManager = incrementalManager;
-            if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
-                final String incrementalPath = dataLoaderParams.getArguments();
-                if (TextUtils.isEmpty(incrementalPath)) {
-                    throw new IOException("Failed to create storage: incrementalPath is empty");
+            if (inheritedDir != null && IncrementalManager.isIncrementalPath(
+                    inheritedDir.getAbsolutePath())) {
+                mInheritedStorage = mIncrementalManager.openStorage(
+                        inheritedDir.getAbsolutePath());
+                if (mInheritedStorage != null) {
+                    if (!mInheritedStorage.isFullyLoaded()) {
+                        throw new IOException("Inherited storage has missing pages.");
+                    }
+
+                    mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
+                            mInheritedStorage, IncrementalManager.CREATE_MODE_CREATE
+                                    | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+                    if (mDefaultStorage == null) {
+                        throw new IOException(
+                                "Couldn't create linked incremental storage at " + stageDir);
+                    }
+                    return;
                 }
-                mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
-                if (mDefaultStorage == null) {
-                    throw new IOException(
-                            "Couldn't open incremental storage at " + incrementalPath);
-                }
-                mDefaultStorage.bind(stageDir.getAbsolutePath());
-            } else {
-                mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
-                        dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
-                                | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false,
-                        statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
-                if (mDefaultStorage == null) {
-                    throw new IOException(
-                            "Couldn't create incremental storage at " + stageDir);
-                }
+            }
+
+            mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
+                    dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
+                            | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+            if (mDefaultStorage == null) {
+                throw new IOException(
+                        "Couldn't create incremental storage at " + stageDir);
             }
         } catch (IOException e) {
             cleanUp();
@@ -149,9 +152,16 @@
     /**
      * Starts or re-starts loading of data.
      */
-    public void startLoading() throws IOException {
-        if (!mDefaultStorage.startLoading()) {
-            throw new IOException("Failed to start loading data for Incremental installation.");
+    void startLoading(
+            @NonNull DataLoaderParams dataLoaderParams,
+            @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.");
         }
     }
 
@@ -163,6 +173,21 @@
     }
 
     /**
+     * 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.
      */
     public void disallowReadLogs() {
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 4b93270..7e7057f 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -22,7 +22,6 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.content.pm.DataLoaderParams;
-import android.content.pm.IDataLoaderStatusListener;
 import android.content.pm.IPackageLoadingProgressCallback;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -95,32 +94,20 @@
      * @param params              IncrementalDataLoaderParams object to configure data loading.
      * @param createMode          Mode for opening an old Incremental File System mount or creating
      *                            a new mount.
-     * @param autoStartDataLoader Set true to immediately start data loader after creating storage.
      * @return IncrementalStorage object corresponding to the mounted directory.
      */
     @Nullable
     public IncrementalStorage createStorage(@NonNull String path,
             @NonNull DataLoaderParams params,
-            @CreateMode int createMode,
-            boolean autoStartDataLoader,
-            @Nullable IDataLoaderStatusListener statusListener,
-            @Nullable StorageHealthCheckParams healthCheckParams,
-            @Nullable IStorageHealthListener healthListener,
-            @NonNull PerUidReadTimeouts[] perUidReadTimeouts) {
+            @CreateMode int createMode) {
         Objects.requireNonNull(path);
         Objects.requireNonNull(params);
-        Objects.requireNonNull(perUidReadTimeouts);
         try {
-            final int id = mService.createStorage(path, params.getData(), createMode,
-                    statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
+            final int id = mService.createStorage(path, params.getData(), createMode);
             if (id < 0) {
                 return null;
             }
-            final IncrementalStorage storage = new IncrementalStorage(mService, id);
-            if (autoStartDataLoader) {
-                storage.startLoading();
-            }
-            return storage;
+            return new IncrementalStorage(mService, id);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -281,15 +268,18 @@
      * Unbinds the target dir and deletes the corresponding storage instance.
      * Deletes the package name and associated storage id from maps.
      */
-    public void onPackageRemoved(@NonNull String codePath) {
+    public void onPackageRemoved(@NonNull File codeFile) {
         try {
+            final String codePath = codeFile.getAbsolutePath();
             final IncrementalStorage storage = openStorage(codePath);
             if (storage == null) {
                 return;
             }
             mLoadingProgressCallbacks.cleanUpCallbacks(storage);
             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) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 5b688bb..e6ce8cd 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -18,11 +18,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.DataLoaderParams;
+import android.content.pm.IDataLoaderStatusListener;
 import android.os.RemoteException;
 
 import java.io.File;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.Objects;
 import java.util.UUID;
 
 /**
@@ -323,6 +326,24 @@
         }
     }
 
+
+    /**
+     * 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
      *
@@ -376,13 +397,21 @@
     }
 
     /**
-     * Informs the data loader service associated with the current storage to start data loader
-     *
-     * @return True if data loader is successfully started.
+     * Iinitializes and starts the DataLoader.
+     * This makes sure all install-time parameters are applied.
+     * 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 {
-            return mService.startLoading(mId);
+            return mService.startLoading(mId, dataLoaderParams.getData(), statusListener,
+                    healthCheckParams, healthListener, perUidReadTimeouts);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return false;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4eaac2e4..7c42569 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3299,17 +3299,28 @@
         }
     }
 
+    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)
             throws IOException {
         for (File fromFile : fromFiles) {
             final String relativePath = getRelativePath(fromFile, fromDir);
-            try {
-                mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
-                        toDir.getAbsolutePath());
-            } catch (InstallerException e) {
-                throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
-                        + fromDir + ", " + toDir + ")", e);
-            }
+            final String fromBase = fromDir.getAbsolutePath();
+            final String toBase = toDir.getAbsolutePath();
+
+            linkFile(relativePath, fromBase, toBase);
         }
 
         Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
@@ -3577,12 +3588,6 @@
 
         // Retrying commit.
         if (mIncrementalFileStorages != null) {
-            try {
-                mIncrementalFileStorages.startLoading();
-            } catch (IOException e) {
-                throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
-                        e.getCause());
-            }
             return false;
         }
 
@@ -3757,9 +3762,15 @@
             };
 
             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,
-                        params, statusListener, healthCheckParams, healthListener, addedFiles,
-                        perUidReadTimeouts);
+                        inheritedDir, params, statusListener, healthCheckParams, healthListener,
+                        addedFiles, perUidReadTimeouts);
                 return false;
             } catch (IOException e) {
                 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d2fc5b4..f772f63 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18163,13 +18163,15 @@
                 return false;
             }
 
-            String codePath = codeFile.getAbsolutePath();
-            if (mIncrementalManager != null && isIncrementalPath(codePath)) {
-                mIncrementalManager.onPackageRemoved(codePath);
-            }
+            final boolean isIncremental = (mIncrementalManager != null && isIncrementalPath(
+                    codeFile.getAbsolutePath()));
 
             removeCodePathLI(codeFile);
 
+            if (isIncremental) {
+                mIncrementalManager.onPackageRemoved(codeFile);
+            }
+
             return true;
         }
 
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index d224428..b2efc71 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -118,18 +118,10 @@
 
 binder::Status BinderIncrementalService::createStorage(
         const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params,
-        int32_t createMode,
-        const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
-        const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
-        const ::android::sp<::android::os::incremental::IStorageHealthListener>& healthListener,
-        const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
-        int32_t* _aidl_return) {
-    *_aidl_return =
-            mImpl.createStorage(path, const_cast<content::pm::DataLoaderParamsParcel&&>(params),
-                                android::incremental::IncrementalService::CreateOptions(createMode),
-                                statusListener,
-                                const_cast<StorageHealthCheckParams&&>(healthCheckParams),
-                                healthListener, perUidReadTimeouts);
+        int32_t createMode, int32_t* _aidl_return) {
+    *_aidl_return = mImpl.createStorage(path, params,
+                                        android::incremental::IncrementalService::CreateOptions(
+                                                createMode));
     return ok();
 }
 
@@ -144,6 +136,21 @@
     return ok();
 }
 
+binder::Status BinderIncrementalService::startLoading(
+        int32_t storageId, const ::android::content::pm::DataLoaderParamsParcel& params,
+        const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
+        const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
+        const ::android::sp<IStorageHealthListener>& healthListener,
+        const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
+        bool* _aidl_return) {
+    *_aidl_return =
+            mImpl.startLoading(storageId, const_cast<content::pm::DataLoaderParamsParcel&&>(params),
+                               statusListener,
+                               const_cast<StorageHealthCheckParams&&>(healthCheckParams),
+                               healthListener, perUidReadTimeouts);
+    return ok();
+}
+
 binder::Status BinderIncrementalService::makeBindMount(int32_t storageId,
                                                        const std::string& sourcePath,
                                                        const std::string& targetFullPath,
@@ -253,9 +260,16 @@
     return ok();
 }
 
+binder::Status BinderIncrementalService::isFullyLoaded(int32_t storageId, int32_t* _aidl_return) {
+    *_aidl_return = mImpl.getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/true)
+                            .blocksRemainingOrError();
+    return ok();
+}
+
 binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId,
                                                             float* _aidl_return) {
-    *_aidl_return = mImpl.getLoadingProgress(storageId).getProgress();
+    *_aidl_return =
+            mImpl.getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false).getProgress();
     return ok();
 }
 
@@ -291,11 +305,6 @@
     return ok();
 }
 
-binder::Status BinderIncrementalService::startLoading(int32_t storageId, bool* _aidl_return) {
-    *_aidl_return = mImpl.startLoading(storageId);
-    return ok();
-}
-
 binder::Status BinderIncrementalService::configureNativeBinaries(
         int32_t storageId, const std::string& apkFullPath, const std::string& libDirRelativePath,
         const std::string& abi, bool extractNativeLibs, bool* _aidl_return) {
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 9a4537a..740c542 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -39,16 +39,18 @@
     void onInvalidStorage(int mountId);
 
     binder::Status openStorage(const std::string& path, int32_t* _aidl_return) final;
-    binder::Status createStorage(
-            const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params,
-            int32_t createMode,
+    binder::Status createStorage(const ::std::string& path,
+                                 const ::android::content::pm::DataLoaderParamsParcel& params,
+                                 int32_t createMode, int32_t* _aidl_return) final;
+    binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId,
+                                       int32_t createMode, int32_t* _aidl_return) final;
+    binder::Status startLoading(
+            int32_t storageId, const ::android::content::pm::DataLoaderParamsParcel& params,
             const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
             const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
             const ::android::sp<IStorageHealthListener>& healthListener,
             const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
-            int32_t* _aidl_return) final;
-    binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId,
-                                       int32_t createMode, int32_t* _aidl_return) final;
+            bool* _aidl_return) final;
     binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath,
                                  const std::string& targetFullPath, int32_t bindType,
                                  int32_t* _aidl_return) final;
@@ -71,12 +73,12 @@
     binder::Status unlink(int32_t storageId, const std::string& path, int32_t* _aidl_return) final;
     binder::Status isFileFullyLoaded(int32_t storageId, const std::string& path,
                                      int32_t* _aidl_return) final;
+    binder::Status isFullyLoaded(int32_t storageId, int32_t* _aidl_return) final;
     binder::Status getLoadingProgress(int32_t storageId, float* _aidl_return) final;
     binder::Status getMetadataByPath(int32_t storageId, const std::string& path,
                                      std::vector<uint8_t>* _aidl_return) final;
     binder::Status getMetadataById(int32_t storageId, const std::vector<uint8_t>& id,
                                    std::vector<uint8_t>* _aidl_return) final;
-    binder::Status startLoading(int32_t storageId, bool* _aidl_return) final;
     binder::Status deleteStorage(int32_t storageId) final;
     binder::Status disallowReadLogs(int32_t storageId) final;
     binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index c9c5489..132f973 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -356,7 +356,9 @@
             dprintf(fd, "    storages (%d): {\n", int(mnt.storages.size()));
             for (auto&& [storageId, storage] : mnt.storages) {
                 dprintf(fd, "      [%d] -> [%s] (%d %% loaded) \n", storageId, storage.name.c_str(),
-                        (int)(getLoadingProgressFromPath(mnt, storage.name.c_str()).getProgress() *
+                        (int)(getLoadingProgressFromPath(mnt, storage.name.c_str(),
+                                                         /*stopOnFirstIncomplete=*/false)
+                                      .getProgress() *
                               100));
             }
             dprintf(fd, "    }\n");
@@ -427,10 +429,8 @@
 }
 
 StorageId IncrementalService::createStorage(
-        std::string_view mountPoint, content::pm::DataLoaderParamsParcel&& dataLoaderParams,
-        CreateOptions options, const DataLoaderStatusListener& statusListener,
-        StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener& healthListener,
-        const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
+        std::string_view mountPoint, const content::pm::DataLoaderParamsParcel& dataLoaderParams,
+        CreateOptions options) {
     LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
     if (!path::isAbsolute(mountPoint)) {
         LOG(ERROR) << "path is not absolute: " << mountPoint;
@@ -538,13 +538,10 @@
         metadata::Mount m;
         m.mutable_storage()->set_id(ifs->mountId);
         m.mutable_loader()->set_type((int)dataLoaderParams.type);
-        m.mutable_loader()->set_allocated_package_name(&dataLoaderParams.packageName);
-        m.mutable_loader()->set_allocated_class_name(&dataLoaderParams.className);
-        m.mutable_loader()->set_allocated_arguments(&dataLoaderParams.arguments);
+        m.mutable_loader()->set_package_name(dataLoaderParams.packageName);
+        m.mutable_loader()->set_class_name(dataLoaderParams.className);
+        m.mutable_loader()->set_arguments(dataLoaderParams.arguments);
         const auto metadata = m.SerializeAsString();
-        m.mutable_loader()->release_arguments();
-        m.mutable_loader()->release_class_name();
-        m.mutable_loader()->release_package_name();
         if (auto err =
                     mIncFs->makeFile(ifs->control,
                                      path::join(ifs->root, constants().mount,
@@ -568,26 +565,9 @@
     // Done here as well, all data structures are in good state.
     secondCleanupOnFailure.release();
 
-    // DataLoader.
-    auto dataLoaderStub = prepareDataLoader(*ifs, std::move(dataLoaderParams), &statusListener,
-                                            std::move(healthCheckParams), &healthListener);
-    CHECK(dataLoaderStub);
-
     mountIt->second = std::move(ifs);
     l.unlock();
 
-    // Per Uid timeouts.
-    if (!perUidReadTimeouts.empty()) {
-        setUidReadTimeouts(mountId, perUidReadTimeouts);
-    }
-
-    if (mSystemReady.load(std::memory_order_relaxed) && !dataLoaderStub->requestCreate()) {
-        // failed to create data loader
-        LOG(ERROR) << "initializeDataLoader() failed";
-        deleteStorage(dataLoaderStub->id());
-        return kInvalidStorageId;
-    }
-
     LOG(INFO) << "created storage " << mountId;
     return mountId;
 }
@@ -634,6 +614,37 @@
     return storageId;
 }
 
+bool IncrementalService::startLoading(StorageId storage,
+                                      content::pm::DataLoaderParamsParcel&& dataLoaderParams,
+                                      const DataLoaderStatusListener& statusListener,
+                                      StorageHealthCheckParams&& healthCheckParams,
+                                      const StorageHealthListener& healthListener,
+                                      const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
+    // Per Uid timeouts.
+    if (!perUidReadTimeouts.empty()) {
+        setUidReadTimeouts(storage, perUidReadTimeouts);
+    }
+
+    // Re-initialize DataLoader.
+    std::unique_lock l(mLock);
+    const auto ifs = getIfsLocked(storage);
+    if (!ifs) {
+        return false;
+    }
+    if (ifs->dataLoaderStub) {
+        ifs->dataLoaderStub->cleanupResources();
+        ifs->dataLoaderStub = {};
+    }
+    l.unlock();
+
+    // DataLoader.
+    auto dataLoaderStub = prepareDataLoader(*ifs, std::move(dataLoaderParams), &statusListener,
+                                            std::move(healthCheckParams), &healthListener);
+    CHECK(dataLoaderStub);
+
+    return dataLoaderStub->requestStart();
+}
+
 IncrementalService::BindPathMap::const_iterator IncrementalService::findStorageLocked(
         std::string_view path) const {
     return findParentPath(mBindsByPath, path);
@@ -960,7 +971,12 @@
         LOG(ERROR) << "Invalid paths in link(): " << normOldPath << " | " << normNewPath;
         return -EINVAL;
     }
-    return mIncFs->link(ifsSrc->control, normOldPath, normNewPath);
+    if (auto err = mIncFs->link(ifsSrc->control, normOldPath, normNewPath); err < 0) {
+        PLOG(ERROR) << "Failed to link " << oldPath << "[" << normOldPath << "]"
+                    << " to " << newPath << "[" << normNewPath << "]";
+        return err;
+    }
+    return 0;
 }
 
 int IncrementalService::unlink(StorageId storage, std::string_view path) {
@@ -1065,23 +1081,6 @@
     return mIncFs->getMetadata(ifs->control, node);
 }
 
-bool IncrementalService::startLoading(StorageId storage) const {
-    DataLoaderStubPtr dataLoaderStub;
-    {
-        std::unique_lock l(mLock);
-        const auto& ifs = getIfsLocked(storage);
-        if (!ifs) {
-            return false;
-        }
-        dataLoaderStub = ifs->dataLoaderStub;
-        if (!dataLoaderStub) {
-            return false;
-        }
-    }
-    dataLoaderStub->requestStart();
-    return true;
-}
-
 void IncrementalService::setUidReadTimeouts(
         StorageId storage, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
     using microseconds = std::chrono::microseconds;
@@ -1092,11 +1091,15 @@
         maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs));
     }
     if (maxPendingTimeUs < Constants::minPerUidTimeout) {
+        LOG(ERROR) << "Skip setting timeouts: maxPendingTime < Constants::minPerUidTimeout"
+                   << duration_cast<milliseconds>(maxPendingTimeUs).count() << "ms < "
+                   << Constants::minPerUidTimeout.count() << "ms";
         return;
     }
 
     const auto ifs = getIfs(storage);
     if (!ifs) {
+        LOG(ERROR) << "Setting read timeouts failed: invalid storage id: " << storage;
         return;
     }
 
@@ -1126,7 +1129,7 @@
     }
 
     // Still loading?
-    const auto progress = getLoadingProgress(storage);
+    const auto progress = getLoadingProgress(storage, /*stopOnFirstIncomplete=*/true);
     if (progress.isError()) {
         // Something is wrong, abort.
         return clearUidReadTimeouts(storage);
@@ -1840,7 +1843,7 @@
 }
 
 IncrementalService::LoadingProgress IncrementalService::getLoadingProgress(
-        StorageId storage) const {
+        StorageId storage, bool stopOnFirstIncomplete) const {
     std::unique_lock l(mLock);
     const auto ifs = getIfsLocked(storage);
     if (!ifs) {
@@ -1853,11 +1856,11 @@
         return {-EINVAL, -EINVAL};
     }
     l.unlock();
-    return getLoadingProgressFromPath(*ifs, storageInfo->second.name);
+    return getLoadingProgressFromPath(*ifs, storageInfo->second.name, stopOnFirstIncomplete);
 }
 
 IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPath(
-        const IncFsMount& ifs, std::string_view storagePath) const {
+        const IncFsMount& ifs, std::string_view storagePath, bool stopOnFirstIncomplete) const {
     ssize_t totalBlocks = 0, filledBlocks = 0;
     const auto filePaths = mFs->listFilesRecursive(storagePath);
     for (const auto& filePath : filePaths) {
@@ -1870,6 +1873,9 @@
         }
         totalBlocks += totalBlocksCount;
         filledBlocks += filledBlocksCount;
+        if (stopOnFirstIncomplete && filledBlocks < totalBlocks) {
+            break;
+        }
     }
 
     return {filledBlocks, totalBlocks};
@@ -1877,7 +1883,7 @@
 
 bool IncrementalService::updateLoadingProgress(
         StorageId storage, const StorageLoadingProgressListener& progressListener) {
-    const auto progress = getLoadingProgress(storage);
+    const auto progress = getLoadingProgress(storage, /*stopOnFirstIncomplete=*/false);
     if (progress.isError()) {
         // Failed to get progress from incfs, abort.
         return false;
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 3066121..5d53bac 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -113,6 +113,10 @@
         bool started() const { return totalBlocks > 0; }
         bool fullyLoaded() const { return !isError() && (totalBlocks == filledBlocks); }
 
+        int blocksRemainingOrError() const {
+            return totalBlocks <= 0 ? totalBlocks : totalBlocks - filledBlocks;
+        }
+
         float getProgress() const {
             return totalBlocks < 0
                     ? totalBlocks
@@ -130,15 +134,18 @@
     void onSystemReady();
 
     StorageId createStorage(std::string_view mountPoint,
-                            content::pm::DataLoaderParamsParcel&& dataLoaderParams,
-                            CreateOptions options, const DataLoaderStatusListener& statusListener,
-                            StorageHealthCheckParams&& healthCheckParams,
-                            const StorageHealthListener& healthListener,
-                            const std::vector<PerUidReadTimeouts>& perUidReadTimeouts);
+                            const content::pm::DataLoaderParamsParcel& dataLoaderParams,
+                            CreateOptions options);
     StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
                                   CreateOptions options = CreateOptions::Default);
     StorageId openStorage(std::string_view path);
 
+    bool startLoading(StorageId storage, content::pm::DataLoaderParamsParcel&& dataLoaderParams,
+                      const DataLoaderStatusListener& statusListener,
+                      StorageHealthCheckParams&& healthCheckParams,
+                      const StorageHealthListener& healthListener,
+                      const std::vector<PerUidReadTimeouts>& perUidReadTimeouts);
+
     int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind);
     int unbind(StorageId storage, std::string_view target);
     void deleteStorage(StorageId storage);
@@ -156,7 +163,9 @@
     int unlink(StorageId storage, std::string_view path);
 
     int isFileFullyLoaded(StorageId storage, std::string_view filePath) const;
-    LoadingProgress getLoadingProgress(StorageId storage) const;
+
+    LoadingProgress getLoadingProgress(StorageId storage, bool stopOnFirstIncomplete) const;
+
     bool registerLoadingProgressListener(StorageId storage,
                                          const StorageLoadingProgressListener& progressListener);
     bool unregisterLoadingProgressListener(StorageId storage);
@@ -167,8 +176,6 @@
     RawMetadata getMetadata(StorageId storage, std::string_view path) const;
     RawMetadata getMetadata(StorageId storage, FileId node) const;
 
-    bool startLoading(StorageId storage) const;
-
     bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
                                  std::string_view libDirRelativePath, std::string_view abi,
                                  bool extractNativeLibs);
@@ -388,7 +395,8 @@
     binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
 
     int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
-    LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
+    LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path,
+                                               bool stopOnFirstIncomplete) const;
 
     int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
                        std::string_view debugFilePath, std::span<const uint8_t> data) const;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index dfa6083..3573177 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -220,6 +220,11 @@
             timeout.minPendingTimeUs = perUidTimeout.minPendingTimeUs;
             timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs;
         }
+
+        LOG(ERROR) << "Set read timeouts: " << timeouts.size() << " ["
+                   << (timeouts.empty() ? -1 : timeouts.front().uid) << "@"
+                   << (timeouts.empty() ? -1 : timeouts.front().minTimeUs / 1000) << "ms]";
+
         return incfs::setUidReadTimeouts(control, timeouts);
     }
 };
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index f0deba7..8713f9d 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -678,9 +678,9 @@
     mVold->mountIncFsFails();
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
 
@@ -689,9 +689,9 @@
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
 
@@ -702,9 +702,9 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
     EXPECT_CALL(*mVold, unmountIncFs(_));
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
 
@@ -716,9 +716,9 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
     EXPECT_CALL(*mVold, unmountIncFs(_));
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
 
@@ -734,24 +734,24 @@
     EXPECT_CALL(*mDataLoader, destroy(_)).Times(0);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
-    ASSERT_LT(storageId, 0);
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
+    ASSERT_GE(storageId, 0);
+    mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
 }
 
 TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
-    EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
-    EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
-    EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+    EXPECT_CALL(*mDataLoader, start(_)).Times(1);
     EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
     mIncrementalService->deleteStorage(storageId);
 }
 
@@ -759,14 +759,15 @@
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
-    EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+    EXPECT_CALL(*mDataLoader, start(_)).Times(2);
     EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
     // Simulated crash/other connection breakage.
     mDataLoaderManager->setDataLoaderStatusDestroyed();
 }
@@ -780,12 +781,13 @@
     EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     mDataLoaderManager->setDataLoaderStatusCreated();
-    ASSERT_TRUE(mIncrementalService->startLoading(storageId));
     mDataLoaderManager->setDataLoaderStatusStarted();
 }
 
@@ -793,16 +795,17 @@
     mDataLoader->initializeCreateOkNoStatus();
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
-    EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
+    EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
     EXPECT_CALL(*mDataLoader, start(_)).Times(1);
     EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
-    ASSERT_TRUE(mIncrementalService->startLoading(storageId));
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     mDataLoaderManager->setDataLoaderStatusCreated();
 }
 
@@ -815,10 +818,12 @@
     EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     mDataLoaderManager->setDataLoaderStatusUnavailable();
 }
 
@@ -836,10 +841,12 @@
     EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(1);
     EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(1);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     mDataLoaderManager->setDataLoaderStatusUnavailable();
     ASSERT_NE(nullptr, mLooper->mCallback);
     ASSERT_NE(nullptr, mLooper->mCallbackData);
@@ -890,10 +897,12 @@
             kFirstTimestampUs - std::chrono::duration_cast<MCS>(unhealthyTimeout).count();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, std::move(params), listener, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {},
+                                      std::move(params), listener, {});
 
     // Healthy state, registered for pending reads.
     ASSERT_NE(nullptr, mLooper->mCallback);
@@ -985,10 +994,12 @@
     // Not expecting callback removal.
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     ASSERT_GE(mDataLoader->setStorageParams(true), 0);
 }
 
@@ -1006,10 +1017,12 @@
     // Not expecting callback removal.
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     ASSERT_GE(mDataLoader->setStorageParams(true), 0);
     // Now disable.
     mIncrementalService->disallowReadLogs(storageId);
@@ -1032,10 +1045,12 @@
     // After callback is called, disable read logs and remove callback.
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(1);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     ASSERT_GE(mDataLoader->setStorageParams(true), 0);
     ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get());
     mAppOpsManager->mStoredCallback->opChanged(0, {});
@@ -1051,10 +1066,12 @@
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     ASSERT_LT(mDataLoader->setStorageParams(true), 0);
 }
 
@@ -1068,10 +1085,12 @@
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     ASSERT_LT(mDataLoader->setStorageParams(true), 0);
 }
 
@@ -1087,18 +1106,20 @@
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+                                                  {}, {}));
     ASSERT_LT(mDataLoader->setStorageParams(true), 0);
 }
 
 TEST_F(IncrementalServiceTest, testMakeDirectory) {
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     std::string dir_path("test");
 
     // Expecting incfs to call makeDir on a path like:
@@ -1115,9 +1136,9 @@
 
 TEST_F(IncrementalServiceTest, testMakeDirectories) {
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     auto first = "first"sv;
     auto second = "second"sv;
     auto third = "third"sv;
@@ -1138,9 +1159,9 @@
     mFs->hasNoFile();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
 }
 
@@ -1149,9 +1170,9 @@
     mFs->hasFiles();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
     ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
 }
@@ -1161,9 +1182,9 @@
     mFs->hasFiles();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
     ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
 }
@@ -1173,9 +1194,9 @@
     mFs->hasFiles();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
     ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
 }
@@ -1185,10 +1206,12 @@
     mFs->hasNoFile();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
-    ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress());
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
+    ASSERT_EQ(1,
+              mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false)
+                      .getProgress());
 }
 
 TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) {
@@ -1196,11 +1219,13 @@
     mFs->hasFiles();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
-    ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId).getProgress());
+    ASSERT_EQ(-1,
+              mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false)
+                      .getProgress());
 }
 
 TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) {
@@ -1208,11 +1233,13 @@
     mFs->hasFiles();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
-    ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress());
+    ASSERT_EQ(1,
+              mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false)
+                      .getProgress());
 }
 
 TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) {
@@ -1220,11 +1247,13 @@
     mFs->hasFiles();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
-    ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId).getProgress());
+    ASSERT_EQ(0.5,
+              mIncrementalService->getLoadingProgress(storageId, /*stopOnFirstIncomplete=*/false)
+                      .getProgress());
 }
 
 TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) {
@@ -1232,9 +1261,9 @@
     mFs->hasFiles();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     sp<NiceMock<MockStorageLoadingProgressListener>> listener{
             new NiceMock<MockStorageLoadingProgressListener>};
     NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
@@ -1257,9 +1286,9 @@
     mFs->hasFiles();
 
     TemporaryDir tempDir;
-    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                                       IncrementalService::CreateOptions::CreateNew,
-                                                       {}, {}, {}, {});
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     sp<NiceMock<MockStorageLoadingProgressListener>> listener{
             new NiceMock<MockStorageLoadingProgressListener>};
     NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
@@ -1275,10 +1304,12 @@
 
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                               IncrementalService::CreateOptions::CreateNew, {},
-                                               StorageHealthCheckParams{}, listener, {});
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, listener,
+                                      {});
+
     StorageHealthCheckParams newParams;
     newParams.blockedTimeoutMs = 10000;
     newParams.unhealthyTimeoutMs = 20000;
@@ -1378,19 +1409,19 @@
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
-    EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+    EXPECT_CALL(*mDataLoader, start(_)).Times(1);
     EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
     EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0);
     EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                               IncrementalService::CreateOptions::CreateNew, {}, {},
-                                               {},
-                                               createPerUidTimeouts(
-                                                       {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}}));
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {},
+                                      createPerUidTimeouts(
+                                              {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}}));
 }
 
 TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) {
@@ -1410,13 +1441,12 @@
 
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
-                                               IncrementalService::CreateOptions::CreateNew, {}, {},
-                                               {},
-                                               createPerUidTimeouts({{0, 1, 2, 3},
-                                                                     {1, 2, 3, 4},
-                                                                     {2, 3, 4, 100000000}}));
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
+    mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {},
+                                      createPerUidTimeouts(
+                                              {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 100000000}}));
 
     {
         // Timed callback present -> 0 progress.