merge in nyc-release history after reset to nyc-dev
diff --git a/etc/init.rc b/etc/init.rc
index dc18659..5915b8d 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -1,6 +1,9 @@
 import /init.recovery.${ro.hardware}.rc
 
 on early-init
+    # Set the security context of /postinstall if present.
+    restorecon /postinstall
+
     start ueventd
     start healthd
 
diff --git a/recovery.cpp b/recovery.cpp
index 4ae685f..fe6793d 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -101,6 +101,7 @@
 char* stage = NULL;
 char* reason = NULL;
 bool modified_flash = false;
+static bool has_cache = false;
 
 /*
  * The recovery tool communicates with the main system through /cache files.
@@ -313,8 +314,8 @@
         }
     }
 
-    // --- if that doesn't work, try the command file
-    if (*argc <= 1) {
+    // --- if that doesn't work, try the command file (if we have /cache).
+    if (*argc <= 1 && has_cache) {
         FILE *fp = fopen_path(COMMAND_FILE, "r");
         if (fp != NULL) {
             char *token;
@@ -436,6 +437,11 @@
 }
 
 static void copy_logs() {
+    // We can do nothing for now if there's no /cache partition.
+    if (!has_cache) {
+        return;
+    }
+
     // We only rotate and record the log of the current session if there are
     // actual attempts to modify the flash, such as wipes, installs from BCB
     // or menu selections. This is to avoid unnecessary rotation (and
@@ -467,7 +473,7 @@
 static void
 finish_recovery(const char *send_intent) {
     // By this point, we're ready to return to the main system...
-    if (send_intent != NULL) {
+    if (send_intent != NULL && has_cache) {
         FILE *fp = fopen_path(INTENT_FILE, "w");
         if (fp == NULL) {
             LOGE("Can't open %s\n", INTENT_FILE);
@@ -480,7 +486,7 @@
     // Save the locale to cache, so if recovery is next started up
     // without a --locale argument (eg, directly from the bootloader)
     // it will use the last-known locale.
-    if (locale != NULL) {
+    if (locale != NULL && has_cache) {
         LOGI("Saving locale \"%s\"\n", locale);
         FILE* fp = fopen_path(LOCALE_FILE, "w");
         fwrite(locale, 1, strlen(locale), fp);
@@ -497,12 +503,13 @@
     set_bootloader_message(&boot);
 
     // Remove the command file, so recovery won't repeat indefinitely.
-    if (ensure_path_mounted(COMMAND_FILE) != 0 ||
-        (unlink(COMMAND_FILE) && errno != ENOENT)) {
-        LOGW("Can't unlink %s\n", COMMAND_FILE);
+    if (has_cache) {
+        if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
+            LOGW("Can't unlink %s\n", COMMAND_FILE);
+        }
+        ensure_path_unmounted(CACHE_ROOT);
     }
 
-    ensure_path_unmounted(CACHE_ROOT);
     sync();  // For good measure.
 }
 
@@ -793,7 +800,7 @@
     bool success =
         device->PreWipeData() &&
         erase_volume("/data") &&
-        erase_volume("/cache") &&
+        (has_cache ? erase_volume("/cache") : true) &&
         device->PostWipeData();
     ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
     return success;
@@ -801,6 +808,11 @@
 
 // Return true on success.
 static bool wipe_cache(bool should_confirm, Device* device) {
+    if (!has_cache) {
+        ui->Print("No /cache partition found.\n");
+        return false;
+    }
+
     if (should_confirm && !yes_no(device, "Wipe cache?", "  THIS CAN NOT BE UNDONE!")) {
         return false;
     }
@@ -814,6 +826,11 @@
 }
 
 static void choose_recovery_file(Device* device) {
+    if (!has_cache) {
+        ui->Print("No /cache partition found.\n");
+        return;
+    }
+
     // "Back" + KEEP_LOG_COUNT * 2 + terminating nullptr entry
     char* entries[1 + KEEP_LOG_COUNT * 2 + 1];
     memset(entries, 0, sizeof(entries));
@@ -1167,6 +1184,8 @@
     printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
 
     load_volume_table();
+    has_cache = volume_for_path(CACHE_ROOT) != nullptr;
+
     get_args(&argc, &argv);
 
     const char *send_intent = NULL;
@@ -1207,7 +1226,7 @@
         }
     }
 
-    if (locale == NULL) {
+    if (locale == nullptr && has_cache) {
         load_locale_from_cache();
     }
     printf("locale is [%s]\n", locale);
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index 2a32108..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,8 +466,7 @@
     return 0;
 }
 
-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
@@ -418,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);
@@ -445,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;
         }
     }
@@ -453,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() {
@@ -540,23 +590,75 @@
 }
 
 int main(int argc, char** argv) {
-    if (argc == 2) {
-        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 d5d803b..52f564e 100644
--- a/uncrypt/uncrypt.rc
+++ b/uncrypt/uncrypt.rc
@@ -1,14 +1,17 @@
 service uncrypt /system/bin/uncrypt
     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