From 2c821a8c0a4fbaa6c7061282e5fc8b5b53e9b45a Mon Sep 17 00:00:00 2001 From: nijel8 Date: Sat, 29 Dec 2018 04:56:41 -0500 Subject: vold_decrypt: Add android 9.0 support * build modified vdc_pie binary with 'checkpw' command support if building with Android 9.0 platform. That command and others we don't care about, are removed from Pie vdc. Our vdc_pie will run if system sdk version is > 27, otherwise system vdc is used. Code adapted from Android 9.0 system/vold/vdc. * include prebuilt vdc_pie(arm, arm64) binary if building with lower than Android 9.0 platform - vdc_pie cannot be build from source with those platforms without additional imports from Android 9.0 * skip vdc "getpwtype" command for Pie - vds communicates with vold directly, no need for connection retries first * add /system/bin/servicemanager to required services * mount per-devive additional partitions needed for decryption listed with device BoardConfig.mk TW_CRYPTO_SYSTEM_VOLD_MOUNT flag like(space separated): TW_CRYPTO_SYSTEM_VOLD_MOUNT := vendor cust odm * add function to backup crypto footer before running vdc commands and restore it after - on Xiaomi Mi Max 3 both Oreo and Pie stock roms vold alters cripto footer when decrypting data in recovery which causes system to ask for crypto password at next reboot although password stays unchanged. Crypto footer backup/restore added as workaround for systems whit ro.build.version.sdk > 25. Also to preserve crypto footer integrity decryption attempts are skipped if footer backup fails to ensure no data loss. Code adapted from https://gerrit.omnirom.org/#/c/android_bootable_recovery/+/31206/ Change-Id: I0a383f3843578fa55595cfea3b7c9c4431646a1a --- crypto/vold_decrypt/Android.bp--skip_soong-- | 15 ++ crypto/vold_decrypt/Android.mk | 40 ++++ .../init.recovery.vold_decrypt.servicemanager.rc | 8 + crypto/vold_decrypt/vdc_pie.cpp | 106 ++++++++++ crypto/vold_decrypt/vold_decrypt.cpp | 223 ++++++++++++++++++--- crypto/vold_decrypt/vold_decrypt.h | 1 + prebuilt/Android.mk | 15 ++ prebuilt/vdc_pie-arm | Bin 0 -> 46588 bytes prebuilt/vdc_pie-arm64 | Bin 0 -> 69824 bytes 9 files changed, 377 insertions(+), 31 deletions(-) create mode 100644 crypto/vold_decrypt/Android.bp--skip_soong-- create mode 100644 crypto/vold_decrypt/init.recovery.vold_decrypt.servicemanager.rc create mode 100644 crypto/vold_decrypt/vdc_pie.cpp create mode 100755 prebuilt/vdc_pie-arm create mode 100755 prebuilt/vdc_pie-arm64 diff --git a/crypto/vold_decrypt/Android.bp--skip_soong-- b/crypto/vold_decrypt/Android.bp--skip_soong-- new file mode 100644 index 000000000..f16942df2 --- /dev/null +++ b/crypto/vold_decrypt/Android.bp--skip_soong-- @@ -0,0 +1,15 @@ +cc_binary { + name: "vdc_pie", + defaults: ["vold_default_flags"], + + srcs: ["vdc_pie.cpp"], + shared_libs: [ + "libbase", + "libbinder", + "libcutils", + "libutils", + ], + static_libs: [ + "libvold_binder", + ], +} diff --git a/crypto/vold_decrypt/Android.mk b/crypto/vold_decrypt/Android.mk index 19c2963c0..860e61f20 100644 --- a/crypto/vold_decrypt/Android.mk +++ b/crypto/vold_decrypt/Android.mk @@ -27,6 +27,12 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) services := $(TW_CRYPTO_USE_SYSTEM_VOLD) endif + # Parse TW_CRYPTO_SYSTEM_VOLD_MOUNT + ifneq ($(TW_CRYPTO_SYSTEM_VOLD_MOUNT),) + # Per device additional partitions to mount + partitions := $(TW_CRYPTO_SYSTEM_VOLD_MOUNT) + endif + # List of .rc files for each additional service rc_files := $(foreach item,$(services),init.recovery.vold_decrypt.$(item).rc) @@ -86,6 +92,10 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) endif endif + ifneq ($(partitions),) + LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_MOUNT='"$(partitions)"' + endif + ifeq ($(TW_CRYPTO_SYSTEM_VOLD_DEBUG),true) # Enabling strace will expose the password in the strace logs!! LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_DEBUG @@ -101,5 +111,35 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) LOCAL_SHARED_LIBRARIES := libcutils include $(BUILD_STATIC_LIBRARY) + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) + include $(CLEAR_VARS) + LOCAL_MODULE := vdc_pie + LOCAL_SRC_FILES := vdc_pie.cpp + LOCAL_MODULE_TAGS := eng + LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES + LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin + LOCAL_CLANG := true + LOCAL_TIDY := true + LOCAL_TIDY_FLAGS := -warnings-as-errors=clang-analyzer-security*,cert-* + LOCAL_TIDY_CHECKS := -*,cert-*,clang,-analyzer-security* + LOCAL_STATIC_LIBRARIES := libvold_binder + LOCAL_SHARED_LIBRARIES := libbase libcutils libutils libbinder + LOCAL_CFLAGS := -Wall + ifeq ($(TWRP_INCLUDE_LOGCAT), true) + LOCAL_CFLAGS += -DTWRP_INCLUDE_LOGCAT + endif + ifneq ($(TARGET_ARCH), arm64) + ifneq ($(TARGET_ARCH), x86_64) + LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker + else + LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + endif + else + LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + endif + + include $(BUILD_EXECUTABLE) + endif + endif endif diff --git a/crypto/vold_decrypt/init.recovery.vold_decrypt.servicemanager.rc b/crypto/vold_decrypt/init.recovery.vold_decrypt.servicemanager.rc new file mode 100644 index 000000000..40672d737 --- /dev/null +++ b/crypto/vold_decrypt/init.recovery.vold_decrypt.servicemanager.rc @@ -0,0 +1,8 @@ +service sys_servicemanager /system/bin/servicemanager + user root + group root + setenv PATH /system/bin + setenv LD_LIBRARY_PATH /system/lib64:/system/lib + disabled + oneshot + seclabel u:r:vold:s0 diff --git a/crypto/vold_decrypt/vdc_pie.cpp b/crypto/vold_decrypt/vdc_pie.cpp new file mode 100644 index 000000000..a84071213 --- /dev/null +++ b/crypto/vold_decrypt/vdc_pie.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "android/os/IVold.h" + +#include +#include + +#include + +static void usage(); + +static android::sp getServiceAggressive() { + static char prop_value[PROPERTY_VALUE_MAX]; + property_get("init.svc.sys_vold", prop_value, "error"); + if (strncmp(prop_value, "running", strlen("running")) != 0) { + printf("vdc_pie: vold is not running, init.svc.sys_vold=%s\n", prop_value); + exit(EINVAL); + } + + android::sp res; + auto sm = android::defaultServiceManager(); + auto name = android::String16("vold"); + for (int i = 0; i < 5000; i++) { + res = sm->checkService(name); + if (res) { + printf("vdc_pie: Got vold, waited %d ms\n", (i * 10)); + break; + } + usleep(10000); // 10ms + } + return res; +} + +static int checkStatus(android::binder::Status status) { + if (status.isOk()) return 0; + std::string ret = status.toString8().string(); +#ifdef TWRP_INCLUDE_LOGCAT + printf("vdc_pie: Decryption failed, vold service returned: %s," + " see logcat for details\n", ret.c_str()); +#else + printf("vdc_pie: Decryption failed, vold service returned: %s\n", ret.c_str()); +#endif + return -1; +} + +int main(int argc, char** argv) { + std::vector args(argv + 1, argv + argc); + + if (args.size() > 0 && args[0] == "--wait") { + // Just ignore the --wait flag + args.erase(args.begin()); + } + + if (args.size() < 2) { + usage(); + exit(5); + } + android::sp binder = getServiceAggressive(); + if (!binder) { + printf("vdc_pie: Failed to obtain vold Binder\n"); + exit(EINVAL); + } + auto vold = android::interface_cast(binder); + + if (args[0] == "cryptfs" && args[1] == "checkpw" && args.size() == 3) { + return checkStatus(vold->fdeCheckPassword(args[2])); + } else { + usage(); + exit(EINVAL); + } + return 0; +} + +static void usage() { + printf("vdc_pie: Usage: vold_pie cryptfs checkpw \n"); +} diff --git a/crypto/vold_decrypt/vold_decrypt.cpp b/crypto/vold_decrypt/vold_decrypt.cpp index 707466e4c..ac872ea28 100644 --- a/crypto/vold_decrypt/vold_decrypt.cpp +++ b/crypto/vold_decrypt/vold_decrypt.cpp @@ -74,6 +74,7 @@ namespace { #define LOGERROR(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]E:" __VA_ARGS__); fflush(fp_kmsg); } } while (0) FILE *fp_kmsg = NULL; +int sdkver = 20; /* Debugging Functions */ @@ -223,7 +224,7 @@ string Wait_For_Property(const string& property_name, int utimeout = SLEEP_MAX_U break; LOGKMSG("waiting for %s to change from '%s' to '%s'\n", property_name.c_str(), prop_value, expected_value.c_str()); utimeout -= SLEEP_MIN_USEC; - usleep(SLEEP_MIN_USEC);; + usleep(SLEEP_MIN_USEC); } } property_get(property_name.c_str(), prop_value, "error"); @@ -766,7 +767,6 @@ vector Get_List_Of_Additional_Services(void) { void Set_Needed_Properties(void) { // vold won't start without ro.storage_structure on Kitkat string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk"); - int sdkver = 20; if (!sdkverstr.empty()) { sdkver = atoi(sdkverstr.c_str()); } @@ -775,8 +775,79 @@ void Set_Needed_Properties(void) { if (!ro_storage_structure.empty()) property_set("ro.storage_structure", ro_storage_structure.c_str()); } + property_set("hwservicemanager.ready", "false"); + property_set("sys.listeners.registered", "false"); + property_set("vendor.sys.listeners.registered", "false"); } +static unsigned int get_blkdev_size(int fd) { + unsigned long nr_sec; + + if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) { + nr_sec = 0; + } + + return (unsigned int) nr_sec; +} + +#define CRYPT_FOOTER_OFFSET 0x4000 +static char footer[16 * 1024]; +const char* userdata_path; +static off64_t offset; + +int footer_br(const string& command) { + int fd; + + if (command == "backup") { + unsigned int nr_sec; + TWPartition* userdata = PartitionManager.Find_Partition_By_Path("/data"); + userdata_path = userdata->Actual_Block_Device.c_str(); + fd = open(userdata_path, O_RDONLY); + if (fd < 0) { + LOGERROR("E:footer_backup: Cannot open '%s': %s\n", userdata_path, strerror(errno)); + return -1; + } + if ((nr_sec = get_blkdev_size(fd))) { + offset = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET; + } else { + LOGERROR("E:footer_br: Failed to get offset\n"); + close(fd); + return -1; + } + if (lseek64(fd, offset, SEEK_SET) == -1) { + LOGERROR("E:footer_backup: Failed to lseek64\n"); + close(fd); + return -1; + } + if (read(fd, footer, sizeof(footer)) != sizeof(footer)) { + LOGERROR("E:footer_br: Failed to read: %s\n", strerror(errno)); + close(fd); + return -1; + } + close(fd); + } else if (command == "restore") { + fd = open(userdata_path, O_WRONLY); + if (fd < 0) { + LOGERROR("E:footer_restore: Cannot open '%s': %s\n", userdata_path, strerror(errno)); + return -1; + } + if (lseek64(fd, offset, SEEK_SET) == -1) { + LOGERROR("E:footer_restore: Failed to lseek64\n"); + close(fd); + return -1; + } + if (write(fd, footer, sizeof(footer)) != sizeof(footer)) { + LOGERROR("E:footer_br: Failed to write: %s\n", strerror(errno)); + close(fd); + return -1; + } + close(fd); + } else { + LOGERROR("E:footer_br: wrong command argument: %s\n", command.c_str()); + return -1; + } + return 0; +} /* vdc Functions */ typedef struct { @@ -801,9 +872,14 @@ int Exec_vdc_cryptfs(const string& command, const string& argument, vdc_ReturnVa } } - const char *cmd[] = { "/system/bin/vdc", "cryptfs" }; + // getpwtype and checkpw commands are removed from Pie vdc, using modified vdc_pie + const char *cmd[] = { "/sbin/vdc_pie", "cryptfs" }; + if (sdkver < 28) + cmd[0] = "/system/bin/vdc"; const char *env[] = { "LD_LIBRARY_PATH=/system/lib64:/system/lib", NULL }; + LOGINFO("sdkver: %d, using %s\n", sdkver, cmd[0]); + #ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG string log_name = "/tmp/strace_vdc_" + command; #endif @@ -918,6 +994,9 @@ int Exec_vdc_cryptfs(const string& command, const string& argument, vdc_ReturnVa return -1; } } + if (sdkver >= 28) { + return WEXITSTATUS(status); + } return 0; } } @@ -930,32 +1009,34 @@ int Run_vdc(const string& Password) { LOGINFO("About to run vdc...\n"); - // Wait for vold connection - gettimeofday(&t1, NULL); - t2 = t1; - while ((t2.tv_sec - t1.tv_sec) < 5) { - // cryptfs getpwtype returns: R1=213(PasswordTypeResult) R2=? R3="password", "pattern", "pin", "default" - res = Exec_vdc_cryptfs("getpwtype", "", &vdcResult); - if (vdcResult.ResponseCode == PASSWORD_TYPE_RESULT) { - res = 0; - break; + // Pie vdc communicates with vold directly, no socket so lets not waste time + if (sdkver < 28) { + // Wait for vold connection + gettimeofday(&t1, NULL); + t2 = t1; + while ((t2.tv_sec - t1.tv_sec) < 5) { + // cryptfs getpwtype returns: R1=213(PasswordTypeResult) R2=? R3="password", "pattern", "pin", "default" + res = Exec_vdc_cryptfs("getpwtype", "", &vdcResult); + if (vdcResult.ResponseCode == PASSWORD_TYPE_RESULT) { + res = 0; + break; + } + LOGINFO("Retrying connection to vold (Reason: %s)\n", vdcResult.Output.c_str()); + usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts + gettimeofday(&t2, NULL); } - LOGINFO("Retrying connection to vold (Reason: %s)\n", vdcResult.Output.c_str()); - usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts - gettimeofday(&t2, NULL); - } - - if (res == 0 && (t2.tv_sec - t1.tv_sec) < 5) - LOGINFO("Connected to vold: %s\n", vdcResult.Output.c_str()); - else if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT) - return VD_ERR_VOLD_OPERATION_TIMEDOUT; // should never happen for getpwtype - else if (res) - return VD_ERR_FORK_EXECL_ERROR; - else if (vdcResult.ResponseCode != -1) - return VD_ERR_VOLD_UNEXPECTED_RESPONSE; - else - return VD_ERR_VDC_FAILED_TO_CONNECT; + if (res == 0 && (t2.tv_sec - t1.tv_sec) < 5) + LOGINFO("Connected to vold: %s\n", vdcResult.Output.c_str()); + else if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT) + return VD_ERR_VOLD_OPERATION_TIMEDOUT; // should never happen for getpwtype + else if (res) + return VD_ERR_FORK_EXECL_ERROR; + else if (vdcResult.ResponseCode != -1) + return VD_ERR_VOLD_UNEXPECTED_RESPONSE; + else + return VD_ERR_VDC_FAILED_TO_CONNECT; + } // Input password from GUI, or default password res = Exec_vdc_cryptfs("checkpw", Password, &vdcResult); @@ -970,6 +1051,12 @@ int Run_vdc(const string& Password) { return VD_ERR_VOLD_UNEXPECTED_RESPONSE; */ + // our vdc returns vold binder op status, + // we care about status.ok() only which is 0 + if (sdkver >= 28) { + vdcResult.Message = res; + } + if (vdcResult.Message != 0) { // try falling back to Lollipop hex passwords string hexPassword = convert_key_to_hex_ascii(Password); @@ -986,8 +1073,9 @@ int Run_vdc(const string& Password) { */ } - // vdc's return value is dependant upon source origin, it will either + // sdk < 28 vdc's return value is dependant upon source origin, it will either // return 0 or ResponseCode, so disregard and focus on decryption instead + // our vdc always returns 0 on success if (vdcResult.Message == 0) { // Decryption successful wait for crypto blk dev Wait_For_Property("ro.crypto.fs_crypto_blkdev"); @@ -1024,6 +1112,20 @@ int Vold_Decrypt_Core(const string& Password) { return VD_ERR_MISSING_VDC; } +#ifdef TW_CRYPTO_SYSTEM_VOLD_MOUNT + vector partitions = TWFunc::Split_String(TW_CRYPTO_SYSTEM_VOLD_MOUNT, " "); + for (size_t i = 0; i < partitions.size(); ++i) { + string mnt_point = "/" + partitions[i]; + if(PartitionManager.Find_Partition_By_Path(mnt_point)) { + if (!PartitionManager.Mount_By_Path(mnt_point, true)) { + LOGERROR("Unable to mount %s\n", mnt_point.c_str()); + return VD_ERR_UNABLE_TO_MOUNT_EXTRA; + } + LOGINFO("%s partition mounted\n", partitions[i].c_str()); + } + } +#endif + fp_kmsg = fopen("/dev/kmsg", "a"); LOGINFO("TW_CRYPTO_USE_SYSTEM_VOLD := true\n"); @@ -1064,8 +1166,20 @@ int Vold_Decrypt_Core(const string& Password) { LOGINFO("Starting services...\n"); #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES for (size_t i = 0; i < Services.size(); ++i) { - if (Services[i].bin_exists) + if (Services[i].bin_exists) { + if (Services[i].Service_Binary.find("keymaster") != string::npos) { + Wait_For_Property("hwservicemanager.ready", 500000, "true"); + LOGINFO(" hwservicemanager is ready.\n"); + } + Services[i].is_running = Start_Service(Services[i].VOLD_Service_Name); + + if (Services[i].Service_Binary == "qseecomd") { + if (Wait_For_Property("sys.listeners.registered", 500000, "true") == "true" + || Wait_For_Property("vendor.sys.listeners.registered", 500000, "true") == "true") + LOGINFO(" qseecomd listeners registered.\n"); + } + } } #endif is_vold_running = Start_Service("sys_vold"); @@ -1080,7 +1194,29 @@ int Vold_Decrypt_Core(const string& Password) { } } #endif - res = Run_vdc(Password); + + /* + * Oreo and Pie vold on some devices alters footer causing + * system to ask for decryption password at next boot although + * password haven't changed so we save footer before and restore it + * after vold operations + */ + if (sdkver > 25) { + if (footer_br("backup") == 0) { + LOGINFO("footer_br: crypto footer backed up\n"); + res = Run_vdc(Password); + if (footer_br("restore") == 0) + LOGINFO("footer_br: crypto footer restored\n"); + else + LOGERROR("footer_br: Failed to restore crypto footer\n"); + } else { + LOGERROR("footer_br: Failed to backup crypto footer, \ + skipping decrypt to prevent data loss. Reboot recovery to try again...\n"); + res = -1; + } + } else { + res = Run_vdc(Password); + } if (res != 0) { LOGINFO("Decryption failed\n"); @@ -1116,13 +1252,38 @@ int Vold_Decrypt_Core(const string& Password) { umount2(PartitionManager.Get_Android_Root_Path().c_str(), MNT_DETACH); } +#ifdef TW_CRYPTO_SYSTEM_VOLD_MOUNT + for (size_t i = 0; i < partitions.size(); ++i) { + string mnt_point = "/" + partitions[i]; + if(PartitionManager.Is_Mounted_By_Path(mnt_point)) { + if (!PartitionManager.UnMount_By_Path(mnt_point, true)) { + LOGINFO("WARNING: %s partition could not be unmounted normally!\n", partitions[i].c_str()); + umount2(mnt_point.c_str(), MNT_DETACH); + } + } + } +#endif + LOGINFO("Finished.\n"); #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES + Set_Needed_Properties(); // Restart previously running services for (size_t i = 0; i < Services.size(); ++i) { - if (Services[i].resume) + if (Services[i].resume) { + if (Services[i].Service_Binary.find("keymaster") != string::npos) { + Wait_For_Property("hwservicemanager.ready", 500000, "true"); + LOGINFO(" hwservicemanager is ready.\n"); + } + Start_Service(Services[i].TWRP_Service_Name); + + if (Services[i].Service_Binary == "qseecomd") { + if (Wait_For_Property("sys.listeners.registered", 500000, "true") == "true" + || Wait_For_Property("vendor.sys.listeners.registered", 500000, "true") == "true") + LOGINFO(" qseecomd listeners registered.\n"); + } + } } #endif diff --git a/crypto/vold_decrypt/vold_decrypt.h b/crypto/vold_decrypt/vold_decrypt.h index ba7a74720..248a42786 100644 --- a/crypto/vold_decrypt/vold_decrypt.h +++ b/crypto/vold_decrypt/vold_decrypt.h @@ -34,6 +34,7 @@ enum { VD_ERR_VOLD_OPERATION_TIMEDOUT = -8, VD_ERR_FORK_EXECL_ERROR = -9, VD_ERR_PASSWORD_EMPTY = -10, + VD_ERR_UNABLE_TO_MOUNT_EXTRA = -11, }; diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk index 235b77c86..27bcd5f41 100644 --- a/prebuilt/Android.mk +++ b/prebuilt/Android.mk @@ -499,3 +499,18 @@ LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin LOCAL_SRC_FILES := $(LOCAL_MODULE) include $(BUILD_PREBUILT) + +ifeq ($(TW_INCLUDE_CRYPTO), true) + ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),) + ifneq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) + # Prebuilt vdc_pie for pre-Pie SDK Platforms + include $(CLEAR_VARS) + LOCAL_MODULE := vdc_pie + LOCAL_MODULE_TAGS := eng + LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES + LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin + LOCAL_SRC_FILES := vdc_pie-$(TARGET_ARCH) + include $(BUILD_PREBUILT) + endif + endif +endif diff --git a/prebuilt/vdc_pie-arm b/prebuilt/vdc_pie-arm new file mode 100755 index 000000000..cf05cad53 Binary files /dev/null and b/prebuilt/vdc_pie-arm differ diff --git a/prebuilt/vdc_pie-arm64 b/prebuilt/vdc_pie-arm64 new file mode 100755 index 000000000..46231402f Binary files /dev/null and b/prebuilt/vdc_pie-arm64 differ -- cgit v1.2.3