Merge "Add support for gid derivation on private volumes" into oc-mr1-dev am: e335ee8f72
am: d3cde53e0e

Change-Id: I89e099d96f3ce6ec26aebe75d3a8ef5e60e0d9c2
diff --git a/Android.mk b/Android.mk
index d0b199d..ccbbb56 100644
--- a/Android.mk
+++ b/Android.mk
@@ -18,11 +18,12 @@
 	Ext4Crypt.cpp \
 	VoldUtil.c \
 	cryptfs.cpp \
-	Disk.cpp \
-	VolumeBase.cpp \
-	PublicVolume.cpp \
-	PrivateVolume.cpp \
-	EmulatedVolume.cpp \
+	model/Disk.cpp \
+	model/VolumeBase.cpp \
+	model/PublicVolume.cpp \
+	model/PrivateVolume.cpp \
+	model/EmulatedVolume.cpp \
+	model/ObbVolume.cpp \
 	Utils.cpp \
 	MoveTask.cpp \
 	Benchmark.cpp \
@@ -35,6 +36,9 @@
 	secontext.cpp \
 	EncryptInplace.cpp \
 	MetadataCrypt.cpp \
+	binder/android/os/IVold.aidl \
+	binder/android/os/IVoldListener.aidl \
+	VoldNativeService.cpp \
 
 common_c_includes := \
 	system/extras/f2fs_utils \
@@ -77,6 +81,7 @@
 
 # TODO: include "cert-err34-c" once we move to Binder
 # TODO: include "cert-err58-cpp" once 36656327 is fixed
+common_local_tidy_enabled := true
 common_local_tidy_flags := -warnings-as-errors=clang-analyzer-security*,cert-*
 common_local_tidy_checks := -*,clang-analyzer-security*,cert-*,-cert-err34-c,-cert-err58-cpp
 
@@ -85,14 +90,7 @@
 
 required_modules :=
 ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
-  ifeq ($(TARGET_USES_MKE2FS), true)
-    vold_cflags += -DTARGET_USES_MKE2FS
-    required_modules += mke2fs
-  else
-    # Adoptable storage has fully moved to mke2fs, so we need both tools
-    required_modules += mke2fs
-    required_modules += make_ext4fs
-  endif
+  required_modules += mke2fs
 endif
 
 include $(CLEAR_VARS)
@@ -100,7 +98,7 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_MODULE := libvold
 LOCAL_CLANG := true
-LOCAL_TIDY := true
+LOCAL_TIDY := $(common_local_tidy_enabled)
 LOCAL_TIDY_FLAGS := $(common_local_tidy_flags)
 LOCAL_TIDY_CHECKS := $(common_local_tidy_checks)
 LOCAL_SRC_FILES := $(common_src_files)
@@ -112,6 +110,8 @@
 LOCAL_CONLYFLAGS := $(vold_conlyflags)
 LOCAL_REQUIRED_MODULES := $(required_modules)
 
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
+
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -119,7 +119,7 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_MODULE := vold
 LOCAL_CLANG := true
-LOCAL_TIDY := true
+LOCAL_TIDY := $(common_local_tidy_enabled)
 LOCAL_TIDY_FLAGS := $(common_local_tidy_flags)
 LOCAL_TIDY_CHECKS := $(common_local_tidy_checks)
 LOCAL_SRC_FILES := \
@@ -136,29 +136,38 @@
 LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
 LOCAL_REQUIRED_MODULES := $(required_modules)
 
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
+
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
 
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_CLANG := true
-LOCAL_TIDY := true
+LOCAL_TIDY := $(common_local_tidy_enabled)
 LOCAL_TIDY_FLAGS := $(common_local_tidy_flags)
 LOCAL_TIDY_CHECKS := $(common_local_tidy_checks)
-LOCAL_SRC_FILES := vdc.cpp
+
+LOCAL_SRC_FILES := \
+	vdc.cpp \
+	binder/android/os/IVold.aidl \
+	binder/android/os/IVoldListener.aidl \
+
 LOCAL_MODULE := vdc
-LOCAL_SHARED_LIBRARIES := libcutils libbase
+LOCAL_SHARED_LIBRARIES := libbase libbinder libcutils libutils
 LOCAL_CFLAGS := $(vold_cflags)
 LOCAL_CONLYFLAGS := $(vold_conlyflags)
 LOCAL_INIT_RC := vdc.rc
 
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
+
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
 
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_CLANG := true
-LOCAL_TIDY := true
+LOCAL_TIDY := $(common_local_tidy_enabled)
 LOCAL_TIDY_FLAGS := $(common_local_tidy_flags)
 LOCAL_TIDY_CHECKS := $(common_local_tidy_checks)
 LOCAL_SRC_FILES:= \
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 8da3f69..60c0898 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -44,7 +44,7 @@
 
 #include "CommandListener.h"
 #include "VolumeManager.h"
-#include "VolumeBase.h"
+#include "model/VolumeBase.h"
 #include "ResponseCode.h"
 #include "Process.h"
 #include "Loop.h"
@@ -53,7 +53,6 @@
 #include "TrimTask.h"
 
 #define DUMP_ARGS 0
-#define DEBUG_APPFUSE 0
 
 using android::base::unique_fd;
 
@@ -617,157 +616,6 @@
     return sendGenericOkFail(cli, 0);
 }
 
-static size_t kAppFuseMaxMountPointName = 32;
-
-static android::status_t getMountPath(uid_t uid, const std::string& name, std::string* path) {
-    if (name.size() > kAppFuseMaxMountPointName) {
-        LOG(ERROR) << "AppFuse mount name is too long.";
-        return -EINVAL;
-    }
-    for (size_t i = 0; i < name.size(); i++) {
-        if (!isalnum(name[i])) {
-            LOG(ERROR) << "AppFuse mount name contains invalid character.";
-            return -EINVAL;
-        }
-    }
-    *path = android::base::StringPrintf("/mnt/appfuse/%d_%s", uid, name.c_str());
-    return android::OK;
-}
-
-static android::status_t mountInNamespace(uid_t uid, int device_fd, const std::string& path) {
-    // Remove existing mount.
-    android::vold::ForceUnmount(path);
-
-    const auto opts = android::base::StringPrintf(
-            "fd=%i,"
-            "rootmode=40000,"
-            "default_permissions,"
-            "allow_other,"
-            "user_id=%d,group_id=%d,"
-            "context=\"u:object_r:app_fuse_file:s0\","
-            "fscontext=u:object_r:app_fusefs:s0",
-            device_fd,
-            uid,
-            uid);
-
-    const int result = TEMP_FAILURE_RETRY(mount(
-            "/dev/fuse", path.c_str(), "fuse",
-            MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()));
-    if (result != 0) {
-        PLOG(ERROR) << "Failed to mount " << path;
-        return -errno;
-    }
-
-    return android::OK;
-}
-
-static android::status_t runCommandInNamespace(const std::string& command,
-                                               uid_t uid,
-                                               pid_t pid,
-                                               const std::string& path,
-                                               int device_fd) {
-    if (DEBUG_APPFUSE) {
-        LOG(DEBUG) << "Run app fuse command " << command << " for the path " << path
-                   << " in namespace " << uid;
-    }
-
-    unique_fd dir(open("/proc", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
-    if (dir.get() == -1) {
-        PLOG(ERROR) << "Failed to open /proc";
-        return -errno;
-    }
-
-    // Obtains process file descriptor.
-    const std::string pid_str = android::base::StringPrintf("%d", pid);
-    const unique_fd pid_fd(
-            openat(dir.get(), pid_str.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
-    if (pid_fd.get() == -1) {
-        PLOG(ERROR) << "Failed to open /proc/" << pid;
-        return -errno;
-    }
-
-    // Check UID of process.
-    {
-        struct stat sb;
-        const int result = fstat(pid_fd.get(), &sb);
-        if (result == -1) {
-            PLOG(ERROR) << "Failed to stat /proc/" << pid;
-            return -errno;
-        }
-        if (sb.st_uid != AID_SYSTEM) {
-            LOG(ERROR) << "Only system can mount appfuse. UID expected=" << AID_SYSTEM
-                    << ", actual=" << sb.st_uid;
-            return -EPERM;
-        }
-    }
-
-    // Matches so far, but refuse to touch if in root namespace
-    {
-        char rootName[PATH_MAX];
-        char pidName[PATH_MAX];
-        const int root_result =
-                android::vold::SaneReadLinkAt(dir.get(), "1/ns/mnt", rootName, PATH_MAX);
-        const int pid_result =
-                android::vold::SaneReadLinkAt(pid_fd.get(), "ns/mnt", pidName, PATH_MAX);
-        if (root_result == -1) {
-            LOG(ERROR) << "Failed to readlink for /proc/1/ns/mnt";
-            return -EPERM;
-        }
-        if (pid_result == -1) {
-            LOG(ERROR) << "Failed to readlink for /proc/" << pid << "/ns/mnt";
-            return -EPERM;
-        }
-        if (!strcmp(rootName, pidName)) {
-            LOG(ERROR) << "Don't mount appfuse in root namespace";
-            return -EPERM;
-        }
-    }
-
-    // We purposefully leave the namespace open across the fork
-    unique_fd ns_fd(openat(pid_fd.get(), "ns/mnt", O_RDONLY)); // not O_CLOEXEC
-    if (ns_fd.get() < 0) {
-        PLOG(ERROR) << "Failed to open namespace for /proc/" << pid << "/ns/mnt";
-        return -errno;
-    }
-
-    int child = fork();
-    if (child == 0) {
-        if (setns(ns_fd.get(), CLONE_NEWNS) != 0) {
-            PLOG(ERROR) << "Failed to setns";
-            _exit(-errno);
-        }
-
-        if (command == "mount") {
-            _exit(mountInNamespace(uid, device_fd, path));
-        } else if (command == "unmount") {
-            // If it's just after all FD opened on mount point are closed, umount2 can fail with
-            // EBUSY. To avoid the case, specify MNT_DETACH.
-            if (umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0 &&
-                    errno != EINVAL && errno != ENOENT) {
-                PLOG(ERROR) << "Failed to unmount directory.";
-                _exit(-errno);
-            }
-            if (rmdir(path.c_str()) != 0) {
-                PLOG(ERROR) << "Failed to remove the mount directory.";
-                _exit(-errno);
-            }
-            _exit(android::OK);
-        } else {
-            LOG(ERROR) << "Unknown appfuse command " << command;
-            _exit(-EPERM);
-        }
-    }
-
-    if (child == -1) {
-        PLOG(ERROR) << "Failed to folk child process";
-        return -errno;
-    }
-
-    android::status_t status;
-    TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
-
-    return status;
-}
 
 CommandListener::AppFuseCmd::AppFuseCmd() : VoldCommand("appfuse") {}
 
@@ -777,66 +625,32 @@
         return 0;
     }
 
-    const std::string command(argv[1]);
+    VolumeManager *vm = VolumeManager::Instance();
+    std::lock_guard<std::mutex> lock(vm->getLock());
 
+    const std::string command(argv[1]);
     if (command == "mount" && argc == 5) {
         const uid_t uid = atoi(argv[2]);
         const pid_t pid = atoi(argv[3]);
-        const std::string name(argv[4]);
+        const int mountId = atoi(argv[4]);
 
-        // Check mount point name.
-        std::string path;
-        if (getMountPath(uid, name, &path) != android::OK) {
-            return cli->sendMsg(ResponseCode::CommandParameterError,
-                                "Invalid mount point name.",
-                                false);
+        unique_fd device_fd;
+        int result = vm->mountAppFuse(uid, pid, mountId, &device_fd);
+        if (result != 0) {
+            return sendGenericOkFail(cli, result);
+        } else {
+            return sendFd(cli, device_fd.get());
         }
-
-        // Create directories.
-        {
-            const android::status_t result = android::vold::PrepareDir(path, 0700, 0, 0);
-            if (result != android::OK) {
-                PLOG(ERROR) << "Failed to prepare directory " << path;
-                return sendGenericOkFail(cli, result);
-            }
-        }
-
-        // Open device FD.
-        unique_fd device_fd(open("/dev/fuse", O_RDWR)); // not O_CLOEXEC
-        if (device_fd.get() == -1) {
-            PLOG(ERROR) << "Failed to open /dev/fuse";
-            return sendGenericOkFail(cli, -errno);
-        }
-
-        // Mount.
-        {
-            const android::status_t result =
-                    runCommandInNamespace(command, uid, pid, path, device_fd.get());
-            if (result != android::OK) {
-                return sendGenericOkFail(cli, result);
-            }
-        }
-
-        return sendFd(cli, device_fd.get());
     } else if (command == "unmount" && argc == 5) {
         const uid_t uid = atoi(argv[2]);
         const uid_t pid = atoi(argv[3]);
-        const std::string name(argv[4]);
+        const int mountId = atoi(argv[4]);
 
-        // Check mount point name.
-        std::string path;
-        if (getMountPath(uid, name, &path) != android::OK) {
-            return cli->sendMsg(ResponseCode::CommandParameterError,
-                                "Invalid mount point name.",
-                                false);
-        }
-
-        const android::status_t result =
-                runCommandInNamespace(command, uid, pid, path, -1 /* device_fd */);
+        int result = vm->unmountAppFuse(uid, pid, mountId);
         return sendGenericOkFail(cli, result);
     }
 
-    return cli->sendMsg(ResponseCode::CommandSyntaxError,  "Unknown appfuse cmd", false);
+    return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown appfuse cmd", false);
 }
 
 android::status_t CommandListener::AppFuseCmd::sendFd(SocketClient *cli, int fd) {
diff --git a/Devmapper.cpp b/Devmapper.cpp
index 4b6942d..c945e9f 100644
--- a/Devmapper.cpp
+++ b/Devmapper.cpp
@@ -32,12 +32,18 @@
 
 #include <cutils/log.h>
 
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <sysutils/SocketClient.h>
 
 #include "Devmapper.h"
 
 #define DEVMAPPER_BUFFER_SIZE 4096
 
+using android::base::StringPrintf;
+
+static const char* kVoldPrefix = "vold:";
+
 int Devmapper::dumpState(SocketClient *c) {
 
     char *buffer = (char *) malloc(1024 * 64);
@@ -130,7 +136,10 @@
     }
 }
 
-int Devmapper::lookupActive(const char *name, char *ubuffer, size_t len) {
+int Devmapper::lookupActive(const char *name_raw, char *ubuffer, size_t len) {
+    auto name_string = StringPrintf("%s%s", kVoldPrefix, name_raw);
+    const char* name = name_string.c_str();
+
     char *buffer = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
     if (!buffer) {
         SLOGE("Error allocating memory (%s)", strerror(errno));
@@ -163,8 +172,11 @@
     return 0;
 }
 
-int Devmapper::create(const char *name, const char *loopFile, const char *key,
+int Devmapper::create(const char *name_raw, const char *loopFile, const char *key,
                       unsigned long numSectors, char *ubuffer, size_t len) {
+    auto name_string = StringPrintf("%s%s", kVoldPrefix, name_raw);
+    const char* name = name_string.c_str();
+
     char *buffer = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
     if (!buffer) {
         SLOGE("Error allocating memory (%s)", strerror(errno));
@@ -261,7 +273,10 @@
     return 0;
 }
 
-int Devmapper::destroy(const char *name) {
+int Devmapper::destroy(const char *name_raw) {
+    auto name_string = StringPrintf("%s%s", kVoldPrefix, name_raw);
+    const char* name = name_string.c_str();
+
     char *buffer = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
     if (!buffer) {
         SLOGE("Error allocating memory (%s)", strerror(errno));
@@ -294,6 +309,74 @@
     return 0;
 }
 
+int Devmapper::destroyAll() {
+    char *buffer = (char *) malloc(1024 * 64);
+    if (!buffer) {
+        SLOGE("Error allocating memory (%s)", strerror(errno));
+        return -1;
+    }
+    memset(buffer, 0, (1024 * 64));
+
+    char *buffer2 = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
+    if (!buffer2) {
+        SLOGE("Error allocating memory (%s)", strerror(errno));
+        free(buffer);
+        return -1;
+    }
+
+    int fd;
+    if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
+        SLOGE("Error opening devmapper (%s)", strerror(errno));
+        free(buffer);
+        free(buffer2);
+        return -1;
+    }
+
+    struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+    ioctlInit(io, (1024 * 64), NULL, 0);
+
+    if (ioctl(fd, DM_LIST_DEVICES, io)) {
+        SLOGE("DM_LIST_DEVICES ioctl failed (%s)", strerror(errno));
+        free(buffer);
+        free(buffer2);
+        close(fd);
+        return -1;
+    }
+
+    struct dm_name_list *n = (struct dm_name_list *) (((char *) buffer) + io->data_start);
+    if (!n->dev) {
+        free(buffer);
+        free(buffer2);
+        close(fd);
+        return 0;
+    }
+
+    unsigned nxt = 0;
+    do {
+        n = (struct dm_name_list *) (((char *) n) + nxt);
+        if (strncmp(n->name, kVoldPrefix, strlen(kVoldPrefix)) == 0) {
+            LOG(DEBUG) << "Tearing down stale dm device named " << n->name;
+
+            memset(buffer2, 0, DEVMAPPER_BUFFER_SIZE);
+            struct dm_ioctl *io2 = (struct dm_ioctl *) buffer2;
+            ioctlInit(io2, DEVMAPPER_BUFFER_SIZE, n->name, 0);
+            if (ioctl(fd, DM_DEV_REMOVE, io2)) {
+                if (errno != ENXIO) {
+                    PLOG(WARNING) << "Failed to destroy dm device named " << n->name;
+                }
+            }
+        } else {
+            LOG(VERBOSE) << "Found unmanaged dm device named " << n->name;
+        }
+        nxt = n->next;
+    } while (nxt);
+
+    free(buffer);
+    free(buffer2);
+    close(fd);
+    return 0;
+}
+
 void *Devmapper::_align(void *ptr, unsigned int a)
 {
         unsigned long agn = --a;
diff --git a/Devmapper.h b/Devmapper.h
index 5b65b53..dcc39d8 100644
--- a/Devmapper.h
+++ b/Devmapper.h
@@ -27,6 +27,7 @@
     static int create(const char *name, const char *loopFile, const char *key,
                       unsigned long numSectors, char *buffer, size_t len);
     static int destroy(const char *name);
+    static int destroyAll();
     static int lookupActive(const char *name, char *buffer, size_t len);
     static int dumpState(SocketClient *c);
 
diff --git a/EncryptInplace.cpp b/EncryptInplace.cpp
index 9aa2a21..4ffb8cd 100644
--- a/EncryptInplace.cpp
+++ b/EncryptInplace.cpp
@@ -18,7 +18,6 @@
 
 #include <stdio.h>
 #include <stdint.h>
-#include <stdbool.h>
 #include <inttypes.h>
 #include <time.h>
 #include <sys/types.h>
diff --git a/Loop.cpp b/Loop.cpp
index 6ec5e6d..325b0d3 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -45,6 +45,8 @@
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
+static const char* kVoldPrefix = "vold:";
+
 int Loop::dumpState(SocketClient *c) {
     int i;
     int fd;
@@ -87,7 +89,10 @@
     return 0;
 }
 
-int Loop::lookupActive(const char *id, char *buffer, size_t len) {
+int Loop::lookupActive(const char *id_raw, char *buffer, size_t len) {
+    auto id_string = StringPrintf("%s%s", kVoldPrefix, id_raw);
+    const char* id = id_string.c_str();
+
     int i;
     int fd;
     char filename[256];
@@ -134,7 +139,10 @@
     return 0;
 }
 
-int Loop::create(const char *id, const char *loopFile, char *loopDeviceBuffer, size_t len) {
+int Loop::create(const char *id_raw, const char *loopFile, char *loopDeviceBuffer, size_t len) {
+    auto id_string = StringPrintf("%s%s", kVoldPrefix, id_raw);
+    const char* id = id_string.c_str();
+
     int i;
     int fd;
     char filename[256];
@@ -267,6 +275,14 @@
         return -errno;
     }
 
+    struct loop_info64 li;
+    memset(&li, 0, sizeof(li));
+    strlcpy((char*) li.lo_crypt_name, kVoldPrefix, LO_NAME_SIZE);
+    if (ioctl(device_fd.get(), LOOP_SET_STATUS64, &li) == -1) {
+        PLOG(ERROR) << "Failed to LOOP_SET_STATUS64";
+        return -errno;
+    }
+
     return 0;
 }
 
@@ -289,9 +305,36 @@
     return 0;
 }
 
-int Loop::destroyByFile(const char * /*loopFile*/) {
-    errno = ENOSYS;
-    return -1;
+int Loop::destroyAll() {
+    for (int i = 0; i < LOOP_MAX; i++) {
+        auto path = StringPrintf("/dev/block/loop%d", i);
+
+        unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
+        if (fd.get() == -1) {
+            if (errno != ENOENT) {
+                PLOG(WARNING) << "Failed to open " << path;
+            }
+            continue;
+        }
+
+        struct loop_info64 li;
+        if (ioctl(fd.get(), LOOP_GET_STATUS64, &li) < 0) {
+            PLOG(WARNING) << "Failed to LOOP_GET_STATUS64 " << path;
+            continue;
+        }
+
+        char* id = (char*) li.lo_crypt_name;
+        if (strncmp(id, kVoldPrefix, strlen(kVoldPrefix)) == 0) {
+            LOG(DEBUG) << "Tearing down stale loop device at " << path << " named " << id;
+
+            if (ioctl(fd.get(), LOOP_CLR_FD, 0) < 0) {
+                PLOG(WARNING) << "Failed to LOOP_CLR_FD " << path;
+            }
+        } else {
+            LOG(VERBOSE) << "Found unmanaged loop device at " << path << " named " << id;
+        }
+    }
+    return 0;
 }
 
 int Loop::createImageFile(const char *file, unsigned long numSectors) {
diff --git a/Loop.h b/Loop.h
index 5d8f427..e3ad239 100644
--- a/Loop.h
+++ b/Loop.h
@@ -32,7 +32,7 @@
     static int create(const char *id, const char *loopFile, char *loopDeviceBuffer, size_t len);
     static int create(const std::string& file, std::string& out_device);
     static int destroyByDevice(const char *loopDevice);
-    static int destroyByFile(const char *loopFile);
+    static int destroyAll();
     static int createImageFile(const char *file, unsigned long numSectors);
     static int resizeImageFile(const char *file, unsigned long numSectors);
 
diff --git a/MoveTask.h b/MoveTask.h
index b1777c0..cb184c3 100644
--- a/MoveTask.h
+++ b/MoveTask.h
@@ -18,7 +18,7 @@
 #define ANDROID_VOLD_MOVE_TASK_H
 
 #include "Utils.h"
-#include "VolumeBase.h"
+#include "model/VolumeBase.h"
 
 #include <thread>
 
diff --git a/Utils.h b/Utils.h
index 82ac440..153c320 100644
--- a/Utils.h
+++ b/Utils.h
@@ -19,6 +19,7 @@
 
 #include "KeyBuffer.h"
 
+#include <android-base/macros.h>
 #include <utils/Errors.h>
 #include <cutils/multiuser.h>
 #include <selinux/selinux.h>
@@ -26,16 +27,10 @@
 #include <vector>
 #include <string>
 
-// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
-// declarations in a class.
-#if !defined(DISALLOW_COPY_AND_ASSIGN)
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
-    TypeName(const TypeName&) = delete;  \
-    void operator=(const TypeName&) = delete
-#endif
-
 struct DIR;
 
+#define ENABLE_BINDER 1
+
 namespace android {
 namespace vold {
 
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
new file mode 100644
index 0000000..f5f0838
--- /dev/null
+++ b/VoldNativeService.cpp
@@ -0,0 +1,677 @@
+/*
+ * Copyright (C) 2017 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 "VoldNativeService.h"
+#include "VolumeManager.h"
+#include "MoveTask.h"
+#include "Process.h"
+#include "TrimTask.h"
+
+#include "cryptfs.h"
+#include "Ext4Crypt.h"
+#include "MetadataCrypt.h"
+
+#include <fstream>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <private/android_filesystem_config.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "vold"
+#endif
+
+using android::base::StringPrintf;
+using std::endl;
+
+namespace android {
+namespace vold {
+
+namespace {
+
+constexpr const char* kDump = "android.permission.DUMP";
+
+static binder::Status ok() {
+    return binder::Status::ok();
+}
+
+static binder::Status exception(uint32_t code, const std::string& msg) {
+    return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+}
+
+static binder::Status error(const std::string& msg) {
+    PLOG(ERROR) << msg;
+    return binder::Status::fromServiceSpecificError(errno, String8(msg.c_str()));
+}
+
+static binder::Status translate(int status) {
+    if (status == 0) {
+        return binder::Status::ok();
+    } else {
+        return binder::Status::fromServiceSpecificError(status);
+    }
+}
+
+static binder::Status translateBool(bool status) {
+    if (status) {
+        return binder::Status::ok();
+    } else {
+        return binder::Status::fromServiceSpecificError(status);
+    }
+}
+
+binder::Status checkPermission(const char* permission) {
+    pid_t pid;
+    uid_t uid;
+
+    if (checkCallingPermission(String16(permission), reinterpret_cast<int32_t*>(&pid),
+            reinterpret_cast<int32_t*>(&uid))) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_SECURITY,
+                StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission));
+    }
+}
+
+binder::Status checkUid(uid_t expectedUid) {
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (uid == expectedUid || uid == AID_ROOT) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_SECURITY,
+                StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
+    }
+}
+
+binder::Status checkArgumentId(const std::string& id) {
+    if (id.empty()) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Missing ID");
+    }
+    for (const char& c : id) {
+        if (!std::isalnum(c) && c != ':' && c != ',') {
+            return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                    StringPrintf("ID %s is malformed", id.c_str()));
+        }
+    }
+    return ok();
+}
+
+binder::Status checkArgumentPath(const std::string& path) {
+    if (path.empty()) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Missing path");
+    }
+    if (path[0] != '/') {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                StringPrintf("Path %s is relative", path.c_str()));
+    }
+    for (const char& c : path) {
+        if (c == '\0' || c == '\n') {
+            return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                    StringPrintf("Path %s is malformed", path.c_str()));
+        }
+    }
+    return ok();
+}
+
+binder::Status checkArgumentHex(const std::string& hex) {
+    // Empty hex strings are allowed
+    for (const char& c : hex) {
+        if (!std::isxdigit(c) && c != ':' && c != '-') {
+            return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                    StringPrintf("Hex %s is malformed", hex.c_str()));
+        }
+    }
+    return ok();
+}
+
+#define ENFORCE_UID(uid) {                                  \
+    binder::Status status = checkUid((uid));                \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+#define CHECK_ARGUMENT_ID(id) {                             \
+    binder::Status status = checkArgumentId((id));          \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+#define CHECK_ARGUMENT_PATH(path) {                         \
+    binder::Status status = checkArgumentPath((path));      \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+#define CHECK_ARGUMENT_HEX(hex) {                           \
+    binder::Status status = checkArgumentHex((hex));        \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+#define ACQUIRE_LOCK \
+    std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock());
+
+#define ACQUIRE_CRYPT_LOCK \
+    std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getCryptLock());
+
+}  // namespace
+
+status_t VoldNativeService::start() {
+    IPCThreadState::self()->disableBackgroundScheduling(true);
+    status_t ret = BinderService<VoldNativeService>::publish();
+    if (ret != android::OK) {
+        return ret;
+    }
+    sp<ProcessState> ps(ProcessState::self());
+    ps->startThreadPool();
+    ps->giveThreadPoolName();
+    return android::OK;
+}
+
+status_t VoldNativeService::dump(int fd, const Vector<String16> & /* args */) {
+    auto out = std::fstream(StringPrintf("/proc/self/fd/%d", fd));
+    const binder::Status dump_permission = checkPermission(kDump);
+    if (!dump_permission.isOk()) {
+        out << dump_permission.toString8() << endl;
+        return PERMISSION_DENIED;
+    }
+
+    ACQUIRE_LOCK;
+    out << "vold is happy!" << endl;
+    out.flush();
+    return NO_ERROR;
+}
+
+binder::Status VoldNativeService::setListener(
+        const android::sp<android::os::IVoldListener>& listener) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    VolumeManager::Instance()->setListener(listener);
+    return ok();
+}
+
+binder::Status VoldNativeService::reset() {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->reset());
+}
+
+binder::Status VoldNativeService::shutdown() {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->shutdown());
+}
+
+binder::Status VoldNativeService::mountAll() {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    struct fstab* fstab = fs_mgr_read_fstab_default();
+    int res = fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
+    fs_mgr_free_fstab(fstab);
+    return translate(res);
+}
+
+binder::Status VoldNativeService::onUserAdded(int32_t userId, int32_t userSerial) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->onUserAdded(userId, userSerial));
+}
+
+binder::Status VoldNativeService::onUserRemoved(int32_t userId) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->onUserRemoved(userId));
+}
+
+binder::Status VoldNativeService::onUserStarted(int32_t userId) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->onUserStarted(userId));
+}
+
+binder::Status VoldNativeService::onUserStopped(int32_t userId) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->onUserStopped(userId));
+}
+
+binder::Status VoldNativeService::partition(const std::string& diskId, int32_t partitionType,
+        int32_t ratio) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_ID(diskId);
+    ACQUIRE_LOCK;
+
+    auto disk = VolumeManager::Instance()->findDisk(diskId);
+    if (disk == nullptr) {
+        return error("Failed to find disk " + diskId);
+    }
+    switch (partitionType) {
+    case PARTITION_TYPE_PUBLIC: return translate(disk->partitionPublic());
+    case PARTITION_TYPE_PRIVATE: return translate(disk->partitionPrivate());
+    case PARTITION_TYPE_MIXED: return translate(disk->partitionMixed(ratio));
+    default: return error("Unknown type " + std::to_string(partitionType));
+    }
+}
+
+binder::Status VoldNativeService::forgetPartition(const std::string& partGuid) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_HEX(partGuid);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->forgetPartition(partGuid));
+}
+
+binder::Status VoldNativeService::mount(const std::string& volId, int32_t mountFlags,
+        int32_t mountUserId) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_ID(volId);
+    ACQUIRE_LOCK;
+
+    auto vol = VolumeManager::Instance()->findVolume(volId);
+    if (vol == nullptr) {
+        return error("Failed to find volume " + volId);
+    }
+
+    vol->setMountFlags(mountFlags);
+    vol->setMountUserId(mountUserId);
+
+    int res = vol->mount();
+    if ((mountFlags & MOUNT_FLAG_PRIMARY) != 0) {
+        VolumeManager::Instance()->setPrimary(vol);
+    }
+    return translate(res);
+}
+
+binder::Status VoldNativeService::unmount(const std::string& volId) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_ID(volId);
+    ACQUIRE_LOCK;
+
+    auto vol = VolumeManager::Instance()->findVolume(volId);
+    if (vol == nullptr) {
+        return error("Failed to find volume " + volId);
+    }
+    return translate(vol->unmount());
+}
+
+binder::Status VoldNativeService::format(const std::string& volId, const std::string& fsType) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_ID(volId);
+    ACQUIRE_LOCK;
+
+    auto vol = VolumeManager::Instance()->findVolume(volId);
+    if (vol == nullptr) {
+        return error("Failed to find volume " + volId);
+    }
+    return translate(vol->format(fsType));
+}
+
+binder::Status VoldNativeService::benchmark(const std::string& volId, int64_t* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_ID(volId);
+    ACQUIRE_LOCK;
+
+    *_aidl_return = VolumeManager::Instance()->benchmarkPrivate(volId);
+    return ok();
+}
+
+binder::Status VoldNativeService::moveStorage(const std::string& fromVolId,
+        const std::string& toVolId) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_ID(fromVolId);
+    CHECK_ARGUMENT_ID(toVolId);
+    ACQUIRE_LOCK;
+
+    auto fromVol = VolumeManager::Instance()->findVolume(fromVolId);
+    auto toVol = VolumeManager::Instance()->findVolume(toVolId);
+    if (fromVol == nullptr) {
+        return error("Failed to find volume " + fromVolId);
+    } else if (toVol == nullptr) {
+        return error("Failed to find volume " + toVolId);
+    }
+    (new android::vold::MoveTask(fromVol, toVol))->start();
+    return ok();
+}
+
+binder::Status VoldNativeService::remountUid(int32_t uid, int32_t remountMode) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    std::string tmp;
+    switch (remountMode) {
+    case REMOUNT_MODE_NONE: tmp = "none"; break;
+    case REMOUNT_MODE_DEFAULT: tmp = "default"; break;
+    case REMOUNT_MODE_READ: tmp = "read"; break;
+    case REMOUNT_MODE_WRITE: tmp = "write"; break;
+    default: return error("Unknown mode " + std::to_string(remountMode));
+    }
+    return translate(VolumeManager::Instance()->remountUid(uid, tmp));
+}
+
+binder::Status VoldNativeService::mkdirs(const std::string& path) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PATH(path);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->mkdirs(path.c_str()));
+}
+
+binder::Status VoldNativeService::createObb(const std::string& sourcePath,
+        const std::string& sourceKey, int32_t ownerGid, std::string* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PATH(sourcePath);
+    CHECK_ARGUMENT_HEX(sourceKey);
+    ACQUIRE_LOCK;
+
+    return translate(
+            VolumeManager::Instance()->createObb(sourcePath, sourceKey, ownerGid, _aidl_return));
+}
+
+binder::Status VoldNativeService::destroyObb(const std::string& volId) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_ID(volId);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->destroyObb(volId));
+}
+
+binder::Status VoldNativeService::fstrim(int32_t fstrimFlags) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    (new android::vold::TrimTask(fstrimFlags))->start();
+    return ok();
+}
+
+binder::Status VoldNativeService::mountAppFuse(int32_t uid, int32_t pid, int32_t mountId,
+        android::base::unique_fd* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->mountAppFuse(uid, pid, mountId, _aidl_return));
+}
+
+binder::Status VoldNativeService::unmountAppFuse(int32_t uid, int32_t pid, int32_t mountId) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->unmountAppFuse(uid, pid, mountId));
+}
+
+binder::Status VoldNativeService::fdeCheckPassword(const std::string& password) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translate(cryptfs_check_passwd(password.c_str()));
+}
+
+binder::Status VoldNativeService::fdeRestart() {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    // Spawn as thread so init can issue commands back to vold without
+    // causing deadlock, usually as a result of prep_data_fs.
+    std::thread(&cryptfs_restart).detach();
+    return ok();
+}
+
+binder::Status VoldNativeService::fdeComplete(int32_t* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    *_aidl_return = cryptfs_crypto_complete();
+    return ok();
+}
+
+static int fdeEnableInternal(int32_t passwordType, const std::string& password,
+        int32_t encryptionFlags) {
+    bool noUi = (encryptionFlags & VoldNativeService::ENCRYPTION_FLAG_NO_UI) != 0;
+
+    std::string how;
+    if ((encryptionFlags & VoldNativeService::ENCRYPTION_FLAG_IN_PLACE) != 0) {
+        how = "inplace";
+    } else if ((encryptionFlags & VoldNativeService::ENCRYPTION_FLAG_WIPE) != 0) {
+        how = "wipe";
+    } else {
+        LOG(ERROR) << "Missing encryption flag";
+        return -1;
+    }
+
+    for (int tries = 0; tries < 2; ++tries) {
+        int rc;
+        if (passwordType == VoldNativeService::PASSWORD_TYPE_DEFAULT) {
+            rc = cryptfs_enable_default(how.c_str(), noUi);
+        } else {
+            rc = cryptfs_enable(how.c_str(), passwordType, password.c_str(), noUi);
+        }
+
+        if (rc == 0) {
+            return 0;
+        } else if (tries == 0) {
+            Process::killProcessesWithOpenFiles(DATA_MNT_POINT, SIGKILL);
+        }
+    }
+
+    return -1;
+}
+
+binder::Status VoldNativeService::fdeEnable(int32_t passwordType,
+        const std::string& password, int32_t encryptionFlags) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    if (e4crypt_is_native()) {
+        if (passwordType != PASSWORD_TYPE_DEFAULT) {
+            return error("Unexpected password type");
+        }
+        if (encryptionFlags != (ENCRYPTION_FLAG_IN_PLACE | ENCRYPTION_FLAG_NO_UI)) {
+            return error("Unexpected flags");
+        }
+        return translateBool(e4crypt_enable_crypto());
+    }
+
+    // Spawn as thread so init can issue commands back to vold without
+    // causing deadlock, usually as a result of prep_data_fs.
+    std::thread(&fdeEnableInternal, passwordType, password, encryptionFlags).detach();
+    return ok();
+}
+
+binder::Status VoldNativeService::fdeChangePassword(int32_t passwordType,
+        const std::string& password) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translate(cryptfs_changepw(passwordType, password.c_str()));
+}
+
+binder::Status VoldNativeService::fdeVerifyPassword(const std::string& password) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translate(cryptfs_verify_passwd(password.c_str()));
+}
+
+binder::Status VoldNativeService::fdeGetField(const std::string& key,
+        std::string* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    char buf[PROPERTY_VALUE_MAX];
+    if (cryptfs_getfield(key.c_str(), buf, sizeof(buf)) != CRYPTO_GETFIELD_OK) {
+        return error(StringPrintf("Failed to read field %s", key.c_str()));
+    } else {
+        *_aidl_return = buf;
+        return ok();
+    }
+}
+
+binder::Status VoldNativeService::fdeSetField(const std::string& key,
+        const std::string& value) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translate(cryptfs_setfield(key.c_str(), value.c_str()));
+}
+
+binder::Status VoldNativeService::fdeGetPasswordType(int32_t* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    *_aidl_return = cryptfs_get_password_type();
+    return ok();
+}
+
+binder::Status VoldNativeService::fdeGetPassword(std::string* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    const char* res = cryptfs_get_password();
+    if (res != nullptr) {
+        *_aidl_return = res;
+    }
+    return ok();
+}
+
+binder::Status VoldNativeService::fdeClearPassword() {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    cryptfs_clear_password();
+    return ok();
+}
+
+binder::Status VoldNativeService::fbeEnable() {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_initialize_global_de());
+}
+
+binder::Status VoldNativeService::mountDefaultEncrypted() {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    if (e4crypt_is_native()) {
+        return translateBool(e4crypt_mount_metadata_encrypted());
+    } else {
+        // Spawn as thread so init can issue commands back to vold without
+        // causing deadlock, usually as a result of prep_data_fs.
+        std::thread(&cryptfs_mount_default_encrypted).detach();
+        return ok();
+    }
+}
+
+binder::Status VoldNativeService::initUser0() {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_init_user0());
+}
+
+binder::Status VoldNativeService::isConvertibleToFbe(bool* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    *_aidl_return = cryptfs_isConvertibleToFBE() != 0;
+    return ok();
+}
+
+binder::Status VoldNativeService::createUserKey(int32_t userId, int32_t userSerial,
+        bool ephemeral) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_vold_create_user_key(userId, userSerial, ephemeral));
+}
+
+binder::Status VoldNativeService::destroyUserKey(int32_t userId) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_destroy_user_key(userId));
+}
+
+binder::Status VoldNativeService::addUserKeyAuth(int32_t userId, int32_t userSerial,
+        const std::string& token, const std::string& secret) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_add_user_key_auth(userId, userSerial, token.c_str(), secret.c_str()));
+}
+
+binder::Status VoldNativeService::fixateNewestUserKeyAuth(int32_t userId) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_fixate_newest_user_key_auth(userId));
+}
+
+binder::Status VoldNativeService::unlockUserKey(int32_t userId, int32_t userSerial,
+        const std::string& token, const std::string& secret) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_unlock_user_key(userId, userSerial, token.c_str(), secret.c_str()));
+}
+
+binder::Status VoldNativeService::lockUserKey(int32_t userId) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_lock_user_key(userId));
+}
+
+binder::Status VoldNativeService::prepareUserStorage(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t userSerial, int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    return translateBool(e4crypt_prepare_user_storage(uuid_, userId, userSerial, flags));
+}
+
+binder::Status VoldNativeService::destroyUserStorage(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    return translateBool(e4crypt_destroy_user_storage(uuid_, userId, flags));
+}
+
+binder::Status VoldNativeService::secdiscard(const std::string& path) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_secdiscard(path.c_str()));
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/VoldNativeService.h b/VoldNativeService.h
new file mode 100644
index 0000000..b6b5d75
--- /dev/null
+++ b/VoldNativeService.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _VOLD_NATIVE_SERVICE_H_
+#define _VOLD_NATIVE_SERVICE_H_
+
+#include <android-base/unique_fd.h>
+#include <binder/BinderService.h>
+
+#include "android/os/BnVold.h"
+
+namespace android {
+namespace vold {
+
+class VoldNativeService : public BinderService<VoldNativeService>, public os::BnVold {
+public:
+    static status_t start();
+    static char const* getServiceName() { return "vold"; }
+    virtual status_t dump(int fd, const Vector<String16> &args) override;
+
+    binder::Status setListener(const android::sp<android::os::IVoldListener>& listener);
+
+    binder::Status reset();
+    binder::Status shutdown();
+    binder::Status mountAll();
+
+    binder::Status onUserAdded(int32_t userId, int32_t userSerial);
+    binder::Status onUserRemoved(int32_t userId);
+    binder::Status onUserStarted(int32_t userId);
+    binder::Status onUserStopped(int32_t userId);
+
+    binder::Status partition(const std::string& diskId, int32_t partitionType, int32_t ratio);
+    binder::Status forgetPartition(const std::string& partGuid);
+
+    binder::Status mount(const std::string& volId, int32_t mountFlags, int32_t mountUserId);
+    binder::Status unmount(const std::string& volId);
+    binder::Status format(const std::string& volId, const std::string& fsType);
+    binder::Status benchmark(const std::string& volId, int64_t* _aidl_return);
+
+    binder::Status moveStorage(const std::string& fromVolId, const std::string& toVolId);
+
+    binder::Status remountUid(int32_t uid, int32_t remountMode);
+
+    binder::Status mkdirs(const std::string& path);
+
+    binder::Status createObb(const std::string& sourcePath, const std::string& sourceKey,
+            int32_t ownerGid, std::string* _aidl_return);
+    binder::Status destroyObb(const std::string& volId);
+
+    binder::Status fstrim(int32_t fstrimFlags);
+
+    binder::Status mountAppFuse(int32_t uid, int32_t pid, int32_t mountId,
+            android::base::unique_fd* _aidl_return);
+    binder::Status unmountAppFuse(int32_t uid, int32_t pid, int32_t mountId);
+
+    binder::Status fdeCheckPassword(const std::string& password);
+    binder::Status fdeRestart();
+    binder::Status fdeComplete(int32_t* _aidl_return);
+    binder::Status fdeEnable(int32_t passwordType,
+            const std::string& password, int32_t encryptionFlags);
+    binder::Status fdeChangePassword(int32_t passwordType,
+            const std::string& password);
+    binder::Status fdeVerifyPassword(const std::string& password);
+    binder::Status fdeGetField(const std::string& key, std::string* _aidl_return);
+    binder::Status fdeSetField(const std::string& key, const std::string& value);
+    binder::Status fdeGetPasswordType(int32_t* _aidl_return);
+    binder::Status fdeGetPassword(std::string* _aidl_return);
+    binder::Status fdeClearPassword();
+
+    binder::Status fbeEnable();
+
+    binder::Status mountDefaultEncrypted();
+    binder::Status initUser0();
+    binder::Status isConvertibleToFbe(bool* _aidl_return);
+
+    binder::Status createUserKey(int32_t userId, int32_t userSerial, bool ephemeral);
+    binder::Status destroyUserKey(int32_t userId);
+
+    binder::Status addUserKeyAuth(int32_t userId, int32_t userSerial,
+            const std::string& token, const std::string& secret);
+    binder::Status fixateNewestUserKeyAuth(int32_t userId);
+
+    binder::Status unlockUserKey(int32_t userId, int32_t userSerial,
+            const std::string& token, const std::string& secret);
+    binder::Status lockUserKey(int32_t userId);
+
+    binder::Status prepareUserStorage(const std::unique_ptr<std::string>& uuid,
+            int32_t userId, int32_t userSerial, int32_t flags);
+    binder::Status destroyUserStorage(const std::unique_ptr<std::string>& uuid,
+            int32_t userId, int32_t flags);
+
+    binder::Status secdiscard(const std::string& path);
+};
+
+}  // namespace vold
+}  // namespace android
+
+#endif  // _VOLD_NATIVE_SERVICE_H_
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 13a943f..022caff 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -48,7 +48,8 @@
 #include <private/android_filesystem_config.h>
 
 #include "Benchmark.h"
-#include "EmulatedVolume.h"
+#include "model/EmulatedVolume.h"
+#include "model/ObbVolume.h"
 #include "VolumeManager.h"
 #include "NetlinkManager.h"
 #include "ResponseCode.h"
@@ -68,6 +69,7 @@
                                          + ((number) & (~((1U << (po2)) - 1))))
 
 using android::base::StringPrintf;
+using android::base::unique_fd;
 
 /*
  * Path to external storage where *only* root can access ASEC image files
@@ -220,6 +222,7 @@
     mSavedDirtyRatio = -1;
     // set dirty ratio to 0 when UMS is active
     mUmsDirtyRatio = 0;
+    mNextObbId = 0;
 }
 
 VolumeManager::~VolumeManager() {
@@ -317,6 +320,9 @@
     // directories that we own, in case we crashed.
     unmountAll();
 
+    Devmapper::destroyAll();
+    Loop::destroyAll();
+
     // Assume that we always have an emulated volume on internal
     // storage; the framework will decide if it should be mounted.
     CHECK(mInternalEmulated == nullptr);
@@ -437,6 +443,11 @@
             return vol;
         }
     }
+    for (const auto& vol : mObbVolumes) {
+        if (vol->getId() == id) {
+            return vol;
+        }
+    }
     return nullptr;
 }
 
@@ -754,7 +765,7 @@
     endmntent(fp);
 
     for (const auto& path : toUnmount) {
-        SLOGW("Tearing down stale mount %s", path.c_str());
+        LOG(DEBUG) << "Tearing down stale mount " << path;
         android::vold::ForceUnmount(path);
     }
 
@@ -1952,7 +1963,7 @@
     return found_mp;
 }
 
-int VolumeManager::mkdirs(char* path) {
+int VolumeManager::mkdirs(const char* path) {
     // Only offer to create directories for paths managed by vold
     if (strncmp(path, "/storage/", 9) == 0) {
         // fs_mkdirs() does symlink checking and relative path enforcement
@@ -1962,3 +1973,223 @@
         return -EINVAL;
     }
 }
+
+static size_t kAppFuseMaxMountPointName = 32;
+
+static android::status_t getMountPath(uid_t uid, const std::string& name, std::string* path) {
+    if (name.size() > kAppFuseMaxMountPointName) {
+        LOG(ERROR) << "AppFuse mount name is too long.";
+        return -EINVAL;
+    }
+    for (size_t i = 0; i < name.size(); i++) {
+        if (!isalnum(name[i])) {
+            LOG(ERROR) << "AppFuse mount name contains invalid character.";
+            return -EINVAL;
+        }
+    }
+    *path = android::base::StringPrintf("/mnt/appfuse/%d_%s", uid, name.c_str());
+    return android::OK;
+}
+
+static android::status_t mountInNamespace(uid_t uid, int device_fd, const std::string& path) {
+    // Remove existing mount.
+    android::vold::ForceUnmount(path);
+
+    const auto opts = android::base::StringPrintf(
+            "fd=%i,"
+            "rootmode=40000,"
+            "default_permissions,"
+            "allow_other,"
+            "user_id=%d,group_id=%d,"
+            "context=\"u:object_r:app_fuse_file:s0\","
+            "fscontext=u:object_r:app_fusefs:s0",
+            device_fd,
+            uid,
+            uid);
+
+    const int result = TEMP_FAILURE_RETRY(mount(
+            "/dev/fuse", path.c_str(), "fuse",
+            MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()));
+    if (result != 0) {
+        PLOG(ERROR) << "Failed to mount " << path;
+        return -errno;
+    }
+
+    return android::OK;
+}
+
+static android::status_t runCommandInNamespace(const std::string& command,
+                                               uid_t uid,
+                                               pid_t pid,
+                                               const std::string& path,
+                                               int device_fd) {
+    if (DEBUG_APPFUSE) {
+        LOG(DEBUG) << "Run app fuse command " << command << " for the path " << path
+                   << " in namespace " << uid;
+    }
+
+    unique_fd dir(open("/proc", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (dir.get() == -1) {
+        PLOG(ERROR) << "Failed to open /proc";
+        return -errno;
+    }
+
+    // Obtains process file descriptor.
+    const std::string pid_str = android::base::StringPrintf("%d", pid);
+    const unique_fd pid_fd(
+            openat(dir.get(), pid_str.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (pid_fd.get() == -1) {
+        PLOG(ERROR) << "Failed to open /proc/" << pid;
+        return -errno;
+    }
+
+    // Check UID of process.
+    {
+        struct stat sb;
+        const int result = fstat(pid_fd.get(), &sb);
+        if (result == -1) {
+            PLOG(ERROR) << "Failed to stat /proc/" << pid;
+            return -errno;
+        }
+        if (sb.st_uid != AID_SYSTEM) {
+            LOG(ERROR) << "Only system can mount appfuse. UID expected=" << AID_SYSTEM
+                    << ", actual=" << sb.st_uid;
+            return -EPERM;
+        }
+    }
+
+    // Matches so far, but refuse to touch if in root namespace
+    {
+        char rootName[PATH_MAX];
+        char pidName[PATH_MAX];
+        const int root_result =
+                android::vold::SaneReadLinkAt(dir.get(), "1/ns/mnt", rootName, PATH_MAX);
+        const int pid_result =
+                android::vold::SaneReadLinkAt(pid_fd.get(), "ns/mnt", pidName, PATH_MAX);
+        if (root_result == -1) {
+            LOG(ERROR) << "Failed to readlink for /proc/1/ns/mnt";
+            return -EPERM;
+        }
+        if (pid_result == -1) {
+            LOG(ERROR) << "Failed to readlink for /proc/" << pid << "/ns/mnt";
+            return -EPERM;
+        }
+        if (!strcmp(rootName, pidName)) {
+            LOG(ERROR) << "Don't mount appfuse in root namespace";
+            return -EPERM;
+        }
+    }
+
+    // We purposefully leave the namespace open across the fork
+    unique_fd ns_fd(openat(pid_fd.get(), "ns/mnt", O_RDONLY)); // not O_CLOEXEC
+    if (ns_fd.get() < 0) {
+        PLOG(ERROR) << "Failed to open namespace for /proc/" << pid << "/ns/mnt";
+        return -errno;
+    }
+
+    int child = fork();
+    if (child == 0) {
+        if (setns(ns_fd.get(), CLONE_NEWNS) != 0) {
+            PLOG(ERROR) << "Failed to setns";
+            _exit(-errno);
+        }
+
+        if (command == "mount") {
+            _exit(mountInNamespace(uid, device_fd, path));
+        } else if (command == "unmount") {
+            // If it's just after all FD opened on mount point are closed, umount2 can fail with
+            // EBUSY. To avoid the case, specify MNT_DETACH.
+            if (umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0 &&
+                    errno != EINVAL && errno != ENOENT) {
+                PLOG(ERROR) << "Failed to unmount directory.";
+                _exit(-errno);
+            }
+            if (rmdir(path.c_str()) != 0) {
+                PLOG(ERROR) << "Failed to remove the mount directory.";
+                _exit(-errno);
+            }
+            _exit(android::OK);
+        } else {
+            LOG(ERROR) << "Unknown appfuse command " << command;
+            _exit(-EPERM);
+        }
+    }
+
+    if (child == -1) {
+        PLOG(ERROR) << "Failed to folk child process";
+        return -errno;
+    }
+
+    android::status_t status;
+    TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
+
+    return status;
+}
+
+int VolumeManager::createObb(const std::string& sourcePath, const std::string& sourceKey,
+        int32_t ownerGid, std::string* outVolId) {
+    int id = mNextObbId++;
+
+    auto vol = std::shared_ptr<android::vold::VolumeBase>(
+            new android::vold::ObbVolume(id, sourcePath, sourceKey, ownerGid));
+    vol->create();
+
+    mObbVolumes.push_back(vol);
+    *outVolId = vol->getId();
+    return android::OK;
+}
+
+int VolumeManager::destroyObb(const std::string& volId) {
+    auto i = mObbVolumes.begin();
+    while (i != mObbVolumes.end()) {
+        if ((*i)->getId() == volId) {
+            (*i)->destroy();
+            i = mObbVolumes.erase(i);
+        } else {
+            ++i;
+        }
+    }
+    return android::OK;
+}
+
+int VolumeManager::mountAppFuse(uid_t uid, pid_t pid, int mountId,
+        android::base::unique_fd* device_fd) {
+    std::string name = std::to_string(mountId);
+
+    // Check mount point name.
+    std::string path;
+    if (getMountPath(uid, name, &path) != android::OK) {
+        LOG(ERROR) << "Invalid mount point name";
+        return -1;
+    }
+
+    // Create directories.
+    const android::status_t result = android::vold::PrepareDir(path, 0700, 0, 0);
+    if (result != android::OK) {
+        PLOG(ERROR) << "Failed to prepare directory " << path;
+        return -1;
+    }
+
+    // Open device FD.
+    device_fd->reset(open("/dev/fuse", O_RDWR)); // not O_CLOEXEC
+    if (device_fd->get() == -1) {
+        PLOG(ERROR) << "Failed to open /dev/fuse";
+        return -1;
+    }
+
+    // Mount.
+    return runCommandInNamespace("mount", uid, pid, path, device_fd->get());
+}
+
+int VolumeManager::unmountAppFuse(uid_t uid, pid_t pid, int mountId) {
+    std::string name = std::to_string(mountId);
+
+    // Check mount point name.
+    std::string path;
+    if (getMountPath(uid, name, &path) != android::OK) {
+        LOG(ERROR) << "Invalid mount point name";
+        return -1;
+    }
+
+    return runCommandInNamespace("unmount", uid, pid, path, -1 /* device_fd */);
+}
diff --git a/VolumeManager.h b/VolumeManager.h
index 537aebe..2751ad5 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -29,18 +29,23 @@
 #include <unordered_map>
 #include <unordered_set>
 
+#include <android-base/unique_fd.h>
 #include <cutils/multiuser.h>
 #include <utils/List.h>
 #include <utils/Timers.h>
 #include <sysutils/SocketListener.h>
 #include <sysutils/NetlinkEvent.h>
 
-#include "Disk.h"
-#include "VolumeBase.h"
+#include "android/os/IVoldListener.h"
+
+#include "model/Disk.h"
+#include "model/VolumeBase.h"
 
 /* The length of an MD5 hash when encoded into ASCII hex characters */
 #define MD5_ASCII_LENGTH_PLUS_NULL ((MD5_DIGEST_LENGTH*2)+1)
 
+#define DEBUG_APPFUSE 0
+
 typedef enum { ASEC, OBB } container_type_t;
 
 class ContainerData {
@@ -91,6 +96,10 @@
 
     // TODO: pipe all requests through VM to avoid exposing this lock
     std::mutex& getLock() { return mLock; }
+    std::mutex& getCryptLock() { return mCryptLock; }
+
+    void setListener(android::sp<android::os::IVoldListener> listener) { mListener = listener; }
+    android::sp<android::os::IVoldListener> getListener() { return mListener; }
 
     int start();
     int stop();
@@ -196,7 +205,14 @@
      * is treated as filename and ignored, unless the path ends with "/".  Also
      * ensures that path belongs to a volume managed by vold.
      */
-    int mkdirs(char* path);
+    int mkdirs(const char* path);
+
+    int createObb(const std::string& path, const std::string& key, int32_t ownerGid,
+            std::string* outVolId);
+    int destroyObb(const std::string& volId);
+
+    int mountAppFuse(uid_t uid, pid_t pid, int mountId, android::base::unique_fd* device_fd);
+    int unmountAppFuse(uid_t uid, pid_t pid, int mountId);
 
 private:
     VolumeManager();
@@ -208,9 +224,13 @@
     int linkPrimary(userid_t userId);
 
     std::mutex mLock;
+    std::mutex mCryptLock;
+
+    android::sp<android::os::IVoldListener> mListener;
 
     std::list<std::shared_ptr<DiskSource>> mDiskSources;
     std::list<std::shared_ptr<android::vold::Disk>> mDisks;
+    std::list<std::shared_ptr<android::vold::VolumeBase>> mObbVolumes;
 
     std::unordered_map<userid_t, int> mAddedUsers;
     std::unordered_set<userid_t> mStartedUsers;
@@ -219,6 +239,8 @@
     std::shared_ptr<android::vold::Disk> mVirtualDisk;
     std::shared_ptr<android::vold::VolumeBase> mInternalEmulated;
     std::shared_ptr<android::vold::VolumeBase> mPrimary;
+
+    int mNextObbId;
 };
 
 extern "C" {
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
new file mode 100644
index 0000000..0a05e6e
--- /dev/null
+++ b/binder/android/os/IVold.aidl
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.os;
+
+import android.os.IVoldListener;
+
+/** {@hide} */
+interface IVold {
+    void setListener(IVoldListener listener);
+
+    void reset();
+    void shutdown();
+    void mountAll();
+
+    void onUserAdded(int userId, int userSerial);
+    void onUserRemoved(int userId);
+    void onUserStarted(int userId);
+    void onUserStopped(int userId);
+
+    void partition(@utf8InCpp String diskId, int partitionType, int ratio);
+    void forgetPartition(@utf8InCpp String partGuid);
+
+    void mount(@utf8InCpp String volId, int mountFlags, int mountUserId);
+    void unmount(@utf8InCpp String volId);
+    void format(@utf8InCpp String volId, @utf8InCpp String fsType);
+    long benchmark(@utf8InCpp String volId);
+
+    void moveStorage(@utf8InCpp String fromVolId, @utf8InCpp String toVolId);
+
+    void remountUid(int uid, int remountMode);
+
+    void mkdirs(@utf8InCpp String path);
+
+    @utf8InCpp String createObb(@utf8InCpp String sourcePath,
+            @utf8InCpp String sourceKey, int ownerGid);
+    void destroyObb(@utf8InCpp String volId);
+
+    void fstrim(int fstrimFlags);
+
+    FileDescriptor mountAppFuse(int uid, int pid, int mountId);
+    void unmountAppFuse(int uid, int pid, int mountId);
+
+    void fdeCheckPassword(@utf8InCpp String password);
+    void fdeRestart();
+    int fdeComplete();
+    void fdeEnable(int passwordType, @utf8InCpp String password, int encryptionFlags);
+    void fdeChangePassword(int passwordType, @utf8InCpp String password);
+    void fdeVerifyPassword(@utf8InCpp String password);
+    @utf8InCpp String fdeGetField(@utf8InCpp String key);
+    void fdeSetField(@utf8InCpp String key, @utf8InCpp String value);
+    int fdeGetPasswordType();
+    @utf8InCpp String fdeGetPassword();
+    void fdeClearPassword();
+
+    void fbeEnable();
+
+    void mountDefaultEncrypted();
+    void initUser0();
+    boolean isConvertibleToFbe();
+
+    void createUserKey(int userId, int userSerial, boolean ephemeral);
+    void destroyUserKey(int userId);
+
+    void addUserKeyAuth(int userId, int userSerial, @utf8InCpp String token, @utf8InCpp String secret);
+    void fixateNewestUserKeyAuth(int userId);
+
+    void unlockUserKey(int userId, int userSerial, @utf8InCpp String token, @utf8InCpp String secret);
+    void lockUserKey(int userId);
+
+    void prepareUserStorage(@nullable @utf8InCpp String uuid, int userId, int userSerial, int storageFlags);
+    void destroyUserStorage(@nullable @utf8InCpp String uuid, int userId, int storageFlags);
+
+    void secdiscard(@utf8InCpp String path);
+
+    const int ENCRYPTION_FLAG_WIPE = 1;
+    const int ENCRYPTION_FLAG_IN_PLACE = 2;
+    const int ENCRYPTION_FLAG_NO_UI = 4;
+
+    const int ENCRYPTION_STATE_NONE = 1;
+    const int ENCRYPTION_STATE_OK = 0;
+    const int ENCRYPTION_STATE_ERROR_UNKNOWN = -1;
+    const int ENCRYPTION_STATE_ERROR_INCOMPLETE = -2;
+    const int ENCRYPTION_STATE_ERROR_INCONSISTENT = -3;
+    const int ENCRYPTION_STATE_ERROR_CORRUPT = -4;
+
+    const int FSTRIM_FLAG_DEEP_TRIM = 1;
+    const int FSTRIM_FLAG_BENCHMARK_AFTER = 2;
+
+    const int MOUNT_FLAG_PRIMARY = 1;
+    const int MOUNT_FLAG_VISIBLE = 2;
+
+    const int PARTITION_TYPE_PUBLIC = 0;
+    const int PARTITION_TYPE_PRIVATE = 1;
+    const int PARTITION_TYPE_MIXED = 2;
+
+    const int PASSWORD_TYPE_PASSWORD = 0;
+    const int PASSWORD_TYPE_DEFAULT = 1;
+    const int PASSWORD_TYPE_PIN = 2;
+    const int PASSWORD_TYPE_PATTERN = 3;
+
+    const int STORAGE_FLAG_DE = 1;
+    const int STORAGE_FLAG_CE = 2;
+
+    const int REMOUNT_MODE_NONE = 0;
+    const int REMOUNT_MODE_DEFAULT = 1;
+    const int REMOUNT_MODE_READ = 2;
+    const int REMOUNT_MODE_WRITE = 3;
+
+    const int VOLUME_STATE_UNMOUNTED = 0;
+    const int VOLUME_STATE_CHECKING = 1;
+    const int VOLUME_STATE_MOUNTED = 2;
+    const int VOLUME_STATE_MOUNTED_READ_ONLY = 3;
+    const int VOLUME_STATE_FORMATTING = 4;
+    const int VOLUME_STATE_EJECTING = 5;
+    const int VOLUME_STATE_UNMOUNTABLE = 6;
+    const int VOLUME_STATE_REMOVED = 7;
+    const int VOLUME_STATE_BAD_REMOVAL = 8;
+
+    const int VOLUME_TYPE_PUBLIC = 0;
+    const int VOLUME_TYPE_PRIVATE = 1;
+    const int VOLUME_TYPE_EMULATED = 2;
+    const int VOLUME_TYPE_ASEC = 3;
+    const int VOLUME_TYPE_OBB = 4;
+}
diff --git a/binder/android/os/IVoldListener.aidl b/binder/android/os/IVoldListener.aidl
new file mode 100644
index 0000000..0dcfc04
--- /dev/null
+++ b/binder/android/os/IVoldListener.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.os;
+
+/** {@hide} */
+oneway interface IVoldListener {
+    void onDiskCreated(@utf8InCpp String diskId, int flags);
+    void onDiskScanned(@utf8InCpp String diskId);
+    void onDiskMetadataChanged(@utf8InCpp String diskId,
+            long sizeBytes, @utf8InCpp String label, @utf8InCpp String sysPath);
+    void onDiskDestroyed(@utf8InCpp String diskId);
+
+    void onVolumeCreated(@utf8InCpp String volId,
+            int type, @utf8InCpp String diskId, @utf8InCpp String partGuid);
+    void onVolumeStateChanged(@utf8InCpp String volId, int state);
+    void onVolumeMetadataChanged(@utf8InCpp String volId,
+            @utf8InCpp String fsType, @utf8InCpp String fsUuid, @utf8InCpp String fsLabel);
+    void onVolumePathChanged(@utf8InCpp String volId,
+            @utf8InCpp String path);
+    void onVolumeInternalPathChanged(@utf8InCpp String volId,
+            @utf8InCpp String internalPath);
+    void onVolumeDestroyed(@utf8InCpp String volId);
+}
diff --git a/cryptfs.cpp b/cryptfs.cpp
index a79909a..adfb284 100644
--- a/cryptfs.cpp
+++ b/cryptfs.cpp
@@ -38,7 +38,6 @@
 #include <openssl/evp.h>
 #include <openssl/sha.h>
 #include <errno.h>
-#include <ext4_utils/ext4.h>
 #include <ext4_utils/ext4_utils.h>
 #include <linux/kdev_t.h>
 #include <fs_mgr.h>
@@ -1814,7 +1813,7 @@
     return rc;
 }
 
-int cryptfs_verify_passwd(char *passwd)
+int cryptfs_verify_passwd(const char *passwd)
 {
     struct crypt_mnt_ftr crypt_ftr;
     /* Allocate enough space for a 256 bit key, but we may use less */
@@ -1913,7 +1912,6 @@
     int rc = -1;
 
     if (type == EXT4_FS) {
-#ifdef TARGET_USES_MKE2FS
         args[0] = "/system/bin/mke2fs";
         args[1] = "-M";
         args[2] = "/data";
@@ -1925,16 +1923,6 @@
         snprintf(size_str, sizeof(size_str), "%" PRId64, size / (4096 / 512));
         args[8] = size_str;
         num_args = 9;
-#else
-        args[0] = "/system/bin/make_ext4fs";
-        args[1] = "-a";
-        args[2] = "/data";
-        args[3] = "-l";
-        snprintf(size_str, sizeof(size_str), "%" PRId64, size * 512);
-        args[4] = size_str;
-        args[5] = crypto_blkdev;
-        num_args = 6;
-#endif
         SLOGI("Making empty filesystem with command %s %s %s %s %s %s\n",
               args[0], args[1], args[2], args[3], args[4], args[5]);
     } else if (type == F2FS_FS) {
@@ -2070,7 +2058,7 @@
     return rc;
 }
 
-int cryptfs_enable_internal(char *howarg, int crypt_type, const char *passwd,
+int cryptfs_enable_internal(const char *howarg, int crypt_type, const char *passwd,
                             int no_ui)
 {
     int how = 0;
@@ -2429,12 +2417,12 @@
     return -1;
 }
 
-int cryptfs_enable(char *howarg, int type, char *passwd, int no_ui)
+int cryptfs_enable(const char *howarg, int type, const char *passwd, int no_ui)
 {
     return cryptfs_enable_internal(howarg, type, passwd, no_ui);
 }
 
-int cryptfs_enable_default(char *howarg, int no_ui)
+int cryptfs_enable_default(const char *howarg, int no_ui)
 {
     return cryptfs_enable_internal(howarg, CRYPT_TYPE_DEFAULT,
                           DEFAULT_PASSWORD, no_ui);
diff --git a/cryptfs.h b/cryptfs.h
index d20d96d..a7b650f 100644
--- a/cryptfs.h
+++ b/cryptfs.h
@@ -229,11 +229,11 @@
 
   int cryptfs_crypto_complete(void);
   int cryptfs_check_passwd(const char *pw);
-  int cryptfs_verify_passwd(char *newpw);
+  int cryptfs_verify_passwd(const char *pw);
   int cryptfs_restart(void);
-  int cryptfs_enable(char *flag, int type, char *passwd, int no_ui);
+  int cryptfs_enable(const char *flag, int type, const char *passwd, int no_ui);
   int cryptfs_changepw(int type, const char *newpw);
-  int cryptfs_enable_default(char *flag, int no_ui);
+  int cryptfs_enable_default(const char *flag, int no_ui);
   int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
           const unsigned char* key, int keysize, char* out_crypto_blkdev);
   int cryptfs_revert_ext_volume(const char* label);
diff --git a/main.cpp b/main.cpp
index 30c839e..8175dc5 100644
--- a/main.cpp
+++ b/main.cpp
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include "Disk.h"
+#include "model/Disk.h"
 #include "VolumeManager.h"
 #include "CommandListener.h"
 #include "CryptCommandListener.h"
 #include "NetlinkManager.h"
+#include "VoldNativeService.h"
 #include "cryptfs.h"
 #include "sehandle.h"
 
@@ -106,6 +107,11 @@
         exit(1);
     }
 
+    if (android::vold::VoldNativeService::start() != android::OK) {
+        LOG(ERROR) << "Unable to start VoldNativeService";
+        exit(1);
+    }
+
     bool has_adoptable;
     bool has_quota;
 
diff --git a/Disk.cpp b/model/Disk.cpp
similarity index 95%
rename from Disk.cpp
rename to model/Disk.cpp
index 9c22400..151937f 100644
--- a/Disk.cpp
+++ b/model/Disk.cpp
@@ -151,7 +151,12 @@
 status_t Disk::create() {
     CHECK(!mCreated);
     mCreated = true;
+#if ENABLE_BINDER
+    auto listener = VolumeManager::Instance()->getListener();
+    if (listener) listener->onDiskCreated(getId(), mFlags);
+#else
     notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
+#endif
     readMetadata();
     readPartitions();
     return OK;
@@ -161,7 +166,12 @@
     CHECK(mCreated);
     destroyAllVolumes();
     mCreated = false;
+#if ENABLE_BINDER
+    auto listener = VolumeManager::Instance()->getListener();
+    if (listener) listener->onDiskDestroyed(getId());
+#else
     notifyEvent(ResponseCode::DiskDestroyed);
+#endif
     return OK;
 }
 
@@ -281,9 +291,15 @@
     }
     }
 
+#if ENABLE_BINDER
+    auto listener = VolumeManager::Instance()->getListener();
+    if (listener) listener->onDiskMetadataChanged(getId(),
+            mSize, mLabel, mSysPath);
+#else
     notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRIu64, mSize));
     notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
     notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);
+#endif
     return OK;
 }
 
@@ -306,7 +322,12 @@
     status_t res = ForkExecvp(cmd, output);
     if (res != OK) {
         LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
+#if ENABLE_BINDER
+        auto listener = VolumeManager::Instance()->getListener();
+        if (listener) listener->onDiskScanned(getId());
+#else
         notifyEvent(ResponseCode::DiskScanned);
+#endif
         mJustPartitioned = false;
         return res;
     }
@@ -372,7 +393,12 @@
         }
     }
 
+#if ENABLE_BINDER
+    auto listener = VolumeManager::Instance()->getListener();
+    if (listener) listener->onDiskScanned(getId());
+#else
     notifyEvent(ResponseCode::DiskScanned);
+#endif
     mJustPartitioned = false;
     return OK;
 }
diff --git a/Disk.h b/model/Disk.h
similarity index 100%
rename from Disk.h
rename to model/Disk.h
diff --git a/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
similarity index 100%
rename from EmulatedVolume.cpp
rename to model/EmulatedVolume.cpp
diff --git a/EmulatedVolume.h b/model/EmulatedVolume.h
similarity index 100%
rename from EmulatedVolume.h
rename to model/EmulatedVolume.h
diff --git a/model/ObbVolume.cpp b/model/ObbVolume.cpp
new file mode 100644
index 0000000..709c7a3
--- /dev/null
+++ b/model/ObbVolume.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 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 "fs/Vfat.h"
+#include "Devmapper.h"
+#include "Loop.h"
+#include "ObbVolume.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#include <private/android_filesystem_config.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/wait.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace vold {
+
+ObbVolume::ObbVolume(int id, const std::string& sourcePath, const std::string& sourceKey,
+        gid_t ownerGid) : VolumeBase(Type::kObb) {
+    setId(StringPrintf("obb:%d", id));
+    mSourcePath = sourcePath;
+    mSourceKey = sourceKey;
+    mOwnerGid = ownerGid;
+}
+
+ObbVolume::~ObbVolume() {
+}
+
+status_t ObbVolume::doCreate() {
+    if (Loop::create(mSourcePath, mLoopPath)) {
+        PLOG(ERROR) << getId() << " failed to create loop";
+        return -1;
+    }
+
+    if (!mSourceKey.empty()) {
+        unsigned long nr_sec = 0;
+        {
+            unique_fd loop_fd(open(mLoopPath.c_str(), O_RDWR | O_CLOEXEC));
+            if (loop_fd.get() == -1) {
+                PLOG(ERROR) << getId() << " failed to open loop";
+                return -1;
+            }
+
+            get_blkdev_size(loop_fd.get(), &nr_sec);
+            if (nr_sec == 0) {
+                PLOG(ERROR) << getId() << " failed to get loop size";
+                return -1;
+            }
+        }
+
+        char tmp[PATH_MAX];
+        if (Devmapper::create(getId().c_str(), mLoopPath.c_str(), mSourceKey.c_str(), nr_sec,
+                tmp, PATH_MAX)) {
+            PLOG(ERROR) << getId() << " failed to create dm";
+            return -1;
+        }
+        mDmPath = tmp;
+        mMountPath = mDmPath;
+    } else {
+        mMountPath = mLoopPath;
+    }
+    return OK;
+}
+
+status_t ObbVolume::doDestroy() {
+    if (!mDmPath.empty() && Devmapper::destroy(getId().c_str())) {
+        PLOG(WARNING) << getId() << " failed to destroy dm";
+    }
+    if (!mLoopPath.empty() && Loop::destroyByDevice(mLoopPath.c_str())) {
+        PLOG(WARNING) << getId() << " failed to destroy loop";
+    }
+    mDmPath.clear();
+    mLoopPath.clear();
+    return OK;
+}
+
+status_t ObbVolume::doMount() {
+    auto path = StringPrintf("/mnt/obb/%s", getId().c_str());
+    setPath(path);
+
+    if (fs_prepare_dir(path.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+        PLOG(ERROR) << getId() << " failed to create mount point";
+        return -1;
+    }
+    if (android::vold::vfat::Mount(mMountPath, path,
+            true, false, true, 0, mOwnerGid, 0227, false)) {
+        PLOG(ERROR) << getId() << " failed to mount";
+        return -1;
+    }
+    return OK;
+}
+
+status_t ObbVolume::doUnmount() {
+    auto path = getPath();
+
+    KillProcessesUsingPath(path);
+    ForceUnmount(path);
+    rmdir(path.c_str());
+
+    return OK;
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/model/ObbVolume.h b/model/ObbVolume.h
new file mode 100644
index 0000000..5ec0cde
--- /dev/null
+++ b/model/ObbVolume.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ANDROID_VOLD_OBB_VOLUME_H
+#define ANDROID_VOLD_OBB_VOLUME_H
+
+#include "VolumeBase.h"
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace vold {
+
+/*
+ * OBB container.
+ */
+class ObbVolume : public VolumeBase {
+public:
+    ObbVolume(int id, const std::string& sourcePath, const std::string& sourceKey,
+            gid_t ownerGid);
+    virtual ~ObbVolume();
+
+protected:
+    status_t doCreate() override;
+    status_t doDestroy() override;
+    status_t doMount() override;
+    status_t doUnmount() override;
+
+private:
+    std::string mSourcePath;
+    std::string mSourceKey;
+    gid_t mOwnerGid;
+
+    std::string mLoopPath;
+    std::string mDmPath;
+    std::string mMountPath;
+
+    DISALLOW_COPY_AND_ASSIGN(ObbVolume);
+};
+
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/PrivateVolume.cpp b/model/PrivateVolume.cpp
similarity index 97%
rename from PrivateVolume.cpp
rename to model/PrivateVolume.cpp
index e66e04d..9a96082 100644
--- a/PrivateVolume.cpp
+++ b/model/PrivateVolume.cpp
@@ -55,9 +55,14 @@
 
 status_t PrivateVolume::readMetadata() {
     status_t res = ReadMetadata(mDmDevPath, mFsType, mFsUuid, mFsLabel);
+#if ENABLE_BINDER
+    auto listener = getListener();
+    if (listener) listener->onVolumeMetadataChanged(getId(), mFsType, mFsUuid, mFsLabel);
+#else
     notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
     notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
     notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
+#endif
     return res;
 }
 
diff --git a/PrivateVolume.h b/model/PrivateVolume.h
similarity index 100%
rename from PrivateVolume.h
rename to model/PrivateVolume.h
diff --git a/PublicVolume.cpp b/model/PublicVolume.cpp
similarity index 97%
rename from PublicVolume.cpp
rename to model/PublicVolume.cpp
index f976c4a..04bafed 100644
--- a/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -53,9 +53,14 @@
 
 status_t PublicVolume::readMetadata() {
     status_t res = ReadMetadataUntrusted(mDevPath, mFsType, mFsUuid, mFsLabel);
+#if ENABLE_BINDER
+    auto listener = getListener();
+    if (listener) listener->onVolumeMetadataChanged(getId(), mFsType, mFsUuid, mFsLabel);
+#else
     notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
     notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
     notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
+#endif
     return res;
 }
 
diff --git a/PublicVolume.h b/model/PublicVolume.h
similarity index 100%
rename from PublicVolume.h
rename to model/PublicVolume.h
diff --git a/VolumeBase.cpp b/model/VolumeBase.cpp
similarity index 87%
rename from VolumeBase.cpp
rename to model/VolumeBase.cpp
index 3f27d87..b2eff3b 100644
--- a/VolumeBase.cpp
+++ b/model/VolumeBase.cpp
@@ -44,7 +44,12 @@
 
 void VolumeBase::setState(State state) {
     mState = state;
+#if ENABLE_BINDER
+    auto listener = getListener();
+    if (listener) listener->onVolumeStateChanged(getId(), static_cast<int32_t>(mState));
+#else
     notifyEvent(ResponseCode::VolumeStateChanged, StringPrintf("%d", mState));
+#endif
 }
 
 status_t VolumeBase::setDiskId(const std::string& diskId) {
@@ -114,7 +119,12 @@
     }
 
     mPath = path;
+#if ENABLE_BINDER
+    auto listener = getListener();
+    if (listener) listener->onVolumePathChanged(getId(), mPath);
+#else
     notifyEvent(ResponseCode::VolumePathChanged, mPath);
+#endif
     return OK;
 }
 
@@ -125,7 +135,12 @@
     }
 
     mInternalPath = internalPath;
+#if ENABLE_BINDER
+    auto listener = getListener();
+    if (listener) listener->onVolumeInternalPathChanged(getId(), mInternalPath);
+#else
     notifyEvent(ResponseCode::VolumeInternalPathChanged, mInternalPath);
+#endif
     return OK;
 }
 
@@ -141,6 +156,14 @@
             StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
 }
 
+android::sp<android::os::IVoldListener> VolumeBase::getListener() {
+    if (mSilent) {
+        return nullptr;
+    } else {
+        return VolumeManager::Instance()->getListener();
+    }
+}
+
 void VolumeBase::addVolume(const std::shared_ptr<VolumeBase>& volume) {
     mVolumes.push_back(volume);
 }
@@ -163,8 +186,14 @@
 
     mCreated = true;
     status_t res = doCreate();
+#if ENABLE_BINDER
+    auto listener = getListener();
+    if (listener) listener->onVolumeCreated(getId(),
+            static_cast<int32_t>(mType), mDiskId, mPartGuid);
+#else
     notifyEvent(ResponseCode::VolumeCreated,
             StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
+#endif
     setState(State::kUnmounted);
     return res;
 }
@@ -183,7 +212,12 @@
         setState(State::kRemoved);
     }
 
+#if ENABLE_BINDER
+    auto listener = getListener();
+    if (listener) listener->onVolumeDestroyed(getId());
+#else
     notifyEvent(ResponseCode::VolumeDestroyed);
+#endif
     status_t res = doDestroy();
     mCreated = false;
     return res;
diff --git a/VolumeBase.h b/model/VolumeBase.h
similarity index 97%
rename from VolumeBase.h
rename to model/VolumeBase.h
index d417019..ac111c2 100644
--- a/VolumeBase.h
+++ b/model/VolumeBase.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_VOLD_VOLUME_BASE_H
 #define ANDROID_VOLD_VOLUME_BASE_H
 
+#include "android/os/IVoldListener.h"
 #include "Utils.h"
 
 #include <cutils/multiuser.h>
@@ -117,6 +118,8 @@
     void notifyEvent(int msg);
     void notifyEvent(int msg, const std::string& value);
 
+    android::sp<android::os::IVoldListener> getListener();
+
 private:
     /* ID that uniquely references volume while alive */
     std::string mId;
diff --git a/tests/Android.mk b/tests/Android.mk
index 4b6573e..9cebd1a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -5,9 +5,10 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_C_INCLUDES := \
-    system/core/fs_mgr/include
+    system/core/fs_mgr/include \
+    system/vold/
 
-LOCAL_STATIC_LIBRARIES := libselinux libvold liblog libcrypto
+LOCAL_STATIC_LIBRARIES := libbase libselinux libvold liblog libcrypto
 
 LOCAL_SRC_FILES := VolumeManager_test.cpp
 LOCAL_MODULE := vold_tests
diff --git a/vdc.cpp b/vdc.cpp
index 7ab5fcc..3a38641 100644
--- a/vdc.cpp
+++ b/vdc.cpp
@@ -30,15 +30,23 @@
 #include <sys/types.h>
 #include <sys/un.h>
 
+#include "android/os/IVold.h"
+
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <binder/IServiceManager.h>
 
 #include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
 
+#define ENABLE_BINDER 1
+
 static void usage(char *progname);
+
+#if !ENABLE_BINDER
 static int do_monitor(int sock, int stop_after_cmd);
 static int do_cmd(int sock, int argc, char **argv);
+#endif
 
 static constexpr int kCommandTimeoutMs = 20 * 1000;
 
@@ -62,11 +70,39 @@
         argc--;
     }
 
-    if (argc < 2) {
+    if (argc < 3) {
         usage(progname);
         exit(5);
     }
 
+#if ENABLE_BINDER
+    std::string arg1 = argv[1];
+    std::string arg2 = argv[2];
+
+    android::sp<android::IBinder> binder = android::defaultServiceManager()->getService(
+            android::String16("vold"));
+    if (!binder) {
+        LOG(ERROR) << "Failed to obtain vold Binder";
+        exit(EINVAL);
+    }
+    auto vold = android::interface_cast<android::os::IVold>(binder);
+
+    if (arg1 == "cryptfs" && arg2 == "enablefilecrypto") {
+        exit(vold->fbeEnable().isOk() ? 0 : ENOTTY);
+    } else if (arg1 == "cryptfs" && arg2 == "init_user0") {
+        exit(vold->initUser0().isOk() ? 0 : ENOTTY);
+    } else if (arg1 == "cryptfs" && arg2 == "enablecrypto") {
+        int passwordType = android::os::IVold::PASSWORD_TYPE_DEFAULT;
+        int encryptionFlags = android::os::IVold::ENCRYPTION_FLAG_IN_PLACE
+                | android::os::IVold::ENCRYPTION_FLAG_NO_UI;
+        exit(vold->fdeEnable(passwordType, "", encryptionFlags).isOk() ? 0 : ENOTTY);
+    } else if (arg1 == "volume" && arg2 == "shutdown") {
+        exit(vold->shutdown().isOk() ? 0 : ENOTTY);
+    } else {
+        LOG(ERROR) << "Raw commands are no longer supported";
+        exit(EINVAL);
+    }
+#else
     const char* sockname = "vold";
     if (!strcmp(argv[1], "cryptfs")) {
         sockname = "cryptd";
@@ -88,8 +124,10 @@
     } else {
         exit(do_cmd(sock, argc, argv));
     }
+#endif
 }
 
+#if !ENABLE_BINDER
 static int do_cmd(int sock, int argc, char **argv) {
     int seq = getpid();
 
@@ -175,6 +213,7 @@
     }
     return EIO;
 }
+#endif
 
 static void usage(char *progname) {
     LOG(INFO) << "Usage: " << progname << " [--wait] <monitor>|<cmd> [arg1] [arg2...]";