Merge "add sideload-host mode to adb"
diff --git a/adb/commandline.c b/adb/commandline.c
index 27e436d..bcfdba0 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -41,7 +41,7 @@
 
 void get_my_path(char *s, size_t maxLen);
 int find_sync_dirs(const char *srcarg,
-        char **android_srcdir_out, char **data_srcdir_out);
+        char **android_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out);
 int install_app(transport_type transport, char* serial, int argc, char** argv);
 int uninstall_app(transport_type transport, char* serial, int argc, char** argv);
 
@@ -196,7 +196,7 @@
         "  adb get-serialno             - prints: <serial-number>\n"
         "  adb get-devpath              - prints: <device-path>\n"
         "  adb status-window            - continuously print device status for a specified device\n"
-        "  adb remount                  - remounts the /system partition on the device read-write\n"
+        "  adb remount                  - remounts the /system and /vendor (if present) partitions on the device read-write\n"
         "  adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n"
         "  adb reboot-bootloader        - reboots the device into the bootloader\n"
         "  adb root                     - restarts the adbd daemon with root permissions\n"
@@ -212,9 +212,9 @@
         "adb sync notes: adb sync [ <directory> ]\n"
         "  <localdir> can be interpreted in several ways:\n"
         "\n"
-        "  - If <directory> is not specified, both /system and /data partitions will be updated.\n"
+        "  - If <directory> is not specified, /system, /vendor (if present), and /data partitions will be updated.\n"
         "\n"
-        "  - If it is \"system\" or \"data\", only the corresponding partition\n"
+        "  - If it is \"system\", \"vendor\" or \"data\", only the corresponding partition\n"
         "    is updated.\n"
         "\n"
         "environmental variables:\n"
@@ -1607,7 +1607,7 @@
     }
 
     if(!strcmp(argv[0], "sync")) {
-        char *srcarg, *android_srcpath, *data_srcpath;
+        char *srcarg, *android_srcpath, *data_srcpath, *vendor_srcpath;
         int listonly = 0;
 
         int ret;
@@ -1627,15 +1627,18 @@
         } else {
             return usage();
         }
-        ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath);
+        ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath, &vendor_srcpath);
         if(ret != 0) return usage();
 
         if(android_srcpath != NULL)
             ret = do_sync_sync(android_srcpath, "/system", listonly);
+        if(ret == 0 && vendor_srcpath != NULL)
+            ret = do_sync_sync(vendor_srcpath, "/vendor", listonly);
         if(ret == 0 && data_srcpath != NULL)
             ret = do_sync_sync(data_srcpath, "/data", listonly);
 
         free(android_srcpath);
+        free(vendor_srcpath);
         free(data_srcpath);
         return ret;
     }
@@ -1746,25 +1749,30 @@
 }
 
 int find_sync_dirs(const char *srcarg,
-        char **android_srcdir_out, char **data_srcdir_out)
+        char **android_srcdir_out, char **data_srcdir_out, char **vendor_srcdir_out)
 {
-    char *android_srcdir, *data_srcdir;
+    char *android_srcdir = NULL, *data_srcdir = NULL, *vendor_srcdir = NULL;
+    struct stat st;
 
     if(srcarg == NULL) {
         android_srcdir = product_file("system");
         data_srcdir = product_file("data");
+        vendor_srcdir = product_file("vendor");
+        /* Check if vendor partition exists */
+        if (lstat(vendor_srcdir, &st) || !S_ISDIR(st.st_mode))
+            vendor_srcdir = NULL;
     } else {
         /* srcarg may be "data", "system" or NULL.
          * if srcarg is NULL, then both data and system are synced
          */
         if(strcmp(srcarg, "system") == 0) {
             android_srcdir = product_file("system");
-            data_srcdir = NULL;
         } else if(strcmp(srcarg, "data") == 0) {
-            android_srcdir = NULL;
             data_srcdir = product_file("data");
+        } else if(strcmp(srcarg, "vendor") == 0) {
+            vendor_srcdir = product_file("vendor");
         } else {
-            /* It's not "system" or "data".
+            /* It's not "system", "vendor", or "data".
              */
             return 1;
         }
@@ -1775,11 +1783,15 @@
     else
         free(android_srcdir);
 
-    if(data_srcdir_out != NULL)
-        *data_srcdir_out = data_srcdir;
+    if(vendor_srcdir_out != NULL)
+        *vendor_srcdir_out = vendor_srcdir;
     else
-        free(data_srcdir);
+        free(vendor_srcdir);
 
+    if(data_srcdir_out != NULL)
+            *data_srcdir_out = data_srcdir;
+        else
+            free(data_srcdir);
     return 0;
 }
 
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
index d3cb113..c1ab808 100644
--- a/adb/file_sync_client.c
+++ b/adb/file_sync_client.c
@@ -697,30 +697,33 @@
             continue;
         strcpy(stat_path, lpath);
         strcat(stat_path, de->d_name);
-        stat(stat_path, &st);
 
-        if (S_ISDIR(st.st_mode)) {
-            ci = mkcopyinfo(lpath, rpath, name, 1);
-            ci->next = dirlist;
-            dirlist = ci;
-        } else {
-            ci = mkcopyinfo(lpath, rpath, name, 0);
-            if(lstat(ci->src, &st)) {
-                fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
-                free(ci);
-                closedir(d);
-                return -1;
-            }
-            if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
-                fprintf(stderr, "skipping special file '%s'\n", ci->src);
-                free(ci);
+        if(!lstat(stat_path, &st)) {
+            if (S_ISDIR(st.st_mode)) {
+                ci = mkcopyinfo(lpath, rpath, name, 1);
+                ci->next = dirlist;
+                dirlist = ci;
             } else {
-                ci->time = st.st_mtime;
-                ci->mode = st.st_mode;
-                ci->size = st.st_size;
-                ci->next = *filelist;
-                *filelist = ci;
+                ci = mkcopyinfo(lpath, rpath, name, 0);
+                if(lstat(ci->src, &st)) {
+                    fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
+                    free(ci);
+                    closedir(d);
+                    return -1;
+                }
+                if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+                    fprintf(stderr, "skipping special file '%s'\n", ci->src);
+                    free(ci);
+                } else {
+                    ci->time = st.st_mtime;
+                    ci->mode = st.st_mode;
+                    ci->size = st.st_size;
+                    ci->next = *filelist;
+                    *filelist = ci;
+                }
             }
+        } else {
+            fprintf(stderr, "cannot lstat '%s': %s\n",stat_path , strerror(errno));
         }
     }
 
diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c
index 1d80d26..e6f64bb 100644
--- a/adb/file_sync_service.c
+++ b/adb/file_sync_service.c
@@ -39,6 +39,11 @@
     return (strncmp(SYSTEM, name, strlen(SYSTEM)) == 0);
 }
 
+static bool is_on_vendor(const char *name) {
+    const char *VENDOR = "/vendor/";
+    return (strncmp(VENDOR, name, strlen(VENDOR)) == 0);
+}
+
 static int mkdirs(char *name)
 {
     int ret;
@@ -54,7 +59,7 @@
         x = adb_dirstart(x);
         if(x == 0) return 0;
         *x = 0;
-        if (is_on_system(name)) {
+        if (is_on_system(name) || is_on_vendor(name)) {
             fs_config(name, 1, &uid, &gid, &mode, &cap);
         }
         ret = adb_mkdir(name, mode);
@@ -369,7 +374,7 @@
         if(*tmp == '/') {
             tmp++;
         }
-        if (is_on_system(path)) {
+        if (is_on_system(path) || is_on_vendor(path)) {
             fs_config(tmp, 0, &uid, &gid, &mode, &cap);
         }
         ret = handle_send_file(s, path, uid, gid, mode, buffer, do_unlink);
diff --git a/adb/remount_service.c b/adb/remount_service.c
index d3a649b..fd81e28 100644
--- a/adb/remount_service.c
+++ b/adb/remount_service.c
@@ -29,6 +29,7 @@
 
 
 static int system_ro = 1;
+static int vendor_ro = 1;
 
 /* Returns the device used to mount a directory in /proc/mounts */
 static char *find_mount(const char *dir)
@@ -67,18 +68,27 @@
     return NULL;
 }
 
+static int hasVendorPartition()
+{
+    struct stat info;
+    if (!lstat("/vendor", &info))
+        if ((info.st_mode & S_IFMT) == S_IFDIR)
+          return true;
+    return false;
+}
+
 /* Init mounts /system as read only, remount to enable writes. */
-static int remount_system()
+static int remount(const char* dir, int* dir_ro)
 {
     char *dev;
     int fd;
     int OFF = 0;
 
-    if (system_ro == 0) {
+    if (dir_ro == 0) {
         return 0;
     }
 
-    dev = find_mount("/system");
+    dev = find_mount(dir);
 
     if (!dev)
         return -1;
@@ -90,11 +100,11 @@
     ioctl(fd, BLKROSET, &OFF);
     adb_close(fd);
 
-    system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL);
+    *dir_ro = mount(dev, dir, "none", MS_REMOUNT, NULL);
 
     free(dev);
 
-    return system_ro;
+    return *dir_ro;
 }
 
 static void write_string(int fd, const char* str)
@@ -104,16 +114,25 @@
 
 void remount_service(int fd, void *cookie)
 {
-    int ret = remount_system();
-
-    if (!ret)
-       write_string(fd, "remount succeeded\n");
-    else {
-        char    buffer[200];
-        snprintf(buffer, sizeof(buffer), "remount failed: %s\n", strerror(errno));
+    char buffer[200];
+    if (remount("/system", &system_ro)) {
+        snprintf(buffer, sizeof(buffer), "remount of system failed: %s\n",strerror(errno));
         write_string(fd, buffer);
     }
 
+    if (hasVendorPartition()) {
+        if (remount("/vendor", &vendor_ro)) {
+            snprintf(buffer, sizeof(buffer), "remount of vendor failed: %s\n",strerror(errno));
+            write_string(fd, buffer);
+        }
+    }
+
+    if (!system_ro && (!vendor_ro || !hasVendorPartition()))
+        write_string(fd, "remount succeeded\n");
+    else {
+        write_string(fd, "remount failed\n");
+    }
+
     adb_close(fd);
 }
 
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index e9b4125..3f94af5 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -245,31 +245,89 @@
     return strcmp(value, "1") ? 0 : 1;
 }
 
-void wipe_data_via_recovery()
+/*
+ * Tries to mount any of the consecutive fstab entries that match
+ * the mountpoint of the one given by fstab->recs[start_idx].
+ *
+ * end_idx: On return, will be the last rec that was looked at.
+ * attempted_idx: On return, will indicate which fstab rec
+ *     succeeded. In case of failure, it will be the start_idx.
+ * Returns
+ *   -1 on failure with errno set to match the 1st mount failure.
+ *   0 on success.
+ */
+static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
 {
-    mkdir("/cache/recovery", 0700);
-    int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC, 0600);
-    if (fd >= 0) {
-        write(fd, "--wipe_data", strlen("--wipe_data") + 1);
-        close(fd);
-    } else {
-        ERROR("could not open /cache/recovery/command\n");
+    int i;
+    int mount_errno = 0;
+    int mounted = 0;
+
+    if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
+      errno = EINVAL;
+      if (end_idx) *end_idx = start_idx;
+      if (attempted_idx) *end_idx = start_idx;
+      return -1;
     }
-    property_set(ANDROID_RB_PROPERTY, "reboot,recovery");
+
+    /* Hunt down an fstab entry for the same mount point that might succeed */
+    for (i = start_idx;
+         /* We required that fstab entries for the same mountpoint be consecutive */
+         i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point);
+         i++) {
+            /*
+             * Don't try to mount/encrypt the same mount point again.
+             * Deal with alternate entries for the same point which are required to be all following
+             * each other.
+             */
+            if (mounted) {
+                ERROR("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted as %s.\n", __func__,
+                     fstab->recs[i].mount_point, i, fstab->recs[i].fs_type, fstab->recs[*attempted_idx].fs_type);
+                continue;
+            }
+
+            if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+                check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                         fstab->recs[i].mount_point);
+            }
+            if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
+                *attempted_idx = i;
+                mounted = 1;
+                if (i != start_idx) {
+                    ERROR("%s(): Mounted %s on %s with fs_type=%s instead of %s\n", __func__,
+                         fstab->recs[i].blk_device, fstab->recs[i].mount_point, fstab->recs[i].fs_type,
+                         fstab->recs[start_idx].fs_type);
+                }
+            } else {
+                /* back up errno for crypto decisions */
+                mount_errno = errno;
+            }
+    }
+
+    /* Adjust i for the case where it was still withing the recs[] */
+    if (i < fstab->num_entries) --i;
+
+    *end_idx = i;
+    if (!mounted) {
+        *attempted_idx = start_idx;
+        errno = mount_errno;
+        return -1;
+    }
+    return 0;
 }
 
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
  * first successful mount.
+ * Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
  */
 int fs_mgr_mount_all(struct fstab *fstab)
 {
-    int i = 0, j = 0;
-    int encryptable = 0;
+    int i = 0;
+    int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
     int error_count = 0;
     int mret = -1;
     int mount_errno = 0;
-    const char *last_ok_mount_point = NULL;
+    int attempted_idx = -1;
 
     if (!fstab) {
         return -1;
@@ -299,55 +357,27 @@
                 continue;
             }
         }
-
-        /*
-         * Don't try to mount/encrypt the same mount point again.
-         * Deal with alternate entries for the same point which are required to be all following
-         * each other.
-         */
-        if (last_ok_mount_point && !strcmp(last_ok_mount_point, fstab->recs[i].mount_point)) {
-            INFO("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted.\n", __func__,
-                 last_ok_mount_point, i, fstab->recs[i].fs_type);
-            continue;
-        }
-        /* Hunt down an fstab entry for the same mount point that might succeed */
-        for (j = i;
-             /* We required that fstab entries for the same mountpoint be consecutive */
-             j < fstab->num_entries && !strcmp(fstab->recs[i].mount_point, fstab->recs[j].mount_point);
-             j++) {
-                if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
-                    check_fs(fstab->recs[j].blk_device, fstab->recs[j].fs_type,
-                             fstab->recs[j].mount_point);
-                }
-                mret = __mount(fstab->recs[j].blk_device, fstab->recs[j].mount_point, &fstab->recs[j]);
-                if (!mret) {
-                    last_ok_mount_point = fstab->recs[j].mount_point;
-                    if (i != j) {
-                        INFO("%s(): some alternate mount worked for mount_point=%s fs_type=%s instead of fs_type=%s\n", __func__,
-                             last_ok_mount_point, fstab->recs[j].fs_type, fstab->recs[i].fs_type);
-                        i = j;   /* We advance the recs index to the working entry */
-                    }
-                    break;
-                } else {
-                    /* back up errno for crypto decisions */
-                    mount_errno = errno;
-                }
-        }
+        int last_idx_inspected;
+        mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
+        i = last_idx_inspected;
+        mount_errno = errno;
 
         /* Deal with encryptability. */
         if (!mret) {
             /* If this is encryptable, need to trigger encryption */
-            if ((fstab->recs[i].fs_mgr_flags & MF_FORCECRYPT)) {
-                if (umount(fstab->recs[i].mount_point) == 0) {
-                    if (!encryptable) {
-                        encryptable = 2;
+            if ((fstab->recs[attempted_idx].fs_mgr_flags & MF_FORCECRYPT)) {
+                if (umount(fstab->recs[attempted_idx].mount_point) == 0) {
+                    if (encryptable == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+                        ERROR("Will try to encrypt %s %s\n", fstab->recs[attempted_idx].mount_point,
+                              fstab->recs[attempted_idx].fs_type);
+                        encryptable = FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
                     } else {
                         ERROR("Only one encryptable/encrypted partition supported\n");
-                        encryptable = 1;
+                        encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
                     }
                 } else {
                     INFO("Could not umount %s - allow continue unencrypted\n",
-                         fstab->recs[i].mount_point);
+                         fstab->recs[attempted_idx].mount_point);
                     continue;
                 }
             }
@@ -357,28 +387,31 @@
 
         /* mount(2) returned an error, check if it's encryptable and deal with it */
         if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
-            fs_mgr_is_encryptable(&fstab->recs[i])) {
-            if(partition_wiped(fstab->recs[i].blk_device) && fstab->recs[i].fs_mgr_flags & MF_FORCECRYPT) {
-                ERROR("Found an encryptable wiped partition with force encrypt. Formating via recovery.\n");
-                wipe_data_via_recovery();  /* This is queue up a reboot */
-                ++error_count;
+            fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
+            if(partition_wiped(fstab->recs[attempted_idx].blk_device)) {
+                ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__,
+                      fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+                      fstab->recs[attempted_idx].fs_type);
+                encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
                 continue;
             } else {
                 /* Need to mount a tmpfs at this mountpoint for now, and set
                  * properties that vold will query later for decrypting
                  */
-                if (fs_mgr_do_tmpfs_mount(fstab->recs[i].mount_point) < 0) {
+                ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__,
+                      fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+                      fstab->recs[attempted_idx].fs_type);
+                if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
                     ++error_count;
                     continue;
                 }
-                last_ok_mount_point = fstab->recs[i].mount_point;
             }
-            encryptable = 1;
+            encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
         } else {
             ERROR("Failed to mount an un-encryptable or wiped partition on"
                    "%s at %s options: %s error: %s\n",
-                   fstab->recs[i].blk_device, fstab->recs[i].mount_point,
-                   fstab->recs[i].fs_options, strerror(mount_errno));
+                   fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+                   fstab->recs[attempted_idx].fs_options, strerror(mount_errno));
             ++error_count;
             continue;
         }
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 7ac8db2..34938fa 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -73,12 +73,8 @@
 #define MF_ZRAMSIZE     0x100
 #define MF_VERIFY       0x200
 #define MF_FORCECRYPT   0x400
-/*
- * There is no emulated sdcard daemon running on /data/media on this device,
- * so treat the physical SD card as the only external storage device,
- * a la the Nexus One.
- */
-#define MF_NOEMULATEDSD 0x400
+#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
+                                 external storage */
 
 #define DM_BUF_SIZE 4096
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index b8bb5aa..d9c58d4 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -53,6 +53,11 @@
 
 struct fstab *fs_mgr_read_fstab(const char *fstab_path);
 void fs_mgr_free_fstab(struct fstab *fstab);
+
+#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 3
+#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 2
+#define FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED 1
+#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 0
 int fs_mgr_mount_all(struct fstab *fstab);
 int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index d11f609..67d4944 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -68,6 +68,7 @@
 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
 
 #define BATTERY_FULL_THRESH     95
+#define SCREEN_ON_BATTERY_THRESH 1
 
 #define LAST_KMSG_PATH          "/proc/last_kmsg"
 #define LAST_KMSG_PSTORE_PATH   "/sys/fs/pstore/console-ramoops"
@@ -172,6 +173,7 @@
 
 static int char_width;
 static int char_height;
+static bool minui_inited;
 
 /* current time in milliseconds */
 static int64_t curr_time_ms(void)
@@ -354,6 +356,27 @@
     if (!batt_anim->run || now < charger->next_screen_transition)
         return;
 
+    if (!minui_inited) {
+        int batt_cap = get_battery_capacity();
+
+        if (batt_cap < SCREEN_ON_BATTERY_THRESH) {
+            LOGV("[%" PRId64 "] level %d, leave screen off\n", now, batt_cap);
+            batt_anim->run = false;
+            charger->next_screen_transition = -1;
+            if (charger->charger_connected)
+                request_suspend(true);
+            return;
+        }
+
+        gr_init();
+        gr_font_size(&char_width, &char_height);
+
+#ifndef CHARGER_DISABLE_INIT_BLANK
+        gr_fb_blank(true);
+#endif
+        minui_inited = true;
+    }
+
     /* animation is over, blank screen and leave */
     if (batt_anim->cur_cycle == batt_anim->num_cycles) {
         reset_animation(batt_anim);
@@ -657,9 +680,6 @@
 
     LOGI("--------------- STARTING CHARGER MODE ---------------\n");
 
-    gr_init();
-    gr_font_size(&char_width, &char_height);
-
     ret = ev_init(input_callback, charger);
     if (!ret) {
         epollfd = ev_get_epollfd();
@@ -694,10 +714,6 @@
 
     ev_sync_key_state(set_key_callback, charger);
 
-#ifndef CHARGER_DISABLE_INIT_BLANK
-    gr_fb_blank(true);
-#endif
-
     charger->next_screen_transition = -1;
     charger->next_key_check = -1;
     charger->next_pwr_check = -1;
diff --git a/include/log/log.h b/include/log/log.h
index 5b76c1a..ace12d6 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -491,7 +491,7 @@
 #endif
 #ifndef LOG_EVENT_STRING
 #define LOG_EVENT_STRING(_tag, _value)                                      \
-    ((void) 0)  /* not implemented -- must combine len with string */
+        (void) __android_log_bswrite(_tag, _value);
 #endif
 /* TODO: something for LIST */
 
diff --git a/include/log/logd.h b/include/log/logd.h
index 379c373..2e6f220 100644
--- a/include/log/logd.h
+++ b/include/log/logd.h
@@ -41,6 +41,7 @@
 int __android_log_bwrite(int32_t tag, const void *payload, size_t len);
 int __android_log_btwrite(int32_t tag, char type, const void *payload,
     size_t len);
+int __android_log_bswrite(int32_t tag, const char *payload);
 
 #ifdef __cplusplus
 }
diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h
index a6554de..3085830 100644
--- a/include/system/audio_policy.h
+++ b/include/system/audio_policy.h
@@ -44,6 +44,9 @@
     AUDIO_POLICY_FORCE_DIGITAL_DOCK,
     AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */
     AUDIO_POLICY_FORCE_SYSTEM_ENFORCED,
+    AUDIO_POLICY_FORCE_SYSTEM_AUDIO_HDMI_ARC,
+    AUDIO_POLICY_FORCE_SYSTEM_AUDIO_SPDIF,
+    AUDIO_POLICY_FORCE_SYSTEM_AUDIO_LINE,
 
     AUDIO_POLICY_FORCE_CFG_CNT,
     AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1,
diff --git a/include/system/sound_trigger.h b/include/system/sound_trigger.h
index 0270baa..57225e9 100644
--- a/include/system/sound_trigger.h
+++ b/include/system/sound_trigger.h
@@ -92,6 +92,7 @@
  */
 struct sound_trigger_sound_model {
     sound_trigger_sound_model_type_t type;        /* model type. e.g. SOUND_MODEL_TYPE_KEYPHRASE */
+    sound_trigger_uuid_t             uuid;        /* unique sound model ID. */
     unsigned int                     data_size;   /* size of opaque model data */
     unsigned int                     data_offset; /* offset of opaque data start from head of struct
                                                     (e.g sizeof struct sound_trigger_sound_model) */
@@ -99,6 +100,7 @@
 
 /* key phrase descriptor */
 struct sound_trigger_phrase {
+    unsigned int id;                /* keyphrase ID */
     unsigned int recognition_mode;  /* recognition modes supported by this key phrase */
     unsigned int num_users;         /* number of users in the key phrase */
     char         locale[SOUND_TRIGGER_MAX_LOCALE_LEN]; /* locale - JAVA Locale style (e.g. en_US) */
diff --git a/init/builtins.c b/init/builtins.c
index 452119e..f7bd8a9 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -474,6 +474,26 @@
 
 }
 
+static int wipe_data_via_recovery()
+{
+    mkdir("/cache/recovery", 0700);
+    int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC, 0600);
+    if (fd >= 0) {
+        write(fd, "--wipe_data", strlen("--wipe_data") + 1);
+        close(fd);
+    } else {
+        ERROR("could not open /cache/recovery/command\n");
+        return -1;
+    }
+    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+    while (1) { pause(); }  // never reached
+}
+
+
+/*
+ * This function might request a reboot, in which case it will
+ * not return.
+ */
 int do_mount_all(int nargs, char **args)
 {
     pid_t pid;
@@ -511,27 +531,33 @@
         if (child_ret == -1) {
             ERROR("fs_mgr_mount_all returned an error\n");
         }
-        exit(child_ret);
+        _exit(child_ret);
     } else {
         /* fork failed, return an error */
         return -1;
     }
 
-    /* ret is 2 if device needs encrypted, 1 if the device appears encrypted,
-     * 0 if not, and -1 on error */
-    if (ret == 2) {
+    if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         property_set("ro.crypto.state", "unencrypted");
         property_set("vold.decrypt", "trigger_encryption");
-    } else if (ret == 1) {
+    } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
         property_set("ro.crypto.state", "encrypted");
         property_set("vold.decrypt", "trigger_default_encryption");
-    } else if (ret == 0) {
+    } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
         property_set("ro.crypto.state", "unencrypted");
         /* If fs_mgr determined this is an unencrypted device, then trigger
          * that action.
          */
         action_for_each_trigger("nonencrypted", action_add_queue_tail);
+    } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
+        /* Setup a wipe via recovery, and reboot into recovery */
+        ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
+        ret = wipe_data_via_recovery();
+        /* If reboot worked, there is no return. */
+    } else if (ret > 0) {
+        ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
     }
+    /* else ... < 0: error */
 
     return ret;
 }
diff --git a/init/devices.c b/init/devices.c
index b896410..304888e 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -170,7 +170,24 @@
     }
 }
 
-static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
+static bool perm_path_matches(const char *path, struct perms_ *dp)
+{
+    if (dp->prefix) {
+        if (strncmp(path, dp->name, strlen(dp->name)) == 0)
+            return true;
+    } else if (dp->wildcard) {
+        if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
+            return true;
+    } else {
+        if (strcmp(path, dp->name) == 0)
+            return true;
+    }
+
+    return false;
+}
+
+static mode_t get_device_perm(const char *path, const char **links,
+                unsigned *uid, unsigned *gid)
 {
     mode_t perm;
     struct listnode *node;
@@ -181,22 +198,30 @@
      * override ueventd.rc
      */
     list_for_each_reverse(node, &dev_perms) {
+        bool match = false;
+
         perm_node = node_to_item(node, struct perm_node, plist);
         dp = &perm_node->dp;
 
-        if (dp->prefix) {
-            if (strncmp(path, dp->name, strlen(dp->name)))
-                continue;
-        } else if (dp->wildcard) {
-            if (fnmatch(dp->name, path, FNM_PATHNAME) != 0)
-                continue;
+        if (perm_path_matches(path, dp)) {
+            match = true;
         } else {
-            if (strcmp(path, dp->name))
-                continue;
+            if (links) {
+                int i;
+                for (i = 0; links[i]; i++) {
+                    if (perm_path_matches(links[i], dp)) {
+                        match = true;
+                        break;
+                    }
+                }
+            }
         }
-        *uid = dp->uid;
-        *gid = dp->gid;
-        return dp->perm;
+
+        if (match) {
+            *uid = dp->uid;
+            *gid = dp->gid;
+            return dp->perm;
+        }
     }
     /* Default if nothing found. */
     *uid = 0;
@@ -215,7 +240,7 @@
     dev_t dev;
     char *secontext = NULL;
 
-    mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+    mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
 
     if (sehandle) {
         selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index f10eb8e..3171c78 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -107,7 +107,7 @@
         close(i);
     }
 
-    i = socket(PF_UNIX, SOCK_DGRAM, 0);
+    i = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
     if (i < 0) {
         ret = -errno;
         write_to_log = __write_to_log_null;
@@ -473,3 +473,25 @@
 
     return write_to_log(LOG_ID_EVENTS, vec, 3);
 }
+
+/*
+ * Like __android_log_bwrite, but used for writing strings to the
+ * event log.
+ */
+int __android_log_bswrite(int32_t tag, const char *payload)
+{
+    struct iovec vec[4];
+    char type = EVENT_TYPE_STRING;
+    uint32_t len = strlen(payload);
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = &type;
+    vec[1].iov_len = sizeof(type);
+    vec[2].iov_base = &len;
+    vec[2].iov_len = sizeof(len);
+    vec[3].iov_base = (void*)payload;
+    vec[3].iov_len = len;
+
+    return write_to_log(LOG_ID_EVENTS, vec, 4);
+}
diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c
index 1d10748..1ed5ecf 100644
--- a/liblog/logd_write_kern.c
+++ b/liblog/logd_write_kern.c
@@ -317,3 +317,25 @@
 
     return write_to_log(LOG_ID_EVENTS, vec, 3);
 }
+
+/*
+ * Like __android_log_bwrite, but used for writing strings to the
+ * event log.
+ */
+int __android_log_bswrite(int32_t tag, const char *payload)
+{
+    struct iovec vec[4];
+    char type = EVENT_TYPE_STRING;
+    uint32_t len = strlen(payload);
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = &type;
+    vec[1].iov_len = sizeof(type);
+    vec[2].iov_base = &len;
+    vec[2].iov_len = sizeof(len);
+    vec[3].iov_base = (void*)payload;
+    vec[3].iov_len = len;
+
+    return write_to_log(LOG_ID_EVENTS, vec, 4);
+}
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 16fe7ee..7c6af42 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -336,7 +336,7 @@
     for (;;) {
         int ret;
 
-        ret = getopt(argc, argv, "cdt:T:gG:sQf:r::n:v:b:BSpP:");
+        ret = getopt(argc, argv, "cdt:T:gG:sQf:r:n:v:b:BSpP:");
 
         if (ret < 0) {
             break;