Installer digests for Incremental installations.

Bug: 160605420
Test: atest ChecksumsTest
Change-Id: I9d46c218cccf87781e9b33711c4d02d94bf824f5
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index c6c80ae..8640dbc 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -64,7 +64,6 @@
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -168,12 +167,11 @@
     }
 
     /**
-     * Serialize checksums to file in binary format.
+     * Serialize checksums to the stream in binary format.
      */
-    public static void writeChecksums(File file, ApkChecksum[] checksums)
+    public static void writeChecksums(OutputStream os, ApkChecksum[] checksums)
             throws IOException, CertificateException {
-        try (OutputStream os = new FileOutputStream(file);
-             DataOutputStream dos = new DataOutputStream(os)) {
+        try (DataOutputStream dos = new DataOutputStream(os)) {
             dos.writeInt(checksums.length);
             for (ApkChecksum checksum : checksums) {
                 final String splitName = checksum.getSplitName();
@@ -190,6 +188,14 @@
                 dos.writeInt(valueBytes.length);
                 dos.write(valueBytes);
 
+                final String packageName = checksum.getSourcePackageName();
+                if (packageName == null) {
+                    dos.writeInt(-1);
+                } else {
+                    dos.writeInt(packageName.length());
+                    dos.writeUTF(packageName);
+                }
+
                 final Certificate cert = checksum.getSourceCertificate();
                 final byte[] certBytes = (cert == null) ? null : cert.getEncoded();
                 if (certBytes == null) {
@@ -218,9 +224,19 @@
                 } else {
                     splitName = dis.readUTF();
                 }
+
                 final int kind = dis.readInt();
+
                 final byte[] valueBytes = new byte[dis.readInt()];
                 dis.read(valueBytes);
+
+                final String packageName;
+                if (dis.readInt() < 0) {
+                    packageName = null;
+                } else {
+                    packageName = dis.readUTF();
+                }
+
                 final byte[] certBytes;
                 final int certBytesLength = dis.readInt();
                 if (certBytesLength < 0) {
@@ -230,7 +246,7 @@
                     dis.read(certBytes);
                 }
                 checksums[i] = new ApkChecksum(splitName, new Checksum(kind, valueBytes),
-                        certBytes);
+                        packageName, certBytes);
             }
             return checksums;
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 48efa5c..2a189c0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -52,6 +52,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyEventLogger;
@@ -153,6 +154,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileFilter;
@@ -240,6 +242,7 @@
     private static final String ATTR_SIGNATURE = "signature";
     private static final String ATTR_CHECKSUM_KIND = "checksumKind";
     private static final String ATTR_CHECKSUM_VALUE = "checksumValue";
+    private static final String ATTR_CHECKSUM_PACKAGE = "checksumPackage";
     private static final String ATTR_CHECKSUM_CERTIFICATE = "checksumCertificate";
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
@@ -394,10 +397,13 @@
 
     static class CertifiedChecksum {
         final @NonNull Checksum mChecksum;
+        final @NonNull String mPackageName;
         final @NonNull byte[] mCertificate;
 
-        CertifiedChecksum(@NonNull Checksum checksum, @NonNull byte[] certificate) {
+        CertifiedChecksum(@NonNull Checksum checksum, @NonNull String packageName,
+                @NonNull byte[] certificate) {
             mChecksum = checksum;
+            mPackageName = packageName;
             mCertificate = certificate;
         }
 
@@ -405,6 +411,10 @@
             return mChecksum;
         }
 
+        String getPackageName() {
+            return mPackageName;
+        }
+
         byte[] getCertificate() {
             return mCertificate;
         }
@@ -951,23 +961,26 @@
             return;
         }
 
-        final PackageManagerInternal pmi = LocalServices.getService(
-                PackageManagerInternal.class);
-        final AndroidPackage callingInstaller = pmi.getPackage(Binder.getCallingUid());
+        final String initiatingPackageName = mInstallSource.initiatingPackageName;
 
+        final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+        appOps.checkPackage(Binder.getCallingUid(), initiatingPackageName);
+
+        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+        final AndroidPackage callingInstaller = pmi.getPackage(initiatingPackageName);
         if (callingInstaller == null) {
             throw new IllegalStateException("Can't obtain calling installer's package.");
         }
 
         // Obtaining array of certificates used for signing the installer package.
-        // According to V2/V3 signing schema, the first certificate corresponds to public key
-        // in the signing block.
-        Signature[] certs = callingInstaller.getSigningDetails().signatures;
+        final Signature[] certs = callingInstaller.getSigningDetails().signatures;
         if (certs == null || certs.length == 0 || certs[0] == null) {
             throw new IllegalStateException(
                     "Can't obtain calling installer package's certificates.");
         }
-        byte[] mainCertificateBytes = certs[0].toByteArray();
+        // According to V2/V3 signing schema, the first certificate corresponds to the public key
+        // in the signing block.
+        final byte[] mainCertificateBytes = certs[0].toByteArray();
 
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
@@ -979,7 +992,8 @@
                     fileChecksums = new ArrayList<>();
                     mChecksums.put(name, fileChecksums);
                 }
-                fileChecksums.add(new CertifiedChecksum(checksum, mainCertificateBytes));
+                fileChecksums.add(new CertifiedChecksum(checksum, initiatingPackageName,
+                        mainCertificateBytes));
             }
         }
     }
@@ -2633,7 +2647,7 @@
         for (int i = 0, size = checksums.size(); i < size; ++i) {
             CertifiedChecksum checksum = checksums.get(i);
             result[i] = new ApkChecksum(splitName, checksum.getChecksum(),
-                    checksum.getCertificate());
+                    checksum.getPackageName(), checksum.getCertificate());
         }
         return result;
     }
@@ -2651,11 +2665,17 @@
             return;
         }
 
-        final File targetDigestsFile = new File(stageDir,
-                ApkChecksums.buildDigestsPathForApk(targetFile.getName()));
-        try {
-            ApkChecksums.writeChecksums(targetDigestsFile,
-                    createApkChecksums(splitName, checksums));
+        final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
+        final File targetDigestsFile = new File(stageDir, targetDigestsPath);
+        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+            ApkChecksums.writeChecksums(os, createApkChecksums(splitName, checksums));
+            final byte[] checksumsBytes = os.toByteArray();
+
+            if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
+                FileUtils.bytesToFile(targetDigestsFile.getAbsolutePath(), checksumsBytes);
+            } else {
+                mIncrementalFileStorages.makeFile(targetDigestsPath, checksumsBytes);
+            }
         } catch (CertificateException e) {
             throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                     "Failed to encode certificate for " + mPackageName, e);
@@ -3975,6 +3995,7 @@
                     writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getKind());
                     writeByteArrayAttribute(out, ATTR_CHECKSUM_VALUE,
                             checksum.getChecksum().getValue());
+                    writeStringAttribute(out, ATTR_CHECKSUM_PACKAGE, checksum.getPackageName());
                     writeByteArrayAttribute(out, ATTR_CHECKSUM_CERTIFICATE,
                             checksum.getCertificate());
                     out.endTag(null, TAG_SESSION_CHECKSUM);
@@ -4127,6 +4148,7 @@
                 final CertifiedChecksum certifiedChecksum = new CertifiedChecksum(
                         new Checksum(readIntAttribute(in, ATTR_CHECKSUM_KIND, 0),
                                 readByteArrayAttribute(in, ATTR_CHECKSUM_VALUE)),
+                        readStringAttribute(in, ATTR_CHECKSUM_PACKAGE),
                         readByteArrayAttribute(in, ATTR_CHECKSUM_CERTIFICATE));
 
                 List<CertifiedChecksum> certifiedChecksums = checksums.get(fileName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 2bbca79..1814a8e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -27,6 +27,8 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import libcore.io.IoUtils;
 
 import java.io.IOException;
@@ -112,7 +114,9 @@
         }
     }
 
-    static class Metadata {
+    /** @hide */
+    @VisibleForTesting
+    public static class Metadata {
         /**
          * Full files read from stdin.
          */
@@ -137,7 +141,9 @@
             return new Metadata(STDIN, fileId);
         }
 
-        static Metadata forLocalFile(String filePath) {
+        /** @hide */
+        @VisibleForTesting
+        public static Metadata forLocalFile(String filePath) {
             return new Metadata(LOCAL_FILE, filePath);
         }
 
@@ -163,7 +169,9 @@
             return new Metadata(mode, data);
         }
 
-        byte[] toByteArray() {
+        /** @hide */
+        @VisibleForTesting
+        public byte[] toByteArray() {
             byte[] dataBytes = this.mData.getBytes(StandardCharsets.UTF_8);
             byte[] result = new byte[1 + dataBytes.length];
             result[0] = this.mMode;
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 9e2bb45..631e185 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -225,6 +225,20 @@
     return res;
 }
 
+static inline unique_fd openLocalFile(JNIEnv* env, const JniIds& jni, jobject shellCommand,
+                                      const std::string& path) {
+    if (shellCommand) {
+        return unique_fd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
+                                                  jni.pmscdGetLocalFile, shellCommand,
+                                                  env->NewStringUTF(path.c_str()))};
+    }
+    auto fd = unique_fd(::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+    if (!fd.ok()) {
+        PLOG(ERROR) << "Failed to open file: " << path << ", error code: " << fd.get();
+    }
+    return fd;
+}
+
 static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject shellCommand,
                                        IncFsSize size, const std::string& filePath) {
     InputDescs result;
@@ -232,9 +246,7 @@
 
     const std::string idsigPath = filePath + ".idsig";
 
-    unique_fd idsigFd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
-                                               jni.pmscdGetLocalFile, shellCommand,
-                                               env->NewStringUTF(idsigPath.c_str()))};
+    unique_fd idsigFd = openLocalFile(env, jni, shellCommand, idsigPath);
     if (idsigFd.ok()) {
         auto treeSize = verityTreeSizeForFile(size);
         auto actualTreeSize = skipIdSigHeaders(idsigFd);
@@ -250,9 +262,7 @@
         });
     }
 
-    unique_fd fileFd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
-                                              jni.pmscdGetLocalFile, shellCommand,
-                                              env->NewStringUTF(filePath.c_str()))};
+    unique_fd fileFd = openLocalFile(env, jni, shellCommand, filePath);
     if (fileFd.ok()) {
         result.push_back(InputDesc{
                 .fd = std::move(fileFd),
@@ -272,6 +282,11 @@
                              std::string(metadata.data, metadata.size));
     }
 
+    if (!shellCommand) {
+        ALOGE("Missing shell command.");
+        return {};
+    }
+
     unique_fd fd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
                                           jni.pmscdGetStdIn, shellCommand)};
     if (!fd.ok()) {
@@ -396,10 +411,6 @@
         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);
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index bf3a896..2f8825b 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -200,16 +200,24 @@
     return {0, id, nfp};
 }
 
+static std::span<const uint8_t> toSpan(const ::std::optional<::std::vector<uint8_t>>& content) {
+    if (!content) {
+        return {};
+    }
+    return {content->data(), (int)content->size()};
+}
+
 binder::Status BinderIncrementalService::makeFile(
         int32_t storageId, const std::string& path,
-        const ::android::os::incremental::IncrementalNewFileParams& params, int32_t* _aidl_return) {
+        const ::android::os::incremental::IncrementalNewFileParams& params,
+        const ::std::optional<::std::vector<uint8_t>>& content, int32_t* _aidl_return) {
     auto [err, fileId, nfp] = toMakeFileParams(params);
     if (err) {
         *_aidl_return = err;
         return ok();
     }
 
-    *_aidl_return = mImpl.makeFile(storageId, path, 0777, fileId, nfp);
+    *_aidl_return = mImpl.makeFile(storageId, path, 0777, fileId, nfp, toSpan(content));
     return ok();
 }
 binder::Status BinderIncrementalService::makeFileFromRange(int32_t storageId,
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 1238498..0a89166 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -58,7 +58,9 @@
     binder::Status makeDirectories(int32_t storageId, const std::string& path,
                                    int32_t* _aidl_return) final;
     binder::Status makeFile(int32_t storageId, const std::string& path,
-                            const IncrementalNewFileParams& params, int32_t* _aidl_return) final;
+                            const IncrementalNewFileParams& params,
+                            const ::std::optional<::std::vector<uint8_t>>& content,
+                            int32_t* _aidl_return) final;
     binder::Status makeFileFromRange(int32_t storageId, const std::string& targetPath,
                                      const std::string& sourcePath, int64_t start, int64_t end,
                                      int32_t* _aidl_return) final;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 10a508b..44a07a1 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -859,7 +859,7 @@
 }
 
 int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
-                                 incfs::NewFileParams params) {
+                                 incfs::NewFileParams params, std::span<const uint8_t> data) {
     if (auto ifs = getIfs(storage)) {
         std::string normPath = normalizePathToStorage(*ifs, storage, path);
         if (normPath.empty()) {
@@ -867,11 +867,15 @@
                        << " failed to normalize: " << path;
             return -EINVAL;
         }
-        auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
-        if (err) {
+        if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
             LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
             return err;
         }
+        if (!data.empty()) {
+            if (auto err = setFileContent(ifs, id, path, data); err) {
+                return err;
+            }
+        }
         return 0;
     }
     return -EINVAL;
@@ -1584,67 +1588,38 @@
 
 void IncrementalService::extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle zipFile,
                                         ZipEntry& entry, const incfs::FileId& libFileId,
-                                        std::string_view targetLibPath,
+                                        std::string_view debugLibPath,
                                         Clock::time_point scheduledTs) {
     if (!ifs) {
-        LOG(INFO) << "Skipping zip file " << targetLibPath << " extraction for an expired mount";
+        LOG(INFO) << "Skipping zip file " << debugLibPath << " extraction for an expired mount";
         return;
     }
 
-    auto libName = path::basename(targetLibPath);
     auto startedTs = Clock::now();
 
     // Write extracted data to new file
     // NOTE: don't zero-initialize memory, it may take a while for nothing
     auto libData = std::unique_ptr<uint8_t[]>(new uint8_t[entry.uncompressed_length]);
     if (ExtractToMemory(zipFile, &entry, libData.get(), entry.uncompressed_length)) {
-        LOG(ERROR) << "Failed to extract native lib zip entry: " << libName;
+        LOG(ERROR) << "Failed to extract native lib zip entry: " << path::basename(debugLibPath);
         return;
     }
 
     auto extractFileTs = Clock::now();
 
-    const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId);
-    if (!writeFd.ok()) {
-        LOG(ERROR) << "Failed to open write fd for: " << targetLibPath
-                   << " errno: " << writeFd.get();
-        return;
-    }
-
-    auto openFileTs = Clock::now();
-    const int numBlocks =
-            (entry.uncompressed_length + constants().blockSize - 1) / constants().blockSize;
-    std::vector<IncFsDataBlock> instructions(numBlocks);
-    auto remainingData = std::span(libData.get(), entry.uncompressed_length);
-    for (int i = 0; i < numBlocks; i++) {
-        const auto blockSize = std::min<long>(constants().blockSize, remainingData.size());
-        instructions[i] = IncFsDataBlock{
-                .fileFd = writeFd.get(),
-                .pageIndex = static_cast<IncFsBlockIndex>(i),
-                .compression = INCFS_COMPRESSION_KIND_NONE,
-                .kind = INCFS_BLOCK_KIND_DATA,
-                .dataSize = static_cast<uint32_t>(blockSize),
-                .data = reinterpret_cast<const char*>(remainingData.data()),
-        };
-        remainingData = remainingData.subspan(blockSize);
-    }
-    auto prepareInstsTs = Clock::now();
-
-    size_t res = mIncFs->writeBlocks(instructions);
-    if (res != instructions.size()) {
-        LOG(ERROR) << "Failed to write data into: " << targetLibPath;
+    if (setFileContent(ifs, libFileId, debugLibPath,
+                       std::span(libData.get(), entry.uncompressed_length))) {
         return;
     }
 
     if (perfLoggingEnabled()) {
         auto endFileTs = Clock::now();
-        LOG(INFO) << "incfs: Extracted " << libName << "(" << entry.compressed_length << " -> "
-                  << entry.uncompressed_length << " bytes): " << elapsedMcs(startedTs, endFileTs)
+        LOG(INFO) << "incfs: Extracted " << path::basename(debugLibPath) << "("
+                  << entry.compressed_length << " -> " << entry.uncompressed_length
+                  << " bytes): " << elapsedMcs(startedTs, endFileTs)
                   << "mcs, scheduling delay: " << elapsedMcs(scheduledTs, startedTs)
                   << " extract: " << elapsedMcs(startedTs, extractFileTs)
-                  << " open: " << elapsedMcs(extractFileTs, openFileTs)
-                  << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
-                  << " write: " << elapsedMcs(prepareInstsTs, endFileTs);
+                  << " open/prepare/write: " << elapsedMcs(extractFileTs, endFileTs);
     }
 }
 
@@ -1677,6 +1652,55 @@
     return mRunning;
 }
 
+int IncrementalService::setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
+                                       std::string_view debugFilePath,
+                                       std::span<const uint8_t> data) const {
+    auto startTs = Clock::now();
+
+    const auto writeFd = mIncFs->openForSpecialOps(ifs->control, fileId);
+    if (!writeFd.ok()) {
+        LOG(ERROR) << "Failed to open write fd for: " << debugFilePath
+                   << " errno: " << writeFd.get();
+        return writeFd.get();
+    }
+
+    const auto dataLength = data.size();
+
+    auto openFileTs = Clock::now();
+    const int numBlocks = (data.size() + constants().blockSize - 1) / constants().blockSize;
+    std::vector<IncFsDataBlock> instructions(numBlocks);
+    for (int i = 0; i < numBlocks; i++) {
+        const auto blockSize = std::min<long>(constants().blockSize, data.size());
+        instructions[i] = IncFsDataBlock{
+                .fileFd = writeFd.get(),
+                .pageIndex = static_cast<IncFsBlockIndex>(i),
+                .compression = INCFS_COMPRESSION_KIND_NONE,
+                .kind = INCFS_BLOCK_KIND_DATA,
+                .dataSize = static_cast<uint32_t>(blockSize),
+                .data = reinterpret_cast<const char*>(data.data()),
+        };
+        data = data.subspan(blockSize);
+    }
+    auto prepareInstsTs = Clock::now();
+
+    size_t res = mIncFs->writeBlocks(instructions);
+    if (res != instructions.size()) {
+        LOG(ERROR) << "Failed to write data into: " << debugFilePath;
+        return res;
+    }
+
+    if (perfLoggingEnabled()) {
+        auto endTs = Clock::now();
+        LOG(INFO) << "incfs: Set file content " << debugFilePath << "(" << dataLength
+                  << " bytes): " << elapsedMcs(startTs, endTs)
+                  << "mcs, open: " << elapsedMcs(startTs, openFileTs)
+                  << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
+                  << " write: " << elapsedMcs(prepareInstsTs, endTs);
+    }
+
+    return 0;
+}
+
 int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& path) const {
     std::unique_lock l(mLock);
     const auto ifs = getIfsLocked(storage);
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index a49e0f3..d820417 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -127,7 +127,7 @@
     int setStorageParams(StorageId storage, bool enableReadLogs);
 
     int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
-                 incfs::NewFileParams params);
+                 incfs::NewFileParams params, std::span<const uint8_t> data);
     int makeDir(StorageId storage, std::string_view path, int mode = 0755);
     int makeDirs(StorageId storage, std::string_view path, int mode = 0755);
 
@@ -349,13 +349,16 @@
     int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
     float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
 
+    int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
+                       std::string_view debugFilePath, std::span<const uint8_t> data) const;
+
     void registerAppOpsCallback(const std::string& packageName);
     bool unregisterAppOpsCallback(const std::string& packageName);
     void onAppOpChanged(const std::string& packageName);
 
     void runJobProcessing();
     void extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle zipFile, ZipEntry& entry,
-                        const incfs::FileId& libFileId, std::string_view targetLibPath,
+                        const incfs::FileId& libFileId, std::string_view debugLibPath,
                         Clock::time_point scheduledTs);
 
     void runCmdLooper();