Merge "Fix typo in DSU installation error message"
diff --git a/aidl/android/gsi/IImageService.aidl b/aidl/android/gsi/IImageService.aidl
index 1195c00..db348ba 100644
--- a/aidl/android/gsi/IImageService.aidl
+++ b/aidl/android/gsi/IImageService.aidl
@@ -98,4 +98,9 @@
      * @return              True on success, false otherwise.
      */
     void zeroFillNewImage(@utf8InCpp String name, long bytes);
+
+    /**
+     * Find and remove all images in the containing folder of this instance.
+     */
+    void removeAllImages();
 }
diff --git a/gsi_service.cpp b/gsi_service.cpp
index 14e7640..5ef4914 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -448,6 +448,7 @@
     binder::Status backingImageExists(const std::string& name, bool* _aidl_return) override;
     binder::Status isImageMapped(const std::string& name, bool* _aidl_return) override;
     binder::Status zeroFillNewImage(const std::string& name, int64_t bytes) override;
+    binder::Status removeAllImages() override;
 
   private:
     bool CheckUid();
@@ -543,6 +544,16 @@
     return binder::Status::ok();
 }
 
+binder::Status ImageService::removeAllImages() {
+    if (!CheckUid()) return UidSecurityError();
+
+    std::lock_guard<std::mutex> guard(parent_->lock());
+    if (!impl_->RemoveAllImages()) {
+        return BinderError("Failed to remove all images");
+    }
+    return binder::Status::ok();
+}
+
 bool ImageService::CheckUid() {
     return uid_ == IPCThreadState::self()->getCallingUid();
 }
diff --git a/libfiemap/binder.cpp b/libfiemap/binder.cpp
index dcb887a..49779f4 100644
--- a/libfiemap/binder.cpp
+++ b/libfiemap/binder.cpp
@@ -42,6 +42,7 @@
     bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
                                   std::string* dev) override;
     bool ZeroFillNewImage(const std::string& name, uint64_t bytes) override;
+    bool RemoveAllImages() override;
 
     std::vector<std::string> GetAllBackingImages() override;
 
@@ -152,6 +153,16 @@
     return true;
 }
 
+bool ImageManagerBinder::RemoveAllImages() {
+    auto status = manager_->removeAllImages();
+    if (!status.isOk()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__
+                   << " binder returned: " << status.exceptionMessage().string();
+        return false;
+    }
+    return true;
+}
+
 static android::sp<IGsid> AcquireIGsid(const std::chrono::milliseconds& timeout_ms) {
     if (android::base::GetProperty("init.svc.gsid", "") != "running") {
         if (!android::base::SetProperty("ctl.start", "gsid") ||
diff --git a/libfiemap/fiemap_writer.cpp b/libfiemap/fiemap_writer.cpp
index 9b947e2..4f26387 100644
--- a/libfiemap/fiemap_writer.cpp
+++ b/libfiemap/fiemap_writer.cpp
@@ -530,7 +530,69 @@
         return false;
     }
 
-    *num_extents = fiemap.fm_mapped_extents;
+    if (num_extents) {
+        *num_extents = fiemap.fm_mapped_extents;
+    }
+    return true;
+}
+
+static bool IsValidExtent(const fiemap_extent* extent, std::string_view file_path) {
+    if (extent->fe_flags & kUnsupportedExtentFlags) {
+        LOG(ERROR) << "Extent at location " << extent->fe_logical << " of file " << file_path
+                   << " has unsupported flags";
+        return false;
+    }
+    return true;
+}
+
+static bool IsLastExtent(const fiemap_extent* extent) {
+    if (!(extent->fe_flags & FIEMAP_EXTENT_LAST)) {
+        LOG(ERROR) << "Extents are being received out-of-order";
+        return false;
+    }
+    return true;
+}
+
+static bool FiemapToExtents(struct fiemap* fiemap, std::vector<struct fiemap_extent>* extents,
+                            uint32_t num_extents, std::string_view file_path) {
+    if (num_extents == 0) return false;
+
+    const struct fiemap_extent* last_extent = &fiemap->fm_extents[num_extents - 1];
+    if (!IsLastExtent(last_extent)) {
+        LOG(ERROR) << "FIEMAP did not return a final extent for file: " << file_path;
+        return false;
+    }
+
+    // Iterate through each extent, read and make sure its valid before adding it to the vector
+    // merging contiguous extents.
+    fiemap_extent* prev = &fiemap->fm_extents[0];
+    if (!IsValidExtent(prev, file_path)) return false;
+
+    for (uint32_t i = 1; i < num_extents; i++) {
+        fiemap_extent* next = &fiemap->fm_extents[i];
+
+        // Make sure extents are returned in order
+        if (next != last_extent && IsLastExtent(next)) return false;
+
+        // Check if extent's flags are valid
+        if (!IsValidExtent(next, file_path)) return false;
+
+        // Check if the current extent is contiguous with the previous one.
+        // An extent can be combined with its predecessor only if:
+        //  1. There is no physical space between the previous and the current
+        //  extent, and
+        //  2. The physical distance between the previous and current extent
+        //  corresponds to their logical distance (contiguous mapping).
+        if (prev->fe_physical + prev->fe_length == next->fe_physical &&
+            next->fe_physical - prev->fe_physical == next->fe_logical - prev->fe_logical) {
+            prev->fe_length += next->fe_length;
+        } else {
+            extents->emplace_back(*prev);
+            prev = next;
+        }
+    }
+    extents->emplace_back(*prev);
+
     return true;
 }
 
@@ -575,27 +637,7 @@
         return false;
     }
 
-    const struct fiemap_extent* last_extent = &fiemap->fm_extents[num_extents - 1];
-    if (!(last_extent->fe_flags & FIEMAP_EXTENT_LAST)) {
-        LOG(ERROR) << "FIEMAP did not return a final extent for file: " << file_path;
-        return false;
-    }
-
-    // Iterate through each extent read and make sure its valid before adding it to the vector
-    for (uint32_t i = 0; i < num_extents; i++) {
-        const struct fiemap_extent* extent = &fiemap->fm_extents[i];
-        if (extent->fe_flags & kUnsupportedExtentFlags) {
-            LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
-                       << " has unsupported flags";
-            return false;
-        }
-        if ((extent->fe_flags & FIEMAP_EXTENT_LAST) && extent != last_extent) {
-            LOG(ERROR) << "Extents are being received out-of-order";
-            return false;
-        }
-        extents->emplace_back(*extent);
-    }
-    return true;
+    return FiemapToExtents(fiemap, extents, num_extents, file_path);
 }
 
 static bool ReadFibmap(int file_fd, const std::string& file_path,
diff --git a/libfiemap/include/libfiemap/image_manager.h b/libfiemap/include/libfiemap/image_manager.h
index efbe9bd..5ff4628 100644
--- a/libfiemap/include/libfiemap/image_manager.h
+++ b/libfiemap/include/libfiemap/image_manager.h
@@ -91,6 +91,9 @@
     // whole file if filled with zeros.
     virtual bool ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;
 
+    // Find and remove all images and metadata for this manager.
+    virtual bool RemoveAllImages() = 0;
+
     virtual bool UnmapImageIfExists(const std::string& name);
 };
 
@@ -115,15 +118,13 @@
     bool IsImageMapped(const std::string& name) override;
     bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
                                   std::string* dev) override;
+    bool RemoveAllImages() override;
 
     std::vector<std::string> GetAllBackingImages();
     // Same as CreateBackingImage, but provides a progress notification.
     bool CreateBackingImage(const std::string& name, uint64_t size, int flags,
                             std::function<bool(uint64_t, uint64_t)>&& on_progress);
 
-    // Find and remove all images and metadata for a given image dir.
-    bool RemoveAllImages();
-
     // Returns true if the named partition exists. This does not check the
     // consistency of the backing image/data file.
     bool PartitionExists(const std::string& name);