Pre-create userdata metadata encryption device.

CreateDevice() implicitly calls WaitForDevice(), which can impact boot
time if there are many uevents waiting to be processed. To alleviate
this, create an empty "userdata" device when vold starts (if metada
encryption is enabled). When it comes time to actually enable metadata
encryption, the device can be re-used and the subsequent Wait should be
much faster.

Bug: 198405417
Test: manual test; device boots
Change-Id: Iaacd10858272f17353475e25075ea1dda13f8fc4
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index 277a908..ef4c41e 100644
--- a/MetadataCrypt.cpp
+++ b/MetadataCrypt.cpp
@@ -52,6 +52,7 @@
 using android::fscrypt::GetFirstApiLevel;
 using android::vold::KeyBuffer;
 using namespace android::dm;
+using namespace std::chrono_literals;
 
 // Parsed from metadata options
 struct CryptoOptions {
@@ -81,6 +82,17 @@
     return KeyGeneration{options.cipher.get_keysize(), true, options.use_hw_wrapped_key};
 }
 
+void defaultkey_precreate_dm_device() {
+    auto& dm = DeviceMapper::Instance();
+    if (dm.GetState(kDmNameUserdata) != DmDeviceState::INVALID) {
+        LOG(INFO) << "Not pre-creating userdata encryption device; device already exists";
+        return;
+    }
+    if (!dm.CreateEmptyDevice(kDmNameUserdata)) {
+        LOG(ERROR) << "Failed to pre-create userdata metadata encryption device";
+    }
+}
+
 static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
     // fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
     // partitions in the fsck domain.
@@ -171,8 +183,19 @@
     table.AddTarget(std::move(target));
 
     auto& dm = DeviceMapper::Instance();
-    if (!dm.CreateDevice(dm_name, table, crypto_blkdev, std::chrono::seconds(5))) {
-        PLOG(ERROR) << "Could not create default-key device " << dm_name;
+    if (dm_name == kDmNameUserdata && dm.GetState(dm_name) == DmDeviceState::SUSPENDED) {
+        // The device was created in advance, populate it now.
+        std::string path;
+        if (!dm.WaitForDevice(dm_name, 5s, crypto_blkdev)) {
+            LOG(ERROR) << "Failed to wait for default-key device " << dm_name;
+            return false;
+        }
+        if (!dm.LoadTableAndActivate(dm_name, table)) {
+            LOG(ERROR) << "Failed to populate default-key device " << dm_name;
+            return false;
+        }
+    } else if (!dm.CreateDevice(dm_name, table, crypto_blkdev, 5s)) {
+        LOG(ERROR) << "Could not create default-key device " << dm_name;
         return false;
     }
     return true;
diff --git a/MetadataCrypt.h b/MetadataCrypt.h
index e482765..06131ad 100644
--- a/MetadataCrypt.h
+++ b/MetadataCrypt.h
@@ -25,6 +25,7 @@
 namespace android {
 namespace vold {
 
+void defaultkey_precreate_dm_device();
 bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
                                       const std::string& mount_point, bool needs_encrypt,
                                       bool should_format, const std::string& fs_type);
diff --git a/main.cpp b/main.cpp
index 1f85fb5..641d13a 100644
--- a/main.cpp
+++ b/main.cpp
@@ -16,6 +16,7 @@
 
 #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
 
+#include "MetadataCrypt.h"
 #include "NetlinkManager.h"
 #include "VoldNativeService.h"
 #include "VoldUtil.h"
@@ -247,6 +248,11 @@
             PLOG(FATAL) << "could not find logical partition " << entry.blk_device;
         }
 
+        if (entry.mount_point == "/data" && !entry.metadata_encryption.empty()) {
+            // Pre-populate userdata dm-devices since the uevents are asynchronous (b/198405417).
+            android::vold::defaultkey_precreate_dm_device();
+        }
+
         if (entry.fs_mgr_flags.vold_managed) {
             if (entry.fs_mgr_flags.nonremovable) {
                 LOG(WARNING) << "nonremovable no longer supported; ignoring volume";