Merge "recovery: Handle devices without /cache partition." am: 479d9dc2f2
am: f2726712ea

* commit 'f2726712ea1e02fdabf595ece1cfeab9a6147386':
  recovery: Handle devices without /cache partition.
diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c
index 09ec876..e7dd17b 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -39,6 +39,11 @@
     pMap->length = sb.st_size;
     pMap->range_count = 1;
     pMap->ranges = malloc(sizeof(MappedRange));
+    if (pMap->ranges == NULL) {
+        LOGE("malloc failed: %s\n", strerror(errno));
+        munmap(memPtr, sb.st_size);
+        return false;
+    }
     pMap->ranges[0].addr = memPtr;
     pMap->ranges[0].length = sb.st_size;
 
@@ -50,7 +55,7 @@
     char block_dev[PATH_MAX+1];
     size_t size;
     unsigned int blksize;
-    unsigned int blocks;
+    size_t blocks;
     unsigned int range_count;
     unsigned int i;
 
@@ -69,49 +74,80 @@
         LOGE("failed to parse block map header\n");
         return -1;
     }
-
-    blocks = ((size-1) / blksize) + 1;
+    if (blksize != 0) {
+        blocks = ((size-1) / blksize) + 1;
+    }
+    if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0) {
+        LOGE("invalid data in block map file: size %zu, blksize %u, range_count %u\n",
+             size, blksize, range_count);
+        return -1;
+    }
 
     pMap->range_count = range_count;
-    pMap->ranges = malloc(range_count * sizeof(MappedRange));
-    memset(pMap->ranges, 0, range_count * sizeof(MappedRange));
+    pMap->ranges = calloc(range_count, sizeof(MappedRange));
+    if (pMap->ranges == NULL) {
+        LOGE("calloc(%u, %zu) failed: %s\n", range_count, sizeof(MappedRange), strerror(errno));
+        return -1;
+    }
 
     // Reserve enough contiguous address space for the whole file.
     unsigned char* reserve;
     reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
     if (reserve == MAP_FAILED) {
         LOGE("failed to reserve address space: %s\n", strerror(errno));
+        free(pMap->ranges);
         return -1;
     }
 
-    pMap->ranges[range_count-1].addr = reserve;
-    pMap->ranges[range_count-1].length = blocks * blksize;
-
     int fd = open(block_dev, O_RDONLY);
     if (fd < 0) {
         LOGE("failed to open block device %s: %s\n", block_dev, strerror(errno));
+        munmap(reserve, blocks * blksize);
+        free(pMap->ranges);
         return -1;
     }
 
     unsigned char* next = reserve;
+    size_t remaining_size = blocks * blksize;
+    bool success = true;
     for (i = 0; i < range_count; ++i) {
-        int start, end;
-        if (fscanf(mapf, "%d %d\n", &start, &end) != 2) {
+        size_t start, end;
+        if (fscanf(mapf, "%zu %zu\n", &start, &end) != 2) {
             LOGE("failed to parse range %d in block map\n", i);
-            return -1;
+            success = false;
+            break;
+        }
+        size_t length = (end - start) * blksize;
+        if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) {
+          LOGE("unexpected range in block map: %zu %zu\n", start, end);
+          success = false;
+          break;
         }
 
-        void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize);
+        void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize);
         if (addr == MAP_FAILED) {
             LOGE("failed to map block %d: %s\n", i, strerror(errno));
-            return -1;
+            success = false;
+            break;
         }
         pMap->ranges[i].addr = addr;
-        pMap->ranges[i].length = (end-start)*blksize;
+        pMap->ranges[i].length = length;
 
-        next += pMap->ranges[i].length;
+        next += length;
+        remaining_size -= length;
+    }
+    if (success && remaining_size != 0) {
+      LOGE("ranges in block map are invalid: remaining_size = %zu\n", remaining_size);
+      success = false;
+    }
+    if (!success) {
+      close(fd);
+      munmap(reserve, blocks * blksize);
+      free(pMap->ranges);
+      return -1;
     }
 
+    close(fd);
     pMap->addr = reserve;
     pMap->length = size;
 
@@ -134,6 +170,7 @@
 
         if (sysMapBlockFile(mapf, pMap) != 0) {
             LOGE("Map of '%s' failed\n", fn);
+            fclose(mapf);
             return -1;
         }
 
diff --git a/recovery.cpp b/recovery.cpp
index 9d6f511..fe6793d 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -79,7 +79,10 @@
 static const char *LOG_FILE = "/cache/recovery/log";
 static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
 static const char *LOCALE_FILE = "/cache/recovery/last_locale";
+static const char *CONVERT_FBE_DIR = "/tmp/convert_fbe";
+static const char *CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe";
 static const char *CACHE_ROOT = "/cache";
+static const char *DATA_ROOT = "/data";
 static const char *SDCARD_ROOT = "/sdcard";
 static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
 static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
@@ -519,6 +522,7 @@
 
 static bool erase_volume(const char* volume) {
     bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
+    bool is_data = (strcmp(volume, DATA_ROOT) == 0);
 
     ui->SetBackground(RecoveryUI::ERASING);
     ui->SetProgressType(RecoveryUI::INDETERMINATE);
@@ -573,7 +577,28 @@
     ui->Print("Formatting %s...\n", volume);
 
     ensure_path_unmounted(volume);
-    int result = format_volume(volume);
+
+    int result;
+
+    if (is_data && reason && strcmp(reason, "convert_fbe") == 0) {
+        // Create convert_fbe breadcrumb file to signal to init
+        // to convert to file based encryption, not full disk encryption
+        if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
+            ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno));
+            return true;
+        }
+        FILE* f = fopen(CONVERT_FBE_FILE, "wb");
+        if (!f) {
+            ui->Print("Failed to convert to file encryption %s\n", strerror(errno));
+            return true;
+        }
+        fclose(f);
+        result = format_volume(volume, CONVERT_FBE_DIR);
+        remove(CONVERT_FBE_FILE);
+        rmdir(CONVERT_FBE_DIR);
+    } else {
+        result = format_volume(volume);
+    }
 
     if (is_cache) {
         while (head) {
diff --git a/roots.cpp b/roots.cpp
index 12c6b5e..f361cb8 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -175,7 +175,7 @@
     return WEXITSTATUS(status);
 }
 
-int format_volume(const char* volume) {
+int format_volume(const char* volume, const char* directory) {
     Volume* v = volume_for_path(volume);
     if (v == NULL) {
         LOGE("unknown volume \"%s\"\n", volume);
@@ -241,7 +241,7 @@
         }
         int result;
         if (strcmp(v->fs_type, "ext4") == 0) {
-            result = make_ext4fs(v->blk_device, length, volume, sehandle);
+            result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory);
         } else {   /* Has to be f2fs because we checked earlier. */
             if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) {
                 LOGE("format_volume: crypt footer + negative length (%zd) not supported on %s\n", length, v->fs_type);
@@ -273,6 +273,10 @@
     return -1;
 }
 
+int format_volume(const char* volume) {
+    return format_volume(volume, NULL);
+}
+
 int setup_install_mounts() {
     if (fstab == NULL) {
         LOGE("can't set up install mounts: no fstab loaded\n");
diff --git a/roots.h b/roots.h
index 6e3b243..a14b7d9 100644
--- a/roots.h
+++ b/roots.h
@@ -41,6 +41,12 @@
 // it is mounted.
 int format_volume(const char* volume);
 
+// Reformat the given volume (must be the mount point only, eg
+// "/cache"), no paths permitted.  Attempts to unmount the volume if
+// it is mounted.
+// Copies 'directory' to root of the newly formatted volume
+int format_volume(const char* volume, const char* directory);
+
 // Ensure that all and only the volumes that packages expect to find
 // mounted (/tmp and /cache) are mounted.  Returns 0 on success.
 int setup_install_mounts();
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index 705744e..e783b9e 100644
--- a/uncrypt/uncrypt.cpp
+++ b/uncrypt/uncrypt.cpp
@@ -39,6 +39,53 @@
 // Recovery can take this block map file and retrieve the underlying
 // file data to use as an update package.
 
+/**
+ * In addition to the uncrypt work, uncrypt also takes care of setting and
+ * clearing the bootloader control block (BCB) at /misc partition.
+ *
+ * uncrypt is triggered as init services on demand. It uses socket to
+ * communicate with its caller (i.e. system_server). The socket is managed by
+ * init (i.e. created prior to the service starts, and destroyed when uncrypt
+ * exits).
+ *
+ * Below is the uncrypt protocol.
+ *
+ *    a. caller                 b. init                    c. uncrypt
+ * ---------------            ------------               --------------
+ *  a1. ctl.start:
+ *    setup-bcb /
+ *    clear-bcb /
+ *    uncrypt
+ *
+ *                         b2. create socket at
+ *                           /dev/socket/uncrypt
+ *
+ *                                                   c3. listen and accept
+ *
+ *  a4. send a 4-byte int
+ *    (message length)
+ *                                                   c5. receive message length
+ *  a6. send message
+ *                                                   c7. receive message
+ *                                                   c8. <do the work; may send
+ *                                                      the progress>
+ *  a9. <may handle progress>
+ *                                                   c10. <upon finishing>
+ *                                                     send "100" or "-1"
+ *
+ *  a11. receive status code
+ *  a12. send a 4-byte int to
+ *    ack the receive of the
+ *    final status code
+ *                                                   c13. receive and exit
+ *
+ *                          b14. destroy the socket
+ *
+ * Note that a12 and c13 are necessary to ensure a11 happens before the socket
+ * gets destroyed in b14.
+ */
+
+#include <arpa/inet.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -49,6 +96,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -63,6 +111,7 @@
 #include <android-base/strings.h>
 #include <cutils/android_reboot.h>
 #include <cutils/properties.h>
+#include <cutils/sockets.h>
 #include <fs_mgr.h>
 
 #define LOG_TAG "uncrypt"
@@ -73,12 +122,21 @@
 
 #define WINDOW_SIZE 5
 
+// uncrypt provides three services: SETUP_BCB, CLEAR_BCB and UNCRYPT.
+//
+// SETUP_BCB and CLEAR_BCB services use socket communication and do not rely
+// on /cache partitions. They will handle requests to reboot into recovery
+// (for applying updates for non-A/B devices, or factory resets for all
+// devices).
+//
+// UNCRYPT service still needs files on /cache partition (UNCRYPT_PATH_FILE
+// and CACHE_BLOCK_MAP). It will be working (and needed) only for non-A/B
+// devices, on which /cache partitions always exist.
 static const std::string CACHE_BLOCK_MAP = "/cache/recovery/block.map";
-static const std::string COMMAND_FILE = "/cache/recovery/command";
-static const std::string STATUS_FILE = "/cache/recovery/uncrypt_status";
 static const std::string UNCRYPT_PATH_FILE = "/cache/recovery/uncrypt_file";
+static const std::string UNCRYPT_SOCKET = "uncrypt";
 
-static struct fstab* fstab = NULL;
+static struct fstab* fstab = nullptr;
 
 static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) {
     if (TEMP_FAILURE_RETRY(lseek64(wfd, offset, SEEK_SET)) == -1) {
@@ -152,6 +210,11 @@
     return NULL;
 }
 
+static bool write_status_to_socket(int status, int socket) {
+    int status_out = htonl(status);
+    return android::base::WriteFully(socket, &status_out, sizeof(int));
+}
+
 // Parse uncrypt_file to find the update package name.
 static bool find_uncrypt_package(const std::string& uncrypt_path_file, std::string* package_name) {
     CHECK(package_name != nullptr);
@@ -167,7 +230,7 @@
 }
 
 static int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
-                             bool encrypted, int status_fd) {
+                             bool encrypted, int socket) {
     std::string err;
     if (!android::base::RemoveFileIfExists(map_file, &err)) {
         ALOGE("failed to remove the existing map file %s: %s", map_file, err.c_str());
@@ -180,9 +243,9 @@
         return -1;
     }
 
-    // Make sure we can write to the status_file.
-    if (!android::base::WriteStringToFd("0\n", status_fd)) {
-        ALOGE("failed to update \"%s\"\n", STATUS_FILE.c_str());
+    // Make sure we can write to the socket.
+    if (!write_status_to_socket(0, socket)) {
+        ALOGE("failed to write to socket %d\n", socket);
         return -1;
     }
 
@@ -234,8 +297,8 @@
         // Update the status file, progress must be between [0, 99].
         int progress = static_cast<int>(100 * (double(pos) / double(sb.st_size)));
         if (progress > last_progress) {
-          last_progress = progress;
-          android::base::WriteStringToFd(std::to_string(progress) + "\n", status_fd);
+            last_progress = progress;
+            write_status_to_socket(progress, socket);
         }
 
         if ((tail+1) % WINDOW_SIZE == head) {
@@ -352,15 +415,12 @@
 }
 
 static std::string get_misc_blk_device() {
-    struct fstab* fstab = read_fstab();
     if (fstab == nullptr) {
         return "";
     }
-    for (int i = 0; i < fstab->num_entries; ++i) {
-        fstab_rec* v = &fstab->recs[i];
-        if (v->mount_point != nullptr && strcmp(v->mount_point, "/misc") == 0) {
-            return v->blk_device;
-        }
+    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+    if (rec != nullptr) {
+        return rec->blk_device;
     }
     return "";
 }
@@ -406,17 +466,7 @@
     return 0;
 }
 
-static void reboot_to_recovery() {
-    ALOGI("rebooting to recovery");
-    property_set("sys.powerctl", "reboot,recovery");
-    while (true) {
-      pause();
-    }
-    ALOGE("reboot didn't succeed?");
-}
-
-static int uncrypt(const char* input_path, const char* map_file, int status_fd) {
-
+static int uncrypt(const char* input_path, const char* map_file, const int socket) {
     ALOGI("update package is \"%s\"", input_path);
 
     // Turn the name of the file we're supposed to convert into an
@@ -427,10 +477,6 @@
         return 1;
     }
 
-    if (read_fstab() == NULL) {
-        return 1;
-    }
-
     bool encryptable;
     bool encrypted;
     const char* blk_dev = find_block_device(path, &encryptable, &encrypted);
@@ -454,7 +500,7 @@
     // and /sdcard we leave the file alone.
     if (strncmp(path, "/data/", 6) == 0) {
         ALOGI("writing block map %s", map_file);
-        if (produce_block_map(path, map_file, blk_dev, encrypted, status_fd) != 0) {
+        if (produce_block_map(path, map_file, blk_dev, encrypted, socket) != 0) {
             return 1;
         }
     }
@@ -462,71 +508,66 @@
     return 0;
 }
 
-static int uncrypt_wrapper(const char* input_path, const char* map_file,
-                           const std::string& status_file) {
-    // The pipe has been created by the system server.
-    unique_fd status_fd(open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR));
-    if (!status_fd) {
-        ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno));
-        return 1;
-    }
-
+static bool uncrypt_wrapper(const char* input_path, const char* map_file, const int socket) {
     std::string package;
     if (input_path == nullptr) {
         if (!find_uncrypt_package(UNCRYPT_PATH_FILE, &package)) {
-            android::base::WriteStringToFd("-1\n", status_fd.get());
-            return 1;
+            write_status_to_socket(-1, socket);
+            return false;
         }
         input_path = package.c_str();
     }
     CHECK(map_file != nullptr);
-    int status = uncrypt(input_path, map_file, status_fd.get());
+    int status = uncrypt(input_path, map_file, socket);
     if (status != 0) {
-        android::base::WriteStringToFd("-1\n", status_fd.get());
-        return 1;
+        write_status_to_socket(-1, socket);
+        return false;
     }
-    android::base::WriteStringToFd("100\n", status_fd.get());
-    return 0;
+    write_status_to_socket(100, socket);
+    return true;
 }
 
-static int clear_bcb(const std::string& status_file) {
-    unique_fd status_fd(open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR));
-    if (!status_fd) {
-        ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno));
-        return 1;
-    }
+static bool clear_bcb(const int socket) {
     bootloader_message boot = {};
     if (write_bootloader_message(&boot) != 0) {
-        android::base::WriteStringToFd("-1\n", status_fd.get());
-        return 1;
+        write_status_to_socket(-1, socket);
+        return false;
     }
-    android::base::WriteStringToFd("100\n", status_fd.get());
-    return 0;
+    write_status_to_socket(100, socket);
+    return true;
 }
 
-static int setup_bcb(const std::string& command_file, const std::string& status_file) {
-    unique_fd status_fd(open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR));
-    if (!status_fd) {
-        ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno));
-        return 1;
+static bool setup_bcb(const int socket) {
+    // c5. receive message length
+    int length;
+    if (!android::base::ReadFully(socket, &length, 4)) {
+        ALOGE("failed to read the length: %s", strerror(errno));
+        return false;
     }
+    length = ntohl(length);
+
+    // c7. receive message
     std::string content;
-    if (!android::base::ReadFileToString(command_file, &content)) {
-        ALOGE("failed to read \"%s\": %s", command_file.c_str(), strerror(errno));
-        android::base::WriteStringToFd("-1\n", status_fd.get());
-        return 1;
+    content.resize(length);
+    if (!android::base::ReadFully(socket, &content[0], length)) {
+        ALOGE("failed to read the length: %s", strerror(errno));
+        return false;
     }
+    ALOGI("  received command: [%s] (%zu)", content.c_str(), content.size());
+
+    // c8. setup the bcb command
     bootloader_message boot = {};
     strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
     strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
     strlcat(boot.recovery, content.c_str(), sizeof(boot.recovery));
     if (write_bootloader_message(&boot) != 0) {
         ALOGE("failed to set bootloader message");
-        android::base::WriteStringToFd("-1\n", status_fd.get());
-        return 1;
+        write_status_to_socket(-1, socket);
+        return false;
     }
-    android::base::WriteStringToFd("100\n", status_fd.get());
-    return 0;
+    // c10. send "100" status
+    write_status_to_socket(100, socket);
+    return true;
 }
 
 static int read_bcb() {
@@ -543,32 +584,81 @@
 static void usage(const char* exename) {
     fprintf(stderr, "Usage of %s:\n", exename);
     fprintf(stderr, "%s [<package_path> <map_file>]  Uncrypt ota package.\n", exename);
-    fprintf(stderr, "%s --reboot  Clear BCB data and reboot to recovery.\n", exename);
     fprintf(stderr, "%s --clear-bcb  Clear BCB data in misc partition.\n", exename);
     fprintf(stderr, "%s --setup-bcb  Setup BCB data by command file.\n", exename);
     fprintf(stderr, "%s --read-bcb   Read BCB data from misc partition.\n", exename);
 }
 
 int main(int argc, char** argv) {
-    if (argc == 2) {
-        if (strcmp(argv[1], "--reboot") == 0) {
-            reboot_to_recovery();
-        } else if (strcmp(argv[1], "--clear-bcb") == 0) {
-            return clear_bcb(STATUS_FILE);
-        } else if (strcmp(argv[1], "--setup-bcb") == 0) {
-            return setup_bcb(COMMAND_FILE, STATUS_FILE);
-        } else if (strcmp(argv[1], "--read-bcb") == 0) {
-            return read_bcb();
-        }
-    } else if (argc == 1 || argc == 3) {
-        const char* input_path = nullptr;
-        const char* map_file = CACHE_BLOCK_MAP.c_str();
-        if (argc == 3) {
-            input_path = argv[1];
-            map_file = argv[2];
-        }
-        return uncrypt_wrapper(input_path, map_file, STATUS_FILE);
+    enum { UNCRYPT, SETUP_BCB, CLEAR_BCB } action;
+    const char* input_path = nullptr;
+    const char* map_file = CACHE_BLOCK_MAP.c_str();
+
+    if (argc == 2 && strcmp(argv[1], "--clear-bcb") == 0) {
+        action = CLEAR_BCB;
+    } else if (argc == 2 && strcmp(argv[1], "--setup-bcb") == 0) {
+        action = SETUP_BCB;
+    } else if (argc ==2 && strcmp(argv[1], "--read-bcb") == 0) {
+        return read_bcb();
+    } else if (argc == 1) {
+        action = UNCRYPT;
+    } else if (argc == 3) {
+        input_path = argv[1];
+        map_file = argv[2];
+        action = UNCRYPT;
+    } else {
+        usage(argv[0]);
+        return 2;
     }
-    usage(argv[0]);
-    return 2;
+
+    if ((fstab = read_fstab()) == nullptr) {
+        return 1;
+    }
+
+    // c3. The socket is created by init when starting the service. uncrypt
+    // will use the socket to communicate with its caller.
+    unique_fd service_socket(android_get_control_socket(UNCRYPT_SOCKET.c_str()));
+    if (!service_socket) {
+        ALOGE("failed to open socket \"%s\": %s", UNCRYPT_SOCKET.c_str(), strerror(errno));
+        return 1;
+    }
+    fcntl(service_socket.get(), F_SETFD, FD_CLOEXEC);
+
+    if (listen(service_socket.get(), 1) == -1) {
+        ALOGE("failed to listen on socket %d: %s", service_socket.get(), strerror(errno));
+        return 1;
+    }
+
+    unique_fd socket_fd(accept4(service_socket.get(), nullptr, nullptr, SOCK_CLOEXEC));
+    if (!socket_fd) {
+        ALOGE("failed to accept on socket %d: %s", service_socket.get(), strerror(errno));
+        return 1;
+    }
+
+    bool success = false;
+    switch (action) {
+        case UNCRYPT:
+            success = uncrypt_wrapper(input_path, map_file, socket_fd.get());
+            break;
+        case SETUP_BCB:
+            success = setup_bcb(socket_fd.get());
+            break;
+        case CLEAR_BCB:
+            success = clear_bcb(socket_fd.get());
+            break;
+        default:  // Should never happen.
+            ALOGE("Invalid uncrypt action code: %d", action);
+            return 1;
+    }
+
+    // c13. Read a 4-byte code from the client before uncrypt exits. This is to
+    // ensure the client to receive the last status code before the socket gets
+    // destroyed.
+    int code;
+    if (android::base::ReadFully(socket_fd.get(), &code, 4)) {
+        ALOGI("  received %d, exiting now", code);
+    } else {
+        ALOGE("failed to read the code: %s", strerror(errno));
+    }
+    return success ? 0 : 1;
 }
diff --git a/uncrypt/uncrypt.rc b/uncrypt/uncrypt.rc
index b07c1da..52f564e 100644
--- a/uncrypt/uncrypt.rc
+++ b/uncrypt/uncrypt.rc
@@ -1,19 +1,17 @@
 service uncrypt /system/bin/uncrypt
     class main
-    disabled
-    oneshot
-
-service pre-recovery /system/bin/uncrypt --reboot
-    class main
+    socket uncrypt stream 600 system system
     disabled
     oneshot
 
 service setup-bcb /system/bin/uncrypt --setup-bcb
     class main
+    socket uncrypt stream 600 system system
     disabled
     oneshot
 
 service clear-bcb /system/bin/uncrypt --clear-bcb
     class main
+    socket uncrypt stream 600 system system
     disabled
-    oneshot
\ No newline at end of file
+    oneshot
diff --git a/wear_ui.cpp b/wear_ui.cpp
index 50aeb38..8a57cff 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -36,6 +36,7 @@
 #include "ui.h"
 #include "cutils/properties.h"
 #include "android-base/strings.h"
+#include "android-base/stringprintf.h"
 
 static int char_width;
 static int char_height;
@@ -653,3 +654,35 @@
     }
     pthread_mutex_unlock(&updateMutex);
 }
+
+void WearRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    PrintV(fmt, false, ap);
+    va_end(ap);
+}
+
+void WearRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
+    std::string str;
+    android::base::StringAppendV(&str, fmt, ap);
+
+    if (copy_to_stdout) {
+        fputs(str.c_str(), stdout);
+    }
+
+    pthread_mutex_lock(&updateMutex);
+    if (text_rows > 0 && text_cols > 0) {
+        for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
+            if (*ptr == '\n' || text_col >= text_cols) {
+                text[text_row][text_col] = '\0';
+                text_col = 0;
+                text_row = (text_row + 1) % text_rows;
+                if (text_row == text_top) text_top = (text_top + 1) % text_rows;
+            }
+            if (*ptr != '\n') text[text_row][text_col++] = *ptr;
+        }
+        text[text_row][text_col] = '\0';
+        update_screen_locked();
+    }
+    pthread_mutex_unlock(&updateMutex);
+}
diff --git a/wear_ui.h b/wear_ui.h
index 63c1b6e..768141c 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -47,6 +47,7 @@
 
     // printing messages
     void Print(const char* fmt, ...);
+    void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3);
     void ShowFile(const char* filename);
     void ShowFile(FILE* fp);
 
@@ -133,6 +134,7 @@
     void ClearText();
     void DrawTextLine(int x, int* y, const char* line, bool bold);
     void DrawTextLines(int x, int* y, const char* const* lines);
+    void PrintV(const char*, bool, va_list);
 };
 
 #endif  // RECOVERY_WEAR_UI_H