Move tradeinmode wipe handling to second-stage init.

Bug: 411739129
Test: adb shell tradeinmode testing wipe
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:585dd6e1ea55af0ab43d6667294c7d9d09eb5330)
Merged-In: Ic8b1a86c03389a37ecfcf516c721fff0655cfd6c
Change-Id: Ic8b1a86c03389a37ecfcf516c721fff0655cfd6c
diff --git a/init/Android.bp b/init/Android.bp
index b209c47..ed8123e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -80,6 +80,7 @@
     "sigchld_handler.cpp",
     "snapuserd_transition.cpp",
     "switch_root.cpp",
+    "tradeinmode.cpp",
     "uevent_listener.cpp",
     "ueventd.cpp",
     "ueventd_parser.cpp",
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index aa6b551..6b413f6 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -32,12 +32,9 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android/avf_cc_flags.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
@@ -49,7 +46,6 @@
 
 #include "block_dev_initializer.h"
 #include "devices.h"
-#include "reboot_utils.h"
 #include "result.h"
 #include "snapuserd_transition.h"
 #include "switch_root.h"
@@ -115,8 +111,6 @@
     bool GetDmVerityDevices(std::set<std::string>* devices);
     bool SetUpDmVerity(FstabEntry* fstab_entry);
 
-    void RequestTradeInModeWipeIfNeeded();
-
     bool InitAvbHandle();
 
     bool need_dm_verity_;
@@ -269,8 +263,6 @@
 }
 
 bool FirstStageMountVBootV2::DoFirstStageMount() {
-    RequestTradeInModeWipeIfNeeded();
-
     if (!IsDmLinearEnabled() && fstab_.empty()) {
         // Nothing to mount.
         LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
@@ -890,55 +882,6 @@
     return true;
 }
 
-void FirstStageMountVBootV2::RequestTradeInModeWipeIfNeeded() {
-    static constexpr const char* kWipeIndicator = "/metadata/tradeinmode/wipe";
-    static constexpr size_t kWipeAttempts = 3;
-
-    if (access(kWipeIndicator, R_OK) == -1) {
-        return;
-    }
-
-    // Write a counter to the wipe indicator, to try and prevent boot loops if
-    // recovery fails to wipe data.
-    uint32_t counter = 0;
-    std::string contents;
-    if (ReadFileToString(kWipeIndicator, &contents)) {
-        android::base::ParseUint(contents, &counter);
-        contents = std::to_string(++counter);
-        if (android::base::WriteStringToFile(contents, kWipeIndicator)) {
-            sync();
-        } else {
-            PLOG(ERROR) << "Failed to update " << kWipeIndicator;
-        }
-    } else {
-        PLOG(ERROR) << "Failed to read " << kWipeIndicator;
-    }
-
-    std::string err;
-    auto misc_device = get_misc_blk_device(&err);
-    if (misc_device.empty()) {
-        LOG(FATAL) << "Could not find misc device: " << err;
-    }
-
-    auto misc_name = android::base::Basename(misc_device);
-    if (!block_dev_init_.InitDevices({misc_name})) {
-        LOG(FATAL) << "Could not find misc device: " << misc_device;
-    }
-
-    // If we've failed to wipe three times, don't include the wipe command. This
-    // will force us to boot into the recovery menu instead where a manual wipe
-    // can be attempted.
-    std::vector<std::string> options;
-    if (counter <= kWipeAttempts) {
-        options.emplace_back("--wipe_data");
-        options.emplace_back("--reason=tradeinmode");
-    }
-    if (!write_bootloader_message(options, &err)) {
-        LOG(FATAL) << "Could not issue wipe: " << err;
-    }
-    RebootSystem(ANDROID_RB_RESTART2, "recovery", "reboot,tradeinmode,wipe");
-}
-
 void SetInitAvbVersionInRecovery() {
     if (!IsRecoveryMode()) {
         LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
diff --git a/init/init.cpp b/init/init.cpp
index 920b926..f6b2941 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -88,6 +88,7 @@
 #include "snapuserd_transition.h"
 #include "subcontext.h"
 #include "system/core/init/property_service.pb.h"
+#include "tradeinmode.h"
 #include "util.h"
 
 #ifndef RECOVERY
@@ -923,6 +924,11 @@
     return {};
 }
 
+static Result<void> CheckTradeInModeStatus([[maybe_unused]] const BuiltinArguments& args) {
+    RequestTradeInModeWipeIfNeeded();
+    return {};
+}
+
 static void SecondStageBootMonitor(int timeout_sec) {
     auto cur_time = boot_clock::now().time_since_epoch();
     int cur_sec = std::chrono::duration_cast<std::chrono::seconds>(cur_time).count();
@@ -1111,6 +1117,7 @@
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+    am.QueueBuiltinAction(CheckTradeInModeStatus, "CheckTradeInModeStatus");
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
     Keychords keychords;
diff --git a/init/tradeinmode.cpp b/init/tradeinmode.cpp
new file mode 100644
index 0000000..1913bae
--- /dev/null
+++ b/init/tradeinmode.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2025 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 <stddef.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+
+#include "reboot_utils.h"
+
+namespace android {
+namespace init {
+
+void RequestTradeInModeWipeIfNeeded() {
+    static constexpr const char* kWipeIndicator = "/metadata/tradeinmode/wipe";
+    static constexpr size_t kWipeAttempts = 3;
+
+    if (access(kWipeIndicator, R_OK) == -1) {
+        return;
+    }
+
+    // Write a counter to the wipe indicator, to try and prevent boot loops if
+    // recovery fails to wipe data.
+    uint32_t counter = 0;
+    std::string contents;
+    if (android::base::ReadFileToString(kWipeIndicator, &contents)) {
+        android::base::ParseUint(contents, &counter);
+        contents = std::to_string(++counter);
+        if (android::base::WriteStringToFile(contents, kWipeIndicator)) {
+            sync();
+        } else {
+            PLOG(ERROR) << "Failed to update " << kWipeIndicator;
+        }
+    } else {
+        PLOG(ERROR) << "Failed to read " << kWipeIndicator;
+    }
+
+    std::string err;
+    auto misc_device = get_misc_blk_device(&err);
+    if (misc_device.empty()) {
+        LOG(FATAL) << "Could not find misc device: " << err;
+    }
+
+    // If we've failed to wipe three times, don't include the wipe command. This
+    // will force us to boot into the recovery menu instead where a manual wipe
+    // can be attempted.
+    std::vector<std::string> options;
+    if (counter <= kWipeAttempts) {
+        options.emplace_back("--wipe_data");
+        options.emplace_back("--reason=tradeinmode");
+    }
+    if (!write_bootloader_message(options, &err)) {
+        LOG(FATAL) << "Could not issue wipe: " << err;
+    }
+    RebootSystem(ANDROID_RB_RESTART2, "recovery", "reboot,tradeinmode,wipe");
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/tradeinmode.h b/init/tradeinmode.h
new file mode 100644
index 0000000..fec2a01
--- /dev/null
+++ b/init/tradeinmode.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#pragma once
+
+namespace android {
+namespace init {
+
+void RequestTradeInModeWipeIfNeeded();
+
+}  // namespace init
+}  // namespace android