Implement vendor-available vold interface

Bug: 362567323
Test: mmm system/vold
Change-Id: I967faeb221741cbd8d6c13b25cd57c37fbb6e839
diff --git a/Android.bp b/Android.bp
index ba3267c..1eeb1a1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -43,6 +43,7 @@
         "libfs_mgr",
         "libsquashfs_utils",
         "libvold_binder",
+        "android.system.vold-V1-cpp",
     ],
     shared_libs: [
         "android.hardware.boot@1.0",
@@ -103,6 +104,11 @@
     export_include_dirs: ["."],
 }
 
+vintf_fragment {
+    name: "android.system.vold-service.xml",
+    src: "android.system.vold-service.xml",
+}
+
 // Static library factored out to support testing
 cc_library_static {
     name: "libvold",
@@ -132,6 +138,7 @@
         "NetlinkManager.cpp",
         "Process.cpp",
         "Utils.cpp",
+        "VendorVoldNativeService.cpp",
         "VoldNativeService.cpp",
         "VoldNativeServiceValidation.cpp",
         "VoldUtil.cpp",
@@ -198,6 +205,7 @@
             ],
         },
     },
+    vintf_fragment_modules: ["android.system.vold-service.xml"],
 }
 
 cc_binary {
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index 598a87b..27d99e7 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -159,6 +159,21 @@
 // Protects isCheckpointing, needsCheckpointWasCalled and code that makes decisions based on status
 // of isCheckpointing
 std::mutex isCheckpointingLock;
+
+std::mutex listenersLock;
+std::vector<android::sp<android::system::vold::IVoldCheckpointListener>> listeners;
+}  // namespace
+
+void notifyCheckpointListeners() {
+    std::lock_guard<std::mutex> lock(listenersLock);
+
+    for (auto& listener : listeners) {
+        listener->onCheckpointingComplete();
+        listener = nullptr;
+    }
+
+    // Reclaim vector memory; we likely won't need it again.
+    listeners = std::vector<android::sp<android::system::vold::IVoldCheckpointListener>>();
 }
 
 Status cp_commitChanges() {
@@ -221,6 +236,8 @@
     if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
         return error(err_str.c_str());
 
+    notifyCheckpointListeners();
+
     std::thread(DoCheckpointCommittedWork).detach();
     return Status::ok();
 }
@@ -300,6 +317,9 @@
     if (ret) {
         ret = content != "0";
         isCheckpointing = ret;
+        if (!isCheckpointing) {
+            notifyCheckpointListeners();
+        }
         return ret;
     }
     return false;
@@ -801,5 +821,20 @@
     needsCheckpointWasCalled = false;
 }
 
+bool cp_registerCheckpointListener(
+        android::sp<android::system::vold::IVoldCheckpointListener> listener) {
+    std::lock_guard<std::mutex> checkpointGuard(isCheckpointingLock);
+    if (needsCheckpointWasCalled && !isCheckpointing) {
+        // Either checkpoint already committed or we didn't need one
+        return false;
+    }
+
+    // Either we don't know whether we need a checkpoint or we're already checkpointing,
+    // so we need to save this listener to notify later.
+    std::lock_guard<std::mutex> listenersGuard(listenersLock);
+    listeners.push_back(std::move(listener));
+    return true;
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/Checkpoint.h b/Checkpoint.h
index 6f3acac..7625310 100644
--- a/Checkpoint.h
+++ b/Checkpoint.h
@@ -17,6 +17,7 @@
 #ifndef _CHECKPOINT_H
 #define _CHECKPOINT_H
 
+#include <android/system/vold/IVold.h>
 #include <binder/Status.h>
 #include <string>
 
@@ -48,6 +49,9 @@
 android::binder::Status cp_markBootAttempt();
 
 void cp_resetCheckpoint();
+
+bool cp_registerCheckpointListener(
+        android::sp<android::system::vold::IVoldCheckpointListener> listener);
 }  // namespace vold
 }  // namespace android
 
diff --git a/VendorVoldNativeService.cpp b/VendorVoldNativeService.cpp
new file mode 100644
index 0000000..6d13a14
--- /dev/null
+++ b/VendorVoldNativeService.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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 "VendorVoldNativeService.h"
+
+#include <mutex>
+
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Trace.h>
+
+#include "Checkpoint.h"
+#include "VoldNativeServiceValidation.h"
+#include "VolumeManager.h"
+
+#define ENFORCE_SYSTEM_OR_ROOT                              \
+    {                                                       \
+        binder::Status status = CheckUidOrRoot(AID_SYSTEM); \
+        if (!status.isOk()) {                               \
+            return status;                                  \
+        }                                                   \
+    }
+
+#define ACQUIRE_LOCK                                                        \
+    std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock()); \
+    ATRACE_CALL();
+
+namespace android::vold {
+
+status_t VendorVoldNativeService::try_start() {
+    auto service_name = String16("android.system.vold.IVold/default");
+    if (!defaultServiceManager()->isDeclared(service_name)) {
+        LOG(DEBUG) << "Service for VendorVoldNativeService (" << service_name << ") not declared.";
+        return OK;
+    }
+    return defaultServiceManager()->addService(std::move(service_name),
+                                               new VendorVoldNativeService());
+}
+
+binder::Status VendorVoldNativeService::registerCheckpointListener(
+        const sp<android::system::vold::IVoldCheckpointListener>& listener,
+        android::system::vold::CheckpointingState* _aidl_return) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    bool possible_checkpointing = cp_registerCheckpointListener(listener);
+    *_aidl_return = possible_checkpointing
+                            ? android::system::vold::CheckpointingState::POSSIBLE_CHECKPOINTING
+                            : android::system::vold::CheckpointingState::CHECKPOINTING_COMPLETE;
+    return binder::Status::ok();
+}
+
+}  // namespace android::vold
diff --git a/VendorVoldNativeService.h b/VendorVoldNativeService.h
new file mode 100644
index 0000000..884ccb0
--- /dev/null
+++ b/VendorVoldNativeService.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef _VENDOR_VOLD_NATIVE_SERVICE_H_
+#define _VENDOR_VOLD_NATIVE_SERVICE_H_
+
+#include <android/system/vold/BnVold.h>
+#include <android/system/vold/CheckpointingState.h>
+#include <android/system/vold/IVoldCheckpointListener.h>
+
+namespace android::vold {
+
+class VendorVoldNativeService : public android::system::vold::BnVold {
+  public:
+    /** Start the service, but if it's not declared, give up and return OK. */
+    static status_t try_start();
+
+    binder::Status registerCheckpointListener(
+            const sp<android::system::vold::IVoldCheckpointListener>& listener,
+            android::system::vold::CheckpointingState* _aidl_return) final;
+};
+
+}  // namespace android::vold
+
+#endif  // _VENDOR_VOLD_NATIVE_SERVICE_H_
\ No newline at end of file
diff --git a/android.system.vold-service.xml b/android.system.vold-service.xml
new file mode 100644
index 0000000..ea084b7
--- /dev/null
+++ b/android.system.vold-service.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="framework">
+    <hal format="aidl">
+        <name>android.system.vold</name>
+        <version>1</version>
+        <interface>
+            <name>IVold</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
\ No newline at end of file
diff --git a/main.cpp b/main.cpp
index 078ee14..bdce76e 100644
--- a/main.cpp
+++ b/main.cpp
@@ -19,6 +19,7 @@
 #include "FsCrypt.h"
 #include "MetadataCrypt.h"
 #include "NetlinkManager.h"
+#include "VendorVoldNativeService.h"
 #include "VoldNativeService.h"
 #include "VoldUtil.h"
 #include "VolumeManager.h"
@@ -126,9 +127,16 @@
         exit(1);
     }
     ATRACE_END();
-
     LOG(DEBUG) << "VoldNativeService::start() completed OK";
 
+    ATRACE_BEGIN("VendorVoldNativeService::try_start");
+    if (android::vold::VendorVoldNativeService::try_start() != android::OK) {
+        LOG(ERROR) << "Unable to start VendorVoldNativeService";
+        exit(1);
+    }
+    ATRACE_END();
+    LOG(DEBUG) << "VendorVoldNativeService::try_start() completed OK";
+
     ATRACE_BEGIN("NetlinkManager::start");
     if (nm->start()) {
         PLOG(ERROR) << "Unable to start NetlinkManager";
diff --git a/tests/VoldFuzzer.cpp b/tests/VoldFuzzer.cpp
index 630a785..b47a783 100644
--- a/tests/VoldFuzzer.cpp
+++ b/tests/VoldFuzzer.cpp
@@ -17,6 +17,7 @@
 #include <android-base/logging.h>
 #include <fuzzbinder/libbinder_driver.h>
 
+#include "VendorVoldNativeService.h"
 #include "VoldNativeService.h"
 #include "sehandle.h"
 
@@ -37,6 +38,7 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     auto voldService = sp<android::vold::VoldNativeService>::make();
-    fuzzService(voldService, FuzzedDataProvider(data, size));
+    auto voldVendorService = sp<android::vold::VendorVoldNativeService>::make();
+    fuzzService({voldService, voldVendorService}, FuzzedDataProvider(data, size));
     return 0;
 }
\ No newline at end of file