[incremental] Use file range mapping for .so if available

Bug: 180535478
Test: IncrementalService unit tests
Change-Id: I663dcdce337c289cacc5dc7224dedf5a55605c86
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 2fa927b..24623b2 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1617,7 +1617,7 @@
     // Need a shared pointer: will be passing it into all unpacking jobs.
     std::shared_ptr<ZipArchive> zipFile(zipFileHandle, [](ZipArchiveHandle h) { CloseArchive(h); });
     void* cookie = nullptr;
-    const auto libFilePrefix = path::join(constants().libDir, abi) + "/";
+    const auto libFilePrefix = path::join(constants().libDir, abi) += "/";
     if (StartIteration(zipFile.get(), &cookie, libFilePrefix, constants().libSuffix)) {
         LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
         return false;
@@ -1627,6 +1627,17 @@
 
     auto openZipTs = Clock::now();
 
+    auto mapFiles = (mIncFs->features() & incfs::Features::v2);
+    incfs::FileId sourceId;
+    if (mapFiles) {
+        sourceId = mIncFs->getFileId(ifs->control, apkFullPath);
+        if (!incfs::isValidFileId(sourceId)) {
+            LOG(WARNING) << "Error getting IncFS file ID for apk path '" << apkFullPath
+                         << "', mapping disabled";
+            mapFiles = false;
+        }
+    }
+
     std::vector<Job> jobQueue;
     ZipEntry entry;
     std::string_view fileName;
@@ -1635,13 +1646,16 @@
             continue;
         }
 
+        const auto entryUncompressed = entry.method == kCompressStored;
+        const auto entryPageAligned = (entry.offset & (constants().blockSize - 1)) == 0;
+
         if (!extractNativeLibs) {
             // ensure the file is properly aligned and unpacked
-            if (entry.method != kCompressStored) {
+            if (!entryUncompressed) {
                 LOG(WARNING) << "Library " << fileName << " must be uncompressed to mmap it";
                 return false;
             }
-            if ((entry.offset & (constants().blockSize - 1)) != 0) {
+            if (!entryPageAligned) {
                 LOG(WARNING) << "Library " << fileName
                              << " must be page-aligned to mmap it, offset = 0x" << std::hex
                              << entry.offset;
@@ -1665,6 +1679,28 @@
             continue;
         }
 
+        if (mapFiles && entryUncompressed && entryPageAligned && entry.uncompressed_length > 0) {
+            incfs::NewMappedFileParams mappedFileParams = {
+                    .sourceId = sourceId,
+                    .sourceOffset = entry.offset,
+                    .size = entry.uncompressed_length,
+            };
+
+            if (auto res = mIncFs->makeMappedFile(ifs->control, targetLibPathAbsolute, 0755,
+                                                  mappedFileParams);
+                res == 0) {
+                if (perfLoggingEnabled()) {
+                    auto doneTs = Clock::now();
+                    LOG(INFO) << "incfs: Mapped " << libName << ": "
+                              << elapsedMcs(startFileTs, doneTs) << "mcs";
+                }
+                continue;
+            } else {
+                LOG(WARNING) << "Failed to map file for: '" << targetLibPath << "' errno: " << res
+                             << "; falling back to full extraction";
+            }
+        }
+
         // Create new lib file without signature info
         incfs::NewFileParams libFileParams = {
                 .size = entry.uncompressed_length,
@@ -1673,7 +1709,7 @@
                 .metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()},
         };
         incfs::FileId libFileId = idFromMetadata(targetLibPath);
-        if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0777, libFileId,
+        if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0755, libFileId,
                                         libFileParams)) {
             LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res;
             // If one lib file fails to be created, abort others as well
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 659d650..36bda49 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -134,10 +134,11 @@
     } mLooper;
 };
 
-class RealIncFs : public IncFsWrapper {
+class RealIncFs final : public IncFsWrapper {
 public:
     RealIncFs() = default;
     ~RealIncFs() final = default;
+    Features features() const final { return incfs::features(); }
     void listExistingMounts(const ExistingMountCallback& cb) const final {
         for (auto mount : incfs::defaultMountRegistry().copyMounts()) {
             auto binds = mount.binds(); // span() doesn't like rvalue containers, needs to save it.
@@ -153,6 +154,10 @@
                        incfs::NewFileParams params) const final {
         return incfs::makeFile(control, path, mode, id, params);
     }
+    ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode,
+                             incfs::NewMappedFileParams params) const final {
+        return incfs::makeMappedFile(control, path, mode, params);
+    }
     ErrorCode makeDir(const Control& control, std::string_view path, int mode) const final {
         return incfs::makeDir(control, path, mode);
     }
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index d60035a..46c0fc6 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -77,18 +77,22 @@
     using ErrorCode = incfs::ErrorCode;
     using UniqueFd = incfs::UniqueFd;
     using WaitResult = incfs::WaitResult;
+    using Features = incfs::Features;
 
     using ExistingMountCallback =
             std::function<void(std::string_view root, std::string_view backingDir,
                                std::span<std::pair<std::string_view, std::string_view>> binds)>;
 
     virtual ~IncFsWrapper() = default;
+    virtual Features features() const = 0;
     virtual void listExistingMounts(const ExistingMountCallback& cb) const = 0;
     virtual Control openMount(std::string_view path) const = 0;
     virtual Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs,
                                   IncFsFd blocksWritten) const = 0;
     virtual ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id,
                                incfs::NewFileParams params) const = 0;
+    virtual ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode,
+                                     incfs::NewMappedFileParams params) const = 0;
     virtual ErrorCode makeDir(const Control& control, std::string_view path, int mode) const = 0;
     virtual ErrorCode makeDirs(const Control& control, std::string_view path, int mode) const = 0;
     virtual incfs::RawMetadata getMetadata(const Control& control, FileId fileid) const = 0;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index ab491ef..154a55f 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -322,6 +322,7 @@
 
 class MockIncFs : public IncFsWrapper {
 public:
+    MOCK_CONST_METHOD0(features, Features());
     MOCK_CONST_METHOD1(listExistingMounts, void(const ExistingMountCallback& cb));
     MOCK_CONST_METHOD1(openMount, Control(std::string_view path));
     MOCK_CONST_METHOD4(createControl,
@@ -330,6 +331,9 @@
     MOCK_CONST_METHOD5(makeFile,
                        ErrorCode(const Control& control, std::string_view path, int mode, FileId id,
                                  NewFileParams params));
+    MOCK_CONST_METHOD4(makeMappedFile,
+                       ErrorCode(const Control& control, std::string_view path, int mode,
+                                 NewMappedFileParams params));
     MOCK_CONST_METHOD3(makeDir, ErrorCode(const Control& control, std::string_view path, int mode));
     MOCK_CONST_METHOD3(makeDirs,
                        ErrorCode(const Control& control, std::string_view path, int mode));