Merge branch 'security-aosp-tm-release' into int/13/fp3

* origin/security-aosp-tm-release:
  Add seal if ashmem-dev is backed by memfd

Change-Id: I2ac52dd310f941600792a2d6193ae22ed3fd9232
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 2c878f0..af0c89e 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -463,6 +463,11 @@
     {"watchdog,gsa,hard", 215},
     {"watchdog,gsa,soft", 216},
     {"watchdog,pmucal", 217},
+    {"reboot,early,bl", 218},
+    {"watchdog,apc,gsa,crashed", 219},
+    {"watchdog,apc,bl31,crashed", 220},
+    {"watchdog,apc,pbl,crashed", 221},
+    {"reboot,memory_protect,hyp", 222},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index ad0231d..5198872 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -14,9 +14,15 @@
         "-Wno-nullability-completeness",
         "-Os",
         "-fno-finite-loops",
+        "-DANDROID_DEBUGGABLE=0",
     ],
 
     local_include_dirs: ["include"],
+    product_variables: {
+        debuggable: {
+            cflags: ["-UANDROID_DEBUGGABLE", "-DANDROID_DEBUGGABLE=1"],
+        }
+    },
 }
 
 cc_library_headers {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e113308..9163ca0 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -36,6 +36,7 @@
 #include <string>
 #include <thread>
 
+#include <android/dlext.h>
 #include <android/fdsan.h>
 #include <android/set_abort_message.h>
 #include <bionic/malloc.h>
@@ -1842,7 +1843,7 @@
   ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
 }
 
-static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+static std::string GetTestLibraryPath() {
   std::string test_lib(testing::internal::GetArgvs()[0]);
   auto const value = test_lib.find_last_of('/');
   if (value == std::string::npos) {
@@ -1850,7 +1851,62 @@
   } else {
     test_lib = test_lib.substr(0, value + 1) + "./";
   }
-  test_lib += "libcrash_test.so";
+  return test_lib + "libcrash_test.so";
+}
+
+static void CreateEmbeddedLibrary(int out_fd) {
+  std::string test_lib(GetTestLibraryPath());
+  android::base::unique_fd fd(open(test_lib.c_str(), O_RDONLY | O_CLOEXEC));
+  ASSERT_NE(fd.get(), -1);
+  off_t file_size = lseek(fd, 0, SEEK_END);
+  ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
+  std::vector<uint8_t> contents(file_size);
+  ASSERT_TRUE(android::base::ReadFully(fd, contents.data(), contents.size()));
+
+  // Put the shared library data at a pagesize() offset.
+  ASSERT_EQ(lseek(out_fd, 4 * getpagesize(), SEEK_CUR), 4 * getpagesize());
+  ASSERT_EQ(static_cast<size_t>(write(out_fd, contents.data(), contents.size())), contents.size());
+}
+
+TEST_F(CrasherTest, non_zero_offset_in_library) {
+  int intercept_result;
+  unique_fd output_fd;
+  TemporaryFile tf;
+  CreateEmbeddedLibrary(tf.fd);
+  StartProcess([&tf]() {
+    android_dlextinfo extinfo{};
+    extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
+    extinfo.library_fd = tf.fd;
+    extinfo.library_fd_offset = 4 * getpagesize();
+    void* handle = android_dlopen_ext(tf.path, RTLD_NOW, &extinfo);
+    if (handle == nullptr) {
+      _exit(1);
+    }
+    void (*crash_func)() = reinterpret_cast<void (*)()>(dlsym(handle, "crash"));
+    if (crash_func == nullptr) {
+      _exit(1);
+    }
+    crash_func();
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  // Verify the crash includes an offset value in the backtrace.
+  std::string match_str = android::base::StringPrintf("%s\\!libcrash_test.so \\(offset 0x%x\\)",
+                                                      tf.path, 4 * getpagesize());
+  ASSERT_MATCH(result, match_str);
+}
+
+static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+  std::string test_lib(GetTestLibraryPath());
 
   *tmp_so_name = std::string(tmp_dir) + "/libcrash_test.so";
   std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index bc08327..68b2e67 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -62,10 +62,11 @@
 #define DEBUGGER_SIGNAL BIONIC_SIGNAL_DEBUGGER
 
 static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
+  bool enabled = true;
+#if ANDROID_DEBUGGABLE
   char value[PROP_VALUE_MAX] = "";
-  bool enabled =
-      !(__system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1") &&
-        __system_property_get("debug.debuggerd.disable", value) > 0 && !strcmp(value, "1"));
+  enabled = !(__system_property_get("debug.debuggerd.disable", value) > 0 && !strcmp(value, "1"));
+#endif
   if (enabled) {
     sigaction(SIGABRT, action, nullptr);
     sigaction(SIGBUS, action, nullptr);
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 0265641..0696601 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -168,8 +168,14 @@
       build_id = StringPrintf(" (BuildId: %s)", frame.build_id().c_str());
     }
 
-    CB(should_log, "      #%02d pc %0*" PRIx64 "  %s%s%s", index++, pointer_width(tombstone) * 2,
-       frame.rel_pc(), frame.file_name().c_str(), function.c_str(), build_id.c_str());
+    std::string line =
+        StringPrintf("      #%02d pc %0*" PRIx64 "  %s", index++, pointer_width(tombstone) * 2,
+                     frame.rel_pc(), frame.file_name().c_str());
+    if (frame.file_map_offset() != 0) {
+      line += StringPrintf(" (offset 0x%" PRIx64 ")", frame.file_map_offset());
+    }
+    line += function + build_id;
+    CB(should_log, "%s", line.c_str());
   }
 }
 
diff --git a/fastboot/device/usb.cpp b/fastboot/device/usb.cpp
index 4115a6d..1257055 100644
--- a/fastboot/device/usb.cpp
+++ b/fastboot/device/usb.cpp
@@ -171,6 +171,16 @@
         if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
             continue;
         }
+        if (read && aiob->events[0].res == -EPIPE) {
+            // On initial connection, some clients will send a ClearFeature(HALT) to
+            // attempt to resynchronize host and device after the fastboot server is killed.
+            // On newer device kernels, the reads we've already dispatched will be cancelled.
+            // Instead of treating this as a failure, which will tear down the interface and
+            // lead to the client doing the same thing again, just resubmit if this happens
+            // before we've actually read anything.
+            PLOG(ERROR) << "aio: got -EPIPE on first read attempt. Re-submitting read... ";
+            continue;
+        }
         int ret = 0;
         for (int i = 0; i < num_bufs; i++) {
             if (aiob->events[i].res < 0) {
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index e9bf9e9..911071a 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
 
     cur="${COMP_WORDS[COMP_CWORD]}"
     if [[ $i -eq $COMP_CWORD ]]; then
-        partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm"
+        partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm vendor_kernel_boot"
         COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
     else
         _fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 39d86f9..79c3ac7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -186,6 +186,11 @@
                   "vendor_dlkm.img",  "vendor_dlkm.sig",
                                                       "vendor_dlkm",
                                                                   true,  ImageType::Normal },
+    { "vendor_kernel_boot",
+                  "vendor_kernel_boot.img",
+                                      "vendor_kernel_boot.sig",
+                                                      "vendor_kernel_boot",
+                                                                  true,  ImageType::BootCritical },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
 };
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6db8f13..0d8828a 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -257,6 +257,7 @@
     },
     auto_gen_config: true,
     require_root: true,
+    compile_multilib: "first",
 }
 
 cc_test {
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 746feeb..75a58a6 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -38,7 +38,7 @@
     : fd_(-1),
       header_(),
       fd_size_(0),
-      merge_op_blocks_(std::make_shared<std::vector<uint32_t>>()),
+      block_pos_index_(std::make_shared<std::vector<int>>()),
       reader_flag_(reader_flag) {}
 
 static void SHA256(const void*, size_t, uint8_t[]) {
@@ -58,13 +58,12 @@
     cow->fd_size_ = fd_size_;
     cow->last_label_ = last_label_;
     cow->ops_ = ops_;
-    cow->merge_op_blocks_ = merge_op_blocks_;
     cow->merge_op_start_ = merge_op_start_;
-    cow->block_map_ = block_map_;
     cow->num_total_data_ops_ = num_total_data_ops_;
     cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
     cow->has_seq_ops_ = has_seq_ops_;
     cow->data_loc_ = data_loc_;
+    cow->block_pos_index_ = block_pos_index_;
     return cow;
 }
 
@@ -415,10 +414,10 @@
 //                        Replace-op-4, Zero-op-9, Replace-op-5 }
 //==============================================================
 bool CowReader::PrepMergeOps() {
-    auto merge_op_blocks = std::make_shared<std::vector<uint32_t>>();
+    auto merge_op_blocks = std::make_unique<std::vector<uint32_t>>();
     std::vector<int> other_ops;
     auto seq_ops_set = std::unordered_set<uint32_t>();
-    auto block_map = std::make_shared<std::unordered_map<uint32_t, int>>();
+    auto block_map = std::make_unique<std::unordered_map<uint32_t, int>>();
     size_t num_seqs = 0;
     size_t read;
 
@@ -477,13 +476,18 @@
 
     merge_op_blocks->insert(merge_op_blocks->end(), other_ops.begin(), other_ops.end());
 
+    for (auto block : *merge_op_blocks) {
+        block_pos_index_->push_back(block_map->at(block));
+    }
+
     num_total_data_ops_ = merge_op_blocks->size();
     if (header_.num_merge_ops > 0) {
         merge_op_start_ = header_.num_merge_ops;
     }
 
-    block_map_ = block_map;
-    merge_op_blocks_ = merge_op_blocks;
+    block_map->clear();
+    merge_op_blocks->clear();
+
     return true;
 }
 
@@ -589,9 +593,7 @@
 class CowRevMergeOpIter final : public ICowOpIter {
   public:
     explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
-                               std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                               std::shared_ptr<std::unordered_map<uint32_t, int>> map,
-                               uint64_t start);
+                               std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
 
     bool Done() override;
     const CowOperation& Get() override;
@@ -602,17 +604,15 @@
 
   private:
     std::shared_ptr<std::vector<CowOperation>> ops_;
-    std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
-    std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
-    std::vector<uint32_t>::reverse_iterator block_riter_;
+    std::vector<int>::reverse_iterator block_riter_;
+    std::shared_ptr<std::vector<int>> cow_op_index_vec_;
     uint64_t start_;
 };
 
 class CowMergeOpIter final : public ICowOpIter {
   public:
     explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
-                            std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                            std::shared_ptr<std::unordered_map<uint32_t, int>> map, uint64_t start);
+                            std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
 
     bool Done() override;
     const CowOperation& Get() override;
@@ -623,26 +623,21 @@
 
   private:
     std::shared_ptr<std::vector<CowOperation>> ops_;
-    std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
-    std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
-    std::vector<uint32_t>::iterator block_iter_;
+    std::vector<int>::iterator block_iter_;
+    std::shared_ptr<std::vector<int>> cow_op_index_vec_;
     uint64_t start_;
 };
 
 CowMergeOpIter::CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
-                               std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                               std::shared_ptr<std::unordered_map<uint32_t, int>> map,
-                               uint64_t start) {
+                               std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start) {
     ops_ = ops;
-    merge_op_blocks_ = merge_op_blocks;
-    map_ = map;
     start_ = start;
-
-    block_iter_ = merge_op_blocks->begin() + start;
+    cow_op_index_vec_ = block_pos_index;
+    block_iter_ = cow_op_index_vec_->begin() + start;
 }
 
 bool CowMergeOpIter::RDone() {
-    return block_iter_ == merge_op_blocks_->begin();
+    return block_iter_ == cow_op_index_vec_->begin();
 }
 
 void CowMergeOpIter::Prev() {
@@ -651,7 +646,7 @@
 }
 
 bool CowMergeOpIter::Done() {
-    return block_iter_ == merge_op_blocks_->end();
+    return block_iter_ == cow_op_index_vec_->end();
 }
 
 void CowMergeOpIter::Next() {
@@ -661,23 +656,20 @@
 
 const CowOperation& CowMergeOpIter::Get() {
     CHECK(!Done());
-    return ops_->data()[map_->at(*block_iter_)];
+    return ops_->data()[*block_iter_];
 }
 
 CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
-                                     std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                                     std::shared_ptr<std::unordered_map<uint32_t, int>> map,
+                                     std::shared_ptr<std::vector<int>> block_pos_index,
                                      uint64_t start) {
     ops_ = ops;
-    merge_op_blocks_ = merge_op_blocks;
-    map_ = map;
     start_ = start;
-
-    block_riter_ = merge_op_blocks->rbegin();
+    cow_op_index_vec_ = block_pos_index;
+    block_riter_ = cow_op_index_vec_->rbegin();
 }
 
 bool CowRevMergeOpIter::RDone() {
-    return block_riter_ == merge_op_blocks_->rbegin();
+    return block_riter_ == cow_op_index_vec_->rbegin();
 }
 
 void CowRevMergeOpIter::Prev() {
@@ -686,7 +678,7 @@
 }
 
 bool CowRevMergeOpIter::Done() {
-    return block_riter_ == merge_op_blocks_->rend() - start_;
+    return block_riter_ == cow_op_index_vec_->rend() - start_;
 }
 
 void CowRevMergeOpIter::Next() {
@@ -696,7 +688,7 @@
 
 const CowOperation& CowRevMergeOpIter::Get() {
     CHECK(!Done());
-    return ops_->data()[map_->at(*block_riter_)];
+    return ops_->data()[*block_riter_];
 }
 
 std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
@@ -704,12 +696,12 @@
 }
 
 std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter(bool ignore_progress) {
-    return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+    return std::make_unique<CowRevMergeOpIter>(ops_, block_pos_index_,
                                                ignore_progress ? 0 : merge_op_start_);
 }
 
 std::unique_ptr<ICowOpIter> CowReader::GetMergeOpIter(bool ignore_progress) {
-    return std::make_unique<CowMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+    return std::make_unique<CowMergeOpIter>(ops_, block_pos_index_,
                                             ignore_progress ? 0 : merge_op_start_);
 }
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index f4d5c72..fbdd6b9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -170,9 +170,8 @@
     uint64_t fd_size_;
     std::optional<uint64_t> last_label_;
     std::shared_ptr<std::vector<CowOperation>> ops_;
-    std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
     uint64_t merge_op_start_{};
-    std::shared_ptr<std::unordered_map<uint32_t, int>> block_map_;
+    std::shared_ptr<std::vector<int>> block_pos_index_;
     uint64_t num_total_data_ops_{};
     uint64_t num_ordered_ops_to_merge_{};
     bool has_seq_ops_{};
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 019b64a..870801e 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -2151,8 +2151,17 @@
         if (!suffix.empty() && !android::base::EndsWith(name, suffix)) {
             continue;
         }
-        snapshots->emplace_back(std::move(name));
+
+        // Insert system and product partition at the beginning so that
+        // during snapshot-merge, these partitions are merged first.
+        if (name == "system_a" || name == "system_b" || name == "product_a" ||
+            name == "product_b") {
+            snapshots->insert(snapshots->begin(), std::move(name));
+        } else {
+            snapshots->emplace_back(std::move(name));
+        }
     }
+
     return true;
 }
 
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 6a348b4..e7ffb16 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2609,7 +2609,6 @@
     }
     void TearDown() override {
         RETURN_IF_NON_VIRTUAL_AB();
-        return;  // BUG(149738928)
 
         EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
                     image_manager_->DeleteBackingImage(kImageName));
@@ -2618,19 +2617,6 @@
     std::unique_ptr<LowSpaceUserdata> userdata_;
 };
 
-TEST_P(ImageManagerTest, CreateImageEnoughAvailSpace) {
-    if (userdata_->available_space() == 0) {
-        GTEST_SKIP() << "/data is full (" << userdata_->available_space()
-                     << " bytes available), skipping";
-    }
-    ASSERT_TRUE(image_manager_->CreateBackingImage(kImageName, userdata_->available_space(),
-                                                   IImageManager::CREATE_IMAGE_DEFAULT))
-            << "Should be able to create image with size = " << userdata_->available_space()
-            << " bytes";
-    ASSERT_TRUE(image_manager_->DeleteBackingImage(kImageName))
-            << "Should be able to delete created image";
-}
-
 TEST_P(ImageManagerTest, CreateImageNoSpace) {
     uint64_t to_allocate = userdata_->free_space() + userdata_->bsize();
     auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
@@ -2755,6 +2741,10 @@
         return false;
     }
 
+    if (!IsCompressionEnabled()) {
+        return false;
+    }
+
     const std::string UNKNOWN = "unknown";
     const std::string vendor_release =
             android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index bc2bceb..d751fb8 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -47,6 +47,7 @@
         "libbase",
         "liblog",
     ],
+    export_include_dirs: ["include"],
     ramdisk_available: true,
 }
 
@@ -68,6 +69,7 @@
         "user-space-merge/snapuserd_readahead.cpp",
         "user-space-merge/snapuserd_transitions.cpp",
         "user-space-merge/snapuserd_server.cpp",
+        "user-space-merge/snapuserd_verify.cpp",
     ],
 
     cflags: [
@@ -88,6 +90,11 @@
         "libext4_utils",
         "liburing",
     ],
+
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+
     include_dirs: ["bionic/libc/kernel"],
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index cebda1c..9a69d58 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -89,6 +89,10 @@
 
     // Return the status of the snapshot
     std::string QuerySnapshotStatus(const std::string& misc_name);
+
+    // Check the update verification status - invoked by update_verifier during
+    // boot
+    bool QueryUpdateVerification();
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 7b1c7a3..e08cf9b 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -269,5 +269,15 @@
     return Receivemsg();
 }
 
+bool SnapuserdClient::QueryUpdateVerification() {
+    std::string msg = "update-verify";
+    if (!Sendmsg(msg)) {
+        LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+        return false;
+    }
+    std::string response = Receivemsg();
+    return response == "success";
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 692cb74..8939b78 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -18,6 +18,7 @@
 
 #include <sys/utsname.h>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/properties.h>
 #include <android-base/scopeguard.h>
 #include <android-base/strings.h>
@@ -70,6 +71,9 @@
 
     read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
                                                      GetSharedPtr());
+
+    update_verify_ = std::make_unique<UpdateVerify>(misc_name_);
+
     return true;
 }
 
@@ -143,17 +147,18 @@
     NotifyRAForMergeReady();
 }
 
-void SnapshotHandler::CheckMergeCompletionStatus() {
+bool SnapshotHandler::CheckMergeCompletionStatus() {
     if (!merge_initiated_) {
         SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
                        << reader_->get_num_total_data_ops();
-        return;
+        return false;
     }
 
     struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
 
     SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
                    << " Total-data-ops: " << reader_->get_num_total_data_ops();
+    return true;
 }
 
 bool SnapshotHandler::ReadMetadata() {
@@ -306,206 +311,6 @@
     return ReadMetadata();
 }
 
-void SnapshotHandler::FinalizeIouring() {
-    io_uring_queue_exit(ring_.get());
-}
-
-bool SnapshotHandler::InitializeIouring(int io_depth) {
-    ring_ = std::make_unique<struct io_uring>();
-
-    int ret = io_uring_queue_init(io_depth, ring_.get(), 0);
-    if (ret) {
-        LOG(ERROR) << "io_uring_queue_init failed with ret: " << ret;
-        return false;
-    }
-
-    LOG(INFO) << "io_uring_queue_init success with io_depth: " << io_depth;
-    return true;
-}
-
-bool SnapshotHandler::ReadBlocksAsync(const std::string& dm_block_device,
-                                      const std::string& partition_name, size_t size) {
-    // 64k block size with io_depth of 64 is optimal
-    // for a single thread. We just need a single thread
-    // to read all the blocks from all dynamic partitions.
-    size_t io_depth = 64;
-    size_t bs = (64 * 1024);
-
-    if (!InitializeIouring(io_depth)) {
-        return false;
-    }
-
-    LOG(INFO) << "ReadBlockAsync start "
-              << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
-              << " Size: " << size;
-
-    auto scope_guard = android::base::make_scope_guard([this]() -> void { FinalizeIouring(); });
-
-    std::vector<std::unique_ptr<struct iovec>> vecs;
-    using AlignedBuf = std::unique_ptr<void, decltype(free)*>;
-    std::vector<AlignedBuf> alignedBufVector;
-
-    /*
-     * TODO: We need aligned memory for DIRECT-IO. However, if we do
-     * a DIRECT-IO and verify the blocks then we need to inform
-     * update-verifier that block verification has been done and
-     * there is no need to repeat the same. We are not there yet
-     * as we need to see if there are any boot time improvements doing
-     * a DIRECT-IO.
-     *
-     * Also, we could you the same function post merge for block verification;
-     * again, we can do a DIRECT-IO instead of thrashing page-cache and
-     * hurting other applications.
-     *
-     * For now, we will just create aligned buffers but rely on buffered
-     * I/O until we have perf numbers to justify DIRECT-IO.
-     */
-    for (int i = 0; i < io_depth; i++) {
-        auto iovec = std::make_unique<struct iovec>();
-        vecs.push_back(std::move(iovec));
-
-        struct iovec* iovec_ptr = vecs[i].get();
-
-        if (posix_memalign(&iovec_ptr->iov_base, BLOCK_SZ, bs)) {
-            LOG(ERROR) << "posix_memalign failed";
-            return false;
-        }
-
-        iovec_ptr->iov_len = bs;
-        alignedBufVector.push_back(
-                std::unique_ptr<void, decltype(free)*>(iovec_ptr->iov_base, free));
-    }
-
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
-    if (fd.get() == -1) {
-        SNAP_PLOG(ERROR) << "File open failed - block-device " << dm_block_device
-                         << " partition-name: " << partition_name;
-        return false;
-    }
-
-    loff_t offset = 0;
-    size_t remain = size;
-    size_t read_sz = io_depth * bs;
-
-    while (remain > 0) {
-        size_t to_read = std::min(remain, read_sz);
-        size_t queue_size = to_read / bs;
-
-        for (int i = 0; i < queue_size; i++) {
-            struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
-            if (!sqe) {
-                SNAP_LOG(ERROR) << "io_uring_get_sqe() failed";
-                return false;
-            }
-
-            struct iovec* iovec_ptr = vecs[i].get();
-
-            io_uring_prep_read(sqe, fd.get(), iovec_ptr->iov_base, iovec_ptr->iov_len, offset);
-            sqe->flags |= IOSQE_ASYNC;
-            offset += bs;
-        }
-
-        int ret = io_uring_submit(ring_.get());
-        if (ret != queue_size) {
-            SNAP_LOG(ERROR) << "submit got: " << ret << " wanted: " << queue_size;
-            return false;
-        }
-
-        for (int i = 0; i < queue_size; i++) {
-            struct io_uring_cqe* cqe;
-
-            int ret = io_uring_wait_cqe(ring_.get(), &cqe);
-            if (ret) {
-                SNAP_PLOG(ERROR) << "wait_cqe failed" << ret;
-                return false;
-            }
-
-            if (cqe->res < 0) {
-                SNAP_LOG(ERROR) << "io failed with res: " << cqe->res;
-                return false;
-            }
-            io_uring_cqe_seen(ring_.get(), cqe);
-        }
-
-        remain -= to_read;
-    }
-
-    LOG(INFO) << "ReadBlockAsync complete: "
-              << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
-              << " Size: " << size;
-    return true;
-}
-
-void SnapshotHandler::ReadBlocksToCache(const std::string& dm_block_device,
-                                        const std::string& partition_name, off_t offset,
-                                        size_t size) {
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
-    if (fd.get() == -1) {
-        SNAP_PLOG(ERROR) << "Error reading " << dm_block_device
-                         << " partition-name: " << partition_name;
-        return;
-    }
-
-    size_t remain = size;
-    off_t file_offset = offset;
-    // We pick 4M I/O size based on the fact that the current
-    // update_verifier has a similar I/O size.
-    size_t read_sz = 1024 * BLOCK_SZ;
-    std::vector<uint8_t> buf(read_sz);
-
-    while (remain > 0) {
-        size_t to_read = std::min(remain, read_sz);
-
-        if (!android::base::ReadFullyAtOffset(fd.get(), buf.data(), to_read, file_offset)) {
-            SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
-                             << " at offset: " << file_offset
-                             << " partition-name: " << partition_name << " total-size: " << size
-                             << " remain_size: " << remain;
-            return;
-        }
-
-        file_offset += to_read;
-        remain -= to_read;
-    }
-
-    SNAP_LOG(INFO) << "Finished reading block-device: " << dm_block_device
-                   << " partition: " << partition_name << " size: " << size
-                   << " offset: " << offset;
-}
-
-void SnapshotHandler::ReadBlocks(const std::string partition_name,
-                                 const std::string& dm_block_device) {
-    SNAP_LOG(DEBUG) << "Reading partition: " << partition_name
-                    << " Block-Device: " << dm_block_device;
-
-    uint64_t dev_sz = 0;
-
-    unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (fd < 0) {
-        SNAP_LOG(ERROR) << "Cannot open block device";
-        return;
-    }
-
-    dev_sz = get_block_device_size(fd.get());
-    if (!dev_sz) {
-        SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
-        return;
-    }
-
-    int num_threads = 2;
-    size_t num_blocks = dev_sz >> BLOCK_SHIFT;
-    size_t num_blocks_per_thread = num_blocks / num_threads;
-    size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
-    off_t offset = 0;
-
-    for (int i = 0; i < num_threads; i++) {
-        std::async(std::launch::async, &SnapshotHandler::ReadBlocksToCache, this, dm_block_device,
-                   partition_name, offset, read_sz_per_thread);
-
-        offset += read_sz_per_thread;
-    }
-}
-
 /*
  * Entry point to launch threads
  */
@@ -526,42 +331,22 @@
                 std::async(std::launch::async, &Worker::RunThread, worker_threads_[i].get()));
     }
 
-    bool second_stage_init = true;
+    bool partition_verification = true;
 
-    // We don't want to read the blocks during first stage init.
+    // We don't want to read the blocks during first stage init or
+    // during post-install phase.
     if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) {
-        second_stage_init = false;
-    }
-
-    if (second_stage_init) {
-        SNAP_LOG(INFO) << "Reading blocks to cache....";
-        auto& dm = DeviceMapper::Instance();
-        auto dm_block_devices = dm.FindDmPartitions();
-        if (dm_block_devices.empty()) {
-            SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
-        } else {
-            auto parts = android::base::Split(misc_name_, "-");
-            std::string partition_name = parts[0];
-
-            const char* suffix_b = "_b";
-            const char* suffix_a = "_a";
-
-            partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
-            partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
-
-            if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
-                SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
-            } else {
-                ReadBlocks(partition_name, dm_block_devices.at(partition_name));
-            }
-        }
-    } else {
-        SNAP_LOG(INFO) << "Not reading block device into cache";
+        partition_verification = false;
     }
 
     std::future<bool> merge_thread =
             std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
 
+    // Now that the worker threads are up, scan the partitions.
+    if (partition_verification) {
+        update_verify_->VerifyUpdatePartition();
+    }
+
     bool ret = true;
     for (auto& t : threads) {
         ret = t.get() && ret;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 83d40f6..42237ef 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -18,6 +18,9 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
 
 #include <condition_variable>
 #include <cstring>
@@ -42,18 +45,22 @@
 #include <liburing.h>
 #include <snapuserd/snapuserd_buffer.h>
 #include <snapuserd/snapuserd_kernel.h>
+#include <storage_literals/storage_literals.h>
 
 namespace android {
 namespace snapshot {
 
 using android::base::unique_fd;
 using namespace std::chrono_literals;
+using namespace android::storage_literals;
 
 static constexpr size_t PAYLOAD_BUFFER_SZ = (1UL << 20);
 static_assert(PAYLOAD_BUFFER_SZ >= BLOCK_SZ);
 
 static constexpr int kNumWorkerThreads = 4;
 
+static constexpr int kNiceValueForMergeThreads = -5;
+
 #define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
 #define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
 
@@ -165,6 +172,36 @@
     std::unique_ptr<struct io_uring> ring_;
 };
 
+class UpdateVerify {
+  public:
+    UpdateVerify(const std::string& misc_name);
+    void VerifyUpdatePartition();
+    bool CheckPartitionVerification();
+
+  private:
+    enum class UpdateVerifyState {
+        VERIFY_UNKNOWN,
+        VERIFY_FAILED,
+        VERIFY_SUCCESS,
+    };
+
+    std::string misc_name_;
+    UpdateVerifyState state_;
+    std::mutex m_lock_;
+    std::condition_variable m_cv_;
+
+    int kMinThreadsToVerify = 1;
+    int kMaxThreadsToVerify = 4;
+    uint64_t kThresholdSize = 512_MiB;
+    uint64_t kBlockSizeVerify = 1_MiB;
+
+    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+    void UpdatePartitionVerificationState(UpdateVerifyState state);
+    bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
+    bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
+                      off_t offset, int skip_blocks, uint64_t dev_sz);
+};
+
 class Worker {
   public:
     Worker(const std::string& cow_device, const std::string& backing_device,
@@ -274,7 +311,7 @@
     const bool& IsAttached() const { return attached_; }
     void AttachControlDevice() { attached_ = true; }
 
-    void CheckMergeCompletionStatus();
+    bool CheckMergeCompletionStatus();
     bool CommitMerge(int num_merge_ops);
 
     void CloseFds() { cow_fd_ = {}; }
@@ -305,6 +342,8 @@
 
     // State transitions for merge
     void InitiateMerge();
+    void MonitorMerge();
+    void WakeupMonitorMergeThread();
     void WaitForMergeComplete();
     bool WaitForMergeBegin();
     void NotifyRAForMergeReady();
@@ -333,6 +372,7 @@
     void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
     void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
     bool MergeInitiated() { return merge_initiated_; }
+    bool MergeMonitored() { return merge_monitored_; }
     double GetMergePercentage() { return merge_completion_percentage_; }
 
     // Merge Block State Transitions
@@ -344,24 +384,16 @@
     MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
 
     bool IsIouringSupported();
+    bool CheckPartitionVerification() { return update_verify_->CheckPartitionVerification(); }
 
   private:
     bool ReadMetadata();
     sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
     chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
-    bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
     struct BufferState* GetBufferState();
     void UpdateMergeCompletionPercentage();
 
-    void ReadBlocks(const std::string partition_name, const std::string& dm_block_device);
-    void ReadBlocksToCache(const std::string& dm_block_device, const std::string& partition_name,
-                           off_t offset, size_t size);
-
-    bool InitializeIouring(int io_depth);
-    void FinalizeIouring();
-    bool ReadBlocksAsync(const std::string& dm_block_device, const std::string& partition_name,
-                         size_t size);
-
     // COW device
     std::string cow_device_;
     // Source device
@@ -407,12 +439,14 @@
     double merge_completion_percentage_;
 
     bool merge_initiated_ = false;
+    bool merge_monitored_ = false;
     bool attached_ = false;
     bool is_socket_present_;
     bool is_io_uring_enabled_ = false;
     bool scratch_space_ = false;
 
     std::unique_ptr<struct io_uring> ring_;
+    std::unique_ptr<UpdateVerify> update_verify_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index c26a2cd..63f47d6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -71,16 +71,16 @@
 }
 
 bool Worker::MergeReplaceZeroOps() {
-    // Flush every 8192 ops. Since all ops are independent and there is no
+    // Flush after merging 2MB. Since all ops are independent and there is no
     // dependency between COW ops, we will flush the data and the number
-    // of ops merged in COW file for every 8192 ops. If there is a crash,
-    // we will end up replaying some of the COW ops which were already merged.
-    // That is ok.
+    // of ops merged in COW block device. If there is a crash, we will
+    // end up replaying some of the COW ops which were already merged. That is
+    // ok.
     //
-    // Why 8192 ops ? Increasing this may improve merge time 3-4 seconds but
-    // we need to make sure that we checkpoint; 8k ops seems optimal. In-case
-    // if there is a crash merge should always make forward progress.
-    int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32;
+    // Although increasing this greater than 2MB may help in improving merge
+    // times; however, on devices with low memory, this can be problematic
+    // when there are multiple merge threads in parallel.
+    int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 2;
     int num_ops_merged = 0;
 
     SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
@@ -543,6 +543,10 @@
         return true;
     }
 
+    if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+        SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+    }
+
     SNAP_LOG(INFO) << "Merge starting..";
 
     if (!Init()) {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index fa2866f..b9e4255 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -727,6 +727,10 @@
 
     InitializeIouring();
 
+    if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+        SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+    }
+
     while (!RAIterDone()) {
         if (!ReadAheadIOStart()) {
             break;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index 82b2b25..1e7daed 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -55,10 +55,19 @@
     if (input == "initiate_merge") return DaemonOps::INITIATE;
     if (input == "merge_percent") return DaemonOps::PERCENTAGE;
     if (input == "getstatus") return DaemonOps::GETSTATUS;
+    if (input == "update-verify") return DaemonOps::UPDATE_VERIFY;
 
     return DaemonOps::INVALID;
 }
 
+UserSnapshotServer::UserSnapshotServer() {
+    monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+    if (monitor_merge_event_fd_ == -1) {
+        PLOG(FATAL) << "monitor_merge_event_fd_: failed to create eventfd";
+    }
+    terminating_ = false;
+}
+
 UserSnapshotServer::~UserSnapshotServer() {
     // Close any client sockets that were added via AcceptClient().
     for (size_t i = 1; i < watched_fds_.size(); i++) {
@@ -249,7 +258,7 @@
                     return Sendmsg(fd, "fail");
                 }
 
-                if (!StartMerge(*iter)) {
+                if (!StartMerge(&lock, *iter)) {
                     return Sendmsg(fd, "fail");
                 }
 
@@ -282,6 +291,14 @@
                 return Sendmsg(fd, merge_status);
             }
         }
+        case DaemonOps::UPDATE_VERIFY: {
+            std::lock_guard<std::mutex> lock(lock_);
+            if (!UpdateVerification(&lock)) {
+                return Sendmsg(fd, "fail");
+            }
+
+            return Sendmsg(fd, "success");
+        }
         default: {
             LOG(ERROR) << "Received unknown message type from client";
             Sendmsg(fd, "fail");
@@ -298,7 +315,7 @@
     }
 
     handler->snapuserd()->CloseFds();
-    handler->snapuserd()->CheckMergeCompletionStatus();
+    bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();
     handler->snapuserd()->UnmapBufferRegion();
 
     auto misc_name = handler->misc_name();
@@ -306,7 +323,11 @@
 
     {
         std::lock_guard<std::mutex> lock(lock_);
-        num_partitions_merge_complete_ += 1;
+        if (merge_completed) {
+            num_partitions_merge_complete_ += 1;
+            active_merge_threads_ -= 1;
+            WakeupMonitorMergeThread();
+        }
         handler->SetThreadTerminated();
         auto iter = FindHandler(&lock, handler->misc_name());
         if (iter == dm_users_.end()) {
@@ -418,6 +439,9 @@
 
         if (th.joinable()) th.join();
     }
+
+    stop_monitor_merge_thread_ = true;
+    WakeupMonitorMergeThread();
 }
 
 void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
@@ -502,13 +526,24 @@
     return true;
 }
 
-bool UserSnapshotServer::StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+bool UserSnapshotServer::StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+                                    const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+    CHECK(proof_of_lock);
+
     if (!handler->snapuserd()->IsAttached()) {
         LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
         return false;
     }
 
-    handler->snapuserd()->InitiateMerge();
+    handler->snapuserd()->MonitorMerge();
+
+    if (!is_merge_monitor_started_.has_value()) {
+        std::thread(&UserSnapshotServer::MonitorMerge, this).detach();
+        is_merge_monitor_started_ = true;
+    }
+
+    merge_handlers_.push(handler);
+    WakeupMonitorMergeThread();
     return true;
 }
 
@@ -590,6 +625,42 @@
     return true;
 }
 
+void UserSnapshotServer::WakeupMonitorMergeThread() {
+    uint64_t notify = 1;
+    ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), &notify, sizeof(notify)));
+    if (rc < 0) {
+        PLOG(FATAL) << "failed to notify monitor merge thread";
+    }
+}
+
+void UserSnapshotServer::MonitorMerge() {
+    while (!stop_monitor_merge_thread_) {
+        uint64_t testVal;
+        ssize_t ret =
+                TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal)));
+        if (ret == -1) {
+            PLOG(FATAL) << "Failed to read from eventfd";
+        } else if (ret == 0) {
+            LOG(FATAL) << "Hit EOF on eventfd";
+        }
+
+        LOG(INFO) << "MonitorMerge: active-merge-threads: " << active_merge_threads_;
+        {
+            std::lock_guard<std::mutex> lock(lock_);
+            while (active_merge_threads_ < kMaxMergeThreads && merge_handlers_.size() > 0) {
+                auto handler = merge_handlers_.front();
+                merge_handlers_.pop();
+                LOG(INFO) << "Starting merge for partition: "
+                          << handler->snapuserd()->GetMiscName();
+                handler->snapuserd()->InitiateMerge();
+                active_merge_threads_ += 1;
+            }
+        }
+    }
+
+    LOG(INFO) << "Exiting MonitorMerge: size: " << merge_handlers_.size();
+}
+
 bool UserSnapshotServer::WaitForSocket() {
     auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
 
@@ -646,6 +717,7 @@
     if (!StartWithSocket(true)) {
         return false;
     }
+
     return Run();
 }
 
@@ -687,5 +759,22 @@
     return true;
 }
 
+bool UserSnapshotServer::UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock) {
+    CHECK(proof_of_lock);
+
+    bool status = true;
+    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+        auto& th = (*iter)->thread();
+        if (th.joinable() && status) {
+            status = (*iter)->snapuserd()->CheckPartitionVerification() && status;
+        } else {
+            // return immediately if there is a failure
+            return false;
+        }
+    }
+
+    return status;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index 34e7941..c2af61f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <poll.h>
+#include <sys/eventfd.h>
 
 #include <cstdio>
 #include <cstring>
@@ -22,6 +23,8 @@
 #include <future>
 #include <iostream>
 #include <mutex>
+#include <optional>
+#include <queue>
 #include <sstream>
 #include <string>
 #include <thread>
@@ -34,6 +37,7 @@
 namespace snapshot {
 
 static constexpr uint32_t kMaxPacketSize = 512;
+static constexpr uint8_t kMaxMergeThreads = 2;
 
 enum class DaemonOps {
     INIT,
@@ -46,6 +50,7 @@
     INITIATE,
     PERCENTAGE,
     GETSTATUS,
+    UPDATE_VERIFY,
     INVALID,
 };
 
@@ -84,13 +89,19 @@
     std::vector<struct pollfd> watched_fds_;
     bool is_socket_present_ = false;
     int num_partitions_merge_complete_ = 0;
+    int active_merge_threads_ = 0;
+    bool stop_monitor_merge_thread_ = false;
     bool is_server_running_ = false;
     bool io_uring_enabled_ = false;
+    std::optional<bool> is_merge_monitor_started_;
+
+    android::base::unique_fd monitor_merge_event_fd_;
 
     std::mutex lock_;
 
     using HandlerList = std::vector<std::shared_ptr<UserSnapshotDmUserHandler>>;
     HandlerList dm_users_;
+    std::queue<std::shared_ptr<UserSnapshotDmUserHandler>> merge_handlers_;
 
     void AddWatchedFd(android::base::borrowed_fd fd, int events);
     void AcceptClient();
@@ -108,6 +119,8 @@
     bool IsTerminating() { return terminating_; }
 
     void RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler);
+    void MonitorMerge();
+
     void JoinAllThreads();
     bool StartWithSocket(bool start_listening);
 
@@ -118,8 +131,10 @@
     double GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock);
     void TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock);
 
+    bool UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock);
+
   public:
-    UserSnapshotServer() { terminating_ = false; }
+    UserSnapshotServer();
     ~UserSnapshotServer();
 
     bool Start(const std::string& socketname);
@@ -133,9 +148,11 @@
                                                           const std::string& backing_device,
                                                           const std::string& base_path_merge);
     bool StartHandler(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
-    bool StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
+    bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+                    const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
     std::string GetMergeStatus(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
 
+    void WakeupMonitorMergeThread();
     void SetTerminating() { terminating_ = true; }
     void ReceivedSocketSignal() { received_socket_signal_ = true; }
     void SetServerRunning() { is_server_running_ = true; }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index d4e1d7c..28c9f68 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -165,6 +165,13 @@
 using namespace android::dm;
 using android::base::unique_fd;
 
+void SnapshotHandler::MonitorMerge() {
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        merge_monitored_ = true;
+    }
+}
+
 // This is invoked once primarily by update-engine to initiate
 // the merge
 void SnapshotHandler::InitiateMerge() {
@@ -361,10 +368,16 @@
 
 std::string SnapshotHandler::GetMergeStatus() {
     bool merge_not_initiated = false;
+    bool merge_monitored = false;
     bool merge_failed = false;
 
     {
         std::lock_guard<std::mutex> lock(lock_);
+
+        if (MergeMonitored()) {
+            merge_monitored = true;
+        }
+
         if (!MergeInitiated()) {
             merge_not_initiated = true;
         }
@@ -387,6 +400,12 @@
             return "snapshot-merge-complete";
         }
 
+        // Merge monitor thread is tracking the merge but the merge thread
+        // is not started yet.
+        if (merge_monitored) {
+            return "snapshot-merge";
+        }
+
         // Return the state as "snapshot". If the device was rebooted during
         // merge, we will return the status as "snapshot". This is ok, as
         // libsnapshot will explicitly resume the merge. This is slightly
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
new file mode 100644
index 0000000..18c1dfc
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "snapuserd_core.h"
+
+#include <android-base/chrono_utils.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android;
+using namespace android::dm;
+using android::base::unique_fd;
+
+UpdateVerify::UpdateVerify(const std::string& misc_name)
+    : misc_name_(misc_name), state_(UpdateVerifyState::VERIFY_UNKNOWN) {}
+
+bool UpdateVerify::CheckPartitionVerification() {
+    auto now = std::chrono::system_clock::now();
+    auto deadline = now + 10s;
+    {
+        std::unique_lock<std::mutex> cv_lock(m_lock_);
+        while (state_ == UpdateVerifyState::VERIFY_UNKNOWN) {
+            auto status = m_cv_.wait_until(cv_lock, deadline);
+            if (status == std::cv_status::timeout) {
+                return false;
+            }
+        }
+    }
+
+    return (state_ == UpdateVerifyState::VERIFY_SUCCESS);
+}
+
+void UpdateVerify::UpdatePartitionVerificationState(UpdateVerifyState state) {
+    {
+        std::lock_guard<std::mutex> lock(m_lock_);
+        state_ = state;
+    }
+    m_cv_.notify_all();
+}
+
+void UpdateVerify::VerifyUpdatePartition() {
+    bool succeeded = false;
+
+    auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+        if (!succeeded) {
+            UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+        }
+    });
+
+    auto& dm = DeviceMapper::Instance();
+    auto dm_block_devices = dm.FindDmPartitions();
+    if (dm_block_devices.empty()) {
+        SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
+        return;
+    }
+
+    const auto parts = android::base::Split(misc_name_, "-");
+    std::string partition_name = parts[0];
+
+    constexpr auto&& suffix_b = "_b";
+    constexpr auto&& suffix_a = "_a";
+
+    partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
+    partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
+
+    if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
+        SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
+        return;
+    }
+
+    if (!VerifyPartition(partition_name, dm_block_devices.at(partition_name))) {
+        SNAP_LOG(ERROR) << "Partition: " << partition_name
+                        << " Block-device: " << dm_block_devices.at(partition_name)
+                        << " verification failed";
+    }
+    succeeded = true;
+}
+
+bool UpdateVerify::VerifyBlocks(const std::string& partition_name,
+                                const std::string& dm_block_device, off_t offset, int skip_blocks,
+                                uint64_t dev_sz) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+    if (fd < 0) {
+        SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+        return false;
+    }
+
+    loff_t file_offset = offset;
+    const uint64_t read_sz = kBlockSizeVerify;
+
+    void* addr;
+    ssize_t page_size = getpagesize();
+    if (posix_memalign(&addr, page_size, read_sz) < 0) {
+        SNAP_PLOG(ERROR) << "posix_memalign failed "
+                         << " page_size: " << page_size << " read_sz: " << read_sz;
+        return false;
+    }
+
+    std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
+
+    uint64_t bytes_read = 0;
+
+    while (true) {
+        size_t to_read = std::min((dev_sz - file_offset), read_sz);
+
+        if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+            SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
+                             << " partition-name: " << partition_name
+                             << " at offset: " << file_offset << " read-size: " << to_read
+                             << " block-size: " << dev_sz;
+            return false;
+        }
+
+        bytes_read += to_read;
+        file_offset += (skip_blocks * kBlockSizeVerify);
+        if (file_offset >= dev_sz) {
+            break;
+        }
+    }
+
+    SNAP_LOG(DEBUG) << "Verification success with bytes-read: " << bytes_read
+                    << " dev_sz: " << dev_sz << " partition_name: " << partition_name;
+
+    return true;
+}
+
+bool UpdateVerify::VerifyPartition(const std::string& partition_name,
+                                   const std::string& dm_block_device) {
+    android::base::Timer timer;
+
+    SNAP_LOG(INFO) << "VerifyPartition: " << partition_name << " Block-device: " << dm_block_device;
+
+    bool succeeded = false;
+    auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+        if (!succeeded) {
+            UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+        }
+    });
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+    if (fd < 0) {
+        SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+        return false;
+    }
+
+    uint64_t dev_sz = get_block_device_size(fd.get());
+    if (!dev_sz) {
+        SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
+        return false;
+    }
+
+    if (!IsBlockAligned(dev_sz)) {
+        SNAP_LOG(ERROR) << "dev_sz: " << dev_sz << " is not block aligned";
+        return false;
+    }
+
+    /*
+     * Not all partitions are of same size. Some partitions are as small as
+     * 100Mb. We can just finish them in a single thread. For bigger partitions
+     * such as product, 4 threads are sufficient enough.
+     *
+     * TODO: With io_uring SQ_POLL support, we can completely cut this
+     * down to just single thread for all partitions and potentially verify all
+     * the partitions with zero syscalls. Additionally, since block layer
+     * supports polling, IO_POLL could be used which will further cut down
+     * latency.
+     */
+    int num_threads = kMinThreadsToVerify;
+    if (dev_sz > kThresholdSize) {
+        num_threads = kMaxThreadsToVerify;
+    }
+
+    std::vector<std::future<bool>> threads;
+    off_t start_offset = 0;
+    const int skip_blocks = num_threads;
+
+    while (num_threads) {
+        threads.emplace_back(std::async(std::launch::async, &UpdateVerify::VerifyBlocks, this,
+                                        partition_name, dm_block_device, start_offset, skip_blocks,
+                                        dev_sz));
+        start_offset += kBlockSizeVerify;
+        num_threads -= 1;
+        if (start_offset >= dev_sz) {
+            break;
+        }
+    }
+
+    bool ret = true;
+    for (auto& t : threads) {
+        ret = t.get() && ret;
+    }
+
+    if (ret) {
+        succeeded = true;
+        UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_SUCCESS);
+        SNAP_LOG(INFO) << "Partition: " << partition_name << " Block-device: " << dm_block_device
+                       << " Size: " << dev_sz
+                       << " verification success. Duration : " << timer.duration().count() << " ms";
+        return true;
+    }
+
+    return false;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
old mode 100644
new mode 100755
index afc2d81..d387eb3
--- a/fs_mgr/libsnapshot/vts_ota_config_test.cpp
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -17,7 +17,17 @@
 #include <android-base/properties.h>
 #include <gtest/gtest.h>
 
+static int GetVsrLevel() {
+    return android::base::GetIntProperty("ro.vendor.api_level", -1);
+}
+
 TEST(VAB, Enabled) {
+    if (!android::base::GetBoolProperty("ro.build.ab_update", false) && (GetVsrLevel() < __ANDROID_API_T__)) {
+        GTEST_SKIP();
+    }
     ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false));
+    if (GetVsrLevel() < __ANDROID_API_T__) {
+        GTEST_SKIP();
+    }
     ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false));
 }
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index aac2cfd..ae8e459 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -28,8 +28,8 @@
 }
 
 TEST(fs, ErofsSupported) {
-    // S and higher for this test.
-    if (GetVsrLevel() < __ANDROID_API_S__) {
+    // T-launch GKI kernels and higher must support EROFS.
+    if (GetVsrLevel() < __ANDROID_API_T__) {
         GTEST_SKIP();
     }
 
diff --git a/healthd/Android.bp b/healthd/Android.bp
index f180006..d046fd4 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -318,7 +318,706 @@
     require_root: true,
 }
 
-// /system/etc/res/images/charger/battery_fail.png
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0001.png",
+    src: "images/battery-charging0001.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0001.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0002.png",
+    src: "images/battery-charging0002.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0002.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0003.png",
+    src: "images/battery-charging0003.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0003.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0004.png",
+    src: "images/battery-charging0004.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0004.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0005.png",
+    src: "images/battery-charging0005.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0005.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0006.png",
+    src: "images/battery-charging0006.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0006.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0007.png",
+    src: "images/battery-charging0007.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0007.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0008.png",
+    src: "images/battery-charging0008.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0008.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0009.png",
+    src: "images/battery-charging0009.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0009.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0010.png",
+    src: "images/battery-charging0010.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0010.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0011.png",
+    src: "images/battery-charging0011.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0011.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0012.png",
+    src: "images/battery-charging0012.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0012.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0013.png",
+    src: "images/battery-charging0013.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0013.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0014.png",
+    src: "images/battery-charging0014.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0014.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0015.png",
+    src: "images/battery-charging0015.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0015.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0016.png",
+    src: "images/battery-charging0016.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0016.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0017.png",
+    src: "images/battery-charging0017.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0017.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0018.png",
+    src: "images/battery-charging0018.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0018.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0019.png",
+    src: "images/battery-charging0019.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0019.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0020.png",
+    src: "images/battery-charging0020.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0020.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0021.png",
+    src: "images/battery-charging0021.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0021.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0022.png",
+    src: "images/battery-charging0022.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0022.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0023.png",
+    src: "images/battery-charging0023.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0023.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0024.png",
+    src: "images/battery-charging0024.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0024.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0025.png",
+    src: "images/battery-charging0025.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0025.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0026.png",
+    src: "images/battery-charging0026.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0026.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0027.png",
+    src: "images/battery-charging0027.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0027.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0028.png",
+    src: "images/battery-charging0028.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0028.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0029.png",
+    src: "images/battery-charging0029.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0029.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0030.png",
+    src: "images/battery-charging0030.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0030.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0031.png",
+    src: "images/battery-charging0031.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0031.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0032.png",
+    src: "images/battery-charging0032.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0032.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0033.png",
+    src: "images/battery-charging0033.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0033.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0034.png",
+    src: "images/battery-charging0034.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0034.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0035.png",
+    src: "images/battery-charging0035.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0035.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0036.png",
+    src: "images/battery-charging0036.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0036.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0037.png",
+    src: "images/battery-charging0037.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0037.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0038.png",
+    src: "images/battery-charging0038.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0038.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0039.png",
+    src: "images/battery-charging0039.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0039.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0040.png",
+    src: "images/battery-charging0040.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0040.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0041.png",
+    src: "images/battery-charging0041.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0041.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0042.png",
+    src: "images/battery-charging0042.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0042.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0043.png",
+    src: "images/battery-charging0043.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0043.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0044.png",
+    src: "images/battery-charging0044.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0044.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0045.png",
+    src: "images/battery-charging0045.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0045.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0046.png",
+    src: "images/battery-charging0046.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0046.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0047.png",
+    src: "images/battery-charging0047.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0047.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0048.png",
+    src: "images/battery-charging0048.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0048.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0049.png",
+    src: "images/battery-charging0049.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0049.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0050.png",
+    src: "images/battery-charging0050.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0050.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0051.png",
+    src: "images/battery-charging0051.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0051.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0052.png",
+    src: "images/battery-charging0052.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0052.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0053.png",
+    src: "images/battery-charging0053.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0053.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0054.png",
+    src: "images/battery-charging0054.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0054.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0055.png",
+    src: "images/battery-charging0055.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0055.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0056.png",
+    src: "images/battery-charging0056.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0056.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0057.png",
+    src: "images/battery-charging0057.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0057.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0058.png",
+    src: "images/battery-charging0058.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0058.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0059.png",
+    src: "images/battery-charging0059.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0059.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0060.png",
+    src: "images/battery-charging0060.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0060.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0061.png",
+    src: "images/battery-charging0061.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0061.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0062.png",
+    src: "images/battery-charging0062.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0062.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0063.png",
+    src: "images/battery-charging0063.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0063.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0064.png",
+    src: "images/battery-charging0064.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0064.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0065.png",
+    src: "images/battery-charging0065.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0065.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0066.png",
+    src: "images/battery-charging0066.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0066.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0067.png",
+    src: "images/battery-charging0067.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0067.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0068.png",
+    src: "images/battery-charging0068.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0068.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0069.png",
+    src: "images/battery-charging0069.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0069.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0070.png",
+    src: "images/battery-charging0070.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0070.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0071.png",
+    src: "images/battery-charging0071.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0071.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0072.png",
+    src: "images/battery-charging0072.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0072.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0073.png",
+    src: "images/battery-charging0073.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0073.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0074.png",
+    src: "images/battery-charging0074.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0074.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0075.png",
+    src: "images/battery-charging0075.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0075.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0076.png",
+    src: "images/battery-charging0076.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0076.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0077.png",
+    src: "images/battery-charging0077.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0077.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0078.png",
+    src: "images/battery-charging0078.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0078.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0079.png",
+    src: "images/battery-charging0079.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0079.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0080.png",
+    src: "images/battery-charging0080.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0080.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0081.png",
+    src: "images/battery-charging0081.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0081.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0082.png",
+    src: "images/battery-charging0082.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0082.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0083.png",
+    src: "images/battery-charging0083.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0083.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0084.png",
+    src: "images/battery-charging0084.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0084.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0085.png",
+    src: "images/battery-charging0085.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0085.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0086.png",
+    src: "images/battery-charging0086.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0086.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0087.png",
+    src: "images/battery-charging0087.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0087.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0088.png",
+    src: "images/battery-charging0088.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0088.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0089.png",
+    src: "images/battery-charging0089.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0089.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0090.png",
+    src: "images/battery-charging0090.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0090.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0091.png",
+    src: "images/battery-charging0091.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0091.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0092.png",
+    src: "images/battery-charging0092.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0092.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0093.png",
+    src: "images/battery-charging0093.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0093.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0094.png",
+    src: "images/battery-charging0094.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0094.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0095.png",
+    src: "images/battery-charging0095.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0095.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0096.png",
+    src: "images/battery-charging0096.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0096.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0097.png",
+    src: "images/battery-charging0097.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0097.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0098.png",
+    src: "images/battery-charging0098.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0098.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0099.png",
+    src: "images/battery-charging0099.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0099.png",
+}
+
+prebuilt_etc {
+    name: "system_core_charger_res_images_battery-charging0100.png",
+    src: "images/battery-charging0100.png",
+    relative_install_path: "res/images/charger",
+    filename: "battery-charging0100.png",
+}
+
 prebuilt_etc {
     name: "system_core_charger_res_images_battery_fail.png",
     src: "images/battery_fail.png",
@@ -326,7 +1025,6 @@
     filename: "battery_fail.png",
 }
 
-// /system/etc/res/images/charger/battery_scale.png
 prebuilt_etc {
     name: "system_core_charger_res_images_battery_scale.png",
     src: "images/battery_scale.png",
@@ -337,6 +1035,106 @@
 phony {
     name: "charger_res_images",
     required: [
+        "system_core_charger_res_images_battery-charging0001.png",
+        "system_core_charger_res_images_battery-charging0002.png",
+        "system_core_charger_res_images_battery-charging0003.png",
+        "system_core_charger_res_images_battery-charging0004.png",
+        "system_core_charger_res_images_battery-charging0005.png",
+        "system_core_charger_res_images_battery-charging0006.png",
+        "system_core_charger_res_images_battery-charging0007.png",
+        "system_core_charger_res_images_battery-charging0008.png",
+        "system_core_charger_res_images_battery-charging0009.png",
+        "system_core_charger_res_images_battery-charging0010.png",
+        "system_core_charger_res_images_battery-charging0011.png",
+        "system_core_charger_res_images_battery-charging0012.png",
+        "system_core_charger_res_images_battery-charging0013.png",
+        "system_core_charger_res_images_battery-charging0014.png",
+        "system_core_charger_res_images_battery-charging0015.png",
+        "system_core_charger_res_images_battery-charging0016.png",
+        "system_core_charger_res_images_battery-charging0017.png",
+        "system_core_charger_res_images_battery-charging0018.png",
+        "system_core_charger_res_images_battery-charging0019.png",
+        "system_core_charger_res_images_battery-charging0020.png",
+        "system_core_charger_res_images_battery-charging0021.png",
+        "system_core_charger_res_images_battery-charging0022.png",
+        "system_core_charger_res_images_battery-charging0023.png",
+        "system_core_charger_res_images_battery-charging0024.png",
+        "system_core_charger_res_images_battery-charging0025.png",
+        "system_core_charger_res_images_battery-charging0026.png",
+        "system_core_charger_res_images_battery-charging0027.png",
+        "system_core_charger_res_images_battery-charging0028.png",
+        "system_core_charger_res_images_battery-charging0029.png",
+        "system_core_charger_res_images_battery-charging0030.png",
+        "system_core_charger_res_images_battery-charging0031.png",
+        "system_core_charger_res_images_battery-charging0032.png",
+        "system_core_charger_res_images_battery-charging0033.png",
+        "system_core_charger_res_images_battery-charging0034.png",
+        "system_core_charger_res_images_battery-charging0035.png",
+        "system_core_charger_res_images_battery-charging0036.png",
+        "system_core_charger_res_images_battery-charging0037.png",
+        "system_core_charger_res_images_battery-charging0038.png",
+        "system_core_charger_res_images_battery-charging0039.png",
+        "system_core_charger_res_images_battery-charging0040.png",
+        "system_core_charger_res_images_battery-charging0041.png",
+        "system_core_charger_res_images_battery-charging0042.png",
+        "system_core_charger_res_images_battery-charging0043.png",
+        "system_core_charger_res_images_battery-charging0044.png",
+        "system_core_charger_res_images_battery-charging0045.png",
+        "system_core_charger_res_images_battery-charging0046.png",
+        "system_core_charger_res_images_battery-charging0047.png",
+        "system_core_charger_res_images_battery-charging0048.png",
+        "system_core_charger_res_images_battery-charging0049.png",
+        "system_core_charger_res_images_battery-charging0050.png",
+        "system_core_charger_res_images_battery-charging0051.png",
+        "system_core_charger_res_images_battery-charging0052.png",
+        "system_core_charger_res_images_battery-charging0053.png",
+        "system_core_charger_res_images_battery-charging0054.png",
+        "system_core_charger_res_images_battery-charging0055.png",
+        "system_core_charger_res_images_battery-charging0056.png",
+        "system_core_charger_res_images_battery-charging0057.png",
+        "system_core_charger_res_images_battery-charging0058.png",
+        "system_core_charger_res_images_battery-charging0059.png",
+        "system_core_charger_res_images_battery-charging0060.png",
+        "system_core_charger_res_images_battery-charging0061.png",
+        "system_core_charger_res_images_battery-charging0062.png",
+        "system_core_charger_res_images_battery-charging0063.png",
+        "system_core_charger_res_images_battery-charging0064.png",
+        "system_core_charger_res_images_battery-charging0065.png",
+        "system_core_charger_res_images_battery-charging0066.png",
+        "system_core_charger_res_images_battery-charging0067.png",
+        "system_core_charger_res_images_battery-charging0068.png",
+        "system_core_charger_res_images_battery-charging0069.png",
+        "system_core_charger_res_images_battery-charging0070.png",
+        "system_core_charger_res_images_battery-charging0071.png",
+        "system_core_charger_res_images_battery-charging0072.png",
+        "system_core_charger_res_images_battery-charging0073.png",
+        "system_core_charger_res_images_battery-charging0074.png",
+        "system_core_charger_res_images_battery-charging0075.png",
+        "system_core_charger_res_images_battery-charging0076.png",
+        "system_core_charger_res_images_battery-charging0077.png",
+        "system_core_charger_res_images_battery-charging0078.png",
+        "system_core_charger_res_images_battery-charging0079.png",
+        "system_core_charger_res_images_battery-charging0080.png",
+        "system_core_charger_res_images_battery-charging0081.png",
+        "system_core_charger_res_images_battery-charging0082.png",
+        "system_core_charger_res_images_battery-charging0083.png",
+        "system_core_charger_res_images_battery-charging0084.png",
+        "system_core_charger_res_images_battery-charging0085.png",
+        "system_core_charger_res_images_battery-charging0086.png",
+        "system_core_charger_res_images_battery-charging0087.png",
+        "system_core_charger_res_images_battery-charging0088.png",
+        "system_core_charger_res_images_battery-charging0089.png",
+        "system_core_charger_res_images_battery-charging0090.png",
+        "system_core_charger_res_images_battery-charging0091.png",
+        "system_core_charger_res_images_battery-charging0092.png",
+        "system_core_charger_res_images_battery-charging0093.png",
+        "system_core_charger_res_images_battery-charging0094.png",
+        "system_core_charger_res_images_battery-charging0095.png",
+        "system_core_charger_res_images_battery-charging0096.png",
+        "system_core_charger_res_images_battery-charging0097.png",
+        "system_core_charger_res_images_battery-charging0098.png",
+        "system_core_charger_res_images_battery-charging0099.png",
+        "system_core_charger_res_images_battery-charging0100.png",
         "system_core_charger_res_images_battery_fail.png",
         "system_core_charger_res_images_battery_scale.png",
     ],
diff --git a/healthd/animation.h b/healthd/animation.h
index c2d5f1c..795010d 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -30,6 +30,7 @@
 
 struct animation {
     struct frame {
+        std::string name;
         int disp_time;
         int min_level;
         int max_level;
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 3e73fcd..7c79319 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -99,7 +99,7 @@
     gr_fb_blank(blank, drm);
 }
 
-/* support screen rotation for foldable phone */
+// support screen rotation for foldable phone
 void HealthdDraw::rotate_screen(int drm) {
     if (!graphics_available) return;
     if (drm == 0)
@@ -108,6 +108,11 @@
         gr_rotate(GRRotation::NONE /* Portrait mode */);
 }
 
+// detect dual display
+bool HealthdDraw::has_multiple_connectors() {
+    return graphics_available && gr_has_multiple_connectors();
+}
+
 void HealthdDraw::clear_screen(void) {
     if (!graphics_available) return;
     gr_color(0, 0, 0, 255);
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 3d4abbd..016db8e 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -38,6 +38,9 @@
   // Rotate screen.
   virtual void rotate_screen(int drm);
 
+  // Detect dual display
+  virtual bool has_multiple_connectors();
+
   static std::unique_ptr<HealthdDraw> Create(animation *anim);
 
  protected:
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 9fe85d4..41d2b63 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -16,6 +16,7 @@
 
 #include <charger/healthd_mode_charger.h>
 
+#include <array>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -162,38 +163,702 @@
 void Charger::InitDefaultAnimationFrames() {
     owned_frames_ = {
             {
-                    .disp_time = 750,
+                    .name = "charger/battery-charging0001.png",
+                    .disp_time = 500,
                     .min_level = 0,
+                    .max_level = 1,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0002.png",
+                    .disp_time = 500,
+                    .min_level = 2,
+                    .max_level = 2,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0003.png",
+                    .disp_time = 500,
+                    .min_level = 3,
+                    .max_level = 3,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0004.png",
+                    .disp_time = 500,
+                    .min_level = 4,
+                    .max_level = 4,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0005.png",
+                    .disp_time = 500,
+                    .min_level = 5,
+                    .max_level = 5,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0006.png",
+                    .disp_time = 500,
+                    .min_level = 6,
+                    .max_level = 6,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0007.png",
+                    .disp_time = 500,
+                    .min_level = 7,
+                    .max_level = 7,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0008.png",
+                    .disp_time = 500,
+                    .min_level = 8,
+                    .max_level = 8,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0009.png",
+                    .disp_time = 500,
+                    .min_level = 9,
+                    .max_level = 9,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0010.png",
+                    .disp_time = 500,
+                    .min_level = 10,
+                    .max_level = 10,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0011.png",
+                    .disp_time = 500,
+                    .min_level = 11,
+                    .max_level = 11,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0012.png",
+                    .disp_time = 500,
+                    .min_level = 12,
+                    .max_level = 12,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0013.png",
+                    .disp_time = 500,
+                    .min_level = 13,
+                    .max_level = 13,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0014.png",
+                    .disp_time = 500,
+                    .min_level = 14,
+                    .max_level = 14,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0015.png",
+                    .disp_time = 500,
+                    .min_level = 15,
+                    .max_level = 15,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0016.png",
+                    .disp_time = 500,
+                    .min_level = 16,
+                    .max_level = 16,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0017.png",
+                    .disp_time = 500,
+                    .min_level = 17,
+                    .max_level = 17,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0018.png",
+                    .disp_time = 500,
+                    .min_level = 18,
+                    .max_level = 18,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0019.png",
+                    .disp_time = 500,
+                    .min_level = 19,
                     .max_level = 19,
                     .surface = NULL,
             },
             {
-                    .disp_time = 750,
-                    .min_level = 0,
+                    .name = "charger/battery-charging0020.png",
+                    .disp_time = 500,
+                    .min_level = 20,
+                    .max_level = 20,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0021.png",
+                    .disp_time = 500,
+                    .min_level = 21,
+                    .max_level = 21,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0022.png",
+                    .disp_time = 500,
+                    .min_level = 22,
+                    .max_level = 22,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0023.png",
+                    .disp_time = 500,
+                    .min_level = 23,
+                    .max_level = 23,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0024.png",
+                    .disp_time = 500,
+                    .min_level = 24,
+                    .max_level = 24,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0025.png",
+                    .disp_time = 500,
+                    .min_level = 25,
+                    .max_level = 25,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0026.png",
+                    .disp_time = 500,
+                    .min_level = 26,
+                    .max_level = 26,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0027.png",
+                    .disp_time = 500,
+                    .min_level = 27,
+                    .max_level = 27,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0028.png",
+                    .disp_time = 500,
+                    .min_level = 28,
+                    .max_level = 28,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0029.png",
+                    .disp_time = 500,
+                    .min_level = 29,
+                    .max_level = 29,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0030.png",
+                    .disp_time = 500,
+                    .min_level = 30,
+                    .max_level = 30,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0031.png",
+                    .disp_time = 500,
+                    .min_level = 31,
+                    .max_level = 31,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0032.png",
+                    .disp_time = 500,
+                    .min_level = 32,
+                    .max_level = 32,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0033.png",
+                    .disp_time = 500,
+                    .min_level = 33,
+                    .max_level = 33,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0034.png",
+                    .disp_time = 500,
+                    .min_level = 34,
+                    .max_level = 34,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0035.png",
+                    .disp_time = 500,
+                    .min_level = 35,
+                    .max_level = 35,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0036.png",
+                    .disp_time = 500,
+                    .min_level = 36,
+                    .max_level = 36,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0037.png",
+                    .disp_time = 500,
+                    .min_level = 37,
+                    .max_level = 37,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0038.png",
+                    .disp_time = 500,
+                    .min_level = 38,
+                    .max_level = 38,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0039.png",
+                    .disp_time = 500,
+                    .min_level = 39,
                     .max_level = 39,
                     .surface = NULL,
             },
             {
-                    .disp_time = 750,
-                    .min_level = 0,
+                    .name = "charger/battery-charging0040.png",
+                    .disp_time = 500,
+                    .min_level = 40,
+                    .max_level = 40,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0041.png",
+                    .disp_time = 500,
+                    .min_level = 41,
+                    .max_level = 41,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0042.png",
+                    .disp_time = 500,
+                    .min_level = 42,
+                    .max_level = 42,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0043.png",
+                    .disp_time = 500,
+                    .min_level = 43,
+                    .max_level = 43,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0044.png",
+                    .disp_time = 500,
+                    .min_level = 44,
+                    .max_level = 44,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0045.png",
+                    .disp_time = 500,
+                    .min_level = 45,
+                    .max_level = 45,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0046.png",
+                    .disp_time = 500,
+                    .min_level = 46,
+                    .max_level = 46,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0047.png",
+                    .disp_time = 500,
+                    .min_level = 47,
+                    .max_level = 47,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0048.png",
+                    .disp_time = 500,
+                    .min_level = 48,
+                    .max_level = 48,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0049.png",
+                    .disp_time = 500,
+                    .min_level = 49,
+                    .max_level = 49,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0050.png",
+                    .disp_time = 500,
+                    .min_level = 50,
+                    .max_level = 50,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0051.png",
+                    .disp_time = 500,
+                    .min_level = 51,
+                    .max_level = 51,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0052.png",
+                    .disp_time = 500,
+                    .min_level = 52,
+                    .max_level = 52,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0053.png",
+                    .disp_time = 500,
+                    .min_level = 53,
+                    .max_level = 53,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0054.png",
+                    .disp_time = 500,
+                    .min_level = 54,
+                    .max_level = 54,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0055.png",
+                    .disp_time = 500,
+                    .min_level = 55,
+                    .max_level = 55,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0056.png",
+                    .disp_time = 500,
+                    .min_level = 56,
+                    .max_level = 56,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0057.png",
+                    .disp_time = 500,
+                    .min_level = 57,
+                    .max_level = 57,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0058.png",
+                    .disp_time = 500,
+                    .min_level = 58,
+                    .max_level = 58,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0059.png",
+                    .disp_time = 500,
+                    .min_level = 59,
                     .max_level = 59,
                     .surface = NULL,
             },
             {
-                    .disp_time = 750,
-                    .min_level = 0,
+                    .name = "charger/battery-charging0060.png",
+                    .disp_time = 500,
+                    .min_level = 60,
+                    .max_level = 60,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0061.png",
+                    .disp_time = 500,
+                    .min_level = 61,
+                    .max_level = 61,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0062.png",
+                    .disp_time = 500,
+                    .min_level = 62,
+                    .max_level = 62,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0063.png",
+                    .disp_time = 500,
+                    .min_level = 63,
+                    .max_level = 63,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0064.png",
+                    .disp_time = 500,
+                    .min_level = 64,
+                    .max_level = 64,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0065.png",
+                    .disp_time = 500,
+                    .min_level = 65,
+                    .max_level = 65,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0066.png",
+                    .disp_time = 500,
+                    .min_level = 66,
+                    .max_level = 66,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0067.png",
+                    .disp_time = 500,
+                    .min_level = 67,
+                    .max_level = 67,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0068.png",
+                    .disp_time = 500,
+                    .min_level = 68,
+                    .max_level = 68,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0069.png",
+                    .disp_time = 500,
+                    .min_level = 69,
+                    .max_level = 69,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0070.png",
+                    .disp_time = 500,
+                    .min_level = 70,
+                    .max_level = 70,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0071.png",
+                    .disp_time = 500,
+                    .min_level = 71,
+                    .max_level = 71,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0072.png",
+                    .disp_time = 500,
+                    .min_level = 72,
+                    .max_level = 72,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0073.png",
+                    .disp_time = 500,
+                    .min_level = 73,
+                    .max_level = 73,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0074.png",
+                    .disp_time = 500,
+                    .min_level = 74,
+                    .max_level = 74,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0075.png",
+                    .disp_time = 500,
+                    .min_level = 75,
+                    .max_level = 75,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0076.png",
+                    .disp_time = 500,
+                    .min_level = 76,
+                    .max_level = 76,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0077.png",
+                    .disp_time = 500,
+                    .min_level = 77,
+                    .max_level = 77,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0078.png",
+                    .disp_time = 500,
+                    .min_level = 78,
+                    .max_level = 78,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0079.png",
+                    .disp_time = 500,
+                    .min_level = 79,
                     .max_level = 79,
                     .surface = NULL,
             },
             {
-                    .disp_time = 750,
+                    .name = "charger/battery-charging0080.png",
+                    .disp_time = 500,
                     .min_level = 80,
+                    .max_level = 80,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0081.png",
+                    .disp_time = 500,
+                    .min_level = 81,
+                    .max_level = 81,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0082.png",
+                    .disp_time = 500,
+                    .min_level = 82,
+                    .max_level = 82,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0083.png",
+                    .disp_time = 500,
+                    .min_level = 83,
+                    .max_level = 83,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0084.png",
+                    .disp_time = 500,
+                    .min_level = 84,
+                    .max_level = 84,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0085.png",
+                    .disp_time = 500,
+                    .min_level = 85,
+                    .max_level = 85,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0086.png",
+                    .disp_time = 500,
+                    .min_level = 86,
+                    .max_level = 86,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0087.png",
+                    .disp_time = 500,
+                    .min_level = 87,
+                    .max_level = 87,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0088.png",
+                    .disp_time = 500,
+                    .min_level = 88,
+                    .max_level = 88,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0089.png",
+                    .disp_time = 500,
+                    .min_level = 89,
+                    .max_level = 89,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0090.png",
+                    .disp_time = 500,
+                    .min_level = 90,
+                    .max_level = 90,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0091.png",
+                    .disp_time = 500,
+                    .min_level = 91,
+                    .max_level = 91,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0092.png",
+                    .disp_time = 500,
+                    .min_level = 92,
+                    .max_level = 92,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0093.png",
+                    .disp_time = 500,
+                    .min_level = 93,
+                    .max_level = 93,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0094.png",
+                    .disp_time = 500,
+                    .min_level = 94,
+                    .max_level = 94,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0095.png",
+                    .disp_time = 500,
+                    .min_level = 95,
                     .max_level = 95,
                     .surface = NULL,
             },
             {
-                    .disp_time = 750,
-                    .min_level = 0,
+                    .name = "charger/battery-charging0096.png",
+                    .disp_time = 500,
+                    .min_level = 96,
+                    .max_level = 96,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0097.png",
+                    .disp_time = 500,
+                    .min_level = 97,
+                    .max_level = 97,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0098.png",
+                    .disp_time = 500,
+                    .min_level = 98,
+                    .max_level = 98,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0099.png",
+                    .disp_time = 500,
+                    .min_level = 99,
+                    .max_level = 99,
+                    .surface = NULL,
+            },
+            {
+                    .name = "charger/battery-charging0100.png",
+                    .disp_time = 500,
+                    .min_level = 100,
                     .max_level = 100,
                     .surface = NULL,
             },
@@ -289,6 +954,18 @@
     anim->run = false;
 }
 
+void Charger::BlankSecScreen() {
+    int drm = drm_ == DRM_INNER ? 1 : 0;
+
+    if (!init_screen_) {
+        /* blank the secondary screen */
+        healthd_draw_->blank_screen(false, drm);
+        healthd_draw_->redraw_screen(&batt_anim_, surf_unknown_);
+        healthd_draw_->blank_screen(true, drm);
+        init_screen_ = true;
+    }
+}
+
 void Charger::UpdateScreenState(int64_t now) {
     int disp_time;
 
@@ -338,6 +1015,9 @@
         reset_animation(&batt_anim_);
         next_screen_transition_ = -1;
         healthd_draw_->blank_screen(true, static_cast<int>(drm_));
+        if (healthd_draw_->has_multiple_connectors()) {
+            BlankSecScreen();
+        }
         screen_blanked_ = true;
         LOGV("[%" PRId64 "] animation done\n", now);
         if (configuration_->ChargerIsOnline()) {
@@ -606,6 +1286,125 @@
     }
 }
 
+/* Follow in ../hardware/qcom/display/liblight/lights.c */
+static constexpr const char* rgb_red_blink_file = "/sys/class/leds/red/breath";
+static constexpr const char* rgb_red_led_file   = "/sys/class/leds/red/brightness";
+static constexpr const char* rgb_green_led_file = "/sys/class/leds/green/brightness";
+static constexpr const char* rgb_blue_led_file  = "/sys/class/leds/blue/brightness";
+using light_buffer_t = std::array<char, 128u>;
+static constexpr unsigned int RGB_ON_LEVEL  = 0x7F;
+static constexpr unsigned int RGB_OFF_LEVEL = 0x00;
+static int write_level_to_led(unsigned int level, const char* led_file)
+{
+    const int fd = open(led_file, O_WRONLY);
+    if (fd < 0) {
+        return -errno;
+    }
+    light_buffer_t buffer{};
+    const int bytes = sprintf(buffer.data(), "%u", level);
+    if (bytes <= 0) {
+        LOGE("Charging RGB: Failed on buffer init!\n");
+        close(fd);
+        return -1;
+    }
+    const int vRet = write(fd, buffer.data(), static_cast<size_t>(bytes));
+    LOGV("Charging RGB: Config LED %s(%d)\n", buffer.data(), vRet);
+    const int errno_ = errno;
+    close(fd);
+    if (vRet == -1) {
+        LOGE("Charging RGB: Failed at write(): %d\n", errno_);
+        return -errno_;
+    }
+    return 0;
+}
+static int write_rgb_red_led_cfg(unsigned int level)
+{
+    const int err = write_level_to_led(level, rgb_red_led_file);
+    LOGV("Charging RGB: Config RED LED (%d)\n", err);
+    return err;
+}
+static int write_rgb_red_blink_cfg(unsigned int level, unsigned int onMS, unsigned int offMS)
+{
+    int blink = 0;
+    if (onMS > 0 && offMS > 0) {
+        /* if ON time == OFF time
+         *   use blink mode 2
+         * else
+         *   use blink mode 1
+         */
+        if (onMS == offMS) {
+            blink = 2;
+        } else {
+            blink = 1;
+        }
+    }
+    if (blink) {
+        const int err = write_level_to_led(blink, rgb_red_blink_file);
+        if (err) {
+            LOGE("Charging RGB: Config RED BLINK failed!\n");
+            return err;
+        }
+        LOGV("Charging RGB: Config RED BLINK (%d)\n", err);
+        return 0;
+    }
+    return write_rgb_red_led_cfg(level);
+}
+static int write_rgb_green_led_cfg(unsigned int level)
+{
+    const int err = write_level_to_led(level, rgb_green_led_file);
+    LOGV("Charging RGB: Config GREEN LED (%d)\n", err);
+    return err;
+}
+static int write_rgb_blue_led_cfg(unsigned int level)
+{
+    const int err = write_level_to_led(level, rgb_blue_led_file);
+    LOGV("Charging RGB: Config BLUE LED (%d)\n", err);
+    return err;
+}
+static void charging_rgbled_off()
+{
+    write_rgb_red_led_cfg(RGB_OFF_LEVEL);
+    write_rgb_green_led_cfg(RGB_OFF_LEVEL);
+    write_rgb_blue_led_cfg(RGB_OFF_LEVEL);
+}
+void Charger::charging_rgbled_update()
+{
+    if (!have_battery_state_) {
+        LOGE("Charging RGB: There is not battery\n");
+        led_last_battery_level_ = -1;  /* Reset record when charger plug out */
+        charging_rgbled_off();
+        return;
+    }
+    if (!configuration_->ChargerIsOnline()) {
+        LOGV("Charging RGB: unplug charging\n");
+        led_last_battery_level_ = -1;  /* Reset record when charger plug out */
+        charging_rgbled_off();
+        return;
+    }
+    const int battery_level = health_info_.battery_level;
+    if (led_last_battery_level_ == battery_level) {
+        return;
+    }
+    LOGV("Charging RGB: Battery Level: %d / %d\n", battery_level, led_last_battery_level_);
+    led_last_battery_level_ = battery_level;
+    if (battery_level >= 100) {
+        write_rgb_green_led_cfg(RGB_ON_LEVEL);
+        write_rgb_red_led_cfg(RGB_OFF_LEVEL);
+    } else if (battery_level < 100 && battery_level >= 90) {
+        write_rgb_green_led_cfg(RGB_ON_LEVEL);
+        write_rgb_red_led_cfg(RGB_OFF_LEVEL);
+    } else if (battery_level < 90 && battery_level >= 15) {
+        write_rgb_green_led_cfg(RGB_ON_LEVEL);
+        write_rgb_red_led_cfg(RGB_ON_LEVEL);
+    } else if (battery_level < 15 && battery_level >= 5) {
+        write_rgb_green_led_cfg(RGB_OFF_LEVEL);
+        write_rgb_red_blink_cfg(RGB_ON_LEVEL, 0x00, 0x00);
+    } else {
+        write_rgb_green_led_cfg(RGB_OFF_LEVEL);
+        write_rgb_red_blink_cfg(RGB_ON_LEVEL, 0x00, 0x00);
+    }
+}
+
 void Charger::OnHeartbeat() {
     // charger* charger = &charger_state;
     int64_t now = curr_time_ms();
@@ -617,6 +1416,8 @@
      * screen transitions (animations, etc)
      */
     UpdateScreenState(now);
+
+    charging_rgbled_update();
 }
 
 void Charger::OnHealthInfoChanged(const ChargerHealthInfo& health_info) {
@@ -767,24 +1568,15 @@
         }
     }
 
-    GRSurface** scale_frames;
-    int scale_count;
-    int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text
-                    // chunk). We are using hard-coded frame.disp_time instead.
-    ret = CreateMultiDisplaySurface(batt_anim_.animation_file, &scale_count, &scale_fps,
-                                    &scale_frames);
-    if (ret < 0) {
-        LOGE("Cannot load battery_scale image\n");
-        batt_anim_.num_frames = 0;
-        batt_anim_.num_cycles = 1;
-    } else if (scale_count != batt_anim_.num_frames) {
-        LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,
-             batt_anim_.num_frames);
-        batt_anim_.num_frames = 0;
-        batt_anim_.num_cycles = 1;
-    } else {
-        for (i = 0; i < batt_anim_.num_frames; i++) {
-            batt_anim_.frames[i].surface = scale_frames[i];
+    for (i = 0; i < batt_anim_.num_frames; i++) {
+        LOGW("[B]%s(%d): frame.name=%s\n", __func__, __LINE__, batt_anim_.frames[i].name.c_str());
+        ret = CreateDisplaySurface((system_animation_root + batt_anim_.frames[i].name).c_str(),
+                                   &batt_anim_.frames[i].surface);
+        if (ret < 0) {
+            LOGE("Cannot load image %s\n", batt_anim_.frames[i].name.c_str());
+            /* TODO: free the already allocated surfaces... */
+            batt_anim_.num_frames = 0;
+            batt_anim_.num_cycles = 1;
         }
     }
     drm_ = DRM_INNER;
diff --git a/healthd/images/battery-charging0001.png b/healthd/images/battery-charging0001.png
new file mode 100644
index 0000000..9f0a378
--- /dev/null
+++ b/healthd/images/battery-charging0001.png
Binary files differ
diff --git a/healthd/images/battery-charging0002.png b/healthd/images/battery-charging0002.png
new file mode 100644
index 0000000..9592934
--- /dev/null
+++ b/healthd/images/battery-charging0002.png
Binary files differ
diff --git a/healthd/images/battery-charging0003.png b/healthd/images/battery-charging0003.png
new file mode 100644
index 0000000..d312f57
--- /dev/null
+++ b/healthd/images/battery-charging0003.png
Binary files differ
diff --git a/healthd/images/battery-charging0004.png b/healthd/images/battery-charging0004.png
new file mode 100644
index 0000000..b883715
--- /dev/null
+++ b/healthd/images/battery-charging0004.png
Binary files differ
diff --git a/healthd/images/battery-charging0005.png b/healthd/images/battery-charging0005.png
new file mode 100644
index 0000000..3af10ac
--- /dev/null
+++ b/healthd/images/battery-charging0005.png
Binary files differ
diff --git a/healthd/images/battery-charging0006.png b/healthd/images/battery-charging0006.png
new file mode 100644
index 0000000..8e35bf1
--- /dev/null
+++ b/healthd/images/battery-charging0006.png
Binary files differ
diff --git a/healthd/images/battery-charging0007.png b/healthd/images/battery-charging0007.png
new file mode 100644
index 0000000..68b0771
--- /dev/null
+++ b/healthd/images/battery-charging0007.png
Binary files differ
diff --git a/healthd/images/battery-charging0008.png b/healthd/images/battery-charging0008.png
new file mode 100644
index 0000000..14db6b2
--- /dev/null
+++ b/healthd/images/battery-charging0008.png
Binary files differ
diff --git a/healthd/images/battery-charging0009.png b/healthd/images/battery-charging0009.png
new file mode 100644
index 0000000..e57a671
--- /dev/null
+++ b/healthd/images/battery-charging0009.png
Binary files differ
diff --git a/healthd/images/battery-charging0010.png b/healthd/images/battery-charging0010.png
new file mode 100644
index 0000000..b753006
--- /dev/null
+++ b/healthd/images/battery-charging0010.png
Binary files differ
diff --git a/healthd/images/battery-charging0011.png b/healthd/images/battery-charging0011.png
new file mode 100644
index 0000000..ff0e354
--- /dev/null
+++ b/healthd/images/battery-charging0011.png
Binary files differ
diff --git a/healthd/images/battery-charging0012.png b/healthd/images/battery-charging0012.png
new file mode 100644
index 0000000..9789542
--- /dev/null
+++ b/healthd/images/battery-charging0012.png
Binary files differ
diff --git a/healthd/images/battery-charging0013.png b/healthd/images/battery-charging0013.png
new file mode 100644
index 0000000..0bdd8a1
--- /dev/null
+++ b/healthd/images/battery-charging0013.png
Binary files differ
diff --git a/healthd/images/battery-charging0014.png b/healthd/images/battery-charging0014.png
new file mode 100644
index 0000000..76c7a0c
--- /dev/null
+++ b/healthd/images/battery-charging0014.png
Binary files differ
diff --git a/healthd/images/battery-charging0015.png b/healthd/images/battery-charging0015.png
new file mode 100644
index 0000000..aff6bbd
--- /dev/null
+++ b/healthd/images/battery-charging0015.png
Binary files differ
diff --git a/healthd/images/battery-charging0016.png b/healthd/images/battery-charging0016.png
new file mode 100644
index 0000000..a4e691a
--- /dev/null
+++ b/healthd/images/battery-charging0016.png
Binary files differ
diff --git a/healthd/images/battery-charging0017.png b/healthd/images/battery-charging0017.png
new file mode 100644
index 0000000..87b2dc1
--- /dev/null
+++ b/healthd/images/battery-charging0017.png
Binary files differ
diff --git a/healthd/images/battery-charging0018.png b/healthd/images/battery-charging0018.png
new file mode 100644
index 0000000..837861f
--- /dev/null
+++ b/healthd/images/battery-charging0018.png
Binary files differ
diff --git a/healthd/images/battery-charging0019.png b/healthd/images/battery-charging0019.png
new file mode 100644
index 0000000..9e1605d
--- /dev/null
+++ b/healthd/images/battery-charging0019.png
Binary files differ
diff --git a/healthd/images/battery-charging0020.png b/healthd/images/battery-charging0020.png
new file mode 100644
index 0000000..8224eb0
--- /dev/null
+++ b/healthd/images/battery-charging0020.png
Binary files differ
diff --git a/healthd/images/battery-charging0021.png b/healthd/images/battery-charging0021.png
new file mode 100644
index 0000000..8b48fcb
--- /dev/null
+++ b/healthd/images/battery-charging0021.png
Binary files differ
diff --git a/healthd/images/battery-charging0022.png b/healthd/images/battery-charging0022.png
new file mode 100644
index 0000000..d1b8c3c
--- /dev/null
+++ b/healthd/images/battery-charging0022.png
Binary files differ
diff --git a/healthd/images/battery-charging0023.png b/healthd/images/battery-charging0023.png
new file mode 100644
index 0000000..2e26268
--- /dev/null
+++ b/healthd/images/battery-charging0023.png
Binary files differ
diff --git a/healthd/images/battery-charging0024.png b/healthd/images/battery-charging0024.png
new file mode 100644
index 0000000..8ccfbb4
--- /dev/null
+++ b/healthd/images/battery-charging0024.png
Binary files differ
diff --git a/healthd/images/battery-charging0025.png b/healthd/images/battery-charging0025.png
new file mode 100644
index 0000000..41776fa
--- /dev/null
+++ b/healthd/images/battery-charging0025.png
Binary files differ
diff --git a/healthd/images/battery-charging0026.png b/healthd/images/battery-charging0026.png
new file mode 100644
index 0000000..498e915
--- /dev/null
+++ b/healthd/images/battery-charging0026.png
Binary files differ
diff --git a/healthd/images/battery-charging0027.png b/healthd/images/battery-charging0027.png
new file mode 100644
index 0000000..52e40fa
--- /dev/null
+++ b/healthd/images/battery-charging0027.png
Binary files differ
diff --git a/healthd/images/battery-charging0028.png b/healthd/images/battery-charging0028.png
new file mode 100644
index 0000000..146d670
--- /dev/null
+++ b/healthd/images/battery-charging0028.png
Binary files differ
diff --git a/healthd/images/battery-charging0029.png b/healthd/images/battery-charging0029.png
new file mode 100644
index 0000000..87b267b
--- /dev/null
+++ b/healthd/images/battery-charging0029.png
Binary files differ
diff --git a/healthd/images/battery-charging0030.png b/healthd/images/battery-charging0030.png
new file mode 100644
index 0000000..f7f9f36
--- /dev/null
+++ b/healthd/images/battery-charging0030.png
Binary files differ
diff --git a/healthd/images/battery-charging0031.png b/healthd/images/battery-charging0031.png
new file mode 100644
index 0000000..5473037
--- /dev/null
+++ b/healthd/images/battery-charging0031.png
Binary files differ
diff --git a/healthd/images/battery-charging0032.png b/healthd/images/battery-charging0032.png
new file mode 100644
index 0000000..ee50bf7
--- /dev/null
+++ b/healthd/images/battery-charging0032.png
Binary files differ
diff --git a/healthd/images/battery-charging0033.png b/healthd/images/battery-charging0033.png
new file mode 100644
index 0000000..160e0f5
--- /dev/null
+++ b/healthd/images/battery-charging0033.png
Binary files differ
diff --git a/healthd/images/battery-charging0034.png b/healthd/images/battery-charging0034.png
new file mode 100644
index 0000000..e54baf8
--- /dev/null
+++ b/healthd/images/battery-charging0034.png
Binary files differ
diff --git a/healthd/images/battery-charging0035.png b/healthd/images/battery-charging0035.png
new file mode 100644
index 0000000..4fcac18
--- /dev/null
+++ b/healthd/images/battery-charging0035.png
Binary files differ
diff --git a/healthd/images/battery-charging0036.png b/healthd/images/battery-charging0036.png
new file mode 100644
index 0000000..7829485
--- /dev/null
+++ b/healthd/images/battery-charging0036.png
Binary files differ
diff --git a/healthd/images/battery-charging0037.png b/healthd/images/battery-charging0037.png
new file mode 100644
index 0000000..593139f
--- /dev/null
+++ b/healthd/images/battery-charging0037.png
Binary files differ
diff --git a/healthd/images/battery-charging0038.png b/healthd/images/battery-charging0038.png
new file mode 100644
index 0000000..49e7499
--- /dev/null
+++ b/healthd/images/battery-charging0038.png
Binary files differ
diff --git a/healthd/images/battery-charging0039.png b/healthd/images/battery-charging0039.png
new file mode 100644
index 0000000..827a609
--- /dev/null
+++ b/healthd/images/battery-charging0039.png
Binary files differ
diff --git a/healthd/images/battery-charging0040.png b/healthd/images/battery-charging0040.png
new file mode 100644
index 0000000..2127825
--- /dev/null
+++ b/healthd/images/battery-charging0040.png
Binary files differ
diff --git a/healthd/images/battery-charging0041.png b/healthd/images/battery-charging0041.png
new file mode 100644
index 0000000..8c81d47
--- /dev/null
+++ b/healthd/images/battery-charging0041.png
Binary files differ
diff --git a/healthd/images/battery-charging0042.png b/healthd/images/battery-charging0042.png
new file mode 100644
index 0000000..e32d2bf
--- /dev/null
+++ b/healthd/images/battery-charging0042.png
Binary files differ
diff --git a/healthd/images/battery-charging0043.png b/healthd/images/battery-charging0043.png
new file mode 100644
index 0000000..ec28657
--- /dev/null
+++ b/healthd/images/battery-charging0043.png
Binary files differ
diff --git a/healthd/images/battery-charging0044.png b/healthd/images/battery-charging0044.png
new file mode 100644
index 0000000..57e33ff
--- /dev/null
+++ b/healthd/images/battery-charging0044.png
Binary files differ
diff --git a/healthd/images/battery-charging0045.png b/healthd/images/battery-charging0045.png
new file mode 100644
index 0000000..28adfbb
--- /dev/null
+++ b/healthd/images/battery-charging0045.png
Binary files differ
diff --git a/healthd/images/battery-charging0046.png b/healthd/images/battery-charging0046.png
new file mode 100644
index 0000000..3aa7409
--- /dev/null
+++ b/healthd/images/battery-charging0046.png
Binary files differ
diff --git a/healthd/images/battery-charging0047.png b/healthd/images/battery-charging0047.png
new file mode 100644
index 0000000..6bac491
--- /dev/null
+++ b/healthd/images/battery-charging0047.png
Binary files differ
diff --git a/healthd/images/battery-charging0048.png b/healthd/images/battery-charging0048.png
new file mode 100644
index 0000000..1de66b4
--- /dev/null
+++ b/healthd/images/battery-charging0048.png
Binary files differ
diff --git a/healthd/images/battery-charging0049.png b/healthd/images/battery-charging0049.png
new file mode 100644
index 0000000..ab80ba5
--- /dev/null
+++ b/healthd/images/battery-charging0049.png
Binary files differ
diff --git a/healthd/images/battery-charging0050.png b/healthd/images/battery-charging0050.png
new file mode 100644
index 0000000..e393bf9
--- /dev/null
+++ b/healthd/images/battery-charging0050.png
Binary files differ
diff --git a/healthd/images/battery-charging0051.png b/healthd/images/battery-charging0051.png
new file mode 100644
index 0000000..300c987
--- /dev/null
+++ b/healthd/images/battery-charging0051.png
Binary files differ
diff --git a/healthd/images/battery-charging0052.png b/healthd/images/battery-charging0052.png
new file mode 100644
index 0000000..da571ae
--- /dev/null
+++ b/healthd/images/battery-charging0052.png
Binary files differ
diff --git a/healthd/images/battery-charging0053.png b/healthd/images/battery-charging0053.png
new file mode 100644
index 0000000..1f64265
--- /dev/null
+++ b/healthd/images/battery-charging0053.png
Binary files differ
diff --git a/healthd/images/battery-charging0054.png b/healthd/images/battery-charging0054.png
new file mode 100644
index 0000000..d21c96e
--- /dev/null
+++ b/healthd/images/battery-charging0054.png
Binary files differ
diff --git a/healthd/images/battery-charging0055.png b/healthd/images/battery-charging0055.png
new file mode 100644
index 0000000..517ddfd
--- /dev/null
+++ b/healthd/images/battery-charging0055.png
Binary files differ
diff --git a/healthd/images/battery-charging0056.png b/healthd/images/battery-charging0056.png
new file mode 100644
index 0000000..2ea3077
--- /dev/null
+++ b/healthd/images/battery-charging0056.png
Binary files differ
diff --git a/healthd/images/battery-charging0057.png b/healthd/images/battery-charging0057.png
new file mode 100644
index 0000000..ef8ae66
--- /dev/null
+++ b/healthd/images/battery-charging0057.png
Binary files differ
diff --git a/healthd/images/battery-charging0058.png b/healthd/images/battery-charging0058.png
new file mode 100644
index 0000000..93dfce8
--- /dev/null
+++ b/healthd/images/battery-charging0058.png
Binary files differ
diff --git a/healthd/images/battery-charging0059.png b/healthd/images/battery-charging0059.png
new file mode 100644
index 0000000..270a2a9
--- /dev/null
+++ b/healthd/images/battery-charging0059.png
Binary files differ
diff --git a/healthd/images/battery-charging0060.png b/healthd/images/battery-charging0060.png
new file mode 100644
index 0000000..8ff09f7
--- /dev/null
+++ b/healthd/images/battery-charging0060.png
Binary files differ
diff --git a/healthd/images/battery-charging0061.png b/healthd/images/battery-charging0061.png
new file mode 100644
index 0000000..0f889cb
--- /dev/null
+++ b/healthd/images/battery-charging0061.png
Binary files differ
diff --git a/healthd/images/battery-charging0062.png b/healthd/images/battery-charging0062.png
new file mode 100644
index 0000000..f596eb0
--- /dev/null
+++ b/healthd/images/battery-charging0062.png
Binary files differ
diff --git a/healthd/images/battery-charging0063.png b/healthd/images/battery-charging0063.png
new file mode 100644
index 0000000..6919e52
--- /dev/null
+++ b/healthd/images/battery-charging0063.png
Binary files differ
diff --git a/healthd/images/battery-charging0064.png b/healthd/images/battery-charging0064.png
new file mode 100644
index 0000000..bae2261
--- /dev/null
+++ b/healthd/images/battery-charging0064.png
Binary files differ
diff --git a/healthd/images/battery-charging0065.png b/healthd/images/battery-charging0065.png
new file mode 100644
index 0000000..5747f9b
--- /dev/null
+++ b/healthd/images/battery-charging0065.png
Binary files differ
diff --git a/healthd/images/battery-charging0066.png b/healthd/images/battery-charging0066.png
new file mode 100644
index 0000000..f874151
--- /dev/null
+++ b/healthd/images/battery-charging0066.png
Binary files differ
diff --git a/healthd/images/battery-charging0067.png b/healthd/images/battery-charging0067.png
new file mode 100644
index 0000000..c77f7f1
--- /dev/null
+++ b/healthd/images/battery-charging0067.png
Binary files differ
diff --git a/healthd/images/battery-charging0068.png b/healthd/images/battery-charging0068.png
new file mode 100644
index 0000000..154c1c6
--- /dev/null
+++ b/healthd/images/battery-charging0068.png
Binary files differ
diff --git a/healthd/images/battery-charging0069.png b/healthd/images/battery-charging0069.png
new file mode 100644
index 0000000..67fe662
--- /dev/null
+++ b/healthd/images/battery-charging0069.png
Binary files differ
diff --git a/healthd/images/battery-charging0070.png b/healthd/images/battery-charging0070.png
new file mode 100644
index 0000000..f297bd9
--- /dev/null
+++ b/healthd/images/battery-charging0070.png
Binary files differ
diff --git a/healthd/images/battery-charging0071.png b/healthd/images/battery-charging0071.png
new file mode 100644
index 0000000..f017a1d
--- /dev/null
+++ b/healthd/images/battery-charging0071.png
Binary files differ
diff --git a/healthd/images/battery-charging0072.png b/healthd/images/battery-charging0072.png
new file mode 100644
index 0000000..3492161
--- /dev/null
+++ b/healthd/images/battery-charging0072.png
Binary files differ
diff --git a/healthd/images/battery-charging0073.png b/healthd/images/battery-charging0073.png
new file mode 100644
index 0000000..687795f
--- /dev/null
+++ b/healthd/images/battery-charging0073.png
Binary files differ
diff --git a/healthd/images/battery-charging0074.png b/healthd/images/battery-charging0074.png
new file mode 100644
index 0000000..f8d651a
--- /dev/null
+++ b/healthd/images/battery-charging0074.png
Binary files differ
diff --git a/healthd/images/battery-charging0075.png b/healthd/images/battery-charging0075.png
new file mode 100644
index 0000000..dabc874
--- /dev/null
+++ b/healthd/images/battery-charging0075.png
Binary files differ
diff --git a/healthd/images/battery-charging0076.png b/healthd/images/battery-charging0076.png
new file mode 100644
index 0000000..774eff4
--- /dev/null
+++ b/healthd/images/battery-charging0076.png
Binary files differ
diff --git a/healthd/images/battery-charging0077.png b/healthd/images/battery-charging0077.png
new file mode 100644
index 0000000..4f2acba
--- /dev/null
+++ b/healthd/images/battery-charging0077.png
Binary files differ
diff --git a/healthd/images/battery-charging0078.png b/healthd/images/battery-charging0078.png
new file mode 100644
index 0000000..e45507d
--- /dev/null
+++ b/healthd/images/battery-charging0078.png
Binary files differ
diff --git a/healthd/images/battery-charging0079.png b/healthd/images/battery-charging0079.png
new file mode 100644
index 0000000..9780cf9
--- /dev/null
+++ b/healthd/images/battery-charging0079.png
Binary files differ
diff --git a/healthd/images/battery-charging0080.png b/healthd/images/battery-charging0080.png
new file mode 100644
index 0000000..33008e5
--- /dev/null
+++ b/healthd/images/battery-charging0080.png
Binary files differ
diff --git a/healthd/images/battery-charging0081.png b/healthd/images/battery-charging0081.png
new file mode 100644
index 0000000..077bab9
--- /dev/null
+++ b/healthd/images/battery-charging0081.png
Binary files differ
diff --git a/healthd/images/battery-charging0082.png b/healthd/images/battery-charging0082.png
new file mode 100644
index 0000000..a18157f
--- /dev/null
+++ b/healthd/images/battery-charging0082.png
Binary files differ
diff --git a/healthd/images/battery-charging0083.png b/healthd/images/battery-charging0083.png
new file mode 100644
index 0000000..523a7b8
--- /dev/null
+++ b/healthd/images/battery-charging0083.png
Binary files differ
diff --git a/healthd/images/battery-charging0084.png b/healthd/images/battery-charging0084.png
new file mode 100644
index 0000000..a2e1468
--- /dev/null
+++ b/healthd/images/battery-charging0084.png
Binary files differ
diff --git a/healthd/images/battery-charging0085.png b/healthd/images/battery-charging0085.png
new file mode 100644
index 0000000..441352e
--- /dev/null
+++ b/healthd/images/battery-charging0085.png
Binary files differ
diff --git a/healthd/images/battery-charging0086.png b/healthd/images/battery-charging0086.png
new file mode 100644
index 0000000..4188355
--- /dev/null
+++ b/healthd/images/battery-charging0086.png
Binary files differ
diff --git a/healthd/images/battery-charging0087.png b/healthd/images/battery-charging0087.png
new file mode 100644
index 0000000..d29f7e9
--- /dev/null
+++ b/healthd/images/battery-charging0087.png
Binary files differ
diff --git a/healthd/images/battery-charging0088.png b/healthd/images/battery-charging0088.png
new file mode 100644
index 0000000..67111c9
--- /dev/null
+++ b/healthd/images/battery-charging0088.png
Binary files differ
diff --git a/healthd/images/battery-charging0089.png b/healthd/images/battery-charging0089.png
new file mode 100644
index 0000000..236488b
--- /dev/null
+++ b/healthd/images/battery-charging0089.png
Binary files differ
diff --git a/healthd/images/battery-charging0090.png b/healthd/images/battery-charging0090.png
new file mode 100644
index 0000000..9f2afa8
--- /dev/null
+++ b/healthd/images/battery-charging0090.png
Binary files differ
diff --git a/healthd/images/battery-charging0091.png b/healthd/images/battery-charging0091.png
new file mode 100644
index 0000000..d938757
--- /dev/null
+++ b/healthd/images/battery-charging0091.png
Binary files differ
diff --git a/healthd/images/battery-charging0092.png b/healthd/images/battery-charging0092.png
new file mode 100644
index 0000000..0194991
--- /dev/null
+++ b/healthd/images/battery-charging0092.png
Binary files differ
diff --git a/healthd/images/battery-charging0093.png b/healthd/images/battery-charging0093.png
new file mode 100644
index 0000000..dda4fd7
--- /dev/null
+++ b/healthd/images/battery-charging0093.png
Binary files differ
diff --git a/healthd/images/battery-charging0094.png b/healthd/images/battery-charging0094.png
new file mode 100644
index 0000000..e40d35b
--- /dev/null
+++ b/healthd/images/battery-charging0094.png
Binary files differ
diff --git a/healthd/images/battery-charging0095.png b/healthd/images/battery-charging0095.png
new file mode 100644
index 0000000..7f12783
--- /dev/null
+++ b/healthd/images/battery-charging0095.png
Binary files differ
diff --git a/healthd/images/battery-charging0096.png b/healthd/images/battery-charging0096.png
new file mode 100644
index 0000000..5859fc6
--- /dev/null
+++ b/healthd/images/battery-charging0096.png
Binary files differ
diff --git a/healthd/images/battery-charging0097.png b/healthd/images/battery-charging0097.png
new file mode 100644
index 0000000..87ba922
--- /dev/null
+++ b/healthd/images/battery-charging0097.png
Binary files differ
diff --git a/healthd/images/battery-charging0098.png b/healthd/images/battery-charging0098.png
new file mode 100644
index 0000000..5839e24
--- /dev/null
+++ b/healthd/images/battery-charging0098.png
Binary files differ
diff --git a/healthd/images/battery-charging0099.png b/healthd/images/battery-charging0099.png
new file mode 100644
index 0000000..005f389
--- /dev/null
+++ b/healthd/images/battery-charging0099.png
Binary files differ
diff --git a/healthd/images/battery-charging0100.png b/healthd/images/battery-charging0100.png
new file mode 100644
index 0000000..6bf7012
--- /dev/null
+++ b/healthd/images/battery-charging0100.png
Binary files differ
diff --git a/healthd/include_charger/charger/healthd_mode_charger.h b/healthd/include_charger/charger/healthd_mode_charger.h
index 28e1fb5..b3816e8 100644
--- a/healthd/include_charger/charger/healthd_mode_charger.h
+++ b/healthd/include_charger/charger/healthd_mode_charger.h
@@ -107,13 +107,17 @@
     void InitAnimation();
     int RequestEnableSuspend();
     int RequestDisableSuspend();
+    void BlankSecScreen();
+    void charging_rgbled_update();
 
     bool have_battery_state_ = false;
     bool screen_blanked_ = false;
+    bool init_screen_ = false;
     int64_t next_screen_transition_ = 0;
     int64_t next_key_check_ = 0;
     int64_t next_pwr_check_ = 0;
     int64_t wait_batt_level_timestamp_ = 0;
+    int led_last_battery_level_ = -1;
 
     DirectRenderManager drm_;
     SrceenSwitch screen_switch_;
diff --git a/init/README.md b/init/README.md
index 13c6ebd..aaafe78 100644
--- a/init/README.md
+++ b/init/README.md
@@ -352,9 +352,10 @@
 
 `socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
 > Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
-  launched process.  _type_ must be "dgram", "stream" or "seqpacket".  _type_
-  may end with "+passcred" to enable SO_PASSCRED on the socket. User and
-  group default to 0.  'seclabel' is the SELinux security context for the
+  launched process.  The socket is created synchronously when the service starts.
+  _type_ must be "dgram", "stream" or "seqpacket".  _type_ may end with "+passcred"
+  to enable SO_PASSCRED on the socket or "+listen" to synchronously make it a listening socket.
+  User and group default to 0.  'seclabel' is the SELinux security context for the
   socket.  It defaults to the service security context, as specified by
   seclabel or computed based on the service executable file security context.
   For native executables see libcutils android\_get\_control\_socket().
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 9f7c215..26341b1 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1379,7 +1379,8 @@
     StartSendingMessages();
 
     if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   false, 0666, 0, 0, {});
+                                   /*passcred=*/false, /*should_listen=*/false, 0666, /*uid=*/0,
+                                   /*gid=*/0, /*socketcon=*/{});
         result.ok()) {
         property_set_fd = *result;
     } else {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 41cf748..423dad8 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -608,7 +608,7 @@
     if (sem_init(&reboot_semaphore, false, 0) == -1) {
         // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
         LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
-        RebootSystem(cmd, reboot_target);
+        RebootSystem(cmd, reboot_target, reason);
     }
 
     // Start a thread to monitor init shutdown process
@@ -636,7 +636,7 @@
     // worry about unmounting it.
     if (!IsDataMounted("*")) {
         sync();
-        RebootSystem(cmd, reboot_target);
+        RebootSystem(cmd, reboot_target, reason);
         abort();
     }
 
@@ -769,7 +769,7 @@
             LOG(INFO) << "Shutdown /data";
         }
     }
-    RebootSystem(cmd, reboot_target);
+    RebootSystem(cmd, reboot_target, reason);
     abort();
 }
 
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index b3fa9fd..1f41163 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -106,7 +106,8 @@
     return value == CAP_SET;
 }
 
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+void __attribute__((noreturn))
+RebootSystem(unsigned int cmd, const std::string& rebootTarget, const std::string& reboot_reason) {
     LOG(INFO) << "Reboot ending, jumping to kernel";
 
     if (!IsRebootCapable()) {
@@ -127,10 +128,12 @@
 
         case ANDROID_RB_THERMOFF:
             if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
+                std::string reason = "shutdown,thermal";
+                if (!reboot_reason.empty()) reason = reboot_reason;
+
                 LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
-                static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
                 syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
-                        LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
+                        LINUX_REBOOT_CMD_RESTART2, reason.c_str());
             } else {
                 reboot(RB_POWER_OFF);
             }
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index a0023b9..09e87ef 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -29,7 +29,8 @@
 // so if any of the attempts to determine this fail, it will still return true.
 bool IsRebootCapable();
 // This is a wrapper around the actual reboot calls.
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target);
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target,
+                                            const std::string& reboot_reason = "");
 void __attribute__((noreturn)) InitFatalReboot(int signal_number);
 void InstallRebootSignalHandlers();
 
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 9e914ee..1ee309d 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -434,11 +434,14 @@
                        << "' instead.";
     }
 
-    if (types.size() > 1) {
-        if (types.size() == 2 && types[1] == "passcred") {
+    for (size_t i = 1; i < types.size(); i++) {
+        if (types[i] == "passcred") {
             socket.passcred = true;
+        } else if (types[i] == "listen") {
+            socket.listen = true;
         } else {
-            return Error() << "Only 'passcred' may be used to modify the socket type";
+            return Error() << "Unknown socket type decoration '" << types[i]
+                           << "'. Known values are ['passcred', 'listen']";
         }
     }
 
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index eed5c65..d19f5ee 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -168,7 +168,8 @@
 
 Result<Descriptor> SocketDescriptor::Create(const std::string& global_context) const {
     const auto& socket_context = context.empty() ? global_context : context;
-    auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, perm, uid, gid, socket_context);
+    auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, listen, perm, uid, gid,
+                               socket_context);
     if (!result.ok()) {
         return result.error();
     }
diff --git a/init/service_utils.h b/init/service_utils.h
index 9b65dca..65a2012 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -54,6 +54,7 @@
     int perm = 0;
     std::string context;
     bool passcred = false;
+    bool listen = false;
     bool persist = false;
 
     // Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.
diff --git a/init/util.cpp b/init/util.cpp
index d1e518b..5b3a73c 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -86,8 +86,8 @@
  * daemon. We communicate the file descriptor's value via the environment
  * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
  */
-Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
-                         gid_t gid, const std::string& socketcon) {
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, bool should_listen,
+                         mode_t perm, uid_t uid, gid_t gid, const std::string& socketcon) {
     if (!socketcon.empty()) {
         if (setsockcreatecon(socketcon.c_str()) == -1) {
             return ErrnoError() << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -142,6 +142,9 @@
     if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
         return ErrnoError() << "Failed to fchmodat socket '" << addr.sun_path << "'";
     }
+    if (should_listen && listen(fd, /* use OS maximum */ 1 << 30)) {
+        return ErrnoError() << "Failed to listen on socket '" << addr.sun_path << "'";
+    }
 
     LOG(INFO) << "Created socket '" << addr.sun_path << "'"
               << ", mode " << std::oct << perm << std::dec
diff --git a/init/util.h b/init/util.h
index bf53675..1e2eef0 100644
--- a/init/util.h
+++ b/init/util.h
@@ -44,8 +44,8 @@
 
 extern void (*trigger_shutdown)(const std::string& command);
 
-Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
-                         gid_t gid, const std::string& socketcon);
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, bool should_listen,
+                         mode_t perm, uid_t uid, gid_t gid, const std::string& socketcon);
 
 Result<std::string> ReadFile(const std::string& path);
 Result<void> WriteFile(const std::string& path, const std::string& content);
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index bdb8075..0030887 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -138,6 +138,7 @@
 #define AID_JC_IDENTITYCRED 1089  /* Javacard Identity Cred HAL - to manage omapi ARA rules */
 #define AID_SDK_SANDBOX 1090      /* SDK sandbox virtual UID */
 #define AID_SECURITY_LOG_WRITER 1091 /* write to security log */
+#define AID_PRNG_SEEDER 1092         /* PRNG seeder daemon */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libsystem/include/system/camera.h b/libsystem/include/system/camera.h
index 2ca90c3..990edcf 100644
--- a/libsystem/include/system/camera.h
+++ b/libsystem/include/system/camera.h
@@ -88,9 +88,20 @@
     // Notify on autofocus start and stop. This is useful in continuous
     // autofocus - FOCUS_MODE_CONTINUOUS_VIDEO and FOCUS_MODE_CONTINUOUS_PICTURE.
     CAMERA_MSG_FOCUS_MOVE = 0x0800,       // notifyCallback
+    CAMERA_MSG_VENDOR_START = 0x1000,
+    CAMERA_MSG_STATS_DATA = CAMERA_MSG_VENDOR_START,
+    CAMERA_MSG_META_DATA = 0x2000,
+    CAMERA_MSG_VENDOR_END = 0x8000,
     CAMERA_MSG_ALL_MSGS = 0xFFFF
 };
 
+/** meta data type in CameraMetaDataCallback */
+enum {
+    CAMERA_META_DATA_ASD = 0x001,    //ASD data
+    CAMERA_META_DATA_FD = 0x002,     //FD/FP data
+    CAMERA_META_DATA_HDR = 0x003,    //Auto HDR data
+};
+
 /** cmdType in sendCommand functions */
 enum {
     CAMERA_CMD_START_SMOOTH_ZOOM = 1,
@@ -189,7 +200,25 @@
      * IMPLEMENTATION_DEFINED, then HALv3 devices will use gralloc usage flags
      * of SW_READ_OFTEN.
      */
-    CAMERA_CMD_SET_VIDEO_FORMAT = 11
+    CAMERA_CMD_SET_VIDEO_FORMAT = 11,
+
+    CAMERA_CMD_VENDOR_START = 20,
+    /**
+     * Commands to enable/disable preview histogram
+     *
+     * Based on user's input to enable/disable histogram from the camera
+     * UI, send the appropriate command to the HAL to turn on/off the histogram
+     * stats and start sending the data to the application.
+     */
+    CAMERA_CMD_HISTOGRAM_ON = CAMERA_CMD_VENDOR_START,
+    CAMERA_CMD_HISTOGRAM_OFF = CAMERA_CMD_VENDOR_START + 1,
+    CAMERA_CMD_HISTOGRAM_SEND_DATA  = CAMERA_CMD_VENDOR_START + 2,
+    CAMERA_CMD_LONGSHOT_ON = CAMERA_CMD_VENDOR_START + 3,
+    CAMERA_CMD_LONGSHOT_OFF = CAMERA_CMD_VENDOR_START + 4,
+    CAMERA_CMD_STOP_LONGSHOT = CAMERA_CMD_VENDOR_START + 5,
+    CAMERA_CMD_METADATA_ON = CAMERA_CMD_VENDOR_START + 6,
+    CAMERA_CMD_METADATA_OFF = CAMERA_CMD_VENDOR_START + 7,
+    CAMERA_CMD_VENDOR_END = 200,
 };
 
 /** camera fatal errors */
@@ -284,10 +313,32 @@
      * -2000, -2000 if this is not supported.
      */
     int32_t mouth[2];
+    int32_t smile_degree;
+    int32_t smile_score;
+    int32_t blink_detected;
+    int32_t face_recognised;
+    int32_t gaze_angle;
+    int32_t updown_dir;
+    int32_t leftright_dir;
+    int32_t roll_dir;
+    int32_t left_right_gaze;
+    int32_t top_bottom_gaze;
+    int32_t leye_blink;
+    int32_t reye_blink;
 
 } camera_face_t;
 
 /**
+ * The information of a data type received in a camera frame.
+ */
+typedef enum {
+    /** Data buffer */
+    CAMERA_FRAME_DATA_BUF = 0x000,
+    /** File descriptor */
+    CAMERA_FRAME_DATA_FD = 0x100
+} camera_frame_data_type_t;
+
+/**
  * The metadata of the frame data.
  */
 typedef struct camera_frame_metadata {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index cd71aa8..2b53d88 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -86,32 +86,6 @@
     mkdir /dev/sys/fs 0755 system system
     mkdir /dev/sys/block 0755 system system
 
-# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
-on early-init && property:ro.product.cpu.abilist32=*
-    exec_start boringssl_self_test32
-on early-init && property:ro.product.cpu.abilist64=*
-    exec_start boringssl_self_test64
-on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
-    exec_start boringssl_self_test_apex32
-on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
-    exec_start boringssl_self_test_apex64
-
-service boringssl_self_test32 /system/bin/boringssl_self_test32
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
-service boringssl_self_test64 /system/bin/boringssl_self_test64
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
-service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
-service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
 on init
     sysclktz 0
 
@@ -502,6 +476,33 @@
     start hwservicemanager
     start vndservicemanager
 
+# Run boringssl self test for each ABI.  Any failures trigger reboot to firmware.
+on init && property:ro.product.cpu.abilist32=*
+    exec_start boringssl_self_test32
+on init && property:ro.product.cpu.abilist64=*
+    exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
+    exec_start boringssl_self_test_apex32
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+    exec_start boringssl_self_test_apex64
+
+service boringssl_self_test32 /system/bin/boringssl_self_test32
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
+
+service boringssl_self_test64 /system/bin/boringssl_self_test64
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
+
+service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
+
+service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
+
+
 # Healthd can trigger a full boot from charger mode by signaling this
 # property when the power button is held.
 on property:sys.boot_from_charger_mode=1
@@ -720,9 +721,13 @@
     # Multi-installed APEXes are selected using persist props.
     # Load persist properties and override properties (if enabled) from /data,
     # before starting apexd.
+    # /data/property should be created before `load_persist_props`
+    mkdir /data/property 0700 root root encryption=Require
     load_persist_props
+
     start logd
     start logd-reinit
+
     # Some existing vendor rc files use 'on load_persist_props_action' to know
     # when persist props are ready. These are difficult to change due to GRF,
     # so continue triggering this action here even though props are already loaded
@@ -842,7 +847,6 @@
     mkdir /data/app-asec 0700 root root encryption=Require
     mkdir /data/app-lib 0771 system system encryption=Require
     mkdir /data/app 0771 system system encryption=Require
-    mkdir /data/property 0700 root root encryption=Require
 
     # create directory for updated font files.
     mkdir /data/fonts/ 0771 root root encryption=Require
@@ -918,20 +922,26 @@
     mkdir /data/user/0 0700 system system encryption=None
     mount none /data/data /data/user/0 bind rec
 
-    # A tmpfs directory, which will contain all apps CE DE data directory that
-    # bind mount from the original source.
+    # A tmpfs directory, which will contain all apps and sdk sandbox CE and DE
+    # data directory that bind mount from the original source.
     mount tmpfs tmpfs /data_mirror nodev noexec nosuid mode=0700,uid=0,gid=1000
     restorecon /data_mirror
     mkdir /data_mirror/data_ce 0700 root root
     mkdir /data_mirror/data_de 0700 root root
+    mkdir /data_mirror/misc_ce 0700 root root
+    mkdir /data_mirror/misc_de 0700 root root
 
     # Create CE and DE data directory for default volume
     mkdir /data_mirror/data_ce/null 0700 root root
     mkdir /data_mirror/data_de/null 0700 root root
+    mkdir /data_mirror/misc_ce/null 0700 root root
+    mkdir /data_mirror/misc_de/null 0700 root root
 
     # Bind mount CE and DE data directory to mirror's default volume directory
     mount none /data/user /data_mirror/data_ce/null bind rec
     mount none /data/user_de /data_mirror/data_de/null bind rec
+    mount none /data/misc_ce /data_mirror/misc_ce/null bind rec
+    mount none /data/misc_de /data_mirror/misc_de/null bind rec
 
     # Create mirror directory for jit profiles
     mkdir /data_mirror/cur_profiles 0700 root root
@@ -1260,11 +1270,13 @@
     setenv HOSTNAME console
 
 on property:ro.debuggable=1
-    # Give writes to anyone for the trace folder on debug builds.
+    # Give writes to the same group for the trace folder on debug builds,
+    # it's further protected by selinux policy.
     # The folder is used to store method traces.
     chmod 0773 /data/misc/trace
-    # Give reads to anyone for the window trace folder on debug builds.
-    chmod 0775 /data/misc/wmtrace
+    # Give writes and reads to anyone for the window trace folder on debug builds,
+    # it's further protected by selinux policy.
+    chmod 0777 /data/misc/wmtrace
     # Give reads to anyone for the accessibility trace folder on debug builds.
     chmod 0775 /data/misc/a11ytrace
 
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 63b09c0..2f0ec8a 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -7,6 +7,9 @@
     socket usap_pool_primary stream 660 root system
     onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
     onrestart write /sys/power/state on
+    # NOTE: If the wakelock name here is changed, then also
+    # update it in SystemSuspend.cpp
+    onrestart write /sys/power/wake_lock zygote_kwl
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 5bde5f4..805267d 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -7,6 +7,9 @@
     socket usap_pool_primary stream 660 root system
     onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
     onrestart write /sys/power/state on
+    # NOTE: If the wakelock name here is changed, then also
+    # update it in SystemSuspend.cpp
+    onrestart write /sys/power/wake_lock zygote_kwl
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index a140c8c..4ec59af 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -37,6 +37,8 @@
 /dev/tty                  0666   root       root
 /dev/random               0666   root       root
 /dev/urandom              0666   root       root
+# Aside from kernel threads, only prng_seeder needs access to HW RNG
+/dev/hw_random            0400   prng_seeder prng_seeder
 /dev/ashmem*              0666   root       root
 /dev/binder               0666   root       root
 /dev/hwbinder             0666   root       root
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 278499f..17d083c 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -226,6 +226,9 @@
         case APPLOADER_ERR_POLICY_VIOLATION:
             LOG(ERROR) << "Error: loading denied by policy engine";
             break;
+        case APPLOADER_ERR_NOT_ENCRYPTED:
+            LOG(ERROR) << "Error: unmet application encryption requirement";
+            break;
         default:
             LOG(ERROR) << "Unrecognized error: " << resp.error;
             break;
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index 306596e..f037692 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -45,6 +45,10 @@
  * @APPLOADER_ERR_INTERNAL:             miscellaneous or internal apploader
  *                                      error not covered by the above
  * @APPLOADER_ERR_INVALID_VERSION:      invalid application version
+ * @APPLOADER_ERR_POLICY_VIOLATION:     signature verification succeeded but
+ *                                      key+manifest combination not allowed
+ *                                      by app loader policy engine
+ * @APPLOADER_ERR_NOT_ENCRYPTED:        unmet application encryption requirement
  */
 enum apploader_error : uint32_t {
     APPLOADER_NO_ERROR = 0,
@@ -57,6 +61,7 @@
     APPLOADER_ERR_INTERNAL,
     APPLOADER_ERR_INVALID_VERSION,
     APPLOADER_ERR_POLICY_VIOLATION,
+    APPLOADER_ERR_NOT_ENCRYPTED,
 };
 
 /**
diff --git a/trusty/storage/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
index 3f1dcb8..255ade1 100644
--- a/trusty/storage/interface/include/trusty/interface/storage.h
+++ b/trusty/storage/interface/include/trusty/interface/storage.h
@@ -70,6 +70,9 @@
  * @STORAGE_ERR_TRANSACT        returned by various operations to indicate that current transaction
  *                              is in error state. Such state could be only cleared by sending
  *                              STORAGE_END_TRANSACTION message.
+ * @STORAGE_ERR_SYNC_FAILURE    indicates that the current operation failed to sync
+ *                              to disk. Only returned if STORAGE_MSG_FLAG_PRE_COMMIT or
+ *                              STORAGE_MSG_FLAG_POST_COMMIT was set for the request.
  */
 enum storage_err {
 	STORAGE_NO_ERROR          = 0,
@@ -80,6 +83,7 @@
 	STORAGE_ERR_NOT_FOUND     = 5,
 	STORAGE_ERR_EXIST         = 6,
 	STORAGE_ERR_TRANSACT      = 7,
+	STORAGE_ERR_SYNC_FAILURE  = 8,
 };
 
 /**
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 94f26d8..e952ee0 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -32,11 +32,11 @@
 
     shared_libs: [
         "libbase",
+        "libcutils",
         "liblog",
         "libhardware_legacy",
     ],
     header_libs: [
-        "libcutils_headers",
         "libgsi_headers",
     ],
 
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 2620034..f015892 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -116,10 +116,11 @@
 static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
     int rc;
 
-    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
+    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && msg->cmd != STORAGE_RPMB_SEND &&
+        msg->cmd != STORAGE_FILE_WRITE) {
         /*
-         * handling post commit messages on non rpmb commands are not
-         * implemented as there is no use case for this yet.
+         * handling post commit messages on commands other than rpmb and write
+         * operations are not implemented as there is no use case for this yet.
          */
         ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd);
         msg->result = STORAGE_ERR_UNIMPLEMENTED;
@@ -129,7 +130,7 @@
     if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {
         rc = storage_sync_checkpoint();
         if (rc < 0) {
-            msg->result = STORAGE_ERR_GENERIC;
+            msg->result = STORAGE_ERR_SYNC_FAILURE;
             return ipc_respond(msg, NULL, 0);
         }
     }
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index c00c399..033dc21 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <cutils/properties.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -43,6 +44,22 @@
 
 static const char *ssdir_name;
 
+/*
+ * Property set to 1 after we have opened a file under ssdir_name. The backing
+ * files for both TD and TDP are currently located under /data/vendor/ss and can
+ * only be opened once userdata is mounted. This storageproxyd service is
+ * restarted when userdata is available, which causes the Trusty storage service
+ * to reconnect and attempt to open the backing files for TD and TDP. Once we
+ * set this property, other users can expect that the Trusty storage service
+ * ports will be available (although they may block if still being initialized),
+ * and connections will not be reset after this point (assuming the
+ * storageproxyd service stays running).
+ */
+#define FS_READY_PROPERTY "ro.vendor.trusty.storage.fs_ready"
+
+/* has FS_READY_PROPERTY been set? */
+static bool fs_ready_initialized = false;
+
 static enum sync_state fs_state;
 static enum sync_state fd_state[FD_TBL_SIZE];
 
@@ -336,6 +353,16 @@
     ALOGV("%s: \"%s\": fd = %u: handle = %d\n",
           __func__, path, rc, resp.handle);
 
+    /* a backing file has been opened, notify any waiting init steps */
+    if (!fs_ready_initialized) {
+        rc = property_set(FS_READY_PROPERTY, "1");
+        if (rc == 0) {
+            fs_ready_initialized = true;
+        } else {
+            ALOGE("Could not set property %s, rc: %d\n", FS_READY_PROPERTY, rc);
+        }
+    }
+
     return ipc_respond(msg, &resp, sizeof(resp));
 
 err_response:
@@ -407,6 +434,14 @@
         goto err_response;
     }
 
+    if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
+        rc = storage_sync_checkpoint();
+        if (rc < 0) {
+            msg->result = STORAGE_ERR_SYNC_FAILURE;
+            goto err_response;
+        }
+    }
+
     msg->result = STORAGE_NO_ERROR;
 
 err_response: