Incremental installations in PackageManagerShellCommand.

- onPrepareImage in native,
- allow to check installation type and choose native or managed
dataloaders,
- native data loader for Incremental,
- install-incremental shell command.

Test: atest PackageManagerShellCommandTest
Test: atest IncrementalServiceTest
Bug: b/136132412 b/133435829
Change-Id: I530a8a203fb50132c1869abd0b869036add18699
diff --git a/core/java/android/os/incremental/IIncrementalManager.aidl b/core/java/android/os/incremental/IIncrementalManager.aidl
index b415bc0..be83aae 100644
--- a/core/java/android/os/incremental/IIncrementalManager.aidl
+++ b/core/java/android/os/incremental/IIncrementalManager.aidl
@@ -33,7 +33,4 @@
     boolean startDataLoader(int mountId);
     void showHealthBlockedUI(int mountId);
     void destroyDataLoader(int mountId);
-
-    // fileId is a 16 byte long identifier.
-    void newFileForDataLoader(int mountId, in byte[] fileId, in byte[] metadata);
 }
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 9d98b3b..2dbaea8 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -17,6 +17,7 @@
 package android.os.incremental;
 
 import android.content.pm.DataLoaderParamsParcel;
+import android.content.pm.IDataLoaderStatusListener;
 import android.os.incremental.IncrementalNewFileParams;
 
 /** @hide */
@@ -33,7 +34,7 @@
      * 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);
+    int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, in IDataLoaderStatusListener listener, int createMode);
     int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
 
     /**
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 25fb3e0..3f8c0fe 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -35,6 +35,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.DataLoaderParams;
+import android.content.pm.IDataLoaderStatusListener;
 import android.content.pm.InstallationFile;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -74,6 +75,7 @@
     public static IncrementalFileStorages initialize(Context context,
             @NonNull File stageDir,
             @NonNull DataLoaderParams dataLoaderParams,
+            @Nullable IDataLoaderStatusListener dataLoaderStatusListener,
             List<InstallationFile> addedFiles) throws IOException {
         // TODO(b/136132412): sanity check if session should not be incremental
         IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
@@ -85,7 +87,13 @@
 
         IncrementalFileStorages result = null;
         try {
-            result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams);
+            result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams,
+                    dataLoaderStatusListener);
+
+            if (!addedFiles.isEmpty()) {
+                result.mDefaultStorage.bind(stageDir.getAbsolutePath());
+            }
+
             for (InstallationFile file : addedFiles) {
                 if (file.getLocation() == LOCATION_DATA_APP) {
                     try {
@@ -93,14 +101,15 @@
                     } catch (IOException e) {
                         // TODO(b/146080380): add incremental-specific error code
                         throw new IOException(
-                                "Failed to add and configure Incremental File: " + file.getName(),
-                                e);
+                                "Failed to add file to IncFS: " + file.getName() + ", reason: "
+                                        + e.getMessage(), e.getCause());
                     }
                 } else {
                     throw new IOException("Unknown file location: " + file.getLocation());
                 }
             }
 
+            // TODO(b/146080380): remove 5 secs wait in startLoading
             if (!result.mDefaultStorage.startLoading()) {
                 // TODO(b/146080380): add incremental-specific error code
                 throw new IOException("Failed to start loading data for Incremental installation.");
@@ -117,7 +126,8 @@
 
     private IncrementalFileStorages(@NonNull File stageDir,
             @NonNull IncrementalManager incrementalManager,
-            @NonNull DataLoaderParams dataLoaderParams) throws IOException {
+            @NonNull DataLoaderParams dataLoaderParams,
+            @Nullable IDataLoaderStatusListener dataLoaderStatusListener) throws IOException {
         mStageDir = stageDir;
         mIncrementalManager = incrementalManager;
         if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
@@ -134,6 +144,7 @@
             }
             mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir,
                     dataLoaderParams,
+                    dataLoaderStatusListener,
                     IncrementalManager.CREATE_MODE_CREATE
                             | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
         }
@@ -144,10 +155,8 @@
     }
 
     private void addApkFile(@NonNull InstallationFile apk) throws IOException {
-        final String stageDirPath = mStageDir.getAbsolutePath();
-        mDefaultStorage.bind(stageDirPath);
-        String apkName = apk.getName();
-        File targetFile = Paths.get(stageDirPath, apkName).toFile();
+        final String apkName = apk.getName();
+        final File targetFile = new File(mStageDir, apkName);
         if (!targetFile.exists()) {
             mDefaultStorage.makeFile(apkName, apk.getLengthBytes(), null, apk.getMetadata(),
                     apk.getSignature());
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index d2d8f85..74a4215 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -22,6 +22,7 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.content.pm.DataLoaderParams;
+import android.content.pm.IDataLoaderStatusListener;
 import android.os.RemoteException;
 import android.util.SparseArray;
 
@@ -103,10 +104,11 @@
      */
     @Nullable
     public IncrementalStorage createStorage(@NonNull String path,
-            @NonNull DataLoaderParams params, @CreateMode int createMode,
+            @NonNull DataLoaderParams params, @Nullable IDataLoaderStatusListener listener,
+            @CreateMode int createMode,
             boolean autoStartDataLoader) {
         try {
-            final int id = mService.createStorage(path, params.getData(), createMode);
+            final int id = mService.createStorage(path, params.getData(), listener, createMode);
             if (id < 0) {
                 return null;
             }
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index b9700b2..4190001 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -240,7 +240,7 @@
     private native boolean nativeDestroyDataLoader(int storageId);
 
     private native boolean nativePrepareImage(int storageId,
-            Collection<InstallationFile> addedFiles, Collection<String> removedFiles);
+            List<InstallationFile> addedFiles, List<String> removedFiles);
 
     private static native void nativeWriteData(long nativeInstance, String name, long offsetBytes,
             long lengthBytes, ParcelFileDescriptor incomingFd);
diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp
index b307a73..ed0d381 100644
--- a/core/jni/android_service_DataLoaderService.cpp
+++ b/core/jni/android_service_DataLoaderService.cpp
@@ -75,8 +75,9 @@
         {"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader},
         {"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader},
         {"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader},
-        {"nativePrepareImage", "(ILjava/util/Collection;Ljava/util/Collection;)Z", (void*)nativePrepareImage},
-        {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V", (void*)nativeWriteData},
+        {"nativePrepareImage", "(ILjava/util/List;Ljava/util/List;)Z", (void*)nativePrepareImage},
+        {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V",
+         (void*)nativeWriteData},
 };
 
 }  // namespace
diff --git a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
index 01150b7..6bd5a69 100644
--- a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
+++ b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
@@ -261,6 +261,11 @@
         mReadLogFd.reset();
     }
 
+    // Installation callback
+    bool onPrepareImage(const android::dataloader::DataLoaderInstallationFiles& addedFiles) final {
+        return true;
+    }
+
     // IFS callbacks.
     void onPendingReads(const android::dataloader::PendingReads& pendingReads) final {
         std::lock_guard lock{mMapsMutex};
@@ -520,6 +525,6 @@
 
 int JNI_OnLoad(JavaVM* jvm, void* /* reserved */) {
   android::dataloader::DataLoader::initialize(
-            [](auto) { return std::make_unique<AdbDataLoader>(); });
+            [](auto, auto) { return std::make_unique<AdbDataLoader>(); });
     return JNI_VERSION_1_6;
 }
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
index 64f25dd..a8121cc 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
@@ -142,16 +142,6 @@
         }
     }
 
-    // TODO: remove this
-    @Override
-    public void newFileForDataLoader(int mountId, byte[] fileId, byte[] metadata) {
-        IDataLoader dataLoader = mDataLoaderManager.getDataLoader(mountId);
-        if (dataLoader == null) {
-            Slog.e(TAG, "Failed to retrieve data loader for ID=" + mountId);
-            return;
-        }
-    }
-
     @Override
     public void showHealthBlockedUI(int mountId) {
         // TODO(b/136132412): implement this
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 97defcd..4ed4de7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2450,17 +2450,6 @@
             }
         }
 
-        // TODO(b/136132412): update with new APIs
-        if (isIncrementalInstallation()) {
-            try {
-                mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext,
-                        stageDir, params.dataLoaderParams, addedFiles);
-                return true;
-            } catch (IOException e) {
-                throw new PackageManagerException(e);
-            }
-        }
-
         final DataLoaderManager dataLoaderManager = mContext.getSystemService(
                 DataLoaderManager.class);
         if (dataLoaderManager == null) {
@@ -2468,6 +2457,7 @@
                     "Failed to find data loader manager service");
         }
 
+        final boolean manualStartAndDestroy = !isIncrementalInstallation();
         IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() {
             @Override
             public void onStatusChanged(int dataLoaderId, int status) {
@@ -2487,7 +2477,11 @@
 
                     switch (status) {
                         case IDataLoaderStatusListener.DATA_LOADER_CREATED: {
-                            dataLoader.start();
+                            if (manualStartAndDestroy) {
+                                // IncrementalFileStorages will call start after all files are
+                                // created in IncFS.
+                                dataLoader.start();
+                            }
                             break;
                         }
                         case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
@@ -2502,7 +2496,9 @@
                             } else {
                                 dispatchStreamValidateAndCommit();
                             }
-                            dataLoader.destroy();
+                            if (manualStartAndDestroy) {
+                                dataLoader.destroy();
+                            }
                             break;
                         }
                         case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
@@ -2510,7 +2506,9 @@
                             onSessionVerificationFailure(
                                     new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
                                             "Failed to prepare image."));
-                            dataLoader.destroy();
+                            if (manualStartAndDestroy) {
+                                dataLoader.destroy();
+                            }
                             break;
                         }
                     }
@@ -2524,6 +2522,17 @@
             }
         };
 
+        if (!manualStartAndDestroy) {
+            try {
+                mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext,
+                        stageDir, params.dataLoaderParams, listener, addedFiles);
+                return false;
+            } catch (IOException e) {
+                throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
+                        e.getCause());
+            }
+        }
+
         final FileSystemConnector connector = new FileSystemConnector(addedFiles);
         final FileSystemControlParcel control = new FileSystemControlParcel();
         control.callback = connector;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c69a62d..3909fdf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -181,6 +181,8 @@
                     return runInstall();
                 case "install-streaming":
                     return runStreamingInstall();
+                case "install-incremental":
+                    return runIncrementalInstall();
                 case "install-abandon":
                 case "install-destroy":
                     return runInstallAbandon();
@@ -1168,6 +1170,15 @@
         return doRunInstall(params);
     }
 
+    private int runIncrementalInstall() throws RemoteException {
+        final InstallParams params = makeInstallParams();
+        if (params.sessionParams.dataLoaderParams == null) {
+            params.sessionParams.setDataLoaderParams(
+                    PackageManagerShellCommandDataLoader.getIncrementalDataLoaderParams(this));
+        }
+        return doRunInstall(params);
+    }
+
     private int runInstall() throws RemoteException {
         return doRunInstall(makeInstallParams());
     }
@@ -3001,17 +3012,21 @@
                         return 1;
                     }
 
-                    session.addFile(LOCATION_DATA_APP, name, sizeBytes, STDIN_PATH_BYTES, null);
+                    // Incremental requires unique metadatas, let's add a name to the dash.
+                    session.addFile(LOCATION_DATA_APP, name, sizeBytes,
+                            ("-" + name).getBytes(StandardCharsets.UTF_8), null);
                     continue;
                 }
 
                 // 3. Local file.
                 final String inPath = arg;
 
-                String name = new File(inPath).getName();
+                final File file = new File(inPath);
+                final String name = file.getName();
+                final long size = file.length();
                 byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8);
 
-                session.addFile(LOCATION_DATA_APP, name, -1, metadata, null);
+                session.addFile(LOCATION_DATA_APP, name, size, metadata, null);
             }
             return 0;
         } finally {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 4170be4..8f30e7d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.pm.DataLoaderParams;
 import android.content.pm.InstallationFile;
+import android.content.pm.PackageInstaller;
 import android.os.ParcelFileDescriptor;
 import android.os.ShellCommand;
 import android.service.dataloader.DataLoaderService;
@@ -55,6 +56,8 @@
     private static final String STDIN_PATH = "-";
 
     private static String getDataLoaderParamsArgs(ShellCommand shellCommand) {
+        nativeInitialize();
+
         int commandId;
         synchronized (sShellCommands) {
             // Clean up old references.
@@ -86,6 +89,11 @@
                 getDataLoaderParamsArgs(shellCommand));
     }
 
+    static DataLoaderParams getIncrementalDataLoaderParams(ShellCommand shellCommand) {
+        return DataLoaderParams.forIncremental(new ComponentName(PACKAGE, CLASS),
+                getDataLoaderParamsArgs(shellCommand), null);
+    }
+
     private static int extractShellCommandId(String args) {
         int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX);
         if (sessionIdIdx < 0) {
@@ -106,7 +114,7 @@
         }
     }
 
-    static class DataLoader implements DataLoaderService.DataLoader {
+    private static class DataLoader implements DataLoaderService.DataLoader {
         private DataLoaderParams mParams = null;
         private FileSystemConnector mConnector = null;
 
@@ -121,17 +129,7 @@
         @Override
         public boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles,
                 @NonNull Collection<String> removedFiles) {
-            final int commandId = extractShellCommandId(mParams.getArguments());
-            if (commandId == INVALID_SHELL_COMMAND_ID) {
-                return false;
-            }
-
-            final WeakReference<ShellCommand> shellCommandRef;
-            synchronized (sShellCommands) {
-                shellCommandRef = sShellCommands.get(commandId, null);
-            }
-            final ShellCommand shellCommand =
-                    shellCommandRef != null ? shellCommandRef.get() : null;
+            ShellCommand shellCommand = lookupShellCommand(mParams.getArguments());
             if (shellCommand == null) {
                 Slog.e(TAG, "Missing shell command.");
                 return false;
@@ -139,14 +137,13 @@
             try {
                 for (InstallationFile file : addedFiles) {
                     String filePath = new String(file.getMetadata(), StandardCharsets.UTF_8);
-                    if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
-                        final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(
-                                shellCommand.getInFileDescriptor());
+                    if (TextUtils.isEmpty(filePath) || filePath.startsWith(STDIN_PATH)) {
+                        final ParcelFileDescriptor inFd = getStdInPFD(shellCommand);
                         mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd);
                     } else {
                         ParcelFileDescriptor incomingFd = null;
                         try {
-                            incomingFd = shellCommand.openFileForSystem(filePath, "r");
+                            incomingFd = getLocalFile(shellCommand, filePath);
                             mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
                                     incomingFd);
                         } finally {
@@ -162,9 +159,45 @@
         }
     }
 
+    static ShellCommand lookupShellCommand(String args) {
+        final int commandId = extractShellCommandId(args);
+        if (commandId == INVALID_SHELL_COMMAND_ID) {
+            return null;
+        }
+
+        final WeakReference<ShellCommand> shellCommandRef;
+        synchronized (sShellCommands) {
+            shellCommandRef = sShellCommands.get(commandId, null);
+        }
+        final ShellCommand shellCommand =
+                shellCommandRef != null ? shellCommandRef.get() : null;
+
+        return shellCommand;
+    }
+
+    static ParcelFileDescriptor getStdInPFD(ShellCommand shellCommand) {
+        try {
+            return ParcelFileDescriptor.dup(shellCommand.getInFileDescriptor());
+        } catch (IOException e) {
+            Slog.e(TAG, "Exception while obtaining STDIN fd", e);
+            return null;
+        }
+    }
+
+    static ParcelFileDescriptor getLocalFile(ShellCommand shellCommand, String filePath) {
+        return shellCommand.openFileForSystem(filePath, "r");
+    }
+
     @Override
     public DataLoaderService.DataLoader onCreateDataLoader(
             @NonNull DataLoaderParams dataLoaderParams) {
-        return new DataLoader();
+        if (dataLoaderParams.getType() == PackageInstaller.DATA_LOADER_TYPE_STREAMING) {
+            // This DataLoader only supports streaming installations.
+            return new DataLoader();
+        }
+        return null;
     }
+
+    /* Native methods */
+    private static native void nativeInitialize();
 }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 49c7e0a3..e888f2a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -2,6 +2,7 @@
     name: "libservices.core",
     defaults: ["libservices.core-libs"],
 
+    cpp_std: "c++2a",
     cflags: [
         "-Wall",
         "-Werror",
@@ -55,6 +56,7 @@
         "com_android_server_am_CachedAppOptimizer.cpp",
         "com_android_server_am_LowMemDetector.cpp",
         "com_android_server_incremental_IncrementalManagerService.cpp",
+        "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
         "onload.cpp",
         ":lib_networkStatsFactory_native",
     ],
@@ -125,6 +127,8 @@
         "libnetdbpf",
         "libnetdutils",
         "libpsi",
+        "libdataloader",
+        "libincfs",
         "android.hardware.audio.common@2.0",
         "android.hardware.broadcastradio@1.0",
         "android.hardware.broadcastradio@1.1",
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
new file mode 100644
index 0000000..d803c1d
--- /dev/null
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_ADB
+#define LOG_TAG "PackageManagerShellCommandDataLoader-jni"
+#include <android-base/logging.h>
+
+#include <android-base/unique_fd.h>
+#include <nativehelper/JNIHelp.h>
+
+#include <core_jni_helpers.h>
+
+#include "dataloader.h"
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+
+namespace {
+
+using android::base::unique_fd;
+
+static constexpr int BUFFER_SIZE = 256 * 1024;
+static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE;
+
+struct JniIds {
+    jclass packageManagerShellCommandDataLoader;
+    jmethodID pmscdLookupShellCommand;
+    jmethodID pmscdGetStdInPFD;
+    jmethodID pmscdGetLocalFile;
+
+    jmethodID parcelFileDescriptorGetFileDescriptor;
+
+    jclass ioUtils;
+    jmethodID ioUtilsCloseQuietly;
+
+    JniIds(JNIEnv* env) {
+        packageManagerShellCommandDataLoader = (jclass)env->NewGlobalRef(
+                FindClassOrDie(env, "com/android/server/pm/PackageManagerShellCommandDataLoader"));
+        pmscdLookupShellCommand =
+                GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader,
+                                       "lookupShellCommand",
+                                       "(Ljava/lang/String;)Landroid/os/ShellCommand;");
+        pmscdGetStdInPFD =
+                GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getStdInPFD",
+                                       "(Landroid/os/ShellCommand;)Landroid/os/"
+                                       "ParcelFileDescriptor;");
+        pmscdGetLocalFile =
+                GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getLocalFile",
+                                       "(Landroid/os/ShellCommand;Ljava/lang/String;)Landroid/os/"
+                                       "ParcelFileDescriptor;");
+
+        auto parcelFileDescriptor = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
+        parcelFileDescriptorGetFileDescriptor =
+                GetMethodIDOrDie(env, parcelFileDescriptor, "getFileDescriptor",
+                                 "()Ljava/io/FileDescriptor;");
+
+        ioUtils = (jclass)env->NewGlobalRef(FindClassOrDie(env, "libcore/io/IoUtils"));
+        ioUtilsCloseQuietly = GetStaticMethodIDOrDie(env, ioUtils, "closeQuietly",
+                                                     "(Ljava/lang/AutoCloseable;)V");
+    }
+};
+
+const JniIds& jniIds(JNIEnv* env) {
+    static const JniIds ids(env);
+    return ids;
+}
+
+static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, jobject pfd) {
+    if (!pfd) {
+        ALOGE("Missing In ParcelFileDescriptor.");
+        return {};
+    }
+    auto managedFd = env->CallObjectMethod(pfd, jni.parcelFileDescriptorGetFileDescriptor);
+    if (!pfd) {
+        ALOGE("Missing In FileDescriptor.");
+        return {};
+    }
+    return unique_fd{dup(jniGetFDFromFileDescriptor(env, managedFd))};
+}
+
+static inline std::pair<unique_fd, bool> openIncomingFile(JNIEnv* env, const JniIds& jni,
+                                                          jobject shellCommand,
+                                                          IncFsSpan metadata) {
+    jobject pfd = nullptr;
+    const bool stdin = (metadata.size == 0 || *metadata.data == '-');
+    if (stdin) {
+        // stdin
+        pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
+                                          jni.pmscdGetStdInPFD, shellCommand);
+    } else {
+        // file
+        const std::string filePath(metadata.data, metadata.size);
+        pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
+                                          jni.pmscdGetLocalFile, shellCommand,
+                                          env->NewStringUTF(filePath.c_str()));
+    }
+
+    auto result = convertPfdToFdAndDup(env, jni, pfd);
+    if (pfd) {
+        // Can be closed after dup.
+        env->CallStaticVoidMethod(jni.ioUtils, jni.ioUtilsCloseQuietly, pfd);
+    }
+
+    return {std::move(result), stdin};
+}
+
+static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        return 0;
+    }
+    return env;
+}
+
+static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm) {
+    JNIEnv* env = GetJNIEnvironment(jvm);
+    if (!env) {
+        int result = jvm->AttachCurrentThread(&env, nullptr);
+        CHECK_EQ(result, JNI_OK) << "thread attach failed";
+        struct VmDetacher {
+            VmDetacher(JavaVM* vm) : mVm(vm) {}
+            ~VmDetacher() { mVm->DetachCurrentThread(); }
+
+        private:
+            JavaVM* const mVm;
+        };
+        static thread_local VmDetacher detacher(jvm);
+    }
+    return env;
+}
+
+class PackageManagerShellCommandDataLoaderDataLoader : public android::dataloader::DataLoader {
+public:
+    PackageManagerShellCommandDataLoaderDataLoader(JavaVM* jvm) : mJvm(jvm) { CHECK(mJvm); }
+
+private:
+    // Lifecycle.
+    bool onCreate(const android::dataloader::DataLoaderParams& params,
+                  android::dataloader::FilesystemConnectorPtr ifs,
+                  android::dataloader::StatusListenerPtr statusListener,
+                  android::dataloader::ServiceConnectorPtr,
+                  android::dataloader::ServiceParamsPtr) final {
+        mArgs = params.arguments();
+        mIfs = ifs;
+        return true;
+    }
+    bool onStart() final { return true; }
+    void onStop() final {}
+    void onDestroy() final {}
+
+    // IFS callbacks.
+    void onPendingReads(const dataloader::PendingReads& pendingReads) final {}
+    void onPageReads(const dataloader::PageReads& pageReads) final {}
+
+    // FS callbacks.
+    bool onPrepareImage(const dataloader::DataLoaderInstallationFiles& addedFiles) final {
+        JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
+        const auto& jni = jniIds(env);
+
+        jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
+                                                           jni.pmscdLookupShellCommand,
+                                                           env->NewStringUTF(mArgs.c_str()));
+        if (!shellCommand) {
+            ALOGE("Missing shell command.");
+            return false;
+        }
+
+        std::vector<char> buffer;
+        buffer.reserve(BUFFER_SIZE);
+
+        std::vector<IncFsDataBlock> blocks;
+        blocks.reserve(BLOCKS_COUNT);
+
+        for (auto&& file : addedFiles) {
+            auto [incomingFd, stdin] = openIncomingFile(env, jni, shellCommand, file.metadata);
+            if (incomingFd < 0) {
+                ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. "
+                      "Error %d",
+                      int(file.metadata.size), file.metadata.data, file.name, errno);
+                return false;
+            }
+
+            const auto fileId = IncFs_FileIdFromMetadata(file.metadata);
+
+            const auto incfsFd(mIfs->openWrite(fileId));
+            if (incfsFd < 0) {
+                ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. "
+                      "Error %d",
+                      int(file.metadata.size), file.metadata.data, file.name, errno);
+                return false;
+            }
+
+            IncFsSize size = file.size;
+            IncFsSize remaining = size;
+            IncFsSize totalSize = 0;
+            IncFsBlockIndex blockIdx = 0;
+            while (remaining > 0) {
+                constexpr auto capacity = BUFFER_SIZE;
+                auto size = buffer.size();
+                if (capacity - size < INCFS_DATA_FILE_BLOCK_SIZE) {
+                    if (!flashToIncFs(incfsFd, false, &blocks, &blockIdx, &buffer)) {
+                        return false;
+                    }
+                    continue;
+                }
+
+                auto toRead = std::min<IncFsSize>(remaining, capacity - size);
+                buffer.resize(size + toRead);
+                auto read = ::read(incomingFd, buffer.data() + size, toRead);
+                if (read == 0) {
+                    if (stdin) {
+                        // eof of stdin, waiting...
+                        ALOGE("eof of stdin, waiting...: %d, remaining: %d, block: %d, read: %d",
+                              int(totalSize), int(remaining), int(blockIdx), int(read));
+                        using namespace std::chrono_literals;
+                        std::this_thread::sleep_for(10ms);
+                        continue;
+                    }
+                    break;
+                }
+                if (read < 0) {
+                    ALOGE("Underlying file read error: %.*s: %d", int(file.metadata.size),
+                          file.metadata.data, int(read));
+                    return false;
+                }
+
+                buffer.resize(size + read);
+                remaining -= read;
+                totalSize += read;
+            }
+            if (!buffer.empty() && !flashToIncFs(incfsFd, true, &blocks, &blockIdx, &buffer)) {
+                return false;
+            }
+        }
+
+        ALOGE("All done.");
+        return true;
+    }
+
+    bool flashToIncFs(int incfsFd, bool eof, std::vector<IncFsDataBlock>* blocks,
+                      IncFsBlockIndex* blockIdx, std::vector<char>* buffer) {
+        int consumed = 0;
+        const auto fullBlocks = buffer->size() / INCFS_DATA_FILE_BLOCK_SIZE;
+        for (int i = 0; i < fullBlocks; ++i) {
+            const auto inst = IncFsDataBlock{
+                    .fileFd = incfsFd,
+                    .pageIndex = (*blockIdx)++,
+                    .compression = INCFS_COMPRESSION_KIND_NONE,
+                    .kind = INCFS_BLOCK_KIND_DATA,
+                    .dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
+                    .data = buffer->data() + consumed,
+            };
+            blocks->push_back(inst);
+            consumed += INCFS_DATA_FILE_BLOCK_SIZE;
+        }
+        const auto remain = buffer->size() - fullBlocks * INCFS_DATA_FILE_BLOCK_SIZE;
+        if (remain && eof) {
+            const auto inst = IncFsDataBlock{
+                    .fileFd = incfsFd,
+                    .pageIndex = (*blockIdx)++,
+                    .compression = INCFS_COMPRESSION_KIND_NONE,
+                    .kind = INCFS_BLOCK_KIND_DATA,
+                    .dataSize = static_cast<uint16_t>(remain),
+                    .data = buffer->data() + consumed,
+            };
+            blocks->push_back(inst);
+            consumed += remain;
+        }
+
+        auto res = mIfs->writeBlocks({blocks->data(), blocks->data() + blocks->size()});
+
+        blocks->clear();
+        buffer->erase(buffer->begin(), buffer->begin() + consumed);
+
+        if (res < 0) {
+            ALOGE("Failed to write block to IncFS: %d", int(res));
+            return false;
+        }
+        return true;
+    }
+
+    JavaVM* const mJvm;
+    std::string mArgs;
+    android::dataloader::FilesystemConnectorPtr mIfs;
+};
+
+static void nativeInitialize(JNIEnv* env, jclass klass) {
+    jniIds(env);
+}
+
+static const JNINativeMethod method_table[] = {
+        {"nativeInitialize", "()V", (void*)nativeInitialize},
+};
+
+} // namespace
+
+int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(
+        JNIEnv* env) {
+    android::dataloader::DataLoader::initialize(
+            [](auto jvm, const auto& params) -> android::dataloader::DataLoaderPtr {
+                if (params.type() == DATA_LOADER_TYPE_INCREMENTAL) {
+                    // This DataLoader only supports incremental installations.
+                    return std::make_unique<PackageManagerShellCommandDataLoaderDataLoader>(jvm);
+                }
+                return {};
+            });
+    return jniRegisterNativeMethods(env,
+                                    "com/android/server/pm/PackageManagerShellCommandDataLoader",
+                                    method_table, NELEM(method_table));
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index c186494..e575432 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -59,6 +59,7 @@
 int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
         JNIEnv* env);
 int register_android_server_incremental_IncrementalManagerService(JNIEnv* env);
+int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
 int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
 };
 
@@ -112,6 +113,7 @@
     register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
             env);
     register_android_server_incremental_IncrementalManagerService(env);
+    register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
     register_android_server_stats_pull_StatsPullAtomService(env);
     return JNI_VERSION_1_4;
 }
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index b2c316a..3fcb57a 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -117,10 +117,9 @@
 
 binder::Status BinderIncrementalService::createStorage(const std::string& path,
                                                        const DataLoaderParamsParcel& params,
+                                                       const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener,
                                                        int32_t createMode, int32_t* _aidl_return) {
-    *_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params),
-                                        android::incremental::IncrementalService::CreateOptions(
-                                                createMode));
+    *_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params), listener, android::incremental::IncrementalService::CreateOptions(createMode));
     return ok();
 }
 
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 51d7de3..4075da6 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -38,9 +38,7 @@
     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, int32_t* _aidl_return) final;
+    binder::Status createStorage(const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params, const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener, 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 makeBindMount(int32_t storageId, const std::string& sourcePath,
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 3b51377..ebadf56 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -30,7 +30,6 @@
 #include <binder/BinderService.h>
 #include <binder/ParcelFileDescriptor.h>
 #include <binder/Status.h>
-#include <openssl/sha.h>
 #include <sys/stat.h>
 #include <uuid/uuid.h>
 #include <zlib.h>
@@ -242,20 +241,7 @@
 }
 
 FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
-    incfs::FileId id = {};
-    if (size_t(metadata.size()) <= sizeof(id)) {
-        memcpy(&id, metadata.data(), metadata.size());
-    } else {
-        uint8_t buffer[SHA_DIGEST_LENGTH];
-        static_assert(sizeof(buffer) >= sizeof(id));
-
-        SHA_CTX ctx;
-        SHA1_Init(&ctx);
-        SHA1_Update(&ctx, metadata.data(), metadata.size());
-        SHA1_Final(buffer, &ctx);
-        memcpy(&id, buffer, sizeof(id));
-    }
-    return id;
+    return IncFs_FileIdFromMetadata({(const char*)metadata.data(), metadata.size()});
 }
 
 IncrementalService::~IncrementalService() = default;
@@ -344,7 +330,7 @@
     std::thread([this, mounts = std::move(mounts)]() {
         std::vector<IfsMountPtr> failedLoaderMounts;
         for (auto&& ifs : mounts) {
-            if (prepareDataLoader(*ifs, nullptr)) {
+            if (prepareDataLoader(*ifs)) {
                 LOG(INFO) << "Successfully started data loader for mount " << ifs->mountId;
             } else {
                 LOG(WARNING) << "Failed to start data loader for mount " << ifs->mountId;
@@ -377,6 +363,7 @@
 
 StorageId IncrementalService::createStorage(std::string_view mountPoint,
                                             DataLoaderParamsParcel&& dataLoaderParams,
+                                            const DataLoaderStatusListener& dataLoaderStatusListener,
                                             CreateOptions options) {
     LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
     if (!path::isAbsolute(mountPoint)) {
@@ -509,7 +496,7 @@
     // Done here as well, all data structures are in good state.
     secondCleanupOnFailure.release();
 
-    if (!prepareDataLoader(*ifs, &dataLoaderParams)) {
+    if (!prepareDataLoader(*ifs, &dataLoaderParams, &dataLoaderStatusListener)) {
         LOG(ERROR) << "prepareDataLoader() failed";
         deleteStorageLocked(*ifs, std::move(l));
         return kInvalidStorageId;
@@ -767,7 +754,6 @@
         if (params.metadata.data && params.metadata.size > 0) {
             metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size);
         }
-        mIncrementalManager->newFileForDataLoader(ifs->mountId, id, metadataBytes);
         return 0;
     }
     return -EINVAL;
@@ -1074,7 +1060,8 @@
 }
 
 bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
-                                           DataLoaderParamsParcel* params) {
+                                           DataLoaderParamsParcel* params,
+                                           const DataLoaderStatusListener* externalListener) {
     if (!mSystemReady.load(std::memory_order_relaxed)) {
         std::unique_lock l(ifs.lock);
         if (params) {
@@ -1117,7 +1104,7 @@
     fsControlParcel.incremental->pendingReads.reset(
             base::unique_fd(::dup(ifs.control.pendingReads)));
     fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs)));
-    sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this);
+    sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this, *externalListener);
     bool created = false;
     auto status = mIncrementalManager->prepareDataLoader(ifs.mountId, fsControlParcel, *dlp,
                                                          listener, &created);
@@ -1247,6 +1234,11 @@
 
 binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId,
                                                                                   int newStatus) {
+    if (externalListener) {
+        // Give an external listener a chance to act before we destroy something.
+        externalListener->onStatusChanged(mountId, newStatus);
+    }
+
     std::unique_lock l(incrementalService.mLock);
     const auto& ifs = incrementalService.getIfsLocked(mountId);
     if (!ifs) {
@@ -1288,6 +1280,12 @@
         case IDataLoaderStatusListener::DATA_LOADER_STOPPED: {
             break;
         }
+        case IDataLoaderStatusListener::DATA_LOADER_IMAGE_READY: {
+            break;
+        }
+        case IDataLoaderStatusListener::DATA_LOADER_IMAGE_NOT_READY: {
+            break;
+        }
         default: {
             LOG(WARNING) << "Unknown data loader status: " << newStatus
                          << " for mount: " << mountId;
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 2e7ced3..75d066b 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -59,6 +59,8 @@
 using TimePoint = std::chrono::time_point<Clock>;
 using Seconds = std::chrono::seconds;
 
+using DataLoaderStatusListener = ::android::sp<::android::content::pm::IDataLoaderStatusListener>;
+
 class IncrementalService final {
 public:
     explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir);
@@ -94,7 +96,9 @@
 
     std::optional<std::future<void>> onSystemReady();
 
-    StorageId createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams,
+    StorageId createStorage(std::string_view mountPoint,
+                            DataLoaderParamsParcel&& dataLoaderParams,
+                            const DataLoaderStatusListener& dataLoaderStatusListener,
                             CreateOptions options = CreateOptions::Default);
     StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
                                   CreateOptions options = CreateOptions::Default);
@@ -129,13 +133,14 @@
                                  std::string_view libDirRelativePath, std::string_view abi);
     class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener {
     public:
-        IncrementalDataLoaderListener(IncrementalService& incrementalService)
-              : incrementalService(incrementalService) {}
+        IncrementalDataLoaderListener(IncrementalService& incrementalService, DataLoaderStatusListener externalListener)
+              : incrementalService(incrementalService), externalListener(externalListener) {}
         // Callbacks interface
         binder::Status onStatusChanged(MountId mount, int newStatus) override;
 
     private:
         IncrementalService& incrementalService;
+        DataLoaderStatusListener externalListener;
     };
 
 private:
@@ -201,7 +206,7 @@
                            std::string&& source, std::string&& target, BindKind kind,
                            std::unique_lock<std::mutex>& mainLock);
 
-    bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params);
+    bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr, const DataLoaderStatusListener* externalListener = nullptr);
     BindPathMap::const_iterator findStorageLocked(std::string_view path) const;
     StorageId findStorageId(std::string_view path) const;
 
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index f0b5672..6421583 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -60,8 +60,6 @@
                                              bool* _aidl_return) const = 0;
     virtual binder::Status startDataLoader(MountId mountId, bool* _aidl_return) const = 0;
     virtual binder::Status destroyDataLoader(MountId mountId) const = 0;
-    virtual binder::Status newFileForDataLoader(MountId mountId, FileId fileid,
-                                                const std::vector<uint8_t>& metadata) const = 0;
     virtual binder::Status showHealthBlockedUI(MountId mountId) const = 0;
 };
 
@@ -128,13 +126,6 @@
     binder::Status destroyDataLoader(MountId mountId) const override {
         return mInterface->destroyDataLoader(mountId);
     }
-    binder::Status newFileForDataLoader(MountId mountId, FileId fileid,
-                                        const std::vector<uint8_t>& metadata) const override {
-        return mInterface->newFileForDataLoader(mountId,
-                                                {(const uint8_t*)fileid.data,
-                                                 (const uint8_t*)fileid.data + sizeof(fileid.data)},
-                                                metadata);
-    }
     binder::Status showHealthBlockedUI(MountId mountId) const override {
         return mInterface->showHealthBlockedUI(mountId);
     }
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 9cdc83e..f7598f7 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -293,7 +293,7 @@
     EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -303,7 +303,7 @@
     EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -316,7 +316,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_));
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -330,7 +330,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_));
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -344,7 +344,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_LT(storageId, 0);
 }
@@ -358,7 +358,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
     mIncrementalService->deleteStorage(storageId);
@@ -373,7 +373,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
     mIncrementalManager->setDataLoaderStatusNotReady();
@@ -389,7 +389,7 @@
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
     mIncrementalManager->setDataLoaderStatusReady();
@@ -404,7 +404,7 @@
     mIncrementalManager->startDataLoaderSuccess();
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     std::string dir_path("test");
 
@@ -428,7 +428,7 @@
     mIncrementalManager->startDataLoaderSuccess();
     TemporaryDir tempDir;
     int storageId =
-            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
                                                IncrementalService::CreateOptions::CreateNew);
     auto first = "first"sv;
     auto second = "second"sv;