Merge "Remove excess logging in secdiscard"
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index f96ca39..1a89039 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -22,6 +22,7 @@
 #include <list>
 #include <memory>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
@@ -37,7 +38,11 @@
 #include <mntent.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
 
+using android::base::GetBoolProperty;
+using android::base::GetUintProperty;
 using android::base::SetProperty;
 using android::binder::Status;
 using android::fs_mgr::Fstab;
@@ -126,7 +131,7 @@
 
 namespace {
 
-bool isCheckpointing = false;
+volatile bool isCheckpointing = false;
 }
 
 Status cp_commitChanges() {
@@ -180,9 +185,37 @@
     return Status::ok();
 }
 
-Status cp_abortChanges() {
-    android_reboot(ANDROID_RB_RESTART2, 0, nullptr);
-    return Status::ok();
+namespace {
+void abort_metadata_file() {
+    std::string oldContent, newContent;
+    int retry = 0;
+    struct stat st;
+    int result = stat(kMetadataCPFile.c_str(), &st);
+
+    // If the file doesn't exist, we aren't managing a checkpoint retry counter
+    if (result != 0) return;
+    if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) {
+        PLOG(ERROR) << "Failed to read checkpoint file";
+        return;
+    }
+    std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
+
+    if (!android::base::ParseInt(retryContent, &retry)) {
+        PLOG(ERROR) << "Could not parse retry count";
+        return;
+    }
+    if (retry > 0) {
+        newContent = "0";
+        if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
+            PLOG(ERROR) << "Could not write checkpoint file";
+    }
+}
+}  // namespace
+
+void cp_abortChanges(const std::string& message, bool retry) {
+    if (!cp_needsCheckpoint()) return;
+    if (!retry) abort_metadata_file();
+    android_reboot(ANDROID_RB_RESTART2, 0, message.c_str());
 }
 
 bool cp_needsRollback() {
@@ -212,6 +245,8 @@
     std::string content;
     sp<IBootControl> module = IBootControl::getService();
 
+    if (isCheckpointing) return isCheckpointing;
+
     if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) {
         isCheckpointing = true;
         return true;
@@ -225,6 +260,53 @@
     return false;
 }
 
+namespace {
+const std::string kSleepTimeProp = "ro.sys.cp_usleeptime";
+const uint32_t usleeptime_default = 1000;  // 1 s
+
+const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes";
+const uint64_t min_free_bytes_default = 100 * (1 << 20);  // 100 MiB
+
+const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full";
+const bool commit_on_full_default = true;
+
+static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) {
+    struct statvfs data;
+    uint64_t free_bytes = 0;
+    uint32_t usleeptime = GetUintProperty(kSleepTimeProp, usleeptime_default, (uint32_t)-1);
+    uint64_t min_free_bytes = GetUintProperty(kSleepTimeProp, min_free_bytes_default, (uint64_t)-1);
+    bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default);
+
+    while (isCheckpointing) {
+        if (is_fs_cp) {
+            statvfs(mnt_pnt.c_str(), &data);
+            free_bytes = data.f_bavail * data.f_frsize;
+        } else {
+            int ret;
+            std::string size_filename = std::string("/sys/") + blk_device.substr(5) + "/bow/free";
+            std::string content;
+            ret = android::base::ReadFileToString(kMetadataCPFile, &content);
+            if (ret) {
+                free_bytes = std::strtoul(content.c_str(), NULL, 10);
+            }
+        }
+        if (free_bytes < min_free_bytes) {
+            if (commit_on_full) {
+                LOG(INFO) << "Low space for checkpointing. Commiting changes";
+                cp_commitChanges();
+                break;
+            } else {
+                LOG(INFO) << "Low space for checkpointing. Rebooting";
+                cp_abortChanges("checkpoint,low_space", false);
+                break;
+            }
+        }
+        usleep(usleeptime);
+    }
+}
+
+}  // namespace
+
 Status cp_prepareCheckpoint() {
     if (!isCheckpointing) {
         return Status::ok();
@@ -256,6 +338,12 @@
 
             setBowState(mount_rec.blk_device, "1");
         }
+        if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) {
+            std::thread(cp_healthDaemon, std::string(mount_rec.mount_point),
+                        std::string(mount_rec.mount_point),
+                        fstab_rec->fs_mgr_flags.checkpoint_fs == 1)
+                .detach();
+        }
     }
     return Status::ok();
 }
diff --git a/Checkpoint.h b/Checkpoint.h
index d077d19..63ead83 100644
--- a/Checkpoint.h
+++ b/Checkpoint.h
@@ -33,7 +33,7 @@
 
 android::binder::Status cp_commitChanges();
 
-android::binder::Status cp_abortChanges();
+void cp_abortChanges(const std::string& message, bool retry);
 
 bool cp_needsRollback();
 
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 6868c83..623cced 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -865,11 +865,12 @@
     return cp_markBootAttempt();
 }
 
-binder::Status VoldNativeService::abortChanges() {
+binder::Status VoldNativeService::abortChanges(const std::string& message, bool retry) {
     ENFORCE_UID(AID_SYSTEM);
     ACQUIRE_LOCK;
 
-    return cp_abortChanges();
+    cp_abortChanges(message, retry);
+    return ok();
 }
 
 binder::Status VoldNativeService::supportsCheckpoint(bool* _aidl_return) {
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 8fbc32d..b90c5b8 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -129,7 +129,7 @@
     binder::Status restoreCheckpoint(const std::string& mountPoint);
     binder::Status restoreCheckpointPart(const std::string& mountPoint, int count);
     binder::Status markBootAttempt();
-    binder::Status abortChanges();
+    binder::Status abortChanges(const std::string& message, bool retry);
     binder::Status supportsCheckpoint(bool* _aidl_return);
     binder::Status supportsBlockCheckpoint(bool* _aidl_return);
     binder::Status supportsFileCheckpoint(bool* _aidl_return);
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 3c5ef78..72f2643 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -99,7 +99,7 @@
     void startCheckpoint(int retry);
     boolean needsCheckpoint();
     boolean needsRollback();
-    void abortChanges();
+    void abortChanges(in @utf8InCpp String device, boolean retry);
     void commitChanges();
     void prepareCheckpoint();
     void restoreCheckpoint(@utf8InCpp String device);
diff --git a/vdc.cpp b/vdc.cpp
index 1ec46c8..76eca3e 100644
--- a/vdc.cpp
+++ b/vdc.cpp
@@ -141,8 +141,10 @@
         checkStatus(vold->restoreCheckpointPart(args[2], count));
     } else if (args[0] == "checkpoint" && args[1] == "markBootAttempt" && args.size() == 2) {
         checkStatus(vold->markBootAttempt());
-    } else if (args[0] == "checkpoint" && args[1] == "abortChanges" && args.size() == 2) {
-        checkStatus(vold->abortChanges());
+    } else if (args[0] == "checkpoint" && args[1] == "abortChanges" && args.size() == 4) {
+        int retry;
+        if (!android::base::ParseInt(args[2], &retry)) exit(EINVAL);
+        checkStatus(vold->abortChanges(args[2], retry != 0));
     } else {
         LOG(ERROR) << "Raw commands are no longer supported";
         exit(EINVAL);