Merge "Use -Werror in system/vold/tests" am: d6d0e91c62 am: 7718457dfc am: 2412d496c8
am: 192ebf8acb

Change-Id: I1c2346c160190e26e4023e2aa4c1091ebf7621ba
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..ae4a451
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,11 @@
+BasedOnStyle: Google
+AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/Android.mk b/Android.mk
index bd457f6..8cbc1d4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -2,9 +2,6 @@
 
 common_src_files := \
 	VolumeManager.cpp \
-	CommandListener.cpp \
-	CryptCommandListener.cpp \
-	VoldCommand.cpp \
 	NetlinkManager.cpp \
 	NetlinkHandler.cpp \
 	Process.cpp \
@@ -13,19 +10,19 @@
 	fs/Vfat.cpp \
 	Loop.cpp \
 	Devmapper.cpp \
-	ResponseCode.cpp \
 	Ext4Crypt.cpp \
-	VoldUtil.c \
+	VoldUtil.cpp \
 	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 \
+	MoveStorage.cpp \
 	Benchmark.cpp \
-	TrimTask.cpp \
+	IdleMaint.cpp \
 	KeyBuffer.cpp \
 	Keymaster.cpp \
 	KeyStorage.cpp \
@@ -34,6 +31,16 @@
 	secontext.cpp \
 	EncryptInplace.cpp \
 	MetadataCrypt.cpp \
+	VoldNativeService.cpp \
+
+common_aidl_files := \
+	binder/android/os/IVold.aidl \
+	binder/android/os/IVoldListener.aidl \
+	binder/android/os/IVoldTaskListener.aidl \
+
+common_aidl_includes := \
+	$(LOCAL_PATH)/binder \
+	frameworks/native/aidl/binder \
 
 common_c_includes := \
 	system/extras/f2fs_utils \
@@ -73,24 +80,17 @@
 	libscrypt_static \
 	libavb \
 
-# 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
+common_local_tidy_checks := -*,clang-analyzer-security*,cert-*,-cert-err58-cpp
 
 vold_conlyflags := -std=c11
 vold_cflags := -Werror -Wall -Wno-missing-field-initializers -Wno-unused-variable -Wno-unused-parameter
 
-required_modules :=
+required_modules := vold_prepare_subdirs
 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)
@@ -98,10 +98,10 @@
 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)
+LOCAL_SRC_FILES := $(common_src_files) $(common_aidl_files)
 LOCAL_C_INCLUDES := $(common_c_includes)
 LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
 LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
@@ -110,6 +110,8 @@
 LOCAL_CONLYFLAGS := $(vold_conlyflags)
 LOCAL_REQUIRED_MODULES := $(required_modules)
 
+LOCAL_AIDL_INCLUDES := $(common_aidl_includes)
+
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -117,12 +119,13 @@
 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 := \
 	main.cpp \
-	$(common_src_files)
+	$(common_src_files) \
+	$(common_aidl_files) \
 
 LOCAL_INIT_RC := vold.rc
 
@@ -134,29 +137,37 @@
 LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
 LOCAL_REQUIRED_MODULES := $(required_modules)
 
+LOCAL_AIDL_INCLUDES := $(common_aidl_includes)
+
 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 \
+	$(common_aidl_files) \
+
 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 := $(common_aidl_includes)
+
 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:= \
@@ -170,4 +181,30 @@
 
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_CLANG := true
+LOCAL_TIDY := $(common_local_tidy_enabled)
+LOCAL_TIDY_FLAGS := $(common_local_tidy_flags)
+LOCAL_TIDY_CHECKS := $(common_local_tidy_checks)
+LOCAL_SRC_FILES:= \
+    prepare_dir.cpp \
+
+LOCAL_MODULE:= prepare_dir
+LOCAL_SHARED_LIBRARIES := libbase libcutils libselinux
+LOCAL_CFLAGS := $(vold_cflags)
+LOCAL_CONLYFLAGS := $(vold_conlyflags)
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= vold_prepare_subdirs
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := vold_prepare_subdirs
+LOCAL_REQUIRED_MODULES := prepare_dir
+
+include $(BUILD_PREBUILT)
+
 include $(LOCAL_PATH)/tests/Android.mk
diff --git a/Asec.h b/Asec.h
deleted file mode 100644
index dd64fd0..0000000
--- a/Asec.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ASEC_H
-#define _ASEC_H
-
-struct asec_superblock {
-#define ASEC_SB_MAGIC 0xc0def00d
-    unsigned int magic;
-
-#define ASEC_SB_VER 1
-    unsigned char ver;
-
-#define ASEC_SB_C_CIPHER_NONE    0
-#define ASEC_SB_C_CIPHER_TWOFISH 1
-#define ASEC_SB_C_CIPHER_AES     2
-    unsigned char c_cipher;
-
-#define ASEC_SB_C_CHAIN_NONE 0
-    unsigned char c_chain;
-
-#define ASEC_SB_C_OPTS_NONE 0
-#define ASEC_SB_C_OPTS_EXT4 1
-    unsigned char c_opts;
-
-#define ASEC_SB_C_MODE_NONE 0
-    unsigned char c_mode;
-} __attribute__((packed));
-
-#endif
diff --git a/Benchmark.cpp b/Benchmark.cpp
index bbab792..63b4dd3 100644
--- a/Benchmark.cpp
+++ b/Benchmark.cpp
@@ -17,13 +17,15 @@
 #include "Benchmark.h"
 #include "BenchmarkGen.h"
 #include "VolumeManager.h"
-#include "ResponseCode.h"
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <cutils/iosched_policy.h>
+#include <hardware_legacy/power.h>
 #include <private/android_filesystem_config.h>
 
+#include <thread>
+
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <unistd.h>
@@ -36,19 +38,24 @@
 namespace android {
 namespace vold {
 
-static void notifyResult(const std::string& path, int64_t create_d,
-        int64_t drop_d, int64_t run_d, int64_t destroy_d) {
-    std::string res(path +
-            + " " + BenchmarkIdent()
-            + " " + std::to_string(create_d)
-            + " " + std::to_string(drop_d)
-            + " " + std::to_string(run_d)
-            + " " + std::to_string(destroy_d));
-    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
-            ResponseCode::BenchmarkResult, res.c_str(), false);
-}
+static const char* kWakeLock = "Benchmark";
 
-static nsecs_t benchmark(const std::string& path) {
+static status_t benchmarkInternal(const std::string& rootPath,
+        android::os::PersistableBundle* extras) {
+    auto path = rootPath;
+    path += "/misc";
+    if (android::vold::PrepareDir(path, 01771, AID_SYSTEM, AID_MISC)) {
+        return -1;
+    }
+    path += "/vold";
+    if (android::vold::PrepareDir(path, 0700, AID_ROOT, AID_ROOT)) {
+        return -1;
+    }
+    path += "/bench";
+    if (android::vold::PrepareDir(path, 0700, AID_ROOT, AID_ROOT)) {
+        return -1;
+    }
+
     errno = 0;
     int orig_prio = getpriority(PRIO_PROCESS, 0);
     if (errno != 0) {
@@ -127,26 +134,27 @@
     LOG(INFO) << "run took " << nanoseconds_to_milliseconds(run_d) << "ms";
     LOG(INFO) << "destroy took " << nanoseconds_to_milliseconds(destroy_d) << "ms";
 
-    notifyResult(path, create_d, drop_d, run_d, destroy_d);
+    extras->putString(String16("path"), String16(path.c_str()));
+    extras->putString(String16("ident"), String16(BenchmarkIdent().c_str()));
+    extras->putLong(String16("create"), create_d);
+    extras->putLong(String16("drop"), drop_d);
+    extras->putLong(String16("run"), run_d);
+    extras->putLong(String16("destroy"), destroy_d);
 
-    return run_d;
+    return 0;
 }
 
-nsecs_t BenchmarkPrivate(const std::string& path) {
-    std::string benchPath(path);
-    benchPath += "/misc";
-    if (android::vold::PrepareDir(benchPath, 01771, AID_SYSTEM, AID_MISC)) {
-        return -1;
+void Benchmark(const std::string& path,
+        const android::sp<android::os::IVoldTaskListener>& listener) {
+    acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock);
+
+    android::os::PersistableBundle extras;
+    status_t res = benchmarkInternal(path, &extras);
+    if (listener) {
+        listener->onFinished(res, extras);
     }
-    benchPath += "/vold";
-    if (android::vold::PrepareDir(benchPath, 0700, AID_ROOT, AID_ROOT)) {
-        return -1;
-    }
-    benchPath += "/bench";
-    if (android::vold::PrepareDir(benchPath, 0700, AID_ROOT, AID_ROOT)) {
-        return -1;
-    }
-    return benchmark(benchPath);
+
+    release_wake_lock(kWakeLock);
 }
 
 }  // namespace vold
diff --git a/Benchmark.h b/Benchmark.h
index 13f9009..4f19b01 100644
--- a/Benchmark.h
+++ b/Benchmark.h
@@ -17,16 +17,15 @@
 #ifndef ANDROID_VOLD_BENCHMARK_H
 #define ANDROID_VOLD_BENCHMARK_H
 
-#include <utils/Errors.h>
-#include <utils/Timers.h>
+#include "android/os/IVoldTaskListener.h"
 
 #include <string>
 
 namespace android {
 namespace vold {
 
-/* Benchmark a private volume mounted at the given path */
-nsecs_t BenchmarkPrivate(const std::string& path);
+void Benchmark(const std::string& path,
+        const android::sp<android::os::IVoldTaskListener>& listener);
 
 }  // namespace vold
 }  // namespace android
diff --git a/CommandListener.cpp b/CommandListener.cpp
deleted file mode 100644
index 8da3f69..0000000
--- a/CommandListener.cpp
+++ /dev/null
@@ -1,877 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-#include <sys/mount.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <fs_mgr.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <ctype.h>
-
-#define LOG_TAG "VoldCmdListener"
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <cutils/fs.h>
-
-#include <sysutils/SocketClient.h>
-#include <private/android_filesystem_config.h>
-
-#include "CommandListener.h"
-#include "VolumeManager.h"
-#include "VolumeBase.h"
-#include "ResponseCode.h"
-#include "Process.h"
-#include "Loop.h"
-#include "Devmapper.h"
-#include "MoveTask.h"
-#include "TrimTask.h"
-
-#define DUMP_ARGS 0
-#define DEBUG_APPFUSE 0
-
-using android::base::unique_fd;
-
-CommandListener::CommandListener() :
-                 FrameworkListener("vold", true) {
-    registerCmd(new DumpCmd());
-    registerCmd(new VolumeCmd());
-    registerCmd(new AsecCmd());
-    registerCmd(new ObbCmd());
-    registerCmd(new StorageCmd());
-    registerCmd(new FstrimCmd());
-    registerCmd(new AppFuseCmd());
-}
-
-#if DUMP_ARGS
-void CommandListener::dumpArgs(int argc, char **argv, int argObscure) {
-    char buffer[4096];
-    char *p = buffer;
-
-    memset(buffer, 0, sizeof(buffer));
-    int i;
-    for (i = 0; i < argc; i++) {
-        unsigned int len = strlen(argv[i]) + 1; // Account for space
-        if (i == argObscure) {
-            len += 2; // Account for {}
-        }
-        if (((p - buffer) + len) < (sizeof(buffer)-1)) {
-            if (i == argObscure) {
-                *p++ = '{';
-                *p++ = '}';
-                *p++ = ' ';
-                continue;
-            }
-            strcpy(p, argv[i]);
-            p+= strlen(argv[i]);
-            if (i != (argc -1)) {
-                *p++ = ' ';
-            }
-        }
-    }
-    SLOGD("%s", buffer);
-}
-#else
-void CommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
-#endif
-
-int CommandListener::sendGenericOkFail(SocketClient *cli, int cond) {
-    if (!cond) {
-        return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
-    } else {
-        return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
-    }
-}
-
-CommandListener::DumpCmd::DumpCmd() :
-                 VoldCommand("dump") {
-}
-
-int CommandListener::DumpCmd::runCommand(SocketClient *cli,
-                                         int /*argc*/, char ** /*argv*/) {
-    cli->sendMsg(0, "Dumping loop status", false);
-    if (Loop::dumpState(cli)) {
-        cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true);
-    }
-    cli->sendMsg(0, "Dumping DM status", false);
-    if (Devmapper::dumpState(cli)) {
-        cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true);
-    }
-    cli->sendMsg(0, "Dumping mounted filesystems", false);
-    FILE *fp = fopen("/proc/mounts", "re");
-    if (fp) {
-        char line[1024];
-        while (fgets(line, sizeof(line), fp)) {
-            line[strlen(line)-1] = '\0';
-            cli->sendMsg(0, line, false);;
-        }
-        fclose(fp);
-    }
-
-    cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false);
-    return 0;
-}
-
-CommandListener::VolumeCmd::VolumeCmd() :
-                 VoldCommand("volume") {
-}
-
-int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
-                                           int argc, char **argv) {
-    dumpArgs(argc, argv, -1);
-
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
-        return 0;
-    }
-
-    VolumeManager *vm = VolumeManager::Instance();
-    std::lock_guard<std::mutex> lock(vm->getLock());
-
-    // TODO: tease out methods not directly related to volumes
-
-    std::string cmd(argv[1]);
-    if (cmd == "reset") {
-        return sendGenericOkFail(cli, vm->reset());
-
-    } else if (cmd == "shutdown") {
-        return sendGenericOkFail(cli, vm->shutdown());
-
-    } else if (cmd == "debug") {
-        return sendGenericOkFail(cli, vm->setDebug(true));
-
-    } else if (cmd == "partition" && argc > 3) {
-        // partition [diskId] [public|private|mixed] [ratio]
-        std::string id(argv[2]);
-        auto disk = vm->findDisk(id);
-        if (disk == nullptr) {
-            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown disk", false);
-        }
-
-        std::string type(argv[3]);
-        if (type == "public") {
-            return sendGenericOkFail(cli, disk->partitionPublic());
-        } else if (type == "private") {
-            return sendGenericOkFail(cli, disk->partitionPrivate());
-        } else if (type == "mixed") {
-            if (argc < 4) {
-                return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
-            }
-            int frac = atoi(argv[4]);
-            return sendGenericOkFail(cli, disk->partitionMixed(frac));
-        } else {
-            return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
-        }
-
-    } else if (cmd == "mkdirs" && argc > 2) {
-        // mkdirs [path]
-        return sendGenericOkFail(cli, vm->mkdirs(argv[2]));
-
-    } else if (cmd == "user_added" && argc > 3) {
-        // user_added [user] [serial]
-        return sendGenericOkFail(cli, vm->onUserAdded(atoi(argv[2]), atoi(argv[3])));
-
-    } else if (cmd == "user_removed" && argc > 2) {
-        // user_removed [user]
-        return sendGenericOkFail(cli, vm->onUserRemoved(atoi(argv[2])));
-
-    } else if (cmd == "user_started" && argc > 2) {
-        // user_started [user]
-        return sendGenericOkFail(cli, vm->onUserStarted(atoi(argv[2])));
-
-    } else if (cmd == "user_stopped" && argc > 2) {
-        // user_stopped [user]
-        return sendGenericOkFail(cli, vm->onUserStopped(atoi(argv[2])));
-
-    } else if (cmd == "mount" && argc > 2) {
-        // mount [volId] [flags] [user]
-        std::string id(argv[2]);
-        auto vol = vm->findVolume(id);
-        if (vol == nullptr) {
-            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
-        }
-
-        int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
-        userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
-
-        vol->setMountFlags(mountFlags);
-        vol->setMountUserId(mountUserId);
-
-        int res = vol->mount();
-        if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
-            vm->setPrimary(vol);
-        }
-        return sendGenericOkFail(cli, res);
-
-    } else if (cmd == "unmount" && argc > 2) {
-        // unmount [volId]
-        std::string id(argv[2]);
-        auto vol = vm->findVolume(id);
-        if (vol == nullptr) {
-            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
-        }
-
-        return sendGenericOkFail(cli, vol->unmount());
-
-    } else if (cmd == "format" && argc > 3) {
-        // format [volId] [fsType|auto]
-        std::string id(argv[2]);
-        std::string fsType(argv[3]);
-        auto vol = vm->findVolume(id);
-        if (vol == nullptr) {
-            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
-        }
-
-        return sendGenericOkFail(cli, vol->format(fsType));
-
-    } else if (cmd == "move_storage" && argc > 3) {
-        // move_storage [fromVolId] [toVolId]
-        auto fromVol = vm->findVolume(std::string(argv[2]));
-        auto toVol = vm->findVolume(std::string(argv[3]));
-        if (fromVol == nullptr || toVol == nullptr) {
-            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
-        }
-
-        (new android::vold::MoveTask(fromVol, toVol))->start();
-        return sendGenericOkFail(cli, 0);
-
-    } else if (cmd == "benchmark" && argc > 2) {
-        // benchmark [volId]
-        std::string id(argv[2]);
-        nsecs_t res = vm->benchmarkPrivate(id);
-        return cli->sendMsg(ResponseCode::CommandOkay,
-                android::base::StringPrintf("%" PRId64, res).c_str(), false);
-
-    } else if (cmd == "forget_partition" && argc > 2) {
-        // forget_partition [partGuid]
-        std::string partGuid(argv[2]);
-        return sendGenericOkFail(cli, vm->forgetPartition(partGuid));
-
-    } else if (cmd == "remount_uid" && argc > 3) {
-        // remount_uid [uid] [none|default|read|write]
-        uid_t uid = atoi(argv[2]);
-        std::string mode(argv[3]);
-        return sendGenericOkFail(cli, vm->remountUid(uid, mode));
-    }
-
-    return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
-}
-
-CommandListener::StorageCmd::StorageCmd() :
-                 VoldCommand("storage") {
-}
-
-int CommandListener::StorageCmd::runCommand(SocketClient *cli,
-                                                      int argc, char **argv) {
-    /* Guarantied to be initialized by vold's main() before the CommandListener is active */
-    extern struct fstab *fstab;
-
-    dumpArgs(argc, argv, -1);
-
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
-        return 0;
-    }
-
-    if (!strcmp(argv[1], "mountall")) {
-        if (argc != 2) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: mountall", false);
-            return 0;
-        }
-        fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
-        cli->sendMsg(ResponseCode::CommandOkay, "Mountall ran successfully", false);
-        return 0;
-    }
-    if (!strcmp(argv[1], "users")) {
-        DIR *dir;
-        struct dirent *de;
-
-        if (argc < 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument: user <mountpoint>", false);
-            return 0;
-        }
-        if (!(dir = opendir("/proc"))) {
-            cli->sendMsg(ResponseCode::OperationFailed, "Failed to open /proc", true);
-            return 0;
-        }
-
-        while ((de = readdir(dir))) {
-            int pid = Process::getPid(de->d_name);
-
-            if (pid < 0) {
-                continue;
-            }
-
-            std::string processName;
-            Process::getProcessName(pid, processName);
-
-            if (Process::checkFileDescriptorSymLinks(pid, argv[2]) ||
-                Process::checkFileMaps(pid, argv[2]) ||
-                Process::checkSymLink(pid, argv[2], "cwd") ||
-                Process::checkSymLink(pid, argv[2], "root") ||
-                Process::checkSymLink(pid, argv[2], "exe")) {
-
-                char msg[1024];
-                snprintf(msg, sizeof(msg), "%d %s", pid, processName.c_str());
-                cli->sendMsg(ResponseCode::StorageUsersListResult, msg, false);
-            }
-        }
-        closedir(dir);
-        cli->sendMsg(ResponseCode::CommandOkay, "Storage user list complete", false);
-    } else {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
-    }
-    return 0;
-}
-
-CommandListener::AsecCmd::AsecCmd() :
-                 VoldCommand("asec") {
-}
-
-void CommandListener::AsecCmd::listAsecsInDirectory(SocketClient *cli, const char *directory) {
-    DIR *d = opendir(directory);
-
-    if (!d) {
-        cli->sendMsg(ResponseCode::OperationFailed, "Failed to open asec dir", true);
-        return;
-    }
-
-    dirent* dent;
-    while ((dent = readdir(d)) != NULL) {
-        if (dent->d_name[0] == '.')
-            continue;
-        if (dent->d_type != DT_REG)
-            continue;
-        size_t name_len = strlen(dent->d_name);
-        if (name_len > 5 && name_len < 260 &&
-                !strcmp(&dent->d_name[name_len - 5], ".asec")) {
-            char id[255];
-            memset(id, 0, sizeof(id));
-            strlcpy(id, dent->d_name, name_len - 4);
-            cli->sendMsg(ResponseCode::AsecListResult, id, false);
-        }
-    }
-    closedir(d);
-}
-
-int CommandListener::AsecCmd::runCommand(SocketClient *cli,
-                                                      int argc, char **argv) {
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
-        return 0;
-    }
-
-    VolumeManager *vm = VolumeManager::Instance();
-    int rc = 0;
-
-    if (!strcmp(argv[1], "list")) {
-        dumpArgs(argc, argv, -1);
-
-        listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_EXT);
-        listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_INT);
-    } else if (!strcmp(argv[1], "create")) {
-        dumpArgs(argc, argv, 5);
-        if (argc != 8) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError,
-                    "Usage: asec create <container-id> <size_mb> <fstype> <key> <ownerUid> "
-                    "<isExternal>", false);
-            return 0;
-        }
-
-        unsigned long numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
-        const bool isExternal = (atoi(argv[7]) == 1);
-        rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]), isExternal);
-    } else if (!strcmp(argv[1], "resize")) {
-        dumpArgs(argc, argv, -1);
-        if (argc != 5) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec resize <container-id> <size_mb> <key>", false);
-            return 0;
-        }
-        unsigned long numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
-        rc = vm->resizeAsec(argv[2], numSectors, argv[4]);
-    } else if (!strcmp(argv[1], "finalize")) {
-        dumpArgs(argc, argv, -1);
-        if (argc != 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec finalize <container-id>", false);
-            return 0;
-        }
-        rc = vm->finalizeAsec(argv[2]);
-    } else if (!strcmp(argv[1], "fixperms")) {
-        dumpArgs(argc, argv, -1);
-        if  (argc != 5) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
-            return 0;
-        }
-
-        char *endptr;
-        gid_t gid = (gid_t) strtoul(argv[3], &endptr, 10);
-        if (*endptr != '\0') {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fixperms <container-id> <gid> <filename>", false);
-            return 0;
-        }
-
-        rc = vm->fixupAsecPermissions(argv[2], gid, argv[4]);
-    } else if (!strcmp(argv[1], "destroy")) {
-        dumpArgs(argc, argv, -1);
-        if (argc < 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec destroy <container-id> [force]", false);
-            return 0;
-        }
-        bool force = false;
-        if (argc > 3 && !strcmp(argv[3], "force")) {
-            force = true;
-        }
-        rc = vm->destroyAsec(argv[2], force);
-    } else if (!strcmp(argv[1], "mount")) {
-        dumpArgs(argc, argv, 3);
-        if (argc != 6) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError,
-                    "Usage: asec mount <namespace-id> <key> <ownerUid> <ro|rw>", false);
-            return 0;
-        }
-        bool readOnly = true;
-        if (!strcmp(argv[5], "rw")) {
-            readOnly = false;
-        }
-        rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]), readOnly);
-    } else if (!strcmp(argv[1], "unmount")) {
-        dumpArgs(argc, argv, -1);
-        if (argc < 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec unmount <container-id> [force]", false);
-            return 0;
-        }
-        bool force = false;
-        if (argc > 3 && !strcmp(argv[3], "force")) {
-            force = true;
-        }
-        rc = vm->unmountAsec(argv[2], force);
-    } else if (!strcmp(argv[1], "rename")) {
-        dumpArgs(argc, argv, -1);
-        if (argc != 4) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError,
-                    "Usage: asec rename <old_id> <new_id>", false);
-            return 0;
-        }
-        rc = vm->renameAsec(argv[2], argv[3]);
-    } else if (!strcmp(argv[1], "path")) {
-        dumpArgs(argc, argv, -1);
-        if (argc != 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec path <container-id>", false);
-            return 0;
-        }
-        char path[255];
-
-        if (!(rc = vm->getAsecMountPath(argv[2], path, sizeof(path)))) {
-            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
-            return 0;
-        }
-    } else if (!strcmp(argv[1], "fspath")) {
-        dumpArgs(argc, argv, -1);
-        if (argc != 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec fspath <container-id>", false);
-            return 0;
-        }
-        char path[255];
-
-        if (!(rc = vm->getAsecFilesystemPath(argv[2], path, sizeof(path)))) {
-            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
-            return 0;
-        }
-    } else {
-        dumpArgs(argc, argv, -1);
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown asec cmd", false);
-    }
-
-    if (!rc) {
-        cli->sendMsg(ResponseCode::CommandOkay, "asec operation succeeded", false);
-    } else {
-        rc = ResponseCode::convertFromErrno();
-        cli->sendMsg(rc, "asec operation failed", true);
-    }
-
-    return 0;
-}
-
-CommandListener::ObbCmd::ObbCmd() :
-                 VoldCommand("obb") {
-}
-
-int CommandListener::ObbCmd::runCommand(SocketClient *cli,
-                                                      int argc, char **argv) {
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
-        return 0;
-    }
-
-    VolumeManager *vm = VolumeManager::Instance();
-    int rc = 0;
-
-    if (!strcmp(argv[1], "list")) {
-        dumpArgs(argc, argv, -1);
-
-        rc = vm->listMountedObbs(cli);
-    } else if (!strcmp(argv[1], "mount")) {
-            dumpArgs(argc, argv, 3);
-            if (argc != 5) {
-                cli->sendMsg(ResponseCode::CommandSyntaxError,
-                        "Usage: obb mount <filename> <key> <ownerGid>", false);
-                return 0;
-            }
-            rc = vm->mountObb(argv[2], argv[3], atoi(argv[4]));
-    } else if (!strcmp(argv[1], "unmount")) {
-        dumpArgs(argc, argv, -1);
-        if (argc < 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb unmount <source file> [force]", false);
-            return 0;
-        }
-        bool force = false;
-        if (argc > 3 && !strcmp(argv[3], "force")) {
-            force = true;
-        }
-        rc = vm->unmountObb(argv[2], force);
-    } else if (!strcmp(argv[1], "path")) {
-        dumpArgs(argc, argv, -1);
-        if (argc != 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: obb path <source file>", false);
-            return 0;
-        }
-        char path[255];
-
-        if (!(rc = vm->getObbMountPath(argv[2], path, sizeof(path)))) {
-            cli->sendMsg(ResponseCode::AsecPathResult, path, false);
-            return 0;
-        }
-    } else {
-        dumpArgs(argc, argv, -1);
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown obb cmd", false);
-    }
-
-    if (!rc) {
-        cli->sendMsg(ResponseCode::CommandOkay, "obb operation succeeded", false);
-    } else {
-        rc = ResponseCode::convertFromErrno();
-        cli->sendMsg(rc, "obb operation failed", true);
-    }
-
-    return 0;
-}
-
-CommandListener::FstrimCmd::FstrimCmd() :
-                 VoldCommand("fstrim") {
-}
-int CommandListener::FstrimCmd::runCommand(SocketClient *cli,
-                                                      int argc, char **argv) {
-    if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
-        cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run fstrim commands", false);
-        return 0;
-    }
-
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
-        return 0;
-    }
-
-    VolumeManager *vm = VolumeManager::Instance();
-    std::lock_guard<std::mutex> lock(vm->getLock());
-
-    int flags = 0;
-
-    std::string cmd(argv[1]);
-    if (cmd == "dotrim") {
-        flags = 0;
-    } else if (cmd == "dotrimbench") {
-        flags = android::vold::TrimTask::Flags::kBenchmarkAfter;
-    } else if (cmd == "dodtrim") {
-        flags = android::vold::TrimTask::Flags::kDeepTrim;
-    } else if (cmd == "dodtrimbench") {
-        flags = android::vold::TrimTask::Flags::kDeepTrim
-                | android::vold::TrimTask::Flags::kBenchmarkAfter;
-    }
-
-    (new android::vold::TrimTask(flags))->start();
-    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") {}
-
-int CommandListener::AppFuseCmd::runCommand(SocketClient *cli, int argc, char **argv) {
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
-        return 0;
-    }
-
-    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]);
-
-        // Check mount point name.
-        std::string path;
-        if (getMountPath(uid, name, &path) != android::OK) {
-            return cli->sendMsg(ResponseCode::CommandParameterError,
-                                "Invalid mount point name.",
-                                false);
-        }
-
-        // 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]);
-
-        // 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 */);
-        return sendGenericOkFail(cli, result);
-    }
-
-    return cli->sendMsg(ResponseCode::CommandSyntaxError,  "Unknown appfuse cmd", false);
-}
-
-android::status_t CommandListener::AppFuseCmd::sendFd(SocketClient *cli, int fd) {
-    struct iovec data;
-    char dataBuffer[128];
-    char controlBuffer[CMSG_SPACE(sizeof(int))];
-    struct msghdr message;
-
-    // Message.
-    memset(&message, 0, sizeof(struct msghdr));
-    message.msg_iov = &data;
-    message.msg_iovlen = 1;
-    message.msg_control = controlBuffer;
-    message.msg_controllen = CMSG_SPACE(sizeof(int));
-
-    // Data.
-    data.iov_base = dataBuffer;
-    data.iov_len = snprintf(dataBuffer,
-                            sizeof(dataBuffer),
-                            "200 %d AppFuse command succeeded",
-                            cli->getCmdNum()) + 1;
-
-    // Control.
-    struct cmsghdr* const controlMessage = CMSG_FIRSTHDR(&message);
-    memset(controlBuffer, 0, CMSG_SPACE(sizeof(int)));
-    controlMessage->cmsg_level = SOL_SOCKET;
-    controlMessage->cmsg_type = SCM_RIGHTS;
-    controlMessage->cmsg_len = CMSG_LEN(sizeof(int));
-    *((int *) CMSG_DATA(controlMessage)) = fd;
-
-    const int result = TEMP_FAILURE_RETRY(sendmsg(cli->getSocket(), &message, 0));
-    if (result == -1) {
-        PLOG(ERROR) << "Failed to send FD from vold";
-        return -errno;
-    }
-
-    return android::OK;
-}
diff --git a/CommandListener.h b/CommandListener.h
deleted file mode 100644
index f858ac0..0000000
--- a/CommandListener.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _COMMANDLISTENER_H__
-#define _COMMANDLISTENER_H__
-
-#include <sysutils/FrameworkListener.h>
-#include <utils/Errors.h>
-#include "VoldCommand.h"
-
-class CommandListener : public FrameworkListener {
-public:
-    CommandListener();
-    virtual ~CommandListener() {}
-
-private:
-    static void dumpArgs(int argc, char **argv, int argObscure);
-    static int sendGenericOkFail(SocketClient *cli, int cond);
-
-    class DumpCmd : public VoldCommand {
-    public:
-        DumpCmd();
-        virtual ~DumpCmd() {}
-        int runCommand(SocketClient *c, int argc, char ** argv);
-    };
-
-    class VolumeCmd : public VoldCommand {
-    public:
-        VolumeCmd();
-        virtual ~VolumeCmd() {}
-        int runCommand(SocketClient *c, int argc, char ** argv);
-    };
-
-    class AsecCmd : public VoldCommand {
-    public:
-        AsecCmd();
-        virtual ~AsecCmd() {}
-        int runCommand(SocketClient *c, int argc, char ** argv);
-    private:
-        void listAsecsInDirectory(SocketClient *c, const char *directory);
-    };
-
-    class ObbCmd : public VoldCommand {
-    public:
-        ObbCmd();
-        virtual ~ObbCmd() {}
-        int runCommand(SocketClient *c, int argc, char ** argv);
-    };
-
-    class StorageCmd : public VoldCommand {
-    public:
-        StorageCmd();
-        virtual ~StorageCmd() {}
-        int runCommand(SocketClient *c, int argc, char ** argv);
-    };
-
-    class FstrimCmd : public VoldCommand {
-    public:
-        FstrimCmd();
-        virtual ~FstrimCmd() {}
-        int runCommand(SocketClient *c, int argc, char ** argv);
-    };
-
-    class AppFuseCmd : public VoldCommand {
-    public:
-        AppFuseCmd();
-        virtual ~AppFuseCmd() {}
-        int runCommand(SocketClient *c, int argc, char ** argv);
-    private:
-        android::status_t sendFd(SocketClient *c, int fd);
-    };
-};
-
-#endif
diff --git a/CryptCommandListener.cpp b/CryptCommandListener.cpp
deleted file mode 100644
index 779338f..0000000
--- a/CryptCommandListener.cpp
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * Copyright (C) 2015 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 <assert.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <fs_mgr.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <inttypes.h>
-
-#include <algorithm>
-#include <thread>
-
-#define LOG_TAG "VoldCryptCmdListener"
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-
-#include <cutils/fs.h>
-#include <cutils/log.h>
-#include <cutils/sockets.h>
-
-#include <sysutils/SocketClient.h>
-#include <private/android_filesystem_config.h>
-
-#include "CryptCommandListener.h"
-#include "Process.h"
-#include "ResponseCode.h"
-#include "cryptfs.h"
-#include "Ext4Crypt.h"
-#include "MetadataCrypt.h"
-#include "Utils.h"
-
-#define DUMP_ARGS 0
-
-CryptCommandListener::CryptCommandListener() :
-FrameworkListener("cryptd", true) {
-    registerCmd(new CryptfsCmd());
-}
-
-#if DUMP_ARGS
-void CryptCommandListener::dumpArgs(int argc, char **argv, int argObscure) {
-    char buffer[4096];
-    char *p = buffer;
-
-    memset(buffer, 0, sizeof(buffer));
-    int i;
-    for (i = 0; i < argc; i++) {
-        unsigned int len = strlen(argv[i]) + 1; // Account for space
-        if (i == argObscure) {
-            len += 2; // Account for {}
-        }
-        if (((p - buffer) + len) < (sizeof(buffer)-1)) {
-            if (i == argObscure) {
-                *p++ = '{';
-                *p++ = '}';
-                *p++ = ' ';
-                continue;
-            }
-            strcpy(p, argv[i]);
-            p+= strlen(argv[i]);
-            if (i != (argc -1)) {
-                *p++ = ' ';
-            }
-        }
-    }
-    SLOGD("%s", buffer);
-}
-#else
-void CryptCommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
-#endif
-
-int CryptCommandListener::sendGenericOkFailOnBool(SocketClient *cli, bool success) {
-    if (success) {
-        return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
-    } else {
-        return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
-    }
-}
-
-CryptCommandListener::CryptfsCmd::CryptfsCmd() :
-                 VoldCommand("cryptfs") {
-}
-
-static int getType(const char* type)
-{
-    if (!strcmp(type, "default")) {
-        return CRYPT_TYPE_DEFAULT;
-    } else if (!strcmp(type, "password")) {
-        return CRYPT_TYPE_PASSWORD;
-    } else if (!strcmp(type, "pin")) {
-        return CRYPT_TYPE_PIN;
-    } else if (!strcmp(type, "pattern")) {
-        return CRYPT_TYPE_PATTERN;
-    } else {
-        return -1;
-    }
-}
-
-static char* parseNull(char* arg) {
-    if (strcmp(arg, "!") == 0) {
-        return nullptr;
-    } else {
-        return arg;
-    }
-}
-
-static bool check_argc(SocketClient *cli, const std::string &subcommand, int argc,
-        int expected, std::string usage) {
-    assert(expected >= 2);
-    if (expected == 2) {
-        assert(usage.empty());
-    } else {
-        assert(!usage.empty());
-        assert(std::count(usage.begin(), usage.end(), ' ') + 3 == expected);
-    }
-    if (argc == expected) {
-        return true;
-    }
-    auto message = std::string() + "Usage: cryptfs " + subcommand;
-    if (!usage.empty()) {
-        message += " " + usage;
-    }
-    cli->sendMsg(ResponseCode::CommandSyntaxError, message.c_str(), false);
-    return false;
-}
-
-static int do_enablecrypto(char* arg2, char* arg4, int type, bool no_ui) {
-    int rc;
-    int tries;
-    for (tries = 0; tries < 2; ++tries) {
-        if (type == CRYPT_TYPE_DEFAULT) {
-            rc = cryptfs_enable_default(arg2, no_ui);
-        } else {
-            rc = cryptfs_enable(arg2, type, arg4, no_ui);
-        }
-
-        if (rc == 0) {
-            free(arg2);
-            free(arg4);
-            return 0;
-        } else if (tries == 0) {
-            Process::killProcessesWithOpenFiles(DATA_MNT_POINT, SIGKILL);
-        }
-    }
-
-    free(arg2);
-    free(arg4);
-    return -1;
-}
-
-int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
-                                                 int argc, char **argv) {
-    if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
-        cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run cryptfs commands", false);
-        return 0;
-    }
-
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing subcommand", false);
-        return 0;
-    }
-
-    int rc = 0;
-
-    std::string subcommand(argv[1]);
-    if (subcommand == "checkpw") {
-        if (!check_argc(cli, subcommand, argc, 3, "<passwd>")) return 0;
-        dumpArgs(argc, argv, 2);
-        rc = cryptfs_check_passwd(argv[2]);
-    } else if (subcommand == "restart") {
-        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
-        dumpArgs(argc, argv, -1);
-
-        // 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();
-    } else if (subcommand == "cryptocomplete") {
-        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
-        dumpArgs(argc, argv, -1);
-        rc = cryptfs_crypto_complete();
-    } else if (subcommand == "enablecrypto") {
-        if (e4crypt_is_native()) {
-            if (argc != 5 || strcmp(argv[2], "inplace") || strcmp(argv[3], "default")
-                    || strcmp(argv[4], "noui")) {
-                cli->sendMsg(ResponseCode::CommandSyntaxError,
-                        "Usage with ext4crypt: cryptfs enablecrypto inplace default noui", false);
-                return 0;
-            }
-            return sendGenericOkFailOnBool(cli, e4crypt_enable_crypto());
-        }
-        const char* syntax = "Usage: cryptfs enablecrypto <wipe|inplace> "
-                             "default|password|pin|pattern [passwd] [noui]";
-
-        // This should be replaced with a command line parser if more options
-        // are added
-        bool valid = true;
-        bool no_ui = false;
-        int type = CRYPT_TYPE_DEFAULT;
-        int options = 4; // Optional parameters are at this offset
-        if (argc < 4) {
-            // Minimum 4 parameters
-            valid = false;
-        } else if (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace") ) {
-            // Second parameter must be wipe or inplace
-            valid = false;
-        } else {
-            // Third parameter must be valid type
-            type = getType(argv[3]);
-            if (type == -1) {
-                valid = false;
-            } else if (type != CRYPT_TYPE_DEFAULT) {
-                options++;
-            }
-        }
-
-        if (valid) {
-            if(argc < options) {
-                // Too few parameters
-                valid = false;
-            } else if (argc == options) {
-                // No more, done
-            } else if (argc == options + 1) {
-                // One option, must be noui
-                if (!strcmp(argv[options], "noui")) {
-                    no_ui = true;
-                } else {
-                    valid = false;
-                }
-            } else {
-                // Too many options
-                valid = false;
-            }
-        }
-
-        if (!valid) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
-            return 0;
-        }
-
-        dumpArgs(argc, argv, 4);
-
-        // Spawn as thread so init can issue commands back to vold without
-        // causing deadlock, usually as a result of prep_data_fs.
-        char* arg2 = argc > 2 ? strdup(argv[2]) : NULL;
-        char* arg4 = argc > 4 ? strdup(argv[4]) : NULL;
-        std::thread(&do_enablecrypto, arg2, arg4, type, no_ui).detach();
-    } else if (subcommand == "enablefilecrypto") {
-        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
-        dumpArgs(argc, argv, -1);
-        rc = e4crypt_initialize_global_de();
-    } else if (subcommand == "changepw") {
-        const char* syntax = "Usage: cryptfs changepw "
-                             "default|password|pin|pattern [newpasswd]";
-        const char* password;
-        if (argc == 3) {
-            password = "";
-        } else if (argc == 4) {
-            password = argv[3];
-        } else {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
-            return 0;
-        }
-        int type = getType(argv[2]);
-        if (type == -1) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
-            return 0;
-        }
-        SLOGD("cryptfs changepw %s {}", argv[2]);
-        rc = cryptfs_changepw(type, password);
-    } else if (subcommand == "verifypw") {
-        if (!check_argc(cli, subcommand, argc, 3, "<passwd>")) return 0;
-        SLOGD("cryptfs verifypw {}");
-        rc = cryptfs_verify_passwd(argv[2]);
-    } else if (subcommand == "getfield") {
-        if (!check_argc(cli, subcommand, argc, 3, "<fieldname>")) return 0;
-        char *valbuf;
-        int valbuf_len = PROPERTY_VALUE_MAX;
-
-        dumpArgs(argc, argv, -1);
-
-        // Increase the buffer size until it is big enough for the field value stored.
-        while (1) {
-            valbuf = (char*)malloc(valbuf_len);
-            if (valbuf == NULL) {
-                cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", false);
-                return 0;
-            }
-            rc = cryptfs_getfield(argv[2], valbuf, valbuf_len);
-            if (rc != CRYPTO_GETFIELD_ERROR_BUF_TOO_SMALL) {
-                break;
-            }
-            free(valbuf);
-            valbuf_len *= 2;
-        }
-        if (rc == CRYPTO_GETFIELD_OK) {
-            cli->sendMsg(ResponseCode::CryptfsGetfieldResult, valbuf, false);
-        }
-        free(valbuf);
-    } else if (subcommand == "setfield") {
-        if (!check_argc(cli, subcommand, argc, 4, "<fieldname> <value>")) return 0;
-        dumpArgs(argc, argv, -1);
-        rc = cryptfs_setfield(argv[2], argv[3]);
-    } else if (subcommand == "mountdefaultencrypted") {
-        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
-        SLOGD("cryptfs mountdefaultencrypted");
-        dumpArgs(argc, argv, -1);
-
-        if (e4crypt_is_native()) {
-            return sendGenericOkFailOnBool(cli, e4crypt_mount_metadata_encrypted());
-        }
-        // 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();
-    } else if (subcommand == "getpwtype") {
-        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
-        SLOGD("cryptfs getpwtype");
-        dumpArgs(argc, argv, -1);
-        switch(cryptfs_get_password_type()) {
-        case CRYPT_TYPE_PASSWORD:
-            cli->sendMsg(ResponseCode::PasswordTypeResult, "password", false);
-            return 0;
-        case CRYPT_TYPE_PATTERN:
-            cli->sendMsg(ResponseCode::PasswordTypeResult, "pattern", false);
-            return 0;
-        case CRYPT_TYPE_PIN:
-            cli->sendMsg(ResponseCode::PasswordTypeResult, "pin", false);
-            return 0;
-        case CRYPT_TYPE_DEFAULT:
-            cli->sendMsg(ResponseCode::PasswordTypeResult, "default", false);
-            return 0;
-        default:
-          /** @TODO better error and make sure handled by callers */
-            cli->sendMsg(ResponseCode::OpFailedStorageNotFound, "Error", false);
-            return 0;
-        }
-    } else if (subcommand == "getpw") {
-        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
-        SLOGD("cryptfs getpw");
-        dumpArgs(argc, argv, -1);
-        const char* password = cryptfs_get_password();
-        if (password) {
-            char* message = 0;
-            int size = asprintf(&message, "{{sensitive}} %s", password);
-            if (size != -1) {
-                cli->sendMsg(ResponseCode::CommandOkay, message, false);
-                memset(message, 0, size);
-                free (message);
-                return 0;
-            }
-        }
-        rc = -1;
-    } else if (subcommand == "clearpw") {
-        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
-        SLOGD("cryptfs clearpw");
-        dumpArgs(argc, argv, -1);
-        cryptfs_clear_password();
-        rc = 0;
-
-    } else if (subcommand == "isConvertibleToFBE") {
-        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
-        // ext4enc:TODO: send a CommandSyntaxError if argv[2] not an integer
-        SLOGD("cryptfs isConvertibleToFBE");
-        dumpArgs(argc, argv, -1);
-        rc = cryptfs_isConvertibleToFBE();
-
-    } else if (subcommand == "init_user0") {
-        if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
-        return sendGenericOkFailOnBool(cli, e4crypt_init_user0());
-
-    } else if (subcommand == "create_user_key") {
-        if (!check_argc(cli, subcommand, argc, 5, "<user> <serial> <ephemeral>")) return 0;
-        return sendGenericOkFailOnBool(cli, e4crypt_vold_create_user_key(
-            atoi(argv[2]), atoi(argv[3]), atoi(argv[4]) != 0));
-
-    } else if (subcommand == "destroy_user_key") {
-        if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
-        return sendGenericOkFailOnBool(cli, e4crypt_destroy_user_key(atoi(argv[2])));
-
-    } else if (subcommand == "add_user_key_auth") {
-        if (!check_argc(cli, subcommand, argc, 6, "<user> <serial> <token> <secret>")) return 0;
-        return sendGenericOkFailOnBool(cli, e4crypt_add_user_key_auth(
-            atoi(argv[2]), atoi(argv[3]), argv[4], argv[5]));
-
-    } else if (subcommand == "fixate_newest_user_key_auth") {
-        if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
-        return sendGenericOkFailOnBool(cli, e4crypt_fixate_newest_user_key_auth(atoi(argv[2])));
-
-    } else if (subcommand == "unlock_user_key") {
-        if (!check_argc(cli, subcommand, argc, 6, "<user> <serial> <token> <secret>")) return 0;
-        return sendGenericOkFailOnBool(cli, e4crypt_unlock_user_key(
-            atoi(argv[2]), atoi(argv[3]), argv[4], argv[5]));
-
-    } else if (subcommand == "lock_user_key") {
-        if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
-        return sendGenericOkFailOnBool(cli, e4crypt_lock_user_key(atoi(argv[2])));
-
-    } else if (subcommand == "prepare_user_storage") {
-        if (!check_argc(cli, subcommand, argc, 6, "<uuid> <user> <serial> <flags>")) return 0;
-        return sendGenericOkFailOnBool(cli, e4crypt_prepare_user_storage(
-            parseNull(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5])));
-
-    } else if (subcommand == "destroy_user_storage") {
-        if (!check_argc(cli, subcommand, argc, 5, "<uuid> <user> <flags>")) return 0;
-        return sendGenericOkFailOnBool(cli,
-                e4crypt_destroy_user_storage(parseNull(argv[2]), atoi(argv[3]), atoi(argv[4])));
-
-    } else if (subcommand == "secdiscard") {
-        if (!check_argc(cli, subcommand, argc, 3, "<path>")) return 0;
-        return sendGenericOkFailOnBool(cli,
-                e4crypt_secdiscard(parseNull(argv[2])));
-
-    } else {
-        dumpArgs(argc, argv, -1);
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs subcommand", false);
-        return 0;
-    }
-
-    // Always report that the command succeeded and return the error code.
-    // The caller will check the return value to see what the error was.
-    char msg[255];
-    snprintf(msg, sizeof(msg), "%d", rc);
-    cli->sendMsg(ResponseCode::CommandOkay, msg, false);
-
-    return 0;
-}
diff --git a/CryptCommandListener.h b/CryptCommandListener.h
deleted file mode 100644
index 478ac02..0000000
--- a/CryptCommandListener.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 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 _CRYPTCOMMANDLISTENER_H__
-#define _CRYPTCOMMANDLISTENER_H__
-
-#include <sysutils/FrameworkListener.h>
-#include <utils/Errors.h>
-#include "VoldCommand.h"
-
-class CryptCommandListener : public FrameworkListener {
-public:
-    CryptCommandListener();
-    virtual ~CryptCommandListener() {}
-
-private:
-    static void dumpArgs(int argc, char **argv, int argObscure);
-    static int sendGenericOkFailOnBool(SocketClient *cli, bool success);
-
-    class CryptfsCmd : public VoldCommand {
-    public:
-        CryptfsCmd();
-        virtual ~CryptfsCmd() {}
-        int runCommand(SocketClient *c, int argc, char ** argv);
-    };
-    int getSocket();
-};
-
-#endif
diff --git a/Devmapper.cpp b/Devmapper.cpp
index 4b6942d..2510771 100644
--- a/Devmapper.cpp
+++ b/Devmapper.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <fcntl.h>
@@ -28,91 +30,18 @@
 
 #include <linux/kdev_t.h>
 
-#define LOG_TAG "Vold"
-
-#include <cutils/log.h>
-
-#include <sysutils/SocketClient.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
 
 #include "Devmapper.h"
 
 #define DEVMAPPER_BUFFER_SIZE 4096
 
-int Devmapper::dumpState(SocketClient *c) {
+using android::base::StringPrintf;
 
-    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);
-
-        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_STATUS, io2)) {
-            if (errno != ENXIO) {
-                SLOGE("DM_DEV_STATUS ioctl failed (%s)", strerror(errno));
-            }
-            io2 = NULL;
-        }
-
-        char *tmp;
-        if (!io2) {
-            asprintf(&tmp, "%s %llu:%llu (no status available)", n->name, MAJOR(n->dev), MINOR(n->dev));
-        } else {
-            asprintf(&tmp, "%s %llu:%llu %d %d 0x%.8x %llu:%llu", n->name, MAJOR(n->dev),
-                    MINOR(n->dev), io2->target_count, io2->open_count, io2->flags, MAJOR(io2->dev),
-                            MINOR(io2->dev));
-        }
-        c->sendMsg(0, tmp, false);
-        free(tmp);
-        nxt = n->next;
-    } while (nxt);
-
-    free(buffer);
-    free(buffer2);
-    close(fd);
-    return 0;
-}
+static const char* kVoldPrefix = "vold:";
 
 void Devmapper::ioctlInit(struct dm_ioctl *io, size_t dataSize,
                           const char *name, unsigned flags) {
@@ -130,50 +59,20 @@
     }
 }
 
-int Devmapper::lookupActive(const char *name, char *ubuffer, size_t len) {
-    char *buffer = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
-    if (!buffer) {
-        SLOGE("Error allocating memory (%s)", strerror(errno));
-        return -1;
-    }
-
-    int fd;
-    if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
-        SLOGE("Error opening devmapper (%s)", strerror(errno));
-        free(buffer);
-        return -1;
-    }
-
-    struct dm_ioctl *io = (struct dm_ioctl *) buffer;
- 
-    ioctlInit(io, DEVMAPPER_BUFFER_SIZE, name, 0);
-    if (ioctl(fd, DM_DEV_STATUS, io)) {
-        if (errno != ENXIO) {
-            SLOGE("DM_DEV_STATUS ioctl failed for lookup (%s)", strerror(errno));
-        }
-        free(buffer);
-        close(fd);
-        return -1;
-    }
-    close(fd);
-
-    unsigned minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
-    free(buffer);
-    snprintf(ubuffer, len, "/dev/block/dm-%u", minor);
-    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));
+        PLOG(ERROR) << "Failed malloc";
         return -1;
     }
 
     int fd;
     if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
-        SLOGE("Error opening devmapper (%s)", strerror(errno));
+        PLOG(ERROR) << "Failed open";
         free(buffer);
         return -1;
     }
@@ -184,7 +83,7 @@
     ioctlInit(io, DEVMAPPER_BUFFER_SIZE, name, 0);
 
     if (ioctl(fd, DM_DEV_CREATE, io)) {
-        SLOGE("Error creating device mapping (%s)", strerror(errno));
+        PLOG(ERROR) << "Failed DM_DEV_CREATE";
         free(buffer);
         close(fd);
         return -1;
@@ -199,7 +98,7 @@
     geoParams += strlen(geoParams) + 1;
     geoParams = (char *) _align(geoParams, 8);
     if (ioctl(fd, DM_DEV_SET_GEOMETRY, io)) {
-        SLOGE("Error setting device geometry (%s)", strerror(errno));
+        PLOG(ERROR) << "Failed DM_DEV_SET_GEOMETRY";
         free(buffer);
         close(fd);
         return -1;
@@ -208,7 +107,7 @@
     // Retrieve the device number we were allocated
     ioctlInit(io, DEVMAPPER_BUFFER_SIZE, name, 0);
     if (ioctl(fd, DM_DEV_STATUS, io)) {
-        SLOGE("Error retrieving devmapper status (%s)", strerror(errno));
+        PLOG(ERROR) << "Failed DM_DEV_STATUS";
         free(buffer);
         close(fd);
         return -1;
@@ -239,7 +138,7 @@
     tgt->next = cryptParams - buffer;
 
     if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        SLOGE("Error loading mapping table (%s)", strerror(errno));
+        PLOG(ERROR) << "Failed DM_TABLE_LOAD";
         free(buffer);
         close(fd);
         return -1;
@@ -249,7 +148,7 @@
     ioctlInit(io, DEVMAPPER_BUFFER_SIZE, name, 0);
 
     if (ioctl(fd, DM_DEV_SUSPEND, io)) {
-        SLOGE("Error Resuming (%s)", strerror(errno));
+        PLOG(ERROR) << "Failed DM_DEV_SUSPEND";
         free(buffer);
         close(fd);
         return -1;
@@ -261,16 +160,19 @@
     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));
+        PLOG(ERROR) << "Failed malloc";
         return -1;
     }
 
     int fd;
     if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
-        SLOGE("Error opening devmapper (%s)", strerror(errno));
+        PLOG(ERROR) << "Failed open";
         free(buffer);
         return -1;
     }
@@ -282,7 +184,7 @@
 
     if (ioctl(fd, DM_DEV_REMOVE, io)) {
         if (errno != ENXIO) {
-            SLOGE("Error destroying device mapping (%s)", strerror(errno));
+            PLOG(ERROR) << "Failed DM_DEV_REMOVE";
         }
         free(buffer);
         close(fd);
@@ -294,6 +196,76 @@
     return 0;
 }
 
+int Devmapper::destroyAll() {
+    ATRACE_NAME("Devmapper::destroyAll");
+    char *buffer = (char *) malloc(1024 * 64);
+    if (!buffer) {
+        PLOG(ERROR) << "Failed malloc";
+        return -1;
+    }
+    memset(buffer, 0, (1024 * 64));
+
+    char *buffer2 = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
+    if (!buffer2) {
+        PLOG(ERROR) << "Failed malloc";
+        free(buffer);
+        return -1;
+    }
+
+    int fd;
+    if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
+        PLOG(ERROR) << "Failed open";
+        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)) {
+        PLOG(ERROR) << "Failed DM_LIST_DEVICES";
+        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);
+        auto name = std::string(n->name);
+        if (android::base::StartsWith(name, kVoldPrefix)) {
+            LOG(DEBUG) << "Tearing down stale dm device named " << 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 " << name;
+                }
+            }
+        } else {
+            LOG(VERBOSE) << "Found unmanaged dm device named " << 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..7bb9786 100644
--- a/Devmapper.h
+++ b/Devmapper.h
@@ -20,15 +20,12 @@
 #include <unistd.h>
 #include <linux/dm-ioctl.h>
 
-class SocketClient;
-
 class Devmapper {
 public:
     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 lookupActive(const char *name, char *buffer, size_t len);
-    static int dumpState(SocketClient *c);
+    static int destroyAll();
 
 private:
     static void *_align(void *ptr, unsigned int a);
diff --git a/EncryptInplace.cpp b/EncryptInplace.cpp
index 6ef1cb0..16a108c 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/Ext4Crypt.cpp b/Ext4Crypt.cpp
index dc2e42a..646a032 100644
--- a/Ext4Crypt.cpp
+++ b/Ext4Crypt.cpp
@@ -19,6 +19,8 @@
 #include "KeyStorage.h"
 #include "KeyUtil.h"
 #include "Utils.h"
+#include "VoldUtil.h"
+
 
 #include <algorithm>
 #include <map>
@@ -89,8 +91,8 @@
     return property_get_bool("persist.sys.emulate_fbe", false);
 }
 
-static const char* escape_null(const char* value) {
-    return (value == nullptr) ? "null" : value;
+static const char* escape_empty(const std::string& value) {
+    return value.empty() ? "null" : value.c_str();
 }
 
 static std::string get_de_key_path(userid_t user_id) {
@@ -261,11 +263,18 @@
     return true;
 }
 
+static void get_file_encryption_modes(const char **contents_mode_ret,
+                                       const char **filenames_mode_ret)
+{
+    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab_default, DATA_MNT_POINT);
+    fs_mgr_get_file_encryption_modes(rec, contents_mode_ret, filenames_mode_ret);
+}
+
 static bool ensure_policy(const std::string& raw_ref, const std::string& path) {
     const char *contents_mode;
     const char *filenames_mode;
 
-    cryptfs_get_file_encryption_modes(&contents_mode, &filenames_mode);
+    get_file_encryption_modes(&contents_mode, &filenames_mode);
 
     if (e4crypt_policy_ensure(path.c_str(),
                               raw_ref.data(), raw_ref.size(),
@@ -304,7 +313,7 @@
             LOG(DEBUG) << "Skipping non-de-key " << entry->d_name;
             continue;
         }
-        userid_t user_id = atoi(entry->d_name);
+        userid_t user_id = std::stoi(entry->d_name);
         if (s_de_key_raw_refs.count(user_id) == 0) {
             auto key_path = de_dir + "/" + entry->d_name;
             KeyBuffer key;
@@ -330,7 +339,7 @@
 
     const char *contents_mode;
     const char *filenames_mode;
-    cryptfs_get_file_encryption_modes(&contents_mode, &filenames_mode);
+    get_file_encryption_modes(&contents_mode, &filenames_mode);
     std::string modestring = std::string(contents_mode) + ":" + filenames_mode;
 
     std::string mode_filename = std::string("/data") + e4crypt_key_mode;
@@ -370,7 +379,7 @@
     // We can only safely prepare DE storage here, since CE keys are probably
     // entangled with user credentials.  The framework will always prepare CE
     // storage once CE keys are installed.
-    if (!e4crypt_prepare_user_storage(nullptr, 0, 0, FLAG_STORAGE_DE)) {
+    if (!e4crypt_prepare_user_storage("", 0, 0, FLAG_STORAGE_DE)) {
         LOG(ERROR) << "Failed to prepare user 0 storage";
         return false;
     }
@@ -482,8 +491,8 @@
     return true;
 }
 
-static bool parse_hex(const char* hex, std::string* result) {
-    if (strcmp("!", hex) == 0) {
+static bool parse_hex(const std::string& hex, std::string* result) {
+    if (hex == "!") {
         *result = "";
         return true;
     }
@@ -494,10 +503,10 @@
     return true;
 }
 
-bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token_hex,
-                          const char* secret_hex) {
+bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
+                               const std::string& secret_hex) {
     LOG(DEBUG) << "e4crypt_add_user_key_auth " << user_id << " serial=" << serial
-               << " token_present=" << (strcmp(token_hex, "!") != 0);
+               << " token_present=" << (token_hex != "!");
     if (!e4crypt_is_native()) return true;
     if (s_ephemeral_users.count(user_id) != 0) return true;
     std::string token, secret;
@@ -534,10 +543,10 @@
 }
 
 // TODO: rename to 'install' for consistency, and take flags to know which keys to install
-bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token_hex,
-                             const char* secret_hex) {
+bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex,
+                             const std::string& secret_hex) {
     LOG(DEBUG) << "e4crypt_unlock_user_key " << user_id << " serial=" << serial
-               << " token_present=" << (strcmp(token_hex, "!") != 0);
+               << " token_present=" << (token_hex != "!");
     if (e4crypt_is_native()) {
         if (s_ce_key_raw_refs.count(user_id) != 0) {
             LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id;
@@ -557,8 +566,8 @@
         // back into a known-good state.
         if (!emulated_unlock(android::vold::BuildDataSystemCePath(user_id), 0771) ||
             !emulated_unlock(android::vold::BuildDataMiscCePath(user_id), 01771) ||
-            !emulated_unlock(android::vold::BuildDataMediaCePath(nullptr, user_id), 0770) ||
-            !emulated_unlock(android::vold::BuildDataUserCePath(nullptr, user_id), 0771)) {
+            !emulated_unlock(android::vold::BuildDataMediaCePath("", user_id), 0770) ||
+            !emulated_unlock(android::vold::BuildDataUserCePath("", user_id), 0771)) {
             LOG(ERROR) << "Failed to unlock user " << user_id;
             return false;
         }
@@ -575,8 +584,8 @@
         // When in emulation mode, we just use chmod
         if (!emulated_lock(android::vold::BuildDataSystemCePath(user_id)) ||
             !emulated_lock(android::vold::BuildDataMiscCePath(user_id)) ||
-            !emulated_lock(android::vold::BuildDataMediaCePath(nullptr, user_id)) ||
-            !emulated_lock(android::vold::BuildDataUserCePath(nullptr, user_id))) {
+            !emulated_lock(android::vold::BuildDataMediaCePath("", user_id)) ||
+            !emulated_lock(android::vold::BuildDataUserCePath("", user_id))) {
             LOG(ERROR) << "Failed to lock user " << user_id;
             return false;
         }
@@ -585,9 +594,21 @@
     return true;
 }
 
-bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial,
-        int flags) {
-    LOG(DEBUG) << "e4crypt_prepare_user_storage for volume " << escape_null(volume_uuid)
+static bool prepare_subdirs(const std::string& action, const std::string& dirtype,
+                            const std::string& volume_uuid, userid_t user_id,
+                            const std::string& path) {
+    if (0 != android::vold::ForkExecvp(std::vector<std::string>{"/system/bin/vold_prepare_subdirs",
+                                                                action, dirtype, volume_uuid,
+                                                                std::to_string(user_id), path})) {
+        LOG(ERROR) << "vold_prepare_subdirs failed on: " << path;
+        return false;
+    }
+    return true;
+}
+
+bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+                                  int flags) {
+    LOG(DEBUG) << "e4crypt_prepare_user_storage for volume " << escape_empty(volume_uuid)
                << ", user " << user_id << ", serial " << serial << ", flags " << flags;
 
     if (flags & FLAG_STORAGE_DE) {
@@ -601,24 +622,33 @@
         auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
         auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
 
-        if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
+        if (volume_uuid.empty()) {
+            if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
 #if MANAGE_MISC_DIRS
-        if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM),
-                multiuser_get_uid(user_id, AID_EVERYBODY))) return false;
+            if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM),
+                    multiuser_get_uid(user_id, AID_EVERYBODY))) return false;
 #endif
-        if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+            if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
 
-        if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
-        if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+            if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+            if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+        }
         if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
 
         if (e4crypt_is_native()) {
             std::string de_raw_ref;
             if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_raw_ref)) return false;
-            if (!ensure_policy(de_raw_ref, system_de_path)) return false;
-            if (!ensure_policy(de_raw_ref, misc_de_path)) return false;
+            if (volume_uuid.empty()) {
+                if (!ensure_policy(de_raw_ref, system_de_path)) return false;
+                if (!ensure_policy(de_raw_ref, misc_de_path)) return false;
+            }
             if (!ensure_policy(de_raw_ref, user_de_path)) return false;
         }
+
+        if (volume_uuid.empty()) {
+            if (!prepare_subdirs("prepare", "misc_de", volume_uuid, user_id, misc_de_path))
+                return false;
+        }
     }
 
     if (flags & FLAG_STORAGE_CE) {
@@ -628,19 +658,28 @@
         auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);
         auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
 
-        if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
-        if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+        if (volume_uuid.empty()) {
+            if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+            if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+        }
         if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
         if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
 
         if (e4crypt_is_native()) {
             std::string ce_raw_ref;
             if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_raw_ref)) return false;
-            if (!ensure_policy(ce_raw_ref, system_ce_path)) return false;
-            if (!ensure_policy(ce_raw_ref, misc_ce_path)) return false;
+            if (volume_uuid.empty()) {
+                if (!ensure_policy(ce_raw_ref, system_ce_path)) return false;
+                if (!ensure_policy(ce_raw_ref, misc_ce_path)) return false;
+
+            }
             if (!ensure_policy(ce_raw_ref, media_ce_path)) return false;
             if (!ensure_policy(ce_raw_ref, user_ce_path)) return false;
+        }
 
+        if (volume_uuid.empty()) {
+            if (!prepare_subdirs("prepare", "misc_ce", volume_uuid, user_id, misc_ce_path))
+                return false;
             // Now that credentials have been installed, we can run restorecon
             // over these paths
             // NOTE: these paths need to be kept in sync with libselinux
@@ -652,11 +691,27 @@
     return true;
 }
 
-bool e4crypt_destroy_user_storage(const char* volume_uuid, userid_t user_id, int flags) {
-    LOG(DEBUG) << "e4crypt_destroy_user_storage for volume " << escape_null(volume_uuid)
+bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags) {
+    LOG(DEBUG) << "e4crypt_destroy_user_storage for volume " << escape_empty(volume_uuid)
                << ", user " << user_id << ", flags " << flags;
     bool res = true;
 
+    if (flags & FLAG_STORAGE_CE) {
+        // CE_n key
+        auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);
+        auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
+        auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);
+        auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
+
+        res &= destroy_dir(media_ce_path);
+        res &= destroy_dir(user_ce_path);
+        if (volume_uuid.empty()) {
+            res &= prepare_subdirs("destroy", "misc_ce", volume_uuid, user_id, misc_ce_path);
+            res &= destroy_dir(system_ce_path);
+            res &= destroy_dir(misc_ce_path);
+        }
+    }
+
     if (flags & FLAG_STORAGE_DE) {
         // DE_sys key
         auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id);
@@ -668,7 +723,9 @@
         auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
         auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
 
-        if (volume_uuid == nullptr) {
+        res &= destroy_dir(user_de_path);
+        if (volume_uuid.empty()) {
+            res &= prepare_subdirs("destroy", "misc_de", volume_uuid, user_id, misc_de_path);
             res &= destroy_dir(system_legacy_path);
 #if MANAGE_MISC_DIRS
             res &= destroy_dir(misc_legacy_path);
@@ -677,27 +734,11 @@
             res &= destroy_dir(system_de_path);
             res &= destroy_dir(misc_de_path);
         }
-        res &= destroy_dir(user_de_path);
-    }
-
-    if (flags & FLAG_STORAGE_CE) {
-        // CE_n key
-        auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);
-        auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
-        auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);
-        auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
-
-        if (volume_uuid == nullptr) {
-            res &= destroy_dir(system_ce_path);
-            res &= destroy_dir(misc_ce_path);
-        }
-        res &= destroy_dir(media_ce_path);
-        res &= destroy_dir(user_ce_path);
     }
 
     return res;
 }
 
-bool e4crypt_secdiscard(const char* path) {
-    return android::vold::runSecdiscardSingle(std::string(path));
+bool e4crypt_secdiscard(const std::string& path) {
+    return android::vold::runSecdiscardSingle(path);
 }
diff --git a/Ext4Crypt.h b/Ext4Crypt.h
index e90167b..d0afd85 100644
--- a/Ext4Crypt.h
+++ b/Ext4Crypt.h
@@ -14,29 +14,29 @@
  * limitations under the License.
  */
 
+#include <string>
+
 #include <stdbool.h>
 #include <sys/cdefs.h>
 
 #include <cutils/multiuser.h>
 
-__BEGIN_DECLS
-
 // General functions
-bool e4crypt_is_native();
 bool e4crypt_initialize_global_de();
 
 bool e4crypt_init_user0();
 bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
 bool e4crypt_destroy_user_key(userid_t user_id);
-bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token,
-                               const char* secret);
+bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
+                               const std::string& secret);
 bool e4crypt_fixate_newest_user_key_auth(userid_t user_id);
 
-bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token, const char* secret);
+bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
+                             const std::string& secret);
 bool e4crypt_lock_user_key(userid_t user_id);
 
-bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial, int flags);
-bool e4crypt_destroy_user_storage(const char* volume_uuid, userid_t user_id, int flags);
+bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+                                  int flags);
+bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
 
-bool e4crypt_secdiscard(const char* path);
-__END_DECLS
+bool e4crypt_secdiscard(const std::string& path);
diff --git a/TrimTask.cpp b/IdleMaint.cpp
similarity index 66%
rename from TrimTask.cpp
rename to IdleMaint.cpp
index 08e6499..ed6374f 100644
--- a/TrimTask.cpp
+++ b/IdleMaint.cpp
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-#include "TrimTask.h"
-#include "Benchmark.h"
+#include "IdleMaint.h"
 #include "Utils.h"
 #include "VolumeManager.h"
-#include "ResponseCode.h"
 
 #include <android-base/stringprintf.h>
 #include <android-base/logging.h>
-#include <cutils/properties.h>
 #include <fs_mgr.h>
 #include <private/android_filesystem_config.h>
 #include <hardware_legacy/power.h>
@@ -34,45 +31,34 @@
 #include <sys/wait.h>
 #include <fcntl.h>
 
-/* From a would-be kernel header */
-#define FIDTRIM         _IOWR('f', 128, struct fstrim_range)    /* Deep discard trim */
-
-#define BENCHMARK_ENABLED 1
-
 using android::base::StringPrintf;
 
 namespace android {
 namespace vold {
 
-static const char* kWakeLock = "TrimTask";
+static const char* kWakeLock = "IdleMaint";
 
-TrimTask::TrimTask(int flags) : mFlags(flags) {
-    // Collect both fstab and vold volumes
-    addFromFstab();
-
+static void addFromVolumeManager(std::list<std::string>* paths) {
     VolumeManager* vm = VolumeManager::Instance();
     std::list<std::string> privateIds;
     vm->listVolumes(VolumeBase::Type::kPrivate, privateIds);
     for (const auto& id : privateIds) {
         auto vol = vm->findVolume(id);
         if (vol != nullptr && vol->getState() == VolumeBase::State::kMounted) {
-            mPaths.push_back(vol->getPath());
+            paths->push_back(vol->getPath());
         }
     }
 }
 
-TrimTask::~TrimTask() {
-}
-
-void TrimTask::addFromFstab() {
+static void addFromFstab(std::list<std::string>* paths) {
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
                                                                fs_mgr_free_fstab);
     struct fstab_rec *prev_rec = NULL;
 
     for (int i = 0; i < fstab->num_entries; i++) {
+        auto fs_type = std::string(fstab->recs[i].fs_type);
         /* Skip raw partitions */
-        if (!strcmp(fstab->recs[i].fs_type, "emmc") ||
-            !strcmp(fstab->recs[i].fs_type, "mtd")) {
+        if (fs_type == "emmc" || fs_type == "mtd") {
             continue;
         }
         /* Skip read-only filesystems */
@@ -93,32 +79,31 @@
             continue;
         }
 
-        mPaths.push_back(fstab->recs[i].mount_point);
+        paths->push_back(fstab->recs[i].mount_point);
         prev_rec = &fstab->recs[i];
     }
 }
 
-void TrimTask::start() {
-    mThread = std::thread(&TrimTask::run, this);
-}
-
-static void notifyResult(const std::string& path, int64_t bytes, int64_t delta) {
-    std::string res(path
-            + " " + std::to_string(bytes)
-            + " " + std::to_string(delta));
-    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
-            ResponseCode::TrimResult, res.c_str(), false);
-}
-
-void TrimTask::run() {
+void Trim(const android::sp<android::os::IVoldTaskListener>& listener) {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock);
 
-    for (const auto& path : mPaths) {
+    // Collect both fstab and vold volumes
+    std::list<std::string> paths;
+    addFromFstab(&paths);
+    addFromVolumeManager(&paths);
+
+    for (const auto& path : paths) {
         LOG(DEBUG) << "Starting trim of " << path;
 
+        android::os::PersistableBundle extras;
+        extras.putString(String16("path"), String16(path.c_str()));
+
         int fd = open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
         if (fd < 0) {
             PLOG(WARNING) << "Failed to open " << path;
+            if (listener) {
+                listener->onStatus(-1, extras);
+            }
             continue;
         }
 
@@ -127,24 +112,27 @@
         range.len = ULLONG_MAX;
 
         nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
-        if (ioctl(fd, (mFlags & Flags::kDeepTrim) ? FIDTRIM : FITRIM, &range)) {
+        if (ioctl(fd, FITRIM, &range)) {
             PLOG(WARNING) << "Trim failed on " << path;
-            notifyResult(path, -1, -1);
+            if (listener) {
+                listener->onStatus(-1, extras);
+            }
         } else {
-            nsecs_t delta = systemTime(SYSTEM_TIME_BOOTTIME) - start;
+            nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
             LOG(INFO) << "Trimmed " << range.len << " bytes on " << path
-                    << " in " << nanoseconds_to_milliseconds(delta) << "ms";
-            notifyResult(path, range.len, delta);
+                    << " in " << nanoseconds_to_milliseconds(time) << "ms";
+            extras.putLong(String16("bytes"), range.len);
+            extras.putLong(String16("time"), time);
+            if (listener) {
+                listener->onStatus(0, extras);
+            }
         }
         close(fd);
+    }
 
-        if (mFlags & Flags::kBenchmarkAfter) {
-#if BENCHMARK_ENABLED
-            BenchmarkPrivate(path);
-#else
-            LOG(DEBUG) << "Benchmark disabled";
-#endif
-        }
+    if (listener) {
+        android::os::PersistableBundle extras;
+        listener->onFinished(0, extras);
     }
 
     release_wake_lock(kWakeLock);
diff --git a/VoldCommand.h b/IdleMaint.h
similarity index 64%
copy from VoldCommand.h
copy to IdleMaint.h
index e435159..38dadfb 100644
--- a/VoldCommand.h
+++ b/IdleMaint.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef _VOLD_COMMAND_H
-#define _VOLD_COMMAND_H
+#ifndef ANDROID_VOLD_IDLE_MAINT_H
+#define ANDROID_VOLD_IDLE_MAINT_H
 
-#include <sysutils/FrameworkCommand.h>
+#include "android/os/IVoldTaskListener.h"
 
-class VoldCommand : public FrameworkCommand {
-public:
-    explicit VoldCommand(const char *cmd);
-    virtual ~VoldCommand() {}
-};
+namespace android {
+namespace vold {
+
+void Trim(const android::sp<android::os::IVoldTaskListener>& listener);
+
+}  // namespace vold
+}  // namespace android
 
 #endif
diff --git a/Keymaster.h b/Keymaster.h
index dc6f1bc..f24a0c0 100644
--- a/Keymaster.h
+++ b/Keymaster.h
@@ -17,8 +17,6 @@
 #ifndef ANDROID_VOLD_KEYMASTER_H
 #define ANDROID_VOLD_KEYMASTER_H
 
-#ifdef __cplusplus
-
 #include "KeyBuffer.h"
 
 #include <memory>
@@ -127,8 +125,7 @@
 }  // namespace vold
 }  // namespace android
 
-#endif // __cplusplus
-
+// FIXME no longer needed now cryptfs is in C++.
 
 /*
  * The following functions provide C bindings to keymaster services
@@ -138,7 +135,6 @@
  * The sign_object function signes an object with the given keymaster
  * key.
  */
-__BEGIN_DECLS
 
 int keymaster_compatibility_cryptfs_scrypt();
 int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size,
@@ -156,6 +152,5 @@
                                              uint8_t** signature_buffer,
                                              size_t* signature_buffer_size);
 
-__END_DECLS
 
 #endif
diff --git a/Loop.cpp b/Loop.cpp
index 6ec5e6d..3736d6a 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
 #include <stdio.h>
 #include <stdlib.h>
+#include <dirent.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
@@ -28,213 +31,20 @@
 
 #include <linux/kdev_t.h>
 
-#define LOG_TAG "Vold"
-
-#include <cutils/log.h>
-
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
+#include <utils/Trace.h>
 
-#include <sysutils/SocketClient.h>
 #include "Loop.h"
-#include "Asec.h"
 #include "VoldUtil.h"
 #include "sehandle.h"
 
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
-int Loop::dumpState(SocketClient *c) {
-    int i;
-    int fd;
-    char filename[256];
-
-    for (i = 0; i < LOOP_MAX; i++) {
-        struct loop_info64 li;
-        int rc;
-
-        snprintf(filename, sizeof(filename), "/dev/block/loop%d", i);
-
-        if ((fd = open(filename, O_RDWR | O_CLOEXEC)) < 0) {
-            if (errno != ENOENT) {
-                SLOGE("Unable to open %s (%s)", filename, strerror(errno));
-            } else {
-                continue;
-            }
-            return -1;
-        }
-
-        rc = ioctl(fd, LOOP_GET_STATUS64, &li);
-        close(fd);
-        if (rc < 0 && errno == ENXIO) {
-            continue;
-        }
-
-        if (rc < 0) {
-            SLOGE("Unable to get loop status for %s (%s)", filename,
-                 strerror(errno));
-            return -1;
-        }
-        char *tmp = NULL;
-        asprintf(&tmp, "%s %d %lld:%lld %llu %lld:%lld %lld 0x%x {%s} {%s}", filename, li.lo_number,
-                MAJOR(li.lo_device), MINOR(li.lo_device), li.lo_inode, MAJOR(li.lo_rdevice),
-                        MINOR(li.lo_rdevice), li.lo_offset, li.lo_flags, li.lo_crypt_name,
-                        li.lo_file_name);
-        c->sendMsg(0, tmp, false);
-        free(tmp);
-    }
-    return 0;
-}
-
-int Loop::lookupActive(const char *id, char *buffer, size_t len) {
-    int i;
-    int fd;
-    char filename[256];
-
-    memset(buffer, 0, len);
-
-    for (i = 0; i < LOOP_MAX; i++) {
-        struct loop_info64 li;
-        int rc;
-
-        snprintf(filename, sizeof(filename), "/dev/block/loop%d", i);
-
-        if ((fd = open(filename, O_RDWR | O_CLOEXEC)) < 0) {
-            if (errno != ENOENT) {
-                SLOGE("Unable to open %s (%s)", filename, strerror(errno));
-            } else {
-                continue;
-            }
-            return -1;
-        }
-
-        rc = ioctl(fd, LOOP_GET_STATUS64, &li);
-        if (rc < 0 && errno == ENXIO) {
-            close(fd);
-            continue;
-        }
-        close(fd);
-
-        if (rc < 0) {
-            SLOGE("Unable to get loop status for %s (%s)", filename,
-                 strerror(errno));
-            return -1;
-        }
-        if (!strncmp((const char*) li.lo_crypt_name, id, LO_NAME_SIZE)) {
-            break;
-        }
-    }
-
-    if (i == LOOP_MAX) {
-        errno = ENOENT;
-        return -1;
-    }
-    strlcpy(buffer, filename, len);
-    return 0;
-}
-
-int Loop::create(const char *id, const char *loopFile, char *loopDeviceBuffer, size_t len) {
-    int i;
-    int fd;
-    char filename[256];
-
-    for (i = 0; i < LOOP_MAX; i++) {
-        struct loop_info64 li;
-        int rc;
-        char *secontext = NULL;
-
-        snprintf(filename, sizeof(filename), "/dev/block/loop%d", i);
-
-        /*
-         * The kernel starts us off with 8 loop nodes, but more
-         * are created on-demand if needed.
-         */
-        mode_t mode = 0660 | S_IFBLK;
-        unsigned int dev = (0xff & i) | ((i << 12) & 0xfff00000) | (7 << 8);
-
-        if (sehandle) {
-            rc = selabel_lookup(sehandle, &secontext, filename, S_IFBLK);
-            if (rc == 0)
-                setfscreatecon(secontext);
-        }
-
-        if (mknod(filename, mode, dev) < 0) {
-            if (errno != EEXIST) {
-                int sverrno = errno;
-                SLOGE("Error creating loop device node (%s)", strerror(errno));
-                if (secontext) {
-                    freecon(secontext);
-                    setfscreatecon(NULL);
-                }
-                errno = sverrno;
-                return -1;
-            }
-        }
-        if (secontext) {
-            freecon(secontext);
-            setfscreatecon(NULL);
-        }
-
-        if ((fd = open(filename, O_RDWR | O_CLOEXEC)) < 0) {
-            SLOGE("Unable to open %s (%s)", filename, strerror(errno));
-            return -1;
-        }
-
-        rc = ioctl(fd, LOOP_GET_STATUS64, &li);
-        if (rc < 0 && errno == ENXIO)
-            break;
-
-        close(fd);
-
-        if (rc < 0) {
-            SLOGE("Unable to get loop status for %s (%s)", filename,
-                 strerror(errno));
-            return -1;
-        }
-    }
-
-    if (i == LOOP_MAX) {
-        SLOGE("Exhausted all loop devices");
-        errno = ENOSPC;
-        return -1;
-    }
-
-    strlcpy(loopDeviceBuffer, filename, len);
-
-    int file_fd;
-
-    if ((file_fd = open(loopFile, O_RDWR | O_CLOEXEC)) < 0) {
-        SLOGE("Unable to open %s (%s)", loopFile, strerror(errno));
-        close(fd);
-        return -1;
-    }
-
-    if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) {
-        SLOGE("Error setting up loopback interface (%s)", strerror(errno));
-        close(file_fd);
-        close(fd);
-        return -1;
-    }
-
-    struct loop_info64 li;
-
-    memset(&li, 0, sizeof(li));
-    strlcpy((char*) li.lo_crypt_name, id, LO_NAME_SIZE);
-    strlcpy((char*) li.lo_file_name, loopFile, LO_NAME_SIZE);
-
-    if (ioctl(fd, LOOP_SET_STATUS64, &li) < 0) {
-        SLOGE("Error setting loopback status (%s)", strerror(errno));
-        close(file_fd);
-        close(fd);
-        return -1;
-    }
-
-    close(fd);
-    close(file_fd);
-
-    return 0;
-}
+static const char* kVoldPrefix = "vold:";
 
 int Loop::create(const std::string& target, std::string& out_device) {
     unique_fd ctl_fd(open("/dev/loop-control", O_RDWR | O_CLOEXEC));
@@ -267,6 +77,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;
 }
 
@@ -275,12 +93,12 @@
 
     device_fd = open(loopDevice, O_RDONLY | O_CLOEXEC);
     if (device_fd < 0) {
-        SLOGE("Failed to open loop (%d)", errno);
+        PLOG(ERROR) << "Failed to open " << loopDevice;
         return -1;
     }
 
     if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) {
-        SLOGE("Failed to destroy loop (%d)", errno);
+        PLOG(ERROR) << "Failed to destroy " << loopDevice;
         close(device_fd);
         return -1;
     }
@@ -289,9 +107,52 @@
     return 0;
 }
 
-int Loop::destroyByFile(const char * /*loopFile*/) {
-    errno = ENOSYS;
-    return -1;
+int Loop::destroyAll() {
+    ATRACE_NAME("Loop::destroyAll");
+
+    DIR* dir;
+    struct dirent* de;
+
+    std::string root = "/dev/block/";
+    if (!(dir = opendir(root.c_str()))) {
+        PLOG(ERROR) << "Failed to opendir";
+        return -1;
+    }
+
+    // Poke through all devices looking for loops
+    while ((de = readdir(dir))) {
+        auto test = std::string(de->d_name);
+        if (!android::base::StartsWith(test, "loop")) continue;
+
+        auto path = root + de->d_name;
+        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;
+        }
+
+        auto id = std::string((char*) li.lo_crypt_name);
+        if (android::base::StartsWith(id, kVoldPrefix)) {
+            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;
+        }
+    }
+
+    closedir(dir);
+    return 0;
 }
 
 int Loop::createImageFile(const char *file, unsigned long numSectors) {
@@ -314,22 +175,22 @@
     int fd;
 
     if ((fd = open(file, O_RDWR | O_CLOEXEC)) < 0) {
-        SLOGE("Error opening imagefile (%s)", strerror(errno));
+        PLOG(ERROR) << "Failed to open " << file;
         return -1;
     }
 
-    SLOGD("Attempting to increase size of %s to %lu sectors.", file, numSectors);
+    LOG(DEBUG) << "Attempting to increase " << file << " to " << numSectors;
 
     if (fallocate(fd, 0, 0, numSectors * 512)) {
         if (errno == ENOSYS || errno == ENOTSUP) {
-            SLOGW("fallocate not found. Falling back to ftruncate.");
+            PLOG(WARNING) << "fallocate not found. Falling back to ftruncate.";
             if (ftruncate(fd, numSectors * 512) < 0) {
-                SLOGE("Error truncating imagefile (%s)", strerror(errno));
+                PLOG(ERROR) << "Failed to ftruncate";
                 close(fd);
                 return -1;
             }
         } else {
-            SLOGE("Error allocating space (%s)", strerror(errno));
+            PLOG(ERROR) << "Failed to fallocate";
             close(fd);
             return -1;
         }
@@ -337,46 +198,3 @@
     close(fd);
     return 0;
 }
-
-int Loop::lookupInfo(const char *loopDevice, struct asec_superblock *sb, unsigned long *nr_sec) {
-    int fd;
-    struct asec_superblock buffer;
-
-    if ((fd = open(loopDevice, O_RDONLY | O_CLOEXEC)) < 0) {
-        SLOGE("Failed to open loopdevice (%s)", strerror(errno));
-        destroyByDevice(loopDevice);
-        return -1;
-    }
-
-    get_blkdev_size(fd, nr_sec);
-    if (*nr_sec == 0) {
-        SLOGE("Failed to get loop size (%s)", strerror(errno));
-        destroyByDevice(loopDevice);
-        close(fd);
-        return -1;
-    }
-
-    /*
-     * Try to read superblock.
-     */
-    memset(&buffer, 0, sizeof(struct asec_superblock));
-    if (lseek(fd, ((*nr_sec - 1) * 512), SEEK_SET) < 0) {
-        SLOGE("lseek failed (%s)", strerror(errno));
-        close(fd);
-        destroyByDevice(loopDevice);
-        return -1;
-    }
-    if (read(fd, &buffer, sizeof(struct asec_superblock)) != sizeof(struct asec_superblock)) {
-        SLOGE("superblock read failed (%s)", strerror(errno));
-        close(fd);
-        destroyByDevice(loopDevice);
-        return -1;
-    }
-    close(fd);
-
-    /*
-     * Superblock successfully read. Copy to caller's struct.
-     */
-    memcpy(sb, &buffer, sizeof(struct asec_superblock));
-    return 0;
-}
diff --git a/Loop.h b/Loop.h
index 5d8f427..130c5b6 100644
--- a/Loop.h
+++ b/Loop.h
@@ -21,22 +21,15 @@
 #include <unistd.h>
 #include <linux/loop.h>
 
-class SocketClient;
-
 class Loop {
 public:
     static const int LOOP_MAX = 4096;
 public:
-    static int lookupActive(const char *id, char *buffer, size_t len);
-    static int lookupInfo(const char *loopDevice, struct asec_superblock *sb, unsigned long *nr_sec);
-    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);
-
-    static int dumpState(SocketClient *c);
 };
 
 #endif
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index 8311813..24047f9 100644
--- a/MetadataCrypt.cpp
+++ b/MetadataCrypt.cpp
@@ -42,7 +42,6 @@
 #include "Utils.h"
 #include "VoldUtil.h"
 
-extern struct fstab *fstab;
 #define DM_CRYPT_BUF_SIZE 4096
 #define TABLE_LOAD_RETRIES 10
 #define DEFAULT_KEY_TARGET_TYPE "default-key"
@@ -58,7 +57,7 @@
         PLOG(ERROR) << "Failed to setexeccon";
         return false;
     }
-    auto mount_rc = fs_mgr_do_mount(fstab, const_cast<char*>(mount_point),
+    auto mount_rc = fs_mgr_do_mount(fstab_default, const_cast<char*>(mount_point),
                                     const_cast<char*>(blk_device), nullptr);
     if (setexeccon(nullptr)) {
         PLOG(ERROR) << "Failed to clear setexeccon";
@@ -73,7 +72,7 @@
 }
 
 static bool read_key(bool create_if_absent, KeyBuffer* key) {
-    auto data_rec = fs_mgr_get_crypt_entry(fstab);
+    auto data_rec = fs_mgr_get_crypt_entry(fstab_default);
     if (!data_rec) {
         LOG(ERROR) << "Failed to get data_rec";
         return false;
@@ -253,7 +252,7 @@
     LOG(DEBUG) << "e4crypt_mount_default_encrypted";
     KeyBuffer key;
     if (!read_key(false, &key)) return false;
-    auto data_rec = fs_mgr_get_crypt_entry(fstab);
+    auto data_rec = fs_mgr_get_crypt_entry(fstab_default);
     if (!data_rec) {
         LOG(ERROR) << "Failed to get data_rec";
         return false;
@@ -283,7 +282,7 @@
     KeyBuffer key_ref;
     if (!read_key(true, &key_ref)) return false;
 
-    auto data_rec = fs_mgr_get_crypt_entry(fstab);
+    auto data_rec = fs_mgr_get_crypt_entry(fstab_default);
     if (!data_rec) {
         LOG(ERROR) << "Failed to get data_rec";
         return false;
diff --git a/MoveTask.cpp b/MoveStorage.cpp
similarity index 76%
rename from MoveTask.cpp
rename to MoveStorage.cpp
index c565752..4f5ebe8 100644
--- a/MoveTask.cpp
+++ b/MoveStorage.cpp
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
-#include "MoveTask.h"
+#include "MoveStorage.h"
 #include "Utils.h"
 #include "VolumeManager.h"
-#include "ResponseCode.h"
 
 #include <android-base/stringprintf.h>
 #include <android-base/logging.h>
 #include <private/android_filesystem_config.h>
 #include <hardware_legacy/power.h>
 
+#include <thread>
+
 #include <dirent.h>
 #include <sys/wait.h>
 
@@ -45,21 +46,12 @@
 
 static const char* kWakeLock = "MoveTask";
 
-MoveTask::MoveTask(const std::shared_ptr<VolumeBase>& from,
-        const std::shared_ptr<VolumeBase>& to) :
-        mFrom(from), mTo(to) {
-}
-
-MoveTask::~MoveTask() {
-}
-
-void MoveTask::start() {
-    mThread = std::thread(&MoveTask::run, this);
-}
-
-static void notifyProgress(int progress) {
-    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(ResponseCode::MoveStatus,
-            StringPrintf("%d", progress).c_str(), false);
+static void notifyProgress(int progress,
+        const android::sp<android::os::IVoldTaskListener>& listener) {
+    if (listener) {
+        android::os::PersistableBundle extras;
+        listener->onStatus(progress, extras);
+    }
 }
 
 static status_t pushBackContents(const std::string& path, std::vector<std::string>& cmd,
@@ -85,8 +77,9 @@
     return found ? OK : -1;
 }
 
-static status_t execRm(const std::string& path, int startProgress, int stepProgress) {
-    notifyProgress(startProgress);
+static status_t execRm(const std::string& path, int startProgress, int stepProgress,
+        const android::sp<android::os::IVoldTaskListener>& listener) {
+    notifyProgress(startProgress, listener);
 
     uint64_t expectedBytes = GetTreeBytes(path);
     uint64_t startFreeBytes = GetFreeBytes(path);
@@ -120,15 +113,15 @@
         sleep(1);
         uint64_t deltaFreeBytes = GetFreeBytes(path) - startFreeBytes;
         notifyProgress(startProgress + CONSTRAIN((int)
-                ((deltaFreeBytes * stepProgress) / expectedBytes), 0, stepProgress));
+                ((deltaFreeBytes * stepProgress) / expectedBytes), 0, stepProgress), listener);
     }
     return -1;
 #endif
 }
 
-static status_t execCp(const std::string& fromPath, const std::string& toPath,
-        int startProgress, int stepProgress) {
-    notifyProgress(startProgress);
+static status_t execCp(const std::string& fromPath, const std::string& toPath, int startProgress,
+        int stepProgress, const android::sp<android::os::IVoldTaskListener>& listener) {
+    notifyProgress(startProgress, listener);
 
     uint64_t expectedBytes = GetTreeBytes(fromPath);
     uint64_t startFreeBytes = GetFreeBytes(toPath);
@@ -171,7 +164,7 @@
         sleep(1);
         uint64_t deltaFreeBytes = startFreeBytes - GetFreeBytes(toPath);
         notifyProgress(startProgress + CONSTRAIN((int)
-                ((deltaFreeBytes * stepProgress) / expectedBytes), 0, stepProgress));
+                ((deltaFreeBytes * stepProgress) / expectedBytes), 0, stepProgress), listener);
     }
     return -1;
 #endif
@@ -191,69 +184,80 @@
     vol->create();
 }
 
-void MoveTask::run() {
-    acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock);
-
+static status_t moveStorageInternal(const std::shared_ptr<VolumeBase>& from,
+        const std::shared_ptr<VolumeBase>& to,
+        const android::sp<android::os::IVoldTaskListener>& listener) {
     std::string fromPath;
     std::string toPath;
 
     // TODO: add support for public volumes
-    if (mFrom->getType() != VolumeBase::Type::kEmulated) goto fail;
-    if (mTo->getType() != VolumeBase::Type::kEmulated) goto fail;
+    if (from->getType() != VolumeBase::Type::kEmulated) goto fail;
+    if (to->getType() != VolumeBase::Type::kEmulated) goto fail;
 
     // Step 1: tear down volumes and mount silently without making
     // visible to userspace apps
     {
         std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock());
-        bringOffline(mFrom);
-        bringOffline(mTo);
+        bringOffline(from);
+        bringOffline(to);
     }
 
-    fromPath = mFrom->getInternalPath();
-    toPath = mTo->getInternalPath();
+    fromPath = from->getInternalPath();
+    toPath = to->getInternalPath();
 
     // Step 2: clean up any stale data
-    if (execRm(toPath, 10, 10) != OK) {
+    if (execRm(toPath, 10, 10, listener) != OK) {
         goto fail;
     }
 
     // Step 3: perform actual copy
-    if (execCp(fromPath, toPath, 20, 60) != OK) {
+    if (execCp(fromPath, toPath, 20, 60, listener) != OK) {
         goto copy_fail;
     }
 
     // NOTE: MountService watches for this magic value to know
     // that move was successful
-    notifyProgress(82);
+    notifyProgress(82, listener);
     {
         std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock());
-        bringOnline(mFrom);
-        bringOnline(mTo);
+        bringOnline(from);
+        bringOnline(to);
     }
 
     // Step 4: clean up old data
-    if (execRm(fromPath, 85, 15) != OK) {
+    if (execRm(fromPath, 85, 15, listener) != OK) {
         goto fail;
     }
 
-    notifyProgress(kMoveSucceeded);
-    release_wake_lock(kWakeLock);
-    return;
+    notifyProgress(kMoveSucceeded, listener);
+    return OK;
 
 copy_fail:
     // if we failed to copy the data we should not leave it laying around
     // in target location. Do not check return value, we can not do any
     // useful anyway.
-    execRm(toPath, 80, 1);
+    execRm(toPath, 80, 1, listener);
 fail:
     {
         std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getLock());
-        bringOnline(mFrom);
-        bringOnline(mTo);
+        bringOnline(from);
+        bringOnline(to);
     }
-    notifyProgress(kMoveFailedInternalError);
+    notifyProgress(kMoveFailedInternalError, listener);
+    return -1;
+}
+
+void MoveStorage(const std::shared_ptr<VolumeBase>& from, const std::shared_ptr<VolumeBase>& to,
+        const android::sp<android::os::IVoldTaskListener>& listener) {
+    acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLock);
+
+    android::os::PersistableBundle extras;
+    status_t res = moveStorageInternal(from, to, listener);
+    if (listener) {
+        listener->onFinished(res, extras);
+    }
+
     release_wake_lock(kWakeLock);
-    return;
 }
 
 }  // namespace vold
diff --git a/MoveTask.h b/MoveStorage.h
similarity index 60%
rename from MoveTask.h
rename to MoveStorage.h
index b1777c0..d271704 100644
--- a/MoveTask.h
+++ b/MoveStorage.h
@@ -14,33 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_VOLD_MOVE_TASK_H
-#define ANDROID_VOLD_MOVE_TASK_H
+#ifndef ANDROID_VOLD_MOVE_STORAGE_H
+#define ANDROID_VOLD_MOVE_STORAGE_H
 
-#include "Utils.h"
-#include "VolumeBase.h"
-
-#include <thread>
+#include "android/os/IVoldTaskListener.h"
+#include "model/VolumeBase.h"
 
 namespace android {
 namespace vold {
 
-class MoveTask {
-public:
-    MoveTask(const std::shared_ptr<VolumeBase>& from, const std::shared_ptr<VolumeBase>& to);
-    virtual ~MoveTask();
-
-    void start();
-
-private:
-    std::shared_ptr<VolumeBase> mFrom;
-    std::shared_ptr<VolumeBase> mTo;
-    std::thread mThread;
-
-    void run();
-
-    DISALLOW_COPY_AND_ASSIGN(MoveTask);
-};
+void MoveStorage(const std::shared_ptr<VolumeBase>& from, const std::shared_ptr<VolumeBase>& to,
+        const android::sp<android::os::IVoldTaskListener>& listener);
 
 }  // namespace vold
 }  // namespace android
diff --git a/NetlinkHandler.cpp b/NetlinkHandler.cpp
index ecda2a0..92131e9 100644
--- a/NetlinkHandler.cpp
+++ b/NetlinkHandler.cpp
@@ -19,9 +19,7 @@
 #include <errno.h>
 #include <string.h>
 
-#define LOG_TAG "Vold"
-
-#include <cutils/log.h>
+#include <android-base/logging.h>
 
 #include <sysutils/NetlinkEvent.h>
 #include "NetlinkHandler.h"
@@ -47,11 +45,11 @@
     const char *subsys = evt->getSubsystem();
 
     if (!subsys) {
-        SLOGW("No subsystem found in netlink event");
+        LOG(WARNING) << "No subsystem found in netlink event";
         return;
     }
 
-    if (!strcmp(subsys, "block")) {
+    if (std::string(subsys) == "block") {
         vm->handleBlockEvent(evt);
     }
 }
diff --git a/NetlinkManager.cpp b/NetlinkManager.cpp
index 90e3c6c..409cdc8 100644
--- a/NetlinkManager.cpp
+++ b/NetlinkManager.cpp
@@ -26,9 +26,7 @@
 
 #include <linux/netlink.h>
 
-#define LOG_TAG "Vold"
-
-#include <cutils/log.h>
+#include <android-base/logging.h>
 
 #include "NetlinkManager.h"
 #include "NetlinkHandler.h"
@@ -60,7 +58,7 @@
 
     if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
             NETLINK_KOBJECT_UEVENT)) < 0) {
-        SLOGE("Unable to create uevent socket: %s", strerror(errno));
+        PLOG(ERROR) << "Unable to create uevent socket";
         return -1;
     }
 
@@ -69,23 +67,23 @@
     // Try using SO_RCVBUF if that fails.
     if ((setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) &&
         (setsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) < 0)) {
-        SLOGE("Unable to set uevent socket SO_RCVBUF/SO_RCVBUFFORCE option: %s", strerror(errno));
+        PLOG(ERROR) << "Unable to set uevent socket SO_RCVBUF/SO_RCVBUFFORCE option";
         goto out;
     }
 
     if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
-        SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
+        PLOG(ERROR) << "Unable to set uevent socket SO_PASSCRED option";
         goto out;
     }
 
     if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
-        SLOGE("Unable to bind uevent socket: %s", strerror(errno));
+        PLOG(ERROR) << "Unable to bind uevent socket";
         goto out;
     }
 
     mHandler = new NetlinkHandler(mSock);
     if (mHandler->start()) {
-        SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
+        PLOG(ERROR) << "Unable to start NetlinkHandler";
         goto out;
     }
 
@@ -100,7 +98,7 @@
     int status = 0;
 
     if (mHandler->stop()) {
-        SLOGE("Unable to stop NetlinkHandler: %s", strerror(errno));
+        PLOG(ERROR) << "Unable to stop NetlinkHandler";
         status = -1;
     }
     delete mHandler;
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..c8dbf77
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,5 @@
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/Process.cpp b/Process.cpp
index 1c0f504..042ba2d 100644
--- a/Process.cpp
+++ b/Process.cpp
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <string.h>
 #include <fcntl.h>
+#include <fts.h>
 #include <dirent.h>
 #include <ctype.h>
 #include <pwd.h>
@@ -27,196 +28,98 @@
 #include <sys/stat.h>
 #include <signal.h>
 
-#define LOG_TAG "ProcessKiller"
+#include <fstream>
+#include <unordered_set>
 
 #include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 #include <android-base/logging.h>
-#include <cutils/log.h>
 
 #include "Process.h"
 
-using android::base::ReadFileToString;
 using android::base::StringPrintf;
 
-int Process::readSymLink(const char *path, char *link, size_t max) {
-    struct stat s;
-    int length;
+namespace android {
+namespace vold {
 
-    if (lstat(path, &s) < 0)
-        return 0;
-    if ((s.st_mode & S_IFMT) != S_IFLNK)
-        return 0;
-
-    // we have a symlink
-    length = readlink(path, link, max- 1);
-    if (length <= 0)
-        return 0;
-    link[length] = 0;
-    return 1;
-}
-
-int Process::pathMatchesMountPoint(const char* path, const char* mountPoint) {
-    int length = strlen(mountPoint);
-    if (length > 1 && strncmp(path, mountPoint, length) == 0) {
-        // we need to do extra checking if mountPoint does not end in a '/'
-        if (mountPoint[length - 1] == '/')
-            return 1;
-        // if mountPoint does not have a trailing slash, we need to make sure
-        // there is one in the path to avoid partial matches.
-        return (path[length] == 0 || path[length] == '/');
-    }
-    
-    return 0;
-}
-
-void Process::getProcessName(int pid, std::string& out_name) {
-    if (!ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &out_name)) {
-        out_name = "???";
-    }
-}
-
-int Process::checkFileDescriptorSymLinks(int pid, const char *mountPoint) {
-    return checkFileDescriptorSymLinks(pid, mountPoint, NULL, 0);
-}
-
-int Process::checkFileDescriptorSymLinks(int pid, const char *mountPoint, char *openFilename, size_t max) {
-
-
-    // compute path to process's directory of open files
-    char    path[PATH_MAX];
-    snprintf(path, sizeof(path), "/proc/%d/fd", pid);
-    DIR *dir = opendir(path);
-    if (!dir)
-        return 0;
-
-    // remember length of the path
-    int parent_length = strlen(path);
-    // append a trailing '/'
-    path[parent_length++] = '/';
-
-    struct dirent* de;
-    while ((de = readdir(dir))) {
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")
-                || strlen(de->d_name) + parent_length + 1 >= PATH_MAX)
-            continue;
-        
-        // append the file name, after truncating to parent directory
-        path[parent_length] = 0;
-        strlcat(path, de->d_name, PATH_MAX);
-
-        char link[PATH_MAX];
-
-        if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint)) {
-            if (openFilename) {
-                memset(openFilename, 0, max);
-                strlcpy(openFilename, link, max);
+static bool checkMaps(const std::string& path, const std::string& prefix) {
+    bool found = false;
+    std::ifstream infile(path);
+    std::string line;
+    while (std::getline(infile, line)) {
+        std::string::size_type pos = line.find('/');
+        if (pos != std::string::npos) {
+            line = line.substr(pos);
+            if (android::base::StartsWith(line, prefix.c_str())) {
+                LOG(WARNING) << "Found map " << path << " referencing " << line;
+                found = true;
             }
-            closedir(dir);
-            return 1;
         }
     }
-
-    closedir(dir);
-    return 0;
+    return found;
 }
 
-int Process::checkFileMaps(int pid, const char *mountPoint) {
-    return checkFileMaps(pid, mountPoint, NULL, 0);
-}
-
-int Process::checkFileMaps(int pid, const char *mountPoint, char *openFilename, size_t max) {
-    FILE *file;
-    char buffer[PATH_MAX + 100];
-
-    snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
-    file = fopen(buffer, "re");
-    if (!file)
-        return 0;
-    
-    while (fgets(buffer, sizeof(buffer), file)) {
-        // skip to the path
-        const char* path = strchr(buffer, '/');
-        if (path && pathMatchesMountPoint(path, mountPoint)) {
-            if (openFilename) {
-                memset(openFilename, 0, max);
-                strlcpy(openFilename, path, max);
-            }
-            fclose(file);
-            return 1;
+static bool checkSymlink(const std::string& path, const std::string& prefix) {
+    std::string res;
+    if (android::base::Readlink(path, &res)) {
+        if (android::base::StartsWith(res, prefix.c_str())) {
+            LOG(WARNING) << "Found symlink " << path << " referencing " << res;
+            return true;
         }
     }
-    
-    fclose(file);
-    return 0;
+    return false;
 }
 
-int Process::checkSymLink(int pid, const char *mountPoint, const char *name) {
-    char    path[PATH_MAX];
-    char    link[PATH_MAX];
+int KillProcessesWithOpenFiles(const std::string& prefix, int signal) {
+    std::unordered_set<pid_t> pids;
 
-    snprintf(path, sizeof(path), "/proc/%d/%s", pid, name);
-    if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint)) 
-        return 1;
-    return 0;
-}
-
-int Process::getPid(const char *s) {
-    int result = 0;
-    while (*s) {
-        if (!isdigit(*s)) return -1;
-        result = 10 * result + (*s++ - '0');
-    }
-    return result;
-}
-
-extern "C" void vold_killProcessesWithOpenFiles(const char *path, int signal) {
-	Process::killProcessesWithOpenFiles(path, signal);
-}
-
-/*
- * Hunt down processes that have files open at the given mount point.
- */
-int Process::killProcessesWithOpenFiles(const char *path, int signal) {
-    int count = 0;
-    DIR* dir;
-    struct dirent* de;
-
-    if (!(dir = opendir("/proc"))) {
-        SLOGE("opendir failed (%s)", strerror(errno));
-        return count;
+    auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
+    if (!proc_d) {
+        PLOG(ERROR) << "Failed to open proc";
+        return -1;
     }
 
-    while ((de = readdir(dir))) {
-        int pid = getPid(de->d_name);
-        if (pid == -1)
-            continue;
+    struct dirent* proc_de;
+    while ((proc_de = readdir(proc_d.get())) != nullptr) {
+        // We only care about valid PIDs
+        pid_t pid;
+        if (proc_de->d_type != DT_DIR) continue;
+        if (!android::base::ParseInt(proc_de->d_name, &pid)) continue;
 
-        std::string name;
-        getProcessName(pid, name);
+        // Look for references to prefix
+        bool found = false;
+        auto path = StringPrintf("/proc/%d", pid);
+        found |= checkMaps(path + "/maps", prefix);
+        found |= checkSymlink(path + "/cwd", prefix);
+        found |= checkSymlink(path + "/root", prefix);
+        found |= checkSymlink(path + "/exe", prefix);
 
-        char openfile[PATH_MAX];
-
-        if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) {
-            SLOGE("Process %s (%d) has open file %s", name.c_str(), pid, openfile);
-        } else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) {
-            SLOGE("Process %s (%d) has open filemap for %s", name.c_str(), pid, openfile);
-        } else if (checkSymLink(pid, path, "cwd")) {
-            SLOGE("Process %s (%d) has cwd within %s", name.c_str(), pid, path);
-        } else if (checkSymLink(pid, path, "root")) {
-            SLOGE("Process %s (%d) has chroot within %s", name.c_str(), pid, path);
-        } else if (checkSymLink(pid, path, "exe")) {
-            SLOGE("Process %s (%d) has executable path within %s", name.c_str(), pid, path);
+        auto fd_path = path + "/fd";
+        auto fd_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(fd_path.c_str()), closedir);
+        if (!fd_d) {
+            PLOG(WARNING) << "Failed to open " << fd_path;
         } else {
-            continue;
+            struct dirent* fd_de;
+            while ((fd_de = readdir(fd_d.get())) != nullptr) {
+                if (fd_de->d_type != DT_LNK) continue;
+                found |= checkSymlink(fd_path + "/" + fd_de->d_name, prefix);
+            }
         }
 
-        if (signal != 0) {
-            SLOGW("Sending %s to process %d", strsignal(signal), pid);
-            kill(pid, signal);
-            count++;
+        if (found) {
+            pids.insert(pid);
         }
     }
-    closedir(dir);
-    return count;
+    if (signal != 0) {
+        for (const auto& pid : pids) {
+            LOG(WARNING) << "Sending " << strsignal(signal) << " to " << pid;
+            kill(pid, signal);
+        }
+    }
+    return pids.size();
 }
+
+}  // namespace vold
+}  // namespace android
diff --git a/Process.h b/Process.h
index 4ddbdb9..1406782 100644
--- a/Process.h
+++ b/Process.h
@@ -17,28 +17,12 @@
 #ifndef _PROCESS_H
 #define _PROCESS_H
 
-#ifdef __cplusplus
+namespace android {
+namespace vold {
 
-class Process {
-public:
-    static int killProcessesWithOpenFiles(const char *path, int signal);
-    static int getPid(const char *s);
-    static int checkSymLink(int pid, const char *path, const char *name);
-    static int checkFileMaps(int pid, const char *path);
-    static int checkFileMaps(int pid, const char *path, char *openFilename, size_t max);
-    static int checkFileDescriptorSymLinks(int pid, const char *mountPoint);
-    static int checkFileDescriptorSymLinks(int pid, const char *mountPoint, char *openFilename, size_t max);
-    static void getProcessName(int pid, std::string& out_name);
-private:
-    static int readSymLink(const char *path, char *link, size_t max);
-    static int pathMatchesMountPoint(const char *path, const char *mountPoint);
-};
+int KillProcessesWithOpenFiles(const std::string& path, int signal);
 
-extern "C" {
-#endif /* __cplusplus */
-	void vold_killProcessesWithOpenFiles(const char *path, int signal);
-#ifdef __cplusplus
-}
-#endif
+}  // namespace vold
+}  // namespace android
 
 #endif
diff --git a/ResponseCode.cpp b/ResponseCode.cpp
deleted file mode 100644
index d7e778d..0000000
--- a/ResponseCode.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-
-#define LOG_TAG "Vold"
-
-#include <cutils/log.h>
-
-#include "ResponseCode.h"
-
-int ResponseCode::convertFromErrno() {
-   if (errno == ENODEV) {
-        return(ResponseCode::OpFailedNoMedia);
-    } else if (errno == ENODATA) {
-        return(ResponseCode::OpFailedMediaBlank);
-    } else if (errno == EIO) {
-        return(ResponseCode::OpFailedMediaCorrupt);
-    } else if (errno == EBUSY) {
-        return(ResponseCode::OpFailedStorageBusy);
-    } else if (errno == ENOENT) {
-        return(ResponseCode::OpFailedStorageNotFound);
-    }
-
-    SLOGW("Returning OperationFailed - no handler for errno %d", errno);
-    return(ResponseCode::OperationFailed);
-}
diff --git a/ResponseCode.h b/ResponseCode.h
deleted file mode 100644
index f2c533e..0000000
--- a/ResponseCode.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _RESPONSECODE_H
-#define _RESPONSECODE_H
-
-class ResponseCode {
-public:
-    // 100 series - Requestion action was initiated; expect another reply
-    // before proceeding with a new command.
-    static const int ActionInitiated  = 100;
-
-    static const int VolumeListResult         = 110;
-    static const int AsecListResult           = 111;
-    static const int StorageUsersListResult   = 112;
-    static const int CryptfsGetfieldResult    = 113;
-
-    // 200 series - Requested action has been successfully completed
-    static const int CommandOkay              = 200;
-    static const int ShareStatusResult        = 210;
-    static const int AsecPathResult           = 211;
-    static const int ShareEnabledResult       = 212;
-    static const int PasswordTypeResult       = 213;
-
-    // 400 series - The command was accepted but the requested action
-    // did not take place.
-    static const int OperationFailed          = 400;
-    static const int OpFailedNoMedia          = 401;
-    static const int OpFailedMediaBlank       = 402;
-    static const int OpFailedMediaCorrupt     = 403;
-    static const int OpFailedVolNotMounted    = 404;
-    static const int OpFailedStorageBusy      = 405;
-    static const int OpFailedStorageNotFound  = 406;
-
-    // 500 series - The command was not accepted and the requested
-    // action did not take place.
-    static const int CommandSyntaxError = 500;
-    static const int CommandParameterError = 501;
-    static const int CommandNoPermission = 502;
-
-    // 600 series - Unsolicited broadcasts
-    static const int UnsolicitedInformational       = 600;
-    static const int VolumeStateChange              = 605;
-    static const int VolumeMountFailedBlank         = 610;
-    static const int VolumeMountFailedDamaged       = 611;
-    static const int VolumeMountFailedNoMedia       = 612;
-    static const int VolumeUuidChange               = 613;
-    static const int VolumeUserLabelChange          = 614;
-
-    static const int ShareAvailabilityChange        = 620;
-
-    static const int VolumeDiskInserted            = 630;
-    static const int VolumeDiskRemoved             = 631;
-    static const int VolumeBadRemoval              = 632;
-
-    static const int DiskCreated = 640;
-    static const int DiskSizeChanged = 641;
-    static const int DiskLabelChanged = 642;
-    static const int DiskScanned = 643;
-    static const int DiskSysPathChanged = 644;
-    static const int DiskDestroyed = 649;
-
-    static const int VolumeCreated = 650;
-    static const int VolumeStateChanged = 651;
-    static const int VolumeFsTypeChanged = 652;
-    static const int VolumeFsUuidChanged = 653;
-    static const int VolumeFsLabelChanged = 654;
-    static const int VolumePathChanged = 655;
-    static const int VolumeInternalPathChanged = 656;
-    static const int VolumeDestroyed = 659;
-
-    static const int MoveStatus = 660;
-    static const int BenchmarkResult = 661;
-    static const int TrimResult = 662;
-
-    static int convertFromErrno();
-};
-#endif
diff --git a/ScryptParameters.h b/ScryptParameters.h
index 1b43ea5..190842b 100644
--- a/ScryptParameters.h
+++ b/ScryptParameters.h
@@ -23,10 +23,6 @@
 #define SCRYPT_PROP "ro.crypto.scrypt_params"
 #define SCRYPT_DEFAULTS "15:3:1"
 
-__BEGIN_DECLS
-
 bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf);
 
-__END_DECLS
-
 #endif
diff --git a/TrimTask.h b/TrimTask.h
deleted file mode 100644
index 2ade7b5..0000000
--- a/TrimTask.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2015 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_TRIM_TASK_H
-#define ANDROID_VOLD_TRIM_TASK_H
-
-#include "Utils.h"
-
-#include <thread>
-#include <list>
-
-namespace android {
-namespace vold {
-
-class TrimTask {
-public:
-    explicit TrimTask(int flags);
-    virtual ~TrimTask();
-
-    enum Flags {
-        kDeepTrim = 1 << 0,
-        kBenchmarkAfter = 1 << 1,
-    };
-
-    void start();
-
-private:
-    int mFlags;
-    std::list<std::string> mPaths;
-    std::thread mThread;
-
-    void addFromFstab();
-    void run();
-
-    DISALLOW_COPY_AND_ASSIGN(TrimTask);
-};
-
-}  // namespace vold
-}  // namespace android
-
-#endif
diff --git a/Utils.cpp b/Utils.cpp
index b6c7bf8..9c19190 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -22,6 +22,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 #include <cutils/fs.h>
 #include <logwrap/logwrap.h>
@@ -59,7 +60,12 @@
 
 static const char* kProcFilesystems = "/proc/filesystems";
 
+// Lock used to protect process-level SELinux changes from racing with each
+// other between multiple threads.
+static std::mutex kSecurityLock;
+
 status_t CreateDeviceNode(const std::string& path, dev_t dev) {
+    std::lock_guard<std::mutex> lock(kSecurityLock);
     const char* cpath = path.c_str();
     status_t res = 0;
 
@@ -97,6 +103,7 @@
 }
 
 status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+    std::lock_guard<std::mutex> lock(kSecurityLock);
     const char* cpath = path.c_str();
 
     char* secontext = nullptr;
@@ -129,19 +136,19 @@
     // we start sending signals
     if (!VolumeManager::shutting_down) sleep(5);
 
-    Process::killProcessesWithOpenFiles(cpath, SIGINT);
+    KillProcessesWithOpenFiles(path, SIGINT);
     if (!VolumeManager::shutting_down) sleep(5);
     if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
         return OK;
     }
 
-    Process::killProcessesWithOpenFiles(cpath, SIGTERM);
+    KillProcessesWithOpenFiles(path, SIGTERM);
     if (!VolumeManager::shutting_down) sleep(5);
     if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
         return OK;
     }
 
-    Process::killProcessesWithOpenFiles(cpath, SIGKILL);
+    KillProcessesWithOpenFiles(path, SIGKILL);
     if (!VolumeManager::shutting_down) sleep(5);
     if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
         return OK;
@@ -151,25 +158,24 @@
 }
 
 status_t KillProcessesUsingPath(const std::string& path) {
-    const char* cpath = path.c_str();
-    if (Process::killProcessesWithOpenFiles(cpath, SIGINT) == 0) {
+    if (KillProcessesWithOpenFiles(path, SIGINT) == 0) {
         return OK;
     }
     if (!VolumeManager::shutting_down) sleep(5);
 
-    if (Process::killProcessesWithOpenFiles(cpath, SIGTERM) == 0) {
+    if (KillProcessesWithOpenFiles(path, SIGTERM) == 0) {
         return OK;
     }
     if (!VolumeManager::shutting_down) sleep(5);
 
-    if (Process::killProcessesWithOpenFiles(cpath, SIGKILL) == 0) {
+    if (KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
         return OK;
     }
     if (!VolumeManager::shutting_down) sleep(5);
 
     // Send SIGKILL a second time to determine if we've
     // actually killed everyone with open files
-    if (Process::killProcessesWithOpenFiles(cpath, SIGKILL) == 0) {
+    if (KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
         return OK;
     }
     PLOG(ERROR) << "Failed to kill processes using " << path;
@@ -184,11 +190,28 @@
     return OK;
 }
 
-static status_t readMetadata(const std::string& path, std::string& fsType,
-        std::string& fsUuid, std::string& fsLabel, bool untrusted) {
-    fsType.clear();
-    fsUuid.clear();
-    fsLabel.clear();
+bool FindValue(const std::string& raw, const std::string& key, std::string* value) {
+    auto qual = key + "=\"";
+    auto start = raw.find(qual);
+    if (start > 0 && raw[start - 1] != ' ') {
+        start = raw.find(qual, start + 1);
+    }
+
+    if (start == std::string::npos) return false;
+    start += qual.length();
+
+    auto end = raw.find("\"", start);
+    if (end == std::string::npos) return false;
+
+    *value = raw.substr(start, end - start);
+    return true;
+}
+
+static status_t readMetadata(const std::string& path, std::string* fsType,
+        std::string* fsUuid, std::string* fsLabel, bool untrusted) {
+    fsType->clear();
+    fsUuid->clear();
+    fsLabel->clear();
 
     std::vector<std::string> cmd;
     cmd.push_back(kBlkidPath);
@@ -209,36 +232,23 @@
         return res;
     }
 
-    char value[128];
     for (const auto& line : output) {
         // Extract values from blkid output, if defined
-        const char* cline = line.c_str();
-        const char* start = strstr(cline, "TYPE=");
-        if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
-            fsType = value;
-        }
-
-        start = strstr(cline, "UUID=");
-        if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
-            fsUuid = value;
-        }
-
-        start = strstr(cline, "LABEL=");
-        if (start != nullptr && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
-            fsLabel = value;
-        }
+        FindValue(line, "TYPE", fsType);
+        FindValue(line, "UUID", fsUuid);
+        FindValue(line, "LABEL", fsLabel);
     }
 
     return OK;
 }
 
-status_t ReadMetadata(const std::string& path, std::string& fsType,
-        std::string& fsUuid, std::string& fsLabel) {
+status_t ReadMetadata(const std::string& path, std::string* fsType,
+        std::string* fsUuid, std::string* fsLabel) {
     return readMetadata(path, fsType, fsUuid, fsLabel, false);
 }
 
-status_t ReadMetadataUntrusted(const std::string& path, std::string& fsType,
-        std::string& fsUuid, std::string& fsLabel) {
+status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType,
+        std::string* fsUuid, std::string* fsLabel) {
     return readMetadata(path, fsType, fsUuid, fsLabel, true);
 }
 
@@ -247,6 +257,7 @@
 }
 
 status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context) {
+    std::lock_guard<std::mutex> lock(kSecurityLock);
     size_t argc = args.size();
     char** argv = (char**) calloc(argc, sizeof(char*));
     for (size_t i = 0; i < argc; i++) {
@@ -279,6 +290,7 @@
 
 status_t ForkExecvp(const std::vector<std::string>& args,
         std::vector<std::string>& output, security_context_t context) {
+    std::lock_guard<std::mutex> lock(kSecurityLock);
     std::string cmd;
     for (size_t i = 0; i < args.size(); i++) {
         cmd += args[i] + " ";
@@ -585,54 +597,54 @@
 }
 
 std::string BuildDataSystemLegacyPath(userid_t userId) {
-    return StringPrintf("%s/system/users/%u", BuildDataPath(nullptr).c_str(), userId);
+    return StringPrintf("%s/system/users/%u", BuildDataPath("").c_str(), userId);
 }
 
 std::string BuildDataSystemCePath(userid_t userId) {
-    return StringPrintf("%s/system_ce/%u", BuildDataPath(nullptr).c_str(), userId);
+    return StringPrintf("%s/system_ce/%u", BuildDataPath("").c_str(), userId);
 }
 
 std::string BuildDataSystemDePath(userid_t userId) {
-    return StringPrintf("%s/system_de/%u", BuildDataPath(nullptr).c_str(), userId);
+    return StringPrintf("%s/system_de/%u", BuildDataPath("").c_str(), userId);
 }
 
 std::string BuildDataMiscLegacyPath(userid_t userId) {
-    return StringPrintf("%s/misc/user/%u", BuildDataPath(nullptr).c_str(), userId);
+    return StringPrintf("%s/misc/user/%u", BuildDataPath("").c_str(), userId);
 }
 
 std::string BuildDataMiscCePath(userid_t userId) {
-    return StringPrintf("%s/misc_ce/%u", BuildDataPath(nullptr).c_str(), userId);
+    return StringPrintf("%s/misc_ce/%u", BuildDataPath("").c_str(), userId);
 }
 
 std::string BuildDataMiscDePath(userid_t userId) {
-    return StringPrintf("%s/misc_de/%u", BuildDataPath(nullptr).c_str(), userId);
+    return StringPrintf("%s/misc_de/%u", BuildDataPath("").c_str(), userId);
 }
 
 // Keep in sync with installd (frameworks/native/cmds/installd/utils.h)
 std::string BuildDataProfilesDePath(userid_t userId) {
-    return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath(nullptr).c_str(), userId);
+    return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath("").c_str(), userId);
 }
 
-std::string BuildDataPath(const char* volumeUuid) {
+std::string BuildDataPath(const std::string& volumeUuid) {
     // TODO: unify with installd path generation logic
-    if (volumeUuid == nullptr) {
+    if (volumeUuid.empty()) {
         return "/data";
     } else {
         CHECK(isValidFilename(volumeUuid));
-        return StringPrintf("/mnt/expand/%s", volumeUuid);
+        return StringPrintf("/mnt/expand/%s", volumeUuid.c_str());
     }
 }
 
-std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userId) {
+std::string BuildDataMediaCePath(const std::string& volumeUuid, userid_t userId) {
     // TODO: unify with installd path generation logic
     std::string data(BuildDataPath(volumeUuid));
     return StringPrintf("%s/media/%u", data.c_str(), userId);
 }
 
-std::string BuildDataUserCePath(const char* volumeUuid, userid_t userId) {
+std::string BuildDataUserCePath(const std::string& volumeUuid, userid_t userId) {
     // TODO: unify with installd path generation logic
     std::string data(BuildDataPath(volumeUuid));
-    if (volumeUuid == nullptr && userId == 0) {
+    if (volumeUuid.empty() && userId == 0) {
         std::string legacy = StringPrintf("%s/data", data.c_str());
         struct stat sb;
         if (lstat(legacy.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) {
@@ -643,7 +655,7 @@
     return StringPrintf("%s/user/%u", data.c_str(), userId);
 }
 
-std::string BuildDataUserDePath(const char* volumeUuid, userid_t userId) {
+std::string BuildDataUserDePath(const std::string& volumeUuid, userid_t userId) {
     // TODO: unify with installd path generation logic
     std::string data(BuildDataPath(volumeUuid));
     return StringPrintf("%s/user_de/%u", data.c_str(), userId);
@@ -673,15 +685,27 @@
     return OK;
 }
 
-status_t SaneReadLinkAt(int dirfd, const char* path, char* buf, size_t bufsiz) {
-    ssize_t len = readlinkat(dirfd, path, buf, bufsiz);
-    if (len < 0) {
-        return -1;
-    } else if (len == (ssize_t) bufsiz) {
-        return -1;
-    } else {
-        buf[len] = '\0';
-        return 0;
+bool Readlinkat(int dirfd, const std::string& path, std::string* result) {
+    // Shamelessly borrowed from android::base::Readlink()
+    result->clear();
+
+    // Most Linux file systems (ext2 and ext4, say) limit symbolic links to
+    // 4095 bytes. Since we'll copy out into the string anyway, it doesn't
+    // waste memory to just start there. We add 1 so that we can recognize
+    // whether it actually fit (rather than being truncated to 4095).
+    std::vector<char> buf(4095 + 1);
+    while (true) {
+        ssize_t size = readlinkat(dirfd, path.c_str(), &buf[0], buf.size());
+        // Unrecoverable error?
+        if (size == -1)
+            return false;
+        // It fit! (If size == buf.size(), it may have been truncated.)
+        if (static_cast<size_t>(size) < buf.size()) {
+            result->assign(&buf[0], size);
+            return true;
+        }
+        // Double our buffer and try again.
+        buf.resize(buf.size() * 2);
     }
 }
 
diff --git a/Utils.h b/Utils.h
index 82ac440..9163006 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,14 +27,6 @@
 #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;
 
 namespace android {
@@ -60,13 +53,15 @@
 /* Creates bind mount from source to target */
 status_t BindMount(const std::string& source, const std::string& target);
 
+bool FindValue(const std::string& raw, const std::string& key, std::string* value);
+
 /* Reads filesystem metadata from device at path */
-status_t ReadMetadata(const std::string& path, std::string& fsType,
-        std::string& fsUuid, std::string& fsLabel);
+status_t ReadMetadata(const std::string& path, std::string* fsType,
+        std::string* fsUuid, std::string* fsLabel);
 
 /* Reads filesystem metadata from untrusted device at path */
-status_t ReadMetadataUntrusted(const std::string& path, std::string& fsType,
-        std::string& fsUuid, std::string& fsLabel);
+status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType,
+        std::string* fsUuid, std::string* fsLabel);
 
 /* Returns either WEXITSTATUS() status, or a negative errno */
 status_t ForkExecvp(const std::vector<std::string>& args);
@@ -110,16 +105,17 @@
 std::string BuildDataMiscDePath(userid_t userid);
 std::string BuildDataProfilesDePath(userid_t userid);
 
-std::string BuildDataPath(const char* volumeUuid);
-std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userid);
-std::string BuildDataUserCePath(const char* volumeUuid, userid_t userid);
-std::string BuildDataUserDePath(const char* volumeUuid, userid_t userid);
+std::string BuildDataPath(const std::string& volumeUuid);
+std::string BuildDataMediaCePath(const std::string& volumeUuid, userid_t userid);
+std::string BuildDataUserCePath(const std::string& volumeUuid, userid_t userid);
+std::string BuildDataUserDePath(const std::string& volumeUuid, userid_t userid);
 
 dev_t GetDevice(const std::string& path);
 
 status_t RestoreconRecursive(const std::string& path);
 
-status_t SaneReadLinkAt(int dirfd, const char* path, char* buf, size_t bufsiz);
+// TODO: promote to android::base
+bool Readlinkat(int dirfd, const std::string& path, std::string* result);
 
 /* Checks if Android is running in QEMU */
 bool IsRunningInEmulator();
diff --git a/VoldCommand.cpp b/VoldCommand.cpp
deleted file mode 100644
index 3c0d58d..0000000
--- a/VoldCommand.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "VoldCommand.h"
-
-VoldCommand::VoldCommand(const char *cmd) :
-              FrameworkCommand(cmd)  {
-}
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
new file mode 100644
index 0000000..d8832d3
--- /dev/null
+++ b/VoldNativeService.cpp
@@ -0,0 +1,734 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
+#include "VoldNativeService.h"
+#include "VolumeManager.h"
+#include "Benchmark.h"
+#include "MoveStorage.h"
+#include "Process.h"
+#include "IdleMaint.h"
+
+#include "cryptfs.h"
+#include "Ext4Crypt.h"
+#include "MetadataCrypt.h"
+
+#include <fstream>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <ext4_utils/ext4_crypt.h>
+#include <fs_mgr.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Trace.h>
+
+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()));
+    }
+    if ((path + '/').find("/../") != std::string::npos) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                StringPrintf("Path %s is shady", 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()); \
+    ATRACE_CALL();
+
+#define ACQUIRE_CRYPT_LOCK \
+    std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getCryptLock()); \
+    ATRACE_CALL();
+
+}  // 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::monitor() {
+    ENFORCE_UID(AID_SYSTEM);
+
+    // Simply acquire/release each lock for watchdog
+    {
+        ACQUIRE_LOCK;
+    }
+    {
+        ACQUIRE_CRYPT_LOCK;
+    }
+
+    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,
+        const android::sp<android::os::IVoldTaskListener>& listener) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_ID(volId);
+    ACQUIRE_LOCK;
+
+    std::string path;
+    if (volId == "private" || volId == "null") {
+        path = "/data";
+    } else {
+        auto vol = VolumeManager::Instance()->findVolume(volId);
+        if (vol == nullptr) {
+            return error("Failed to find volume " + volId);
+        }
+        if (vol->getType() != VolumeBase::Type::kPrivate) {
+            return error("Volume " + volId + " not private");
+        }
+        if (vol->getState() != VolumeBase::State::kMounted) {
+            return error("Volume " + volId + " not mounted");
+        }
+        path = vol->getPath();
+    }
+
+    if (path.empty()) {
+        return error("Volume " + volId + " missing path");
+    }
+
+    std::thread([=]() {
+        android::vold::Benchmark(path, listener);
+    }).detach();
+    return ok();
+}
+
+binder::Status VoldNativeService::moveStorage(const std::string& fromVolId,
+        const std::string& toVolId, const android::sp<android::os::IVoldTaskListener>& listener) {
+    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);
+    }
+
+    std::thread([=]() {
+        android::vold::MoveStorage(fromVol, toVol, listener);
+    }).detach();
+    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));
+}
+
+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,
+        const android::sp<android::os::IVoldTaskListener>& listener) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    std::thread([=]() {
+        android::vold::Trim(listener);
+    }).detach();
+    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) {
+            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, secret));
+}
+
+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, secret));
+}
+
+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);
+    std::string empty_string = "";
+    auto uuid_ = uuid ? *uuid : empty_string;
+    CHECK_ARGUMENT_HEX(uuid_);
+
+    ACQUIRE_CRYPT_LOCK;
+    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);
+    std::string empty_string = "";
+    auto uuid_ = uuid ? *uuid : empty_string;
+    CHECK_ARGUMENT_HEX(uuid_);
+
+    ACQUIRE_CRYPT_LOCK;
+    return translateBool(e4crypt_destroy_user_storage(uuid_, userId, flags));
+}
+
+binder::Status VoldNativeService::secdiscard(const std::string& path) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PATH(path);
+    ACQUIRE_CRYPT_LOCK;
+
+    return translateBool(e4crypt_secdiscard(path));
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/VoldNativeService.h b/VoldNativeService.h
new file mode 100644
index 0000000..dd2b854
--- /dev/null
+++ b/VoldNativeService.h
@@ -0,0 +1,115 @@
+/*
+ * 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 monitor();
+    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,
+            const android::sp<android::os::IVoldTaskListener>& listener);
+
+    binder::Status moveStorage(const std::string& fromVolId, const std::string& toVolId,
+            const android::sp<android::os::IVoldTaskListener>& listener);
+
+    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,
+            const android::sp<android::os::IVoldTaskListener>& listener);
+
+    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/VoldUtil.c b/VoldUtil.cpp
similarity index 95%
rename from VoldUtil.c
rename to VoldUtil.cpp
index e5bc912..afe8b53 100644
--- a/VoldUtil.c
+++ b/VoldUtil.cpp
@@ -17,6 +17,8 @@
 #include <sys/ioctl.h>
 #include <linux/fs.h>
 
+struct fstab *fstab_default;
+
 void get_blkdev_size(int fd, unsigned long* nr_sec) {
   if ((ioctl(fd, BLKGETSIZE, nr_sec)) == -1) {
     *nr_sec = 0;
diff --git a/VoldUtil.h b/VoldUtil.h
index 5738382..fd66672 100644
--- a/VoldUtil.h
+++ b/VoldUtil.h
@@ -17,12 +17,13 @@
 #ifndef _VOLDUTIL_H
 #define _VOLDUTIL_H
 
+#include <fstab/fstab.h>
 #include <sys/cdefs.h>
 
+extern struct fstab *fstab_default;
+
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
-__BEGIN_DECLS
 void get_blkdev_size(int fd, unsigned long* nr_sec);
-__END_DECLS
 
 #endif
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 13a943f..c1d51d9 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <fts.h>
 #include <mntent.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -32,14 +33,12 @@
 
 #include <linux/kdev_t.h>
 
-#define LOG_TAG "Vold"
-
-#include <openssl/md5.h>
-
 #include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 #include <cutils/fs.h>
-#include <cutils/log.h>
+#include <utils/Trace.h>
 
 #include <selinux/android.h>
 
@@ -47,47 +46,21 @@
 
 #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"
 #include "Loop.h"
 #include "fs/Ext4.h"
 #include "fs/Vfat.h"
 #include "Utils.h"
 #include "Devmapper.h"
 #include "Process.h"
-#include "Asec.h"
 #include "VoldUtil.h"
 #include "cryptfs.h"
 
-#define MASS_STORAGE_FILE_PATH  "/sys/class/android_usb/android0/f_mass_storage/lun/file"
-
-#define ROUND_UP_POWER_OF_2(number, po2) (((!!((number) & ((1U << (po2)) - 1))) << (po2))\
-                                         + ((number) & (~((1U << (po2)) - 1))))
-
 using android::base::StringPrintf;
-
-/*
- * Path to external storage where *only* root can access ASEC image files
- */
-const char *VolumeManager::SEC_ASECDIR_EXT   = "/mnt/secure/asec";
-
-/*
- * Path to internal storage where *only* root can access ASEC image files
- */
-const char *VolumeManager::SEC_ASECDIR_INT   = "/data/app-asec";
-
-/*
- * Path to where secure containers are mounted
- */
-const char *VolumeManager::ASECDIR           = "/mnt/asec";
-
-/*
- * Path to where OBBs are mounted
- */
-const char *VolumeManager::LOOPDIR           = "/mnt/obb";
+using android::base::unique_fd;
 
 bool VolumeManager::shutting_down = false;
 
@@ -103,107 +76,6 @@
 static const unsigned int kMajorBlockExperimentalMin = 240;
 static const unsigned int kMajorBlockExperimentalMax = 254;
 
-/* writes superblock at end of file or device given by name */
-static int writeSuperBlock(const char* name, struct asec_superblock *sb, unsigned int numImgSectors) {
-    int sbfd = open(name, O_RDWR | O_CLOEXEC);
-    if (sbfd < 0) {
-        SLOGE("Failed to open %s for superblock write (%s)", name, strerror(errno));
-        return -1;
-    }
-
-    if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) {
-        SLOGE("Failed to lseek for superblock (%s)", strerror(errno));
-        close(sbfd);
-        return -1;
-    }
-
-    if (write(sbfd, sb, sizeof(struct asec_superblock)) != sizeof(struct asec_superblock)) {
-        SLOGE("Failed to write superblock (%s)", strerror(errno));
-        close(sbfd);
-        return -1;
-    }
-    close(sbfd);
-    return 0;
-}
-
-static unsigned long adjustSectorNumExt4(unsigned long numSectors) {
-    // Ext4 started to reserve 2% or 4096 clusters, whichever is smaller for
-    // preventing costly operations or unexpected ENOSPC error.
-    // Ext4::format() uses default block size without clustering.
-    unsigned long clusterSectors = 4096 / 512;
-    unsigned long reservedSectors = (numSectors * 2)/100 + (numSectors % 50 > 0);
-    numSectors += reservedSectors > (4096 * clusterSectors) ? (4096 * clusterSectors) : reservedSectors;
-    return ROUND_UP_POWER_OF_2(numSectors, 3);
-}
-
-static unsigned long adjustSectorNumFAT(unsigned long numSectors) {
-    /*
-    * Add some headroom
-    */
-    unsigned long fatSize = (((numSectors * 4) / 512) + 1) * 2;
-    numSectors += fatSize + 2;
-    /*
-    * FAT is aligned to 32 kb with 512b sectors.
-    */
-    return ROUND_UP_POWER_OF_2(numSectors, 6);
-}
-
-static int setupLoopDevice(char* buffer, size_t len, const char* asecFileName, const char* idHash, bool debug) {
-    if (Loop::lookupActive(idHash, buffer, len)) {
-        if (Loop::create(idHash, asecFileName, buffer, len)) {
-            SLOGE("ASEC loop device creation failed for %s (%s)", asecFileName, strerror(errno));
-            return -1;
-        }
-        if (debug) {
-            SLOGD("New loop device created at %s", buffer);
-        }
-    } else {
-        if (debug) {
-            SLOGD("Found active loopback for %s at %s", asecFileName, buffer);
-        }
-    }
-    return 0;
-}
-
-static int setupDevMapperDevice(char* buffer, size_t len, const char* loopDevice, const char* asecFileName, const char* key, const char* idHash , unsigned long numImgSectors, bool* createdDMDevice, bool debug) {
-    if (strcmp(key, "none")) {
-        if (Devmapper::lookupActive(idHash, buffer, len)) {
-            if (Devmapper::create(idHash, loopDevice, key, numImgSectors,
-                                  buffer, len)) {
-                SLOGE("ASEC device mapping failed for %s (%s)", asecFileName, strerror(errno));
-                return -1;
-            }
-            if (debug) {
-                SLOGD("New devmapper instance created at %s", buffer);
-            }
-        } else {
-            if (debug) {
-                SLOGD("Found active devmapper for %s at %s", asecFileName, buffer);
-            }
-        }
-        *createdDMDevice = true;
-    } else {
-        strlcpy(buffer, loopDevice, len);
-        *createdDMDevice = false;
-    }
-    return 0;
-}
-
-static void waitForDevMapper(const char *dmDevice) {
-    /*
-     * Wait for the device mapper node to be created. Sometimes it takes a
-     * while. Wait for up to 1 second. We could also inspect incoming uevents,
-     * but that would take more effort.
-     */
-    int tries = 25;
-    while (tries--) {
-        if (!access(dmDevice, F_OK) || errno != ENOENT) {
-            break;
-        }
-        usleep(40 * 1000);
-    }
-}
-
 VolumeManager *VolumeManager::sInstance = NULL;
 
 VolumeManager *VolumeManager::Instance() {
@@ -214,52 +86,15 @@
 
 VolumeManager::VolumeManager() {
     mDebug = false;
-    mActiveContainers = new AsecIdCollection();
-    mBroadcaster = NULL;
-    mUmsSharingCount = 0;
-    mSavedDirtyRatio = -1;
-    // set dirty ratio to 0 when UMS is active
-    mUmsDirtyRatio = 0;
+    mNextObbId = 0;
 }
 
 VolumeManager::~VolumeManager() {
-    delete mActiveContainers;
-}
-
-char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) {
-    static const char* digits = "0123456789abcdef";
-
-    unsigned char sig[MD5_DIGEST_LENGTH];
-
-    if (buffer == NULL) {
-        SLOGE("Destination buffer is NULL");
-        errno = ESPIPE;
-        return NULL;
-    } else if (id == NULL) {
-        SLOGE("Source buffer is NULL");
-        errno = ESPIPE;
-        return NULL;
-    } else if (len < MD5_ASCII_LENGTH_PLUS_NULL) {
-        SLOGE("Target hash buffer size < %d bytes (%zu)",
-                MD5_ASCII_LENGTH_PLUS_NULL, len);
-        errno = ESPIPE;
-        return NULL;
-    }
-
-    MD5(reinterpret_cast<const unsigned char*>(id), strlen(id), sig);
-
-    char *p = buffer;
-    for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
-        *p++ = digits[sig[i] >> 4];
-        *p++ = digits[sig[i] & 0x0F];
-    }
-    *p = '\0';
-
-    return buffer;
 }
 
 int VolumeManager::updateVirtualDisk() {
-    if (property_get_bool(kPropVirtualDisk, false)) {
+    ATRACE_NAME("VolumeManager::updateVirtualDisk");
+    if (android::base::GetBoolProperty(kPropVirtualDisk, false)) {
         if (access(kPathVirtualDisk, F_OK) != 0) {
             Loop::createImageFile(kPathVirtualDisk, kSizeVirtualDisk / 512);
         }
@@ -313,10 +148,15 @@
 }
 
 int VolumeManager::start() {
+    ATRACE_NAME("VolumeManager::start");
+
     // Always start from a clean slate by unmounting everything in
     // 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);
@@ -351,8 +191,8 @@
 
     if (devType != "disk") return;
 
-    int major = atoi(evt->findParam("MAJOR"));
-    int minor = atoi(evt->findParam("MINOR"));
+    int major = std::stoi(evt->findParam("MAJOR"));
+    int minor = std::stoi(evt->findParam("MINOR"));
     dev_t device = makedev(major, minor);
 
     switch (evt->getAction()) {
@@ -437,6 +277,11 @@
             return vol;
         }
     }
+    for (const auto& vol : mObbVolumes) {
+        if (vol->getId() == id) {
+            return vol;
+        }
+    }
     return nullptr;
 }
 
@@ -448,25 +293,6 @@
     }
 }
 
-nsecs_t VolumeManager::benchmarkPrivate(const std::string& id) {
-    std::string path;
-    if (id == "private" || id == "null") {
-        path = "/data";
-    } else {
-        auto vol = findVolume(id);
-        if (vol != nullptr && vol->getState() == android::vold::VolumeBase::State::kMounted) {
-            path = vol->getPath();
-        }
-    }
-
-    if (path.empty()) {
-        LOG(WARNING) << "Failed to find volume for " << id;
-        return -1;
-    }
-
-    return android::vold::BenchmarkPrivate(path);
-}
-
 int VolumeManager::forgetPartition(const std::string& partGuid) {
     std::string normalizedGuid;
     if (android::vold::NormalizeHex(partGuid, normalizedGuid)) {
@@ -493,13 +319,12 @@
     std::string target(StringPrintf("/mnt/user/%d/primary", userId));
     if (TEMP_FAILURE_RETRY(unlink(target.c_str()))) {
         if (errno != ENOENT) {
-            SLOGW("Failed to unlink %s: %s", target.c_str(), strerror(errno));
+            PLOG(WARNING) << "Failed to unlink " << target;
         }
     }
     LOG(DEBUG) << "Linking " << source << " to " << target;
     if (TEMP_FAILURE_RETRY(symlink(source.c_str(), target.c_str()))) {
-        SLOGW("Failed to link %s to %s: %s", source.c_str(), target.c_str(),
-                strerror(errno));
+        PLOG(WARNING) << "Failed to link";
         return -errno;
     }
     return 0;
@@ -542,12 +367,10 @@
     return 0;
 }
 
-static int unmount_tree(const char* path) {
-    size_t path_len = strlen(path);
-
+static int unmount_tree(const std::string& prefix) {
     FILE* fp = setmntent("/proc/mounts", "r");
     if (fp == NULL) {
-        ALOGE("Error opening /proc/mounts: %s", strerror(errno));
+        PLOG(ERROR) << "Failed to open /proc/mounts";
         return -errno;
     }
 
@@ -556,15 +379,16 @@
     std::list<std::string> toUnmount;
     mntent* mentry;
     while ((mentry = getmntent(fp)) != NULL) {
-        if (strncmp(mentry->mnt_dir, path, path_len) == 0) {
-            toUnmount.push_front(std::string(mentry->mnt_dir));
+        auto test = std::string(mentry->mnt_dir) + "/";
+        if (android::base::StartsWith(test, prefix.c_str())) {
+            toUnmount.push_front(test);
         }
     }
     endmntent(fp);
 
     for (const auto& path : toUnmount) {
         if (umount2(path.c_str(), MNT_DETACH)) {
-            ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno));
+            PLOG(ERROR) << "Failed to unmount " << path;
         }
     }
     return 0;
@@ -575,8 +399,8 @@
 
     DIR* dir;
     struct dirent* de;
-    char rootName[PATH_MAX];
-    char pidName[PATH_MAX];
+    std::string rootName;
+    std::string pidName;
     int pidFd;
     int nsFd;
     struct stat sb;
@@ -588,8 +412,8 @@
     }
 
     // Figure out root namespace to compare against below
-    if (android::vold::SaneReadLinkAt(dirfd(dir), "1/ns/mnt", rootName, PATH_MAX) == -1) {
-        PLOG(ERROR) << "Failed to readlink";
+    if (!android::vold::Readlinkat(dirfd(dir), "1/ns/mnt", &rootName)) {
+        PLOG(ERROR) << "Failed to read root namespace";
         closedir(dir);
         return -1;
     }
@@ -613,11 +437,11 @@
 
         // Matches so far, but refuse to touch if in root namespace
         LOG(DEBUG) << "Found matching PID " << de->d_name;
-        if (android::vold::SaneReadLinkAt(pidFd, "ns/mnt", pidName, PATH_MAX) == -1) {
+        if (!android::vold::Readlinkat(pidFd, "ns/mnt", &pidName)) {
             PLOG(WARNING) << "Failed to read namespace for " << de->d_name;
             goto next;
         }
-        if (!strcmp(rootName, pidName)) {
+        if (rootName == pidName) {
             LOG(WARNING) << "Skipping due to root namespace";
             goto next;
         }
@@ -635,7 +459,7 @@
                 _exit(1);
             }
 
-            unmount_tree("/storage");
+            unmount_tree("/storage/");
 
             std::string storageSource;
             if (mode == "default") {
@@ -724,6 +548,7 @@
 
 int VolumeManager::unmountAll() {
     std::lock_guard<std::mutex> lock(mLock);
+    ATRACE_NAME("VolumeManager::unmountAll()");
 
     // First, try gracefully unmounting all known devices
     if (mInternalEmulated != nullptr) {
@@ -737,7 +562,7 @@
     // force unmount those just to be safe.
     FILE* fp = setmntent("/proc/mounts", "r");
     if (fp == NULL) {
-        SLOGE("Error opening /proc/mounts: %s", strerror(errno));
+        PLOG(ERROR) << "Failed to open /proc/mounts";
         return -errno;
     }
 
@@ -746,1219 +571,247 @@
     std::list<std::string> toUnmount;
     mntent* mentry;
     while ((mentry = getmntent(fp)) != NULL) {
-        if (strncmp(mentry->mnt_dir, "/mnt/", 5) == 0
-                || strncmp(mentry->mnt_dir, "/storage/", 9) == 0) {
-            toUnmount.push_front(std::string(mentry->mnt_dir));
+        auto test = std::string(mentry->mnt_dir);
+        if (android::base::StartsWith(test, "/mnt/")
+                || android::base::StartsWith(test, "/storage/")) {
+            toUnmount.push_front(test);
         }
     }
     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);
     }
 
     return 0;
 }
 
-int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) {
-    char idHash[33];
-    if (!asecHash(sourceFile, idHash, sizeof(idHash))) {
-        SLOGE("Hash of '%s' failed (%s)", sourceFile, strerror(errno));
-        return -1;
-    }
-
-    memset(mountPath, 0, mountPathLen);
-    int written = snprintf(mountPath, mountPathLen, "%s/%s", VolumeManager::LOOPDIR, idHash);
-    if ((written < 0) || (written >= mountPathLen)) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (access(mountPath, F_OK)) {
-        errno = ENOENT;
-        return -1;
-    }
-
-    return 0;
-}
-
-int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
-    char asecFileName[255];
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("getAsecMountPath: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
-        SLOGE("Couldn't find ASEC %s", id);
-        return -1;
-    }
-
-    memset(buffer, 0, maxlen);
-    if (access(asecFileName, F_OK)) {
-        errno = ENOENT;
-        return -1;
-    }
-
-    int written = snprintf(buffer, maxlen, "%s/%s", VolumeManager::ASECDIR, id);
-    if ((written < 0) || (written >= maxlen)) {
-        SLOGE("getAsecMountPath failed for %s: couldn't construct path in buffer", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    return 0;
-}
-
-int VolumeManager::getAsecFilesystemPath(const char *id, char *buffer, int maxlen) {
-    char asecFileName[255];
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("getAsecFilesystemPath: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
-        SLOGE("Couldn't find ASEC %s", id);
-        return -1;
-    }
-
-    memset(buffer, 0, maxlen);
-    if (access(asecFileName, F_OK)) {
-        errno = ENOENT;
-        return -1;
-    }
-
-    int written = snprintf(buffer, maxlen, "%s", asecFileName);
-    if ((written < 0) || (written >= maxlen)) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    return 0;
-}
-
-int VolumeManager::createAsec(const char *id, unsigned long numSectors, const char *fstype,
-        const char *key, const int ownerUid, bool isExternal) {
-    struct asec_superblock sb;
-    memset(&sb, 0, sizeof(sb));
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("createAsec: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    const bool wantFilesystem = strcmp(fstype, "none");
-    bool usingExt4 = false;
-    if (wantFilesystem) {
-        usingExt4 = !strcmp(fstype, "ext4");
-        if (usingExt4) {
-            sb.c_opts |= ASEC_SB_C_OPTS_EXT4;
-        } else if (strcmp(fstype, "fat")) {
-            SLOGE("Invalid filesystem type %s", fstype);
-            errno = EINVAL;
-            return -1;
-        }
-    }
-
-    sb.magic = ASEC_SB_MAGIC;
-    sb.ver = ASEC_SB_VER;
-
-    if (numSectors < ((1024*1024)/512)) {
-        SLOGE("Invalid container size specified (%lu sectors)", numSectors);
-        errno = EINVAL;
-        return -1;
-    }
-
-    char asecFileName[255];
-
-    if (!findAsec(id, asecFileName, sizeof(asecFileName))) {
-        SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
-                asecFileName, strerror(errno));
-        errno = EADDRINUSE;
-        return -1;
-    }
-
-    const char *asecDir = isExternal ? VolumeManager::SEC_ASECDIR_EXT : VolumeManager::SEC_ASECDIR_INT;
-
-    int written = snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", asecDir, id);
-    if ((written < 0) || (size_t(written) >= sizeof(asecFileName))) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (!access(asecFileName, F_OK)) {
-        SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
-                asecFileName, strerror(errno));
-        errno = EADDRINUSE;
-        return -1;
-    }
-
-    unsigned long numImgSectors;
-    if (usingExt4)
-        numImgSectors = adjustSectorNumExt4(numSectors);
-    else
-        numImgSectors = adjustSectorNumFAT(numSectors);
-
-    // Add +1 for our superblock which is at the end
-    if (Loop::createImageFile(asecFileName, numImgSectors + 1)) {
-        SLOGE("ASEC image file creation failed (%s)", strerror(errno));
-        return -1;
-    }
-
-    char idHash[33];
-    if (!asecHash(id, idHash, sizeof(idHash))) {
-        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
-        unlink(asecFileName);
-        return -1;
-    }
-
-    char loopDevice[255];
-    if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
-        SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
-        unlink(asecFileName);
-        return -1;
-    }
-
-    char dmDevice[255];
-    bool cleanupDm = false;
-
-    if (strcmp(key, "none")) {
-        // XXX: This is all we support for now
-        sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH;
-        if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice,
-                             sizeof(dmDevice))) {
-            SLOGE("ASEC device mapping failed (%s)", strerror(errno));
-            Loop::destroyByDevice(loopDevice);
-            unlink(asecFileName);
-            return -1;
-        }
-        cleanupDm = true;
-    } else {
-        sb.c_cipher = ASEC_SB_C_CIPHER_NONE;
-        strlcpy(dmDevice, loopDevice, sizeof(dmDevice));
-    }
-
-    /*
-     * Drop down the superblock at the end of the file
-     */
-    if (writeSuperBlock(loopDevice, &sb, numImgSectors)) {
-        if (cleanupDm) {
-            Devmapper::destroy(idHash);
-        }
-        Loop::destroyByDevice(loopDevice);
-        unlink(asecFileName);
-        return -1;
-    }
-
-    if (wantFilesystem) {
-        int formatStatus;
-        char mountPoint[255];
-
-        int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
-        if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-            SLOGE("ASEC fs format failed: couldn't construct mountPoint");
-            if (cleanupDm) {
-                Devmapper::destroy(idHash);
-            }
-            Loop::destroyByDevice(loopDevice);
-            unlink(asecFileName);
-            return -1;
-        }
-
-        if (usingExt4) {
-            formatStatus = android::vold::ext4::Format(dmDevice, numImgSectors, mountPoint);
-        } else {
-            formatStatus = android::vold::vfat::Format(dmDevice, numImgSectors);
-        }
-
-        if (formatStatus < 0) {
-            SLOGE("ASEC fs format failed (%s)", strerror(errno));
-            if (cleanupDm) {
-                Devmapper::destroy(idHash);
-            }
-            Loop::destroyByDevice(loopDevice);
-            unlink(asecFileName);
-            return -1;
-        }
-
-        if (mkdir(mountPoint, 0000)) {
-            if (errno != EEXIST) {
-                SLOGE("Mountpoint creation failed (%s)", strerror(errno));
-                if (cleanupDm) {
-                    Devmapper::destroy(idHash);
-                }
-                Loop::destroyByDevice(loopDevice);
-                unlink(asecFileName);
-                return -1;
-            }
-        }
-
-        int mountStatus;
-        if (usingExt4) {
-            mountStatus = android::vold::ext4::Mount(dmDevice, mountPoint,
-                    false, false, false);
-        } else {
-            mountStatus = android::vold::vfat::Mount(dmDevice, mountPoint,
-                    false, false, false, ownerUid, 0, 0000, false);
-        }
-
-        if (mountStatus) {
-            SLOGE("ASEC FAT mount failed (%s)", strerror(errno));
-            if (cleanupDm) {
-                Devmapper::destroy(idHash);
-            }
-            Loop::destroyByDevice(loopDevice);
-            unlink(asecFileName);
-            return -1;
-        }
-
-        if (usingExt4) {
-            int dirfd = open(mountPoint, O_DIRECTORY | O_CLOEXEC);
-            if (dirfd >= 0) {
-                if (fchown(dirfd, ownerUid, AID_SYSTEM)
-                        || fchmod(dirfd, S_IRUSR | S_IWUSR | S_IXUSR | S_ISGID | S_IRGRP | S_IXGRP)) {
-                    SLOGI("Cannot chown/chmod new ASEC mount point %s", mountPoint);
-                }
-                close(dirfd);
-            }
-        }
-    } else {
-        SLOGI("Created raw secure container %s (no filesystem)", id);
-    }
-
-    mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
-    return 0;
-}
-
-int VolumeManager::resizeAsec(const char *id, unsigned long numSectors, const char *key) {
-    char asecFileName[255];
-    char mountPoint[255];
-    bool cleanupDm = false;
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("resizeAsec: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
-        SLOGE("Couldn't find ASEC %s", id);
-        return -1;
-    }
-
-    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-       SLOGE("ASEC resize failed for %s: couldn't construct mountpoint", id);
-       return -1;
-    }
-
-    if (isMountpointMounted(mountPoint)) {
-       SLOGE("ASEC %s mounted. Unmount before resizing", id);
-       errno = EBUSY;
-       return -1;
-    }
-
-    struct asec_superblock sb;
-    int fd;
-    unsigned long oldNumSec = 0;
-
-    if ((fd = open(asecFileName, O_RDONLY | O_CLOEXEC)) < 0) {
-        SLOGE("Failed to open ASEC file (%s)", strerror(errno));
-        return -1;
-    }
-
-    struct stat info;
-    if (fstat(fd, &info) < 0) {
-        SLOGE("Failed to get file size (%s)", strerror(errno));
-        close(fd);
-        return -1;
-    }
-
-    oldNumSec = info.st_size / 512;
-
-    /*
-     * Try to read superblock.
-     */
-    memset(&sb, 0, sizeof(struct asec_superblock));
-    if (lseek(fd, ((oldNumSec - 1) * 512), SEEK_SET) < 0) {
-        SLOGE("lseek failed (%s)", strerror(errno));
-        close(fd);
-        return -1;
-    }
-    if (read(fd, &sb, sizeof(struct asec_superblock)) != sizeof(struct asec_superblock)) {
-        SLOGE("superblock read failed (%s)", strerror(errno));
-        close(fd);
-        return -1;
-    }
-    close(fd);
-
-    if (mDebug) {
-        SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
-    }
-    if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
-        SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
-        errno = EMEDIUMTYPE;
-        return -1;
-    }
-
-    unsigned long numImgSectors;
-    if (!(sb.c_opts & ASEC_SB_C_OPTS_EXT4)) {
-        SLOGE("Only ext4 partitions are supported for resize");
-        errno = EINVAL;
-        return -1;
-    } else {
-        numImgSectors = adjustSectorNumExt4(numSectors);
-    }
-
-    /*
-     *  add one block for the superblock
-     */
-    SLOGD("Resizing from %lu sectors to %lu sectors", oldNumSec, numImgSectors + 1);
-    if (oldNumSec == numImgSectors + 1) {
-        SLOGW("Size unchanged; ignoring resize request");
-        return 0;
-    } else if (oldNumSec > numImgSectors + 1) {
-        SLOGE("Only growing is currently supported.");
-        close(fd);
-        return -1;
-    }
-
-    if (Loop::resizeImageFile(asecFileName, numImgSectors + 1)) {
-        SLOGE("Resize of ASEC image file failed. Could not resize %s", id);
-        return -1;
-    }
-
-    /*
-     * Drop down a copy of the superblock at the end of the file
-     */
-    if (writeSuperBlock(asecFileName, &sb, numImgSectors))
-        goto fail;
-
-    char idHash[33];
-    if (!asecHash(id, idHash, sizeof(idHash))) {
-        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
-        goto fail;
-    }
-
-    char loopDevice[255];
-    if (setupLoopDevice(loopDevice, sizeof(loopDevice), asecFileName, idHash, mDebug))
-        goto fail;
-
-    char dmDevice[255];
-
-    if (setupDevMapperDevice(dmDevice, sizeof(dmDevice), loopDevice, asecFileName, key, idHash, numImgSectors, &cleanupDm, mDebug)) {
-        Loop::destroyByDevice(loopDevice);
-        goto fail;
-    }
-
-    /*
-     * Wait for the device mapper node to be created.
-     */
-    waitForDevMapper(dmDevice);
-
-    if (android::vold::ext4::Resize(dmDevice, numImgSectors)) {
-        SLOGE("Unable to resize %s (%s)", id, strerror(errno));
-        if (cleanupDm) {
-            Devmapper::destroy(idHash);
-        }
-        Loop::destroyByDevice(loopDevice);
-        goto fail;
-    }
-
-    return 0;
-fail:
-    Loop::resizeImageFile(asecFileName, oldNumSec);
-    return -1;
-}
-
-int VolumeManager::finalizeAsec(const char *id) {
-    char asecFileName[255];
-    char loopDevice[255];
-    char mountPoint[255];
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("finalizeAsec: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
-        SLOGE("Couldn't find ASEC %s", id);
-        return -1;
-    }
-
-    char idHash[33];
-    if (!asecHash(id, idHash, sizeof(idHash))) {
-        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
-        return -1;
-    }
-
-    if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
-        SLOGE("Unable to finalize %s (%s)", id, strerror(errno));
-        return -1;
-    }
-
-    unsigned long nr_sec = 0;
-    struct asec_superblock sb;
-
-    if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
-        return -1;
-    }
-
-    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-        SLOGE("ASEC finalize failed: couldn't construct mountPoint");
-        return -1;
-    }
-
-    int result = 0;
-    if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
-        result = android::vold::ext4::Mount(loopDevice, mountPoint,
-                true, true, true);
-    } else {
-        result = android::vold::vfat::Mount(loopDevice, mountPoint,
-                true, true, true, 0, 0, 0227, false);
-    }
-
-    if (result) {
-        SLOGE("ASEC finalize mount failed (%s)", strerror(errno));
-        return -1;
-    }
-
-    if (mDebug) {
-        SLOGD("ASEC %s finalized", id);
-    }
-    return 0;
-}
-
-int VolumeManager::fixupAsecPermissions(const char *id, gid_t gid, const char* filename) {
-    char asecFileName[255];
-    char loopDevice[255];
-    char mountPoint[255];
-
-    if (gid < AID_APP) {
-        SLOGE("Group ID is not in application range");
-        return -1;
-    }
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("fixupAsecPermissions: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
-        SLOGE("Couldn't find ASEC %s", id);
-        return -1;
-    }
-
-    char idHash[33];
-    if (!asecHash(id, idHash, sizeof(idHash))) {
-        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
-        return -1;
-    }
-
-    if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
-        SLOGE("Unable fix permissions during lookup on %s (%s)", id, strerror(errno));
-        return -1;
-    }
-
-    unsigned long nr_sec = 0;
-    struct asec_superblock sb;
-
-    if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
-        return -1;
-    }
-
-    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-        SLOGE("Unable remount to fix permissions for %s: couldn't construct mountpoint", id);
-        return -1;
-    }
-
-    int result = 0;
-    if ((sb.c_opts & ASEC_SB_C_OPTS_EXT4) == 0) {
-        return 0;
-    }
-
-    int ret = android::vold::ext4::Mount(loopDevice, mountPoint,
-            false /* read-only */,
-            true  /* remount */,
-            false /* executable */);
-    if (ret) {
-        SLOGE("Unable remount to fix permissions for %s (%s)", id, strerror(errno));
-        return -1;
-    }
-
-    char *paths[] = { mountPoint, NULL };
-
-    FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL);
-    if (fts) {
-        // Traverse the entire hierarchy and chown to system UID.
-        for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
-            // We don't care about the lost+found directory.
-            if (!strcmp(ftsent->fts_name, "lost+found")) {
-                continue;
-            }
-
-            /*
-             * There can only be one file marked as private right now.
-             * This should be more robust, but it satisfies the requirements
-             * we have for right now.
-             */
-            const bool privateFile = !strcmp(ftsent->fts_name, filename);
-
-            int fd = open(ftsent->fts_accpath, O_NOFOLLOW | O_CLOEXEC);
-            if (fd < 0) {
-                SLOGE("Couldn't open file %s: %s", ftsent->fts_accpath, strerror(errno));
-                result = -1;
-                continue;
-            }
-
-            result |= fchown(fd, AID_SYSTEM, privateFile? gid : AID_SYSTEM);
-
-            if (ftsent->fts_info & FTS_D) {
-                result |= fchmod(fd, 0755);
-            } else if (ftsent->fts_info & FTS_F) {
-                result |= fchmod(fd, privateFile ? 0640 : 0644);
-            }
-
-            if (selinux_android_restorecon(ftsent->fts_path, 0) < 0) {
-                SLOGE("restorecon failed for %s: %s\n", ftsent->fts_path, strerror(errno));
-                result |= -1;
-            }
-
-            close(fd);
-        }
-        fts_close(fts);
-
-        // Finally make the directory readable by everyone.
-        int dirfd = open(mountPoint, O_DIRECTORY | O_CLOEXEC);
-        if (dirfd < 0 || fchmod(dirfd, 0755)) {
-            SLOGE("Couldn't change owner of existing directory %s: %s", mountPoint, strerror(errno));
-            result |= -1;
-        }
-        close(dirfd);
-    } else {
-        result |= -1;
-    }
-
-    result |= android::vold::ext4::Mount(loopDevice, mountPoint,
-            true /* read-only */,
-            true /* remount */,
-            true /* execute */);
-
-    if (result) {
-        SLOGE("ASEC fix permissions failed (%s)", strerror(errno));
-        return -1;
-    }
-
-    if (mDebug) {
-        SLOGD("ASEC %s permissions fixed", id);
-    }
-    return 0;
-}
-
-int VolumeManager::renameAsec(const char *id1, const char *id2) {
-    char asecFilename1[255];
-    char *asecFilename2;
-    char mountPoint[255];
-
-    const char *dir;
-
-    if (!isLegalAsecId(id1)) {
-        SLOGE("renameAsec: Invalid asec id1 \"%s\"", id1);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (!isLegalAsecId(id2)) {
-        SLOGE("renameAsec: Invalid asec id2 \"%s\"", id2);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (findAsec(id1, asecFilename1, sizeof(asecFilename1), &dir)) {
-        SLOGE("Couldn't find ASEC %s", id1);
-        return -1;
-    }
-
-    asprintf(&asecFilename2, "%s/%s.asec", dir, id2);
-
-    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id1);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-        SLOGE("Rename failed: couldn't construct mountpoint");
-        goto out_err;
-    }
-
-    if (isMountpointMounted(mountPoint)) {
-        SLOGW("Rename attempt when src mounted");
-        errno = EBUSY;
-        goto out_err;
-    }
-
-    written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id2);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-        SLOGE("Rename failed: couldn't construct mountpoint2");
-        goto out_err;
-    }
-
-    if (isMountpointMounted(mountPoint)) {
-        SLOGW("Rename attempt when dst mounted");
-        errno = EBUSY;
-        goto out_err;
-    }
-
-    if (!access(asecFilename2, F_OK)) {
-        SLOGE("Rename attempt when dst exists");
-        errno = EADDRINUSE;
-        goto out_err;
-    }
-
-    if (rename(asecFilename1, asecFilename2)) {
-        SLOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno));
-        goto out_err;
-    }
-
-    free(asecFilename2);
-    return 0;
-
-out_err:
-    free(asecFilename2);
-    return -1;
-}
-
-#define UNMOUNT_RETRIES 5
-#define UNMOUNT_SLEEP_BETWEEN_RETRY_MS (1000 * 1000)
-int VolumeManager::unmountAsec(const char *id, bool force) {
-    char asecFileName[255];
-    char mountPoint[255];
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("unmountAsec: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
-        SLOGE("Couldn't find ASEC %s", id);
-        return -1;
-    }
-
-    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-        SLOGE("ASEC unmount failed for %s: couldn't construct mountpoint", id);
-        return -1;
-    }
-
-    char idHash[33];
-    if (!asecHash(id, idHash, sizeof(idHash))) {
-        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
-        return -1;
-    }
-
-    return unmountLoopImage(id, idHash, asecFileName, mountPoint, force);
-}
-
-int VolumeManager::unmountObb(const char *fileName, bool force) {
-    char mountPoint[255];
-
-    char idHash[33];
-    if (!asecHash(fileName, idHash, sizeof(idHash))) {
-        SLOGE("Hash of '%s' failed (%s)", fileName, strerror(errno));
-        return -1;
-    }
-
-    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::LOOPDIR, idHash);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-        SLOGE("OBB unmount failed for %s: couldn't construct mountpoint", fileName);
-        return -1;
-    }
-
-    return unmountLoopImage(fileName, idHash, fileName, mountPoint, force);
-}
-
-int VolumeManager::unmountLoopImage(const char *id, const char *idHash,
-        const char *fileName, const char *mountPoint, bool force) {
-    if (!isMountpointMounted(mountPoint)) {
-        SLOGE("Unmount request for %s when not mounted", id);
-        errno = ENOENT;
-        return -1;
-    }
-
-    int i, rc;
-    for (i = 1; i <= UNMOUNT_RETRIES; i++) {
-        rc = umount(mountPoint);
-        if (!rc) {
-            break;
-        }
-        if (rc && (errno == EINVAL || errno == ENOENT)) {
-            SLOGI("Container %s unmounted OK", id);
-            rc = 0;
-            break;
-        }
-        SLOGW("%s unmount attempt %d failed (%s)",
-              id, i, strerror(errno));
-
-        int signal = 0; // default is to just complain
-
-        if (force) {
-            if (i > (UNMOUNT_RETRIES - 2))
-                signal = SIGKILL;
-            else if (i > (UNMOUNT_RETRIES - 3))
-                signal = SIGTERM;
-        }
-
-        Process::killProcessesWithOpenFiles(mountPoint, signal);
-        usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
-    }
-
-    if (rc) {
-        errno = EBUSY;
-        SLOGE("Failed to unmount container %s (%s)", id, strerror(errno));
-        return -1;
-    }
-
-    int retries = 10;
-
-    while(retries--) {
-        if (!rmdir(mountPoint)) {
-            break;
-        }
-
-        SLOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno));
-        usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
-    }
-
-    if (!retries) {
-        SLOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno));
-    }
-
-    for (i=1; i <= UNMOUNT_RETRIES; i++) {
-        if (Devmapper::destroy(idHash) && errno != ENXIO) {
-            SLOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
-            usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
-            continue;
-        } else {
-          break;
-        }
-    }
-
-    char loopDevice[255];
-    if (!Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
-        Loop::destroyByDevice(loopDevice);
-    } else {
-        SLOGW("Failed to find loop device for {%s} (%s)", fileName, strerror(errno));
-    }
-
-    AsecIdCollection::iterator it;
-    for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) {
-        ContainerData* cd = *it;
-        if (!strcmp(cd->id, id)) {
-            free(*it);
-            mActiveContainers->erase(it);
-            break;
-        }
-    }
-    if (it == mActiveContainers->end()) {
-        SLOGW("mActiveContainers is inconsistent!");
-    }
-    return 0;
-}
-
-int VolumeManager::destroyAsec(const char *id, bool force) {
-    char asecFileName[255];
-    char mountPoint[255];
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("destroyAsec: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
-        SLOGE("Couldn't find ASEC %s", id);
-        return -1;
-    }
-
-    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-        SLOGE("ASEC destroy failed for %s: couldn't construct mountpoint", id);
-        return -1;
-    }
-
-    if (isMountpointMounted(mountPoint)) {
-        if (mDebug) {
-            SLOGD("Unmounting container before destroy");
-        }
-        if (unmountAsec(id, force)) {
-            SLOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
-            return -1;
-        }
-    }
-
-    if (unlink(asecFileName)) {
-        SLOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
-        return -1;
-    }
-
-    if (mDebug) {
-        SLOGD("ASEC %s destroyed", id);
-    }
-    return 0;
-}
-
-/*
- * Legal ASEC ids consist of alphanumeric characters, '-',
- * '_', or '.'. ".." is not allowed. The first or last character
- * of the ASEC id cannot be '.' (dot).
- */
-bool VolumeManager::isLegalAsecId(const char *id) const {
-    size_t i;
-    size_t len = strlen(id);
-
-    if (len == 0) {
-        return false;
-    }
-    if ((id[0] == '.') || (id[len - 1] == '.')) {
-        return false;
-    }
-
-    for (i = 0; i < len; i++) {
-        if (id[i] == '.') {
-            // i=0 is guaranteed never to have a dot. See above.
-            if (id[i-1] == '.') return false;
-            continue;
-        }
-        if (id[i] == '_' || id[i] == '-') continue;
-        if (id[i] >= 'a' && id[i] <= 'z') continue;
-        if (id[i] >= 'A' && id[i] <= 'Z') continue;
-        if (id[i] >= '0' && id[i] <= '9') continue;
-        return false;
-    }
-
-    return true;
-}
-
-bool VolumeManager::isAsecInDirectory(const char *dir, const char *asecName) const {
-    int dirfd = open(dir, O_DIRECTORY | O_CLOEXEC);
-    if (dirfd < 0) {
-        SLOGE("Couldn't open internal ASEC dir (%s)", strerror(errno));
-        return false;
-    }
-
-    struct stat sb;
-    bool ret = (fstatat(dirfd, asecName, &sb, AT_SYMLINK_NOFOLLOW) == 0)
-        && S_ISREG(sb.st_mode);
-
-    close(dirfd);
-
-    return ret;
-}
-
-int VolumeManager::findAsec(const char *id, char *asecPath, size_t asecPathLen,
-        const char **directory) const {
-    char *asecName;
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("findAsec: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (asprintf(&asecName, "%s.asec", id) < 0) {
-        SLOGE("Couldn't allocate string to write ASEC name");
-        return -1;
-    }
-
-    const char *dir;
-    if (isAsecInDirectory(VolumeManager::SEC_ASECDIR_INT, asecName)) {
-        dir = VolumeManager::SEC_ASECDIR_INT;
-    } else if (isAsecInDirectory(VolumeManager::SEC_ASECDIR_EXT, asecName)) {
-        dir = VolumeManager::SEC_ASECDIR_EXT;
-    } else {
-        free(asecName);
-        return -1;
-    }
-
-    if (directory != NULL) {
-        *directory = dir;
-    }
-
-    if (asecPath != NULL) {
-        int written = snprintf(asecPath, asecPathLen, "%s/%s", dir, asecName);
-        if ((written < 0) || (size_t(written) >= asecPathLen)) {
-            SLOGE("findAsec failed for %s: couldn't construct ASEC path", id);
-            free(asecName);
-            return -1;
-        }
-    }
-
-    free(asecName);
-    return 0;
-}
-
-int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid, bool readOnly) {
-    char asecFileName[255];
-    char mountPoint[255];
-
-    if (!isLegalAsecId(id)) {
-        SLOGE("mountAsec: Invalid asec id \"%s\"", id);
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
-        SLOGE("Couldn't find ASEC %s", id);
-        return -1;
-    }
-
-    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-        SLOGE("ASEC mount failed for %s: couldn't construct mountpoint", id);
-        return -1;
-    }
-
-    if (isMountpointMounted(mountPoint)) {
-        SLOGE("ASEC %s already mounted", id);
-        errno = EBUSY;
-        return -1;
-    }
-
-    char idHash[33];
-    if (!asecHash(id, idHash, sizeof(idHash))) {
-        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
-        return -1;
-    }
-
-    char loopDevice[255];
-    if (setupLoopDevice(loopDevice, sizeof(loopDevice), asecFileName, idHash, mDebug))
-        return -1;
-
-    char dmDevice[255];
-    bool cleanupDm = false;
-
-    unsigned long nr_sec = 0;
-    struct asec_superblock sb;
-
-    if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
-        return -1;
-    }
-
-    if (mDebug) {
-        SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
-    }
-    if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
-        SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
-        Loop::destroyByDevice(loopDevice);
-        errno = EMEDIUMTYPE;
-        return -1;
-    }
-    nr_sec--; // We don't want the devmapping to extend onto our superblock
-
-    if (setupDevMapperDevice(dmDevice, sizeof(dmDevice), loopDevice, asecFileName, key, idHash , nr_sec, &cleanupDm, mDebug)) {
-        Loop::destroyByDevice(loopDevice);
-        return -1;
-    }
-
-    if (mkdir(mountPoint, 0000)) {
-        if (errno != EEXIST) {
-            SLOGE("Mountpoint creation failed (%s)", strerror(errno));
-            if (cleanupDm) {
-                Devmapper::destroy(idHash);
-            }
-            Loop::destroyByDevice(loopDevice);
-            return -1;
-        }
-    }
-
-    /*
-     * Wait for the device mapper node to be created.
-     */
-    waitForDevMapper(dmDevice);
-
-    int result;
-    if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
-        result = android::vold::ext4::Mount(dmDevice, mountPoint,
-                readOnly, false, readOnly);
-    } else {
-        result = android::vold::vfat::Mount(dmDevice, mountPoint,
-                readOnly, false, readOnly, ownerUid, 0, 0222, false);
-    }
-
-    if (result) {
-        SLOGE("ASEC mount failed (%s)", strerror(errno));
-        if (cleanupDm) {
-            Devmapper::destroy(idHash);
-        }
-        Loop::destroyByDevice(loopDevice);
-        return -1;
-    }
-
-    mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
-    if (mDebug) {
-        SLOGD("ASEC %s mounted", id);
-    }
-    return 0;
-}
-
-/**
- * Mounts an image file <code>img</code>.
- */
-int VolumeManager::mountObb(const char *img, const char *key, int ownerGid) {
-    char mountPoint[255];
-
-    char idHash[33];
-    if (!asecHash(img, idHash, sizeof(idHash))) {
-        SLOGE("Hash of '%s' failed (%s)", img, strerror(errno));
-        return -1;
-    }
-
-    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::LOOPDIR, idHash);
-    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
-        SLOGE("OBB mount failed for %s: couldn't construct mountpoint", img);
-        return -1;
-    }
-
-    if (isMountpointMounted(mountPoint)) {
-        SLOGE("Image %s already mounted", img);
-        errno = EBUSY;
-        return -1;
-    }
-
-    char loopDevice[255];
-    if (setupLoopDevice(loopDevice, sizeof(loopDevice), img, idHash, mDebug))
-        return -1;
-
-    char dmDevice[255];
-    bool cleanupDm = false;
-    int fd;
-    unsigned long nr_sec = 0;
-
-    if ((fd = open(loopDevice, O_RDWR | O_CLOEXEC)) < 0) {
-        SLOGE("Failed to open loopdevice (%s)", strerror(errno));
-        Loop::destroyByDevice(loopDevice);
-        return -1;
-    }
-
-    get_blkdev_size(fd, &nr_sec);
-    if (nr_sec == 0) {
-        SLOGE("Failed to get loop size (%s)", strerror(errno));
-        Loop::destroyByDevice(loopDevice);
-        close(fd);
-        return -1;
-    }
-
-    close(fd);
-
-    if (setupDevMapperDevice(dmDevice, sizeof(loopDevice), loopDevice, img,key, idHash, nr_sec, &cleanupDm, mDebug)) {
-        Loop::destroyByDevice(loopDevice);
-        return -1;
-    }
-
-    if (mkdir(mountPoint, 0755)) {
-        if (errno != EEXIST) {
-            SLOGE("Mountpoint creation failed (%s)", strerror(errno));
-            if (cleanupDm) {
-                Devmapper::destroy(idHash);
-            }
-            Loop::destroyByDevice(loopDevice);
-            return -1;
-        }
-    }
-
-    /*
-     * Wait for the device mapper node to be created.
-     */
-    waitForDevMapper(dmDevice);
-
-    if (android::vold::vfat::Mount(dmDevice, mountPoint,
-            true, false, true, 0, ownerGid, 0227, false)) {
-        SLOGE("Image mount failed (%s)", strerror(errno));
-        if (cleanupDm) {
-            Devmapper::destroy(idHash);
-        }
-        Loop::destroyByDevice(loopDevice);
-        return -1;
-    }
-
-    mActiveContainers->push_back(new ContainerData(strdup(img), OBB));
-    if (mDebug) {
-        SLOGD("Image %s mounted", img);
-    }
-    return 0;
-}
-
-int VolumeManager::listMountedObbs(SocketClient* cli) {
-    FILE *fp = setmntent("/proc/mounts", "r");
-    if (fp == NULL) {
-        SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
-        return -1;
-    }
-
-    // Create a string to compare against that has a trailing slash
-    int loopDirLen = strlen(VolumeManager::LOOPDIR);
-    char loopDir[loopDirLen + 2];
-    strlcpy(loopDir, VolumeManager::LOOPDIR, sizeof(loopDir));
-    loopDir[loopDirLen++] = '/';
-    loopDir[loopDirLen] = '\0';
-
-    mntent* mentry;
-    while ((mentry = getmntent(fp)) != NULL) {
-        if (!strncmp(mentry->mnt_dir, loopDir, loopDirLen)) {
-            int fd = open(mentry->mnt_fsname, O_RDONLY | O_CLOEXEC);
-            if (fd >= 0) {
-                struct loop_info64 li;
-                if (ioctl(fd, LOOP_GET_STATUS64, &li) >= 0) {
-                    cli->sendMsg(ResponseCode::AsecListResult,
-                            (const char*) li.lo_file_name, false);
-                }
-                close(fd);
-            }
-        }
-    }
-    endmntent(fp);
-    return 0;
-}
-
 extern "C" int vold_unmountAll(void) {
     VolumeManager *vm = VolumeManager::Instance();
     return vm->unmountAll();
 }
 
-bool VolumeManager::isMountpointMounted(const char *mp)
-{
-    FILE *fp = setmntent("/proc/mounts", "r");
-    if (fp == NULL) {
-        SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
-        return false;
-    }
-
-    bool found_mp = false;
-    mntent* mentry;
-    while ((mentry = getmntent(fp)) != NULL) {
-        if (strcmp(mentry->mnt_dir, mp) == 0) {
-            found_mp = true;
-            break;
-        }
-    }
-    endmntent(fp);
-    return found_mp;
-}
-
-int VolumeManager::mkdirs(char* path) {
+int VolumeManager::mkdirs(const std::string& path) {
     // Only offer to create directories for paths managed by vold
-    if (strncmp(path, "/storage/", 9) == 0) {
+    if (android::base::StartsWith(path, "/storage/")) {
         // fs_mkdirs() does symlink checking and relative path enforcement
-        return fs_mkdirs(path, 0700);
+        return fs_mkdirs(path.c_str(), 0700);
     } else {
-        SLOGE("Failed to find mounted volume for %s", path);
+        LOG(ERROR) << "Failed to find mounted volume for " << path;
         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
+    {
+        std::string rootName;
+        std::string pidName;
+        if (!android::vold::Readlinkat(dir.get(), "1/ns/mnt", &rootName)
+                || !android::vold::Readlinkat(pid_fd.get(), "ns/mnt", &pidName)) {
+            PLOG(ERROR) << "Failed to read namespaces";
+            return -EPERM;
+        }
+        if (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..4f62de9 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -29,68 +29,38 @@
 #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"
 
-/* The length of an MD5 hash when encoded into ASCII hex characters */
-#define MD5_ASCII_LENGTH_PLUS_NULL ((MD5_DIGEST_LENGTH*2)+1)
+#include "model/Disk.h"
+#include "model/VolumeBase.h"
 
-typedef enum { ASEC, OBB } container_type_t;
-
-class ContainerData {
-public:
-    ContainerData(char* _id, container_type_t _type)
-            : id(_id)
-            , type(_type)
-    {}
-
-    ~ContainerData() {
-        if (id != NULL) {
-            free(id);
-            id = NULL;
-        }
-    }
-
-    char *id;
-    container_type_t type;
-};
-
-typedef android::List<ContainerData*> AsecIdCollection;
+#define DEBUG_APPFUSE 0
 
 class VolumeManager {
 public:
-    static const char *SEC_ASECDIR_EXT;
-    static const char *SEC_ASECDIR_INT;
-    static const char *ASECDIR;
-    static const char *LOOPDIR;
-
     //TODO remove this with better solution, b/64143519
     static bool shutting_down;
 
 private:
     static VolumeManager *sInstance;
 
-    SocketListener        *mBroadcaster;
-
-    AsecIdCollection      *mActiveContainers;
     bool                   mDebug;
 
-    // for adjusting /proc/sys/vm/dirty_ratio when UMS is active
-    int                    mUmsSharingCount;
-    int                    mSavedDirtyRatio;
-    int                    mUmsDirtyRatio;
-
 public:
     virtual ~VolumeManager();
 
     // 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();
@@ -123,8 +93,6 @@
 
     void listVolumes(android::vold::VolumeBase::Type type, std::list<std::string>& list);
 
-    nsecs_t benchmarkPrivate(const std::string& id);
-
     int forgetPartition(const std::string& partGuid);
 
     int onUserAdded(userid_t userId, int userSerialNumber);
@@ -143,52 +111,11 @@
     /* Unmount all volumes, usually for encryption */
     int unmountAll();
 
-    /* ASEC */
-    int findAsec(const char *id, char *asecPath = NULL, size_t asecPathLen = 0,
-            const char **directory = NULL) const;
-    int createAsec(const char *id, unsigned long numSectors, const char *fstype,
-                   const char *key, const int ownerUid, bool isExternal);
-    int resizeAsec(const char *id, unsigned long numSectors, const char *key);
-    int finalizeAsec(const char *id);
-
-    /**
-     * Fixes ASEC permissions on a filesystem that has owners and permissions.
-     * This currently means EXT4-based ASEC containers.
-     *
-     * There is a single file that can be marked as "private" and will not have
-     * world-readable permission. The group for that file will be set to the gid
-     * supplied.
-     *
-     * Returns 0 on success.
-     */
-    int fixupAsecPermissions(const char *id, gid_t gid, const char* privateFilename);
-    int destroyAsec(const char *id, bool force);
-    int mountAsec(const char *id, const char *key, int ownerUid, bool readOnly);
-    int unmountAsec(const char *id, bool force);
-    int renameAsec(const char *id1, const char *id2);
-    int getAsecMountPath(const char *id, char *buffer, int maxlen);
-    int getAsecFilesystemPath(const char *id, char *buffer, int maxlen);
-
-    /* Loopback images */
-    int listMountedObbs(SocketClient* cli);
-    int mountObb(const char *fileName, const char *key, int ownerUid);
-    int unmountObb(const char *fileName, bool force);
-    int getObbMountPath(const char *id, char *buffer, int maxlen);
-
-    /* Shared between ASEC and Loopback images */
-    int unmountLoopImage(const char *containerId, const char *loopId,
-            const char *fileName, const char *mountPoint, bool force);
-
     int updateVirtualDisk();
     int setDebug(bool enable);
 
-    void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
-    SocketListener *getBroadcaster() { return mBroadcaster; }
-
     static VolumeManager *Instance();
 
-    static char *asecHash(const char *id, char *buffer, size_t len);
-
     /*
      * Ensure that all directories along given path exist, creating parent
      * directories as needed.  Validates that given path is absolute and that
@@ -196,21 +123,29 @@
      * 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 std::string& 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();
     void readInitialState();
-    bool isMountpointMounted(const char *mp);
-    bool isAsecInDirectory(const char *dir, const char *asec) const;
-    bool isLegalAsecId(const char *id) const;
 
     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 +154,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..d47f113
--- /dev/null
+++ b/binder/android/os/IVold.aidl
@@ -0,0 +1,140 @@
+/*
+ * 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;
+import android.os.IVoldTaskListener;
+
+/** {@hide} */
+interface IVold {
+    void setListener(IVoldListener listener);
+
+    void monitor();
+    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);
+    void benchmark(@utf8InCpp String volId, IVoldTaskListener listener);
+
+    void moveStorage(@utf8InCpp String fromVolId, @utf8InCpp String toVolId,
+            IVoldTaskListener listener);
+
+    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, IVoldTaskListener listener);
+
+    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 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/VoldCommand.h b/binder/android/os/IVoldTaskListener.aidl
similarity index 65%
rename from VoldCommand.h
rename to binder/android/os/IVoldTaskListener.aidl
index e435159..e2bac04 100644
--- a/VoldCommand.h
+++ b/binder/android/os/IVoldTaskListener.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef _VOLD_COMMAND_H
-#define _VOLD_COMMAND_H
+package android.os;
 
-#include <sysutils/FrameworkCommand.h>
+import android.os.PersistableBundle;
 
-class VoldCommand : public FrameworkCommand {
-public:
-    explicit VoldCommand(const char *cmd);
-    virtual ~VoldCommand() {}
-};
-
-#endif
+/** {@hide} */
+oneway interface IVoldTaskListener {
+    void onStatus(int status, in PersistableBundle extras);
+    void onFinished(int status, in PersistableBundle extras);
+}
diff --git a/cryptfs.cpp b/cryptfs.cpp
index eb89e1b..ae9b0af 100644
--- a/cryptfs.cpp
+++ b/cryptfs.cpp
@@ -38,7 +38,7 @@
 #include <openssl/evp.h>
 #include <openssl/sha.h>
 #include <errno.h>
-#include <ext4_utils/ext4.h>
+#include <ext4_utils/ext4_crypt.h>
 #include <ext4_utils/ext4_utils.h>
 #include <linux/kdev_t.h>
 #include <fs_mgr.h>
@@ -191,8 +191,6 @@
 static int password_expiry_time = 0;
 static const int password_max_age_seconds = 60;
 
-extern struct fstab *fstab;
-
 enum class RebootType {reboot, recovery, shutdown};
 static void cryptfs_reboot(RebootType rt)
 {
@@ -295,7 +293,7 @@
   int rc = -1;
 
   if (!cached_data) {
-    fs_mgr_get_crypt_info(fstab, key_loc, real_blkdev, sizeof(key_loc));
+    fs_mgr_get_crypt_info(fstab_default, key_loc, real_blkdev, sizeof(key_loc));
 
     if (!strcmp(key_loc, KEY_IN_FOOTER)) {
       if ( (fd = open(real_blkdev, O_RDWR|O_CLOEXEC)) < 0) {
@@ -1279,10 +1277,10 @@
         if (kill) {
             if (i == (WAIT_UNMOUNT_COUNT - 3)) {
                 SLOGW("sending SIGHUP to processes with open files\n");
-                vold_killProcessesWithOpenFiles(mountpoint, SIGTERM);
+                android::vold::KillProcessesWithOpenFiles(mountpoint, SIGTERM);
             } else if (i == (WAIT_UNMOUNT_COUNT - 2)) {
                 SLOGW("sending SIGKILL to processes with open files\n");
-                vold_killProcessesWithOpenFiles(mountpoint, SIGKILL);
+                android::vold::KillProcessesWithOpenFiles(mountpoint, SIGKILL);
             }
         }
 
@@ -1293,7 +1291,7 @@
       SLOGD("unmounting %s succeeded\n", mountpoint);
       rc = 0;
     } else {
-      vold_killProcessesWithOpenFiles(mountpoint, 0);
+      android::vold::KillProcessesWithOpenFiles(mountpoint, 0);
       SLOGE("unmounting %s failed: %s\n", mountpoint, strerror(err));
       rc = -1;
     }
@@ -1419,8 +1417,8 @@
          */
         char ro_prop[PROPERTY_VALUE_MAX];
         property_get("ro.crypto.readonly", ro_prop, "");
-        if (strlen(ro_prop) > 0 && atoi(ro_prop)) {
-            struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, DATA_MNT_POINT);
+        if (strlen(ro_prop) > 0 && std::stoi(ro_prop)) {
+            struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab_default, DATA_MNT_POINT);
             rec->flags |= MS_RDONLY;
         }
 
@@ -1436,7 +1434,7 @@
             SLOGE("Failed to setexeccon");
             return -1;
         }
-        while ((mount_rc = fs_mgr_do_mount(fstab, DATA_MNT_POINT,
+        while ((mount_rc = fs_mgr_do_mount(fstab_default, DATA_MNT_POINT,
                                            crypto_blkdev, 0))
                != 0) {
             if (mount_rc == FS_MGR_DOMNT_BUSY) {
@@ -1517,7 +1515,7 @@
   }
 
   if (get_crypt_ftr_and_key(&crypt_ftr)) {
-    fs_mgr_get_crypt_info(fstab, key_loc, 0, sizeof(key_loc));
+    fs_mgr_get_crypt_info(fstab_default, key_loc, 0, sizeof(key_loc));
 
     /*
      * Only report this error if key_loc is a file and it exists.
@@ -1585,7 +1583,7 @@
     }
   }
 
-  fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));
+  fs_mgr_get_crypt_info(fstab_default, 0, real_blkdev, sizeof(real_blkdev));
 
   // Create crypto block device - all (non fatal) code paths
   // need it
@@ -1617,7 +1615,7 @@
     snprintf(tmp_mount_point, sizeof(tmp_mount_point), "%s/tmp_mnt",
              mount_point);
     mkdir(tmp_mount_point, 0755);
-    if (fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, tmp_mount_point)) {
+    if (fs_mgr_do_mount(fstab_default, DATA_MNT_POINT, crypto_blkdev, tmp_mount_point)) {
       SLOGE("Error temp mounting decrypted block device\n");
       delete_crypto_blk_dev(label);
 
@@ -1813,7 +1811,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 */
@@ -1912,7 +1910,6 @@
     int rc = -1;
 
     if (type == EXT4_FS) {
-#ifdef TARGET_USES_MKE2FS
         args[0] = "/system/bin/mke2fs";
         args[1] = "-M";
         args[2] = "/data";
@@ -1924,16 +1921,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) {
@@ -2027,7 +2014,7 @@
     tot_encryption_size = crypt_ftr->fs_size;
 
     if (how == CRYPTO_ENABLE_WIPE) {
-        struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, DATA_MNT_POINT);
+        struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab_default, DATA_MNT_POINT);
         int fs_type = get_fs_type(rec);
         if (fs_type < 0) {
             SLOGE("cryptfs_enable: unsupported fs type %s\n", rec->fs_type);
@@ -2064,7 +2051,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;
@@ -2127,8 +2114,8 @@
     }
 
     // TODO refactor fs_mgr_get_crypt_info to get both in one call
-    fs_mgr_get_crypt_info(fstab, key_loc, 0, sizeof(key_loc));
-    fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));
+    fs_mgr_get_crypt_info(fstab_default, key_loc, 0, sizeof(key_loc));
+    fs_mgr_get_crypt_info(fstab_default, 0, real_blkdev, sizeof(real_blkdev));
 
     /* Get the size of the real block device */
     fd = open(real_blkdev, O_RDONLY|O_CLOEXEC);
@@ -2423,12 +2410,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);
@@ -2559,30 +2546,23 @@
  * Test if key is part of the multi-entry (field, index) sequence. Return non-zero if key is in the
  * sequence and its index is greater than or equal to index. Return 0 otherwise.
  */
-static int match_multi_entry(const char *key, const char *field, unsigned index) {
-    unsigned int field_len;
-    unsigned int key_index;
-    field_len = strlen(field);
+int match_multi_entry(const char *key, const char *field, unsigned index) {
+    std::string key_ = key;
+    std::string field_ = field;
 
-    if (index == 0) {
-        // The first key in a multi-entry field is just the filedname itself.
-        if (!strcmp(key, field)) {
-            return 1;
-        }
+    std::string parsed_field;
+    unsigned parsed_index;
+
+    std::string::size_type split = key_.find_last_of('_');
+    if (split == std::string::npos) {
+        parsed_field = key_;
+        parsed_index = 0;
+    } else {
+        parsed_field = key_.substr(0, split);
+        parsed_index = std::stoi(key_.substr(split + 1));
     }
-    // Match key against "%s_%d" % (field, index)
-    if (strlen(key) < field_len + 1 + 1) {
-        // Need at least a '_' and a digit.
-        return 0;
-    }
-    if (strncmp(key, field, field_len)) {
-        // If the key does not begin with field, it's not a match.
-        return 0;
-    }
-    if (1 != sscanf(&key[field_len],"_%d", &key_index)) {
-        return 0;
-    }
-    return key_index >= index;
+
+    return parsed_field == field_ && parsed_index >= index;
 }
 
 /*
@@ -2883,79 +2863,6 @@
 
 int cryptfs_isConvertibleToFBE()
 {
-    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, DATA_MNT_POINT);
+    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab_default, DATA_MNT_POINT);
     return fs_mgr_is_convertible_to_fbe(rec) ? 1 : 0;
 }
-
-int cryptfs_create_default_ftr(struct crypt_mnt_ftr* crypt_ftr, __attribute__((unused))int key_length)
-{
-    if (cryptfs_init_crypt_mnt_ftr(crypt_ftr)) {
-        SLOGE("Failed to initialize crypt_ftr");
-        return -1;
-    }
-
-    if (create_encrypted_random_key(DEFAULT_PASSWORD, crypt_ftr->master_key,
-                                    crypt_ftr->salt, crypt_ftr)) {
-        SLOGE("Cannot create encrypted master key\n");
-        return -1;
-    }
-
-    //crypt_ftr->keysize = key_length / 8;
-    return 0;
-}
-
-int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
-                           unsigned char* master_key)
-{
-    int rc;
-
-    unsigned char* intermediate_key = 0;
-    size_t intermediate_key_size = 0;
-
-    if (password == 0 || *password == 0) {
-        password = DEFAULT_PASSWORD;
-    }
-
-    rc = decrypt_master_key(password, master_key, ftr, &intermediate_key,
-                            &intermediate_key_size);
-
-    if (rc) {
-        SLOGE("Can't calculate intermediate key");
-        return rc;
-    }
-
-    int N = 1 << ftr->N_factor;
-    int r = 1 << ftr->r_factor;
-    int p = 1 << ftr->p_factor;
-
-    unsigned char scrypted_intermediate_key[sizeof(ftr->scrypted_intermediate_key)];
-
-    rc = crypto_scrypt(intermediate_key, intermediate_key_size,
-                       ftr->salt, sizeof(ftr->salt), N, r, p,
-                       scrypted_intermediate_key,
-                       sizeof(scrypted_intermediate_key));
-
-    free(intermediate_key);
-
-    if (rc) {
-        SLOGE("Can't scrypt intermediate key");
-        return rc;
-    }
-
-    return memcmp(scrypted_intermediate_key, ftr->scrypted_intermediate_key,
-                  intermediate_key_size);
-}
-
-int cryptfs_set_password(struct crypt_mnt_ftr* ftr, const char* password,
-                         const unsigned char* master_key)
-{
-    return encrypt_master_key(password, ftr->salt, master_key, ftr->master_key,
-                              ftr);
-}
-
-void cryptfs_get_file_encryption_modes(const char **contents_mode_ret,
-                                       const char **filenames_mode_ret)
-{
-    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, DATA_MNT_POINT);
-    fs_mgr_get_file_encryption_modes(rec, contents_mode_ret, filenames_mode_ret);
-}
diff --git a/cryptfs.h b/cryptfs.h
index d20d96d..5642e29 100644
--- a/cryptfs.h
+++ b/cryptfs.h
@@ -222,6 +222,7 @@
 extern "C" {
 #endif
 
+  int match_multi_entry(const char *key, const char *field, unsigned index);
   int wait_and_unmount(const char *mountpoint, bool kill);
 
   typedef int (*kdf_func)(const char *passwd, const unsigned char *salt,
@@ -229,11 +230,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);
@@ -245,15 +246,6 @@
   void cryptfs_clear_password(void);
   int cryptfs_isConvertibleToFBE(void);
 
-  // Functions for file encryption to use to inherit our encryption logic
-  int cryptfs_create_default_ftr(struct crypt_mnt_ftr* ftr, int key_length);
-  int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
-                             unsigned char* master_key);
-  int cryptfs_set_password(struct crypt_mnt_ftr* ftr, const char* password,
-                           const unsigned char* master_key);
-  void cryptfs_get_file_encryption_modes(const char **contents_mode_ret,
-                                         const char **filenames_mode_ret);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/fs/Ext4.cpp b/fs/Ext4.cpp
index 0cf4f9e..89b8414 100644
--- a/fs/Ext4.cpp
+++ b/fs/Ext4.cpp
@@ -42,6 +42,7 @@
 #include <android-base/stringprintf.h>
 #include <cutils/log.h>
 #include <cutils/properties.h>
+#include <ext4_utils/ext4_crypt.h>
 #include <logwrap/logwrap.h>
 #include <selinux/selinux.h>
 
@@ -145,7 +146,7 @@
     rc = mount(c_source, c_target, "ext4", flags, NULL);
 
     if (rc && errno == EROFS) {
-        SLOGE("%s appears to be a read only filesystem - retrying mount RO", c_source);
+        LOG(ERROR) << source << " appears to be a read only filesystem - retrying mount RO";
         flags |= MS_RDONLY;
         rc = mount(c_source, c_target, "ext4", flags, NULL);
     }
diff --git a/fs/Vfat.cpp b/fs/Vfat.cpp
index dc1fe33..538178e 100644
--- a/fs/Vfat.cpp
+++ b/fs/Vfat.cpp
@@ -35,12 +35,8 @@
 
 #include <linux/kdev_t.h>
 
-#define LOG_TAG "Vold"
-
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
-#include <cutils/log.h>
-#include <cutils/properties.h>
 #include <selinux/selinux.h>
 
 #include <logwrap/logwrap.h>
@@ -65,11 +61,6 @@
 }
 
 status_t Check(const std::string& source) {
-    if (access(kFsckPath, X_OK)) {
-        SLOGW("Skipping fs checks\n");
-        return 0;
-    }
-
     int pass = 1;
     int rc = 0;
     do {
@@ -83,38 +74,37 @@
         rc = ForkExecvp(cmd, sFsckUntrustedContext);
 
         if (rc < 0) {
-            SLOGE("Filesystem check failed due to logwrap error");
+            LOG(ERROR) << "Filesystem check failed due to logwrap error";
             errno = EIO;
             return -1;
         }
 
         switch(rc) {
         case 0:
-            SLOGI("Filesystem check completed OK");
+            LOG(INFO) << "Filesystem check completed OK";
             return 0;
 
         case 2:
-            SLOGE("Filesystem check failed (not a FAT filesystem)");
+            LOG(ERROR) << "Filesystem check failed (not a FAT filesystem)";
             errno = ENODATA;
             return -1;
 
         case 4:
             if (pass++ <= 3) {
-                SLOGW("Filesystem modified - rechecking (pass %d)",
-                        pass);
+                LOG(WARNING) << "Filesystem modified - rechecking (pass " << pass << ")";
                 continue;
             }
-            SLOGE("Failing check after too many rechecks");
+            LOG(ERROR) << "Failing check after too many rechecks";
             errno = EIO;
             return -1;
 
         case 8:
-            SLOGE("Filesystem check failed (no filesystem)");
+            LOG(ERROR) << "Filesystem check failed (no filesystem)";
             errno = ENODATA;
             return -1;
 
         default:
-            SLOGE("Filesystem check failed (unknown exit code %d)", rc);
+            LOG(ERROR) << "Filesystem check failed (unknown exit code " << rc << ")";
             errno = EIO;
             return -1;
         }
@@ -128,7 +118,6 @@
         bool createLost) {
     int rc;
     unsigned long flags;
-    char mountData[255];
 
     const char* c_source = source.c_str();
     const char* c_target = target.c_str();
@@ -139,31 +128,29 @@
     flags |= (ro ? MS_RDONLY : 0);
     flags |= (remount ? MS_REMOUNT : 0);
 
-    snprintf(mountData, sizeof(mountData),
+    auto mountData = android::base::StringPrintf(
             "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,shortname=mixed",
             ownerUid, ownerGid, permMask, permMask);
 
-    rc = mount(c_source, c_target, "vfat", flags, mountData);
+    rc = mount(c_source, c_target, "vfat", flags, mountData.c_str());
 
     if (rc && errno == EROFS) {
-        SLOGE("%s appears to be a read only filesystem - retrying mount RO", c_source);
+        LOG(ERROR) << source << " appears to be a read only filesystem - retrying mount RO";
         flags |= MS_RDONLY;
-        rc = mount(c_source, c_target, "vfat", flags, mountData);
+        rc = mount(c_source, c_target, "vfat", flags, mountData.c_str());
     }
 
     if (rc == 0 && createLost) {
-        char *lost_path;
-        asprintf(&lost_path, "%s/LOST.DIR", c_target);
-        if (access(lost_path, F_OK)) {
+        auto lost_path = android::base::StringPrintf("%s/LOST.DIR", target.c_str());
+        if (access(lost_path.c_str(), F_OK)) {
             /*
              * Create a LOST.DIR in the root so we have somewhere to put
              * lost cluster chains (fsck_msdos doesn't currently do this)
              */
-            if (mkdir(lost_path, 0755)) {
-                SLOGE("Unable to create LOST.DIR (%s)", strerror(errno));
+            if (mkdir(lost_path.c_str(), 0755)) {
+                PLOG(ERROR) << "Unable to create LOST.DIR";
             }
         }
-        free(lost_path);
     }
 
     return rc;
@@ -189,16 +176,16 @@
 
     int rc = ForkExecvp(cmd);
     if (rc < 0) {
-        SLOGE("Filesystem format failed due to logwrap error");
+        LOG(ERROR) << "Filesystem format failed due to logwrap error";
         errno = EIO;
         return -1;
     }
 
     if (rc == 0) {
-        SLOGI("Filesystem formatted OK");
+        LOG(INFO) << "Filesystem formatted OK";
         return 0;
     } else {
-        SLOGE("Format failed (unknown exit code %d)", rc);
+        LOG(ERROR) << "Format failed (unknown exit code " << rc << ")";
         errno = EIO;
         return -1;
     }
diff --git a/main.cpp b/main.cpp
index 30c839e..01a2168 100644
--- a/main.cpp
+++ b/main.cpp
@@ -14,19 +14,21 @@
  * limitations under the License.
  */
 
-#include "Disk.h"
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
+#include "model/Disk.h"
 #include "VolumeManager.h"
-#include "CommandListener.h"
-#include "CryptCommandListener.h"
 #include "NetlinkManager.h"
+#include "VoldNativeService.h"
+#include "VoldUtil.h"
 #include "cryptfs.h"
 #include "sehandle.h"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <cutils/klog.h>
-#include <cutils/properties.h>
-#include <cutils/sockets.h>
+#include <utils/Trace.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -43,8 +45,6 @@
 static void coldboot(const char *path);
 static void parse_args(int argc, char** argv);
 
-struct fstab *fstab;
-
 struct selabel_handle *sehandle;
 
 using android::base::StringPrintf;
@@ -53,6 +53,8 @@
     setenv("ANDROID_LOG_TAGS", "*:v", 1);
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
+    ATRACE_BEGIN("main");
+
     LOG(INFO) << "Vold 3.0 (the awakening) firing up";
 
     LOG(VERBOSE) << "Detected support for:"
@@ -61,8 +63,6 @@
             << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");
 
     VolumeManager *vm;
-    CommandListener *cl;
-    CryptCommandListener *ccl;
     NetlinkManager *nm;
 
     parse_args(argc, argv);
@@ -72,10 +72,6 @@
         selinux_android_set_sehandle(sehandle);
     }
 
-    // Quickly throw a CLOEXEC on the socket we just inherited from init
-    fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);
-    fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);
-
     mkdir("/dev/block/vold", 0755);
 
     /* For when cryptfs checks and mounts an encrypted filesystem */
@@ -92,15 +88,10 @@
         exit(1);
     }
 
-    if (property_get_bool("vold.debug", false)) {
+    if (android::base::GetBoolProperty("vold.debug", false)) {
         vm->setDebug(true);
     }
 
-    cl = new CommandListener();
-    ccl = new CryptCommandListener();
-    vm->setBroadcaster((SocketListener *) cl);
-    nm->setBroadcaster((SocketListener *) cl);
-
     if (vm->start()) {
         PLOG(ERROR) << "Unable to start VolumeManager";
         exit(1);
@@ -113,39 +104,35 @@
         PLOG(ERROR) << "Error reading configuration... continuing anyways";
     }
 
+    ATRACE_BEGIN("VoldNativeService::start");
+    if (android::vold::VoldNativeService::start() != android::OK) {
+        LOG(ERROR) << "Unable to start VoldNativeService";
+        exit(1);
+    }
+    ATRACE_END();
+
+    ATRACE_BEGIN("NetlinkManager::start");
     if (nm->start()) {
         PLOG(ERROR) << "Unable to start NetlinkManager";
         exit(1);
     }
-
-    /*
-     * Now that we're up, we can respond to commands
-     */
-    if (cl->startListener()) {
-        PLOG(ERROR) << "Unable to start CommandListener";
-        exit(1);
-    }
-
-    if (ccl->startListener()) {
-        PLOG(ERROR) << "Unable to start CryptCommandListener";
-        exit(1);
-    }
+    ATRACE_END();
 
     // This call should go after listeners are started to avoid
     // a deadlock between vold and init (see b/34278978 for details)
-    property_set("vold.has_adoptable", has_adoptable ? "1" : "0");
-    property_set("vold.has_quota", has_quota ? "1" : "0");
+    android::base::SetProperty("vold.has_adoptable", has_adoptable ? "1" : "0");
+    android::base::SetProperty("vold.has_quota", has_quota ? "1" : "0");
 
     // Do coldboot here so it won't block booting,
     // also the cold boot is needed in case we have flash drive
     // connected before Vold launched
     coldboot("/sys/block");
-    // Eventually we'll become the monitoring thread
-    while(1) {
-        pause();
-    }
 
-    LOG(ERROR) << "Vold exiting";
+    ATRACE_END();
+
+    android::IPCThreadState::self()->joinThreadPool();
+    LOG(INFO) << "vold shutting down";
+
     exit(0);
 }
 
@@ -209,6 +196,7 @@
 }
 
 static void coldboot(const char *path) {
+    ATRACE_NAME("coldboot");
     DIR *d = opendir(path);
     if(d) {
         do_coldboot(d, 0);
@@ -217,8 +205,10 @@
 }
 
 static int process_config(VolumeManager *vm, bool* has_adoptable, bool* has_quota) {
-    fstab = fs_mgr_read_fstab_default();
-    if (!fstab) {
+    ATRACE_NAME("process_config");
+
+    fstab_default = fs_mgr_read_fstab_default();
+    if (!fstab_default) {
         PLOG(ERROR) << "Failed to open default fstab";
         return -1;
     }
@@ -226,27 +216,28 @@
     /* Loop through entries looking for ones that vold manages */
     *has_adoptable = false;
     *has_quota = false;
-    for (int i = 0; i < fstab->num_entries; i++) {
-        if (fs_mgr_is_quota(&fstab->recs[i])) {
+    for (int i = 0; i < fstab_default->num_entries; i++) {
+        auto rec = &fstab_default->recs[i];
+        if (fs_mgr_is_quota(rec)) {
             *has_quota = true;
         }
 
-        if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
-            if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
+        if (fs_mgr_is_voldmanaged(rec)) {
+            if (fs_mgr_is_nonremovable(rec)) {
                 LOG(WARNING) << "nonremovable no longer supported; ignoring volume";
                 continue;
             }
 
-            std::string sysPattern(fstab->recs[i].blk_device);
-            std::string nickname(fstab->recs[i].label);
+            std::string sysPattern(rec->blk_device);
+            std::string nickname(rec->label);
             int flags = 0;
 
-            if (fs_mgr_is_encryptable(&fstab->recs[i])) {
+            if (fs_mgr_is_encryptable(rec)) {
                 flags |= android::vold::Disk::Flags::kAdoptable;
                 *has_adoptable = true;
             }
-            if (fs_mgr_is_noemulatedsd(&fstab->recs[i])
-                    || property_get_bool("vold.debug.default_primary", false)) {
+            if (fs_mgr_is_noemulatedsd(rec)
+                    || android::base::GetBoolProperty("vold.debug.default_primary", false)) {
                 flags |= android::vold::Disk::Flags::kDefaultPrimary;
             }
 
diff --git a/Disk.cpp b/model/Disk.cpp
similarity index 87%
rename from Disk.cpp
rename to model/Disk.cpp
index 9c22400..291c2e1 100644
--- a/Disk.cpp
+++ b/model/Disk.cpp
@@ -20,14 +20,16 @@
 #include "Utils.h"
 #include "VolumeBase.h"
 #include "VolumeManager.h"
-#include "ResponseCode.h"
 #include "Ext4Crypt.h"
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
-#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/parseint.h>
 #include <diskconfig/diskconfig.h>
+#include <ext4_utils/ext4_crypt.h>
 
 #include <vector>
 #include <fcntl.h>
@@ -151,7 +153,10 @@
 status_t Disk::create() {
     CHECK(!mCreated);
     mCreated = true;
-    notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
+
+    auto listener = VolumeManager::Instance()->getListener();
+    if (listener) listener->onDiskCreated(getId(), mFlags);
+
     readMetadata();
     readPartitions();
     return OK;
@@ -161,7 +166,10 @@
     CHECK(mCreated);
     destroyAllVolumes();
     mCreated = false;
-    notifyEvent(ResponseCode::DiskDestroyed);
+
+    auto listener = VolumeManager::Instance()->getListener();
+    if (listener) listener->onDiskDestroyed(getId());
+
     return OK;
 }
 
@@ -257,7 +265,11 @@
             PLOG(WARNING) << "Failed to read manufacturer from " << path;
             return -errno;
         }
-        uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);
+        int64_t manfid;
+        if (!android::base::ParseInt(tmp, &manfid)) {
+            PLOG(WARNING) << "Failed to parse manufacturer " << tmp;
+            return -EINVAL;
+        }
         // Our goal here is to give the user a meaningful label, ideally
         // matching whatever is silk-screened on the card.  To reduce
         // user confusion, this list doesn't contain white-label manfid.
@@ -281,14 +293,15 @@
     }
     }
 
-    notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRIu64, mSize));
-    notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
-    notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);
+    auto listener = VolumeManager::Instance()->getListener();
+    if (listener) listener->onDiskMetadataChanged(getId(),
+            mSize, mLabel, mSysPath);
+
     return OK;
 }
 
 status_t Disk::readPartitions() {
-    int8_t maxMinors = getMaxMinors();
+    int maxMinors = getMaxMinors();
     if (maxMinors < 0) {
         return -ENOTSUP;
     }
@@ -306,7 +319,10 @@
     status_t res = ForkExecvp(cmd, output);
     if (res != OK) {
         LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
-        notifyEvent(ResponseCode::DiskScanned);
+
+        auto listener = VolumeManager::Instance()->getListener();
+        if (listener) listener->onDiskScanned(getId());
+
         mJustPartitioned = false;
         return res;
     }
@@ -314,31 +330,40 @@
     Table table = Table::kUnknown;
     bool foundParts = false;
     for (const auto& line : output) {
-        char* cline = (char*) line.c_str();
-        char* token = strtok(cline, kSgdiskToken);
-        if (token == nullptr) continue;
+        auto split = android::base::Split(line, kSgdiskToken);
+        auto it = split.begin();
+        if (it == split.end()) continue;
 
-        if (!strcmp(token, "DISK")) {
-            const char* type = strtok(nullptr, kSgdiskToken);
-            if (!strcmp(type, "mbr")) {
+        if (*it == "DISK") {
+            if (++it == split.end()) continue;
+            if (*it == "mbr") {
                 table = Table::kMbr;
-            } else if (!strcmp(type, "gpt")) {
+            } else if (*it == "gpt") {
                 table = Table::kGpt;
+            } else {
+                LOG(WARNING) << "Invalid partition table " << *it;
+                continue;
             }
-        } else if (!strcmp(token, "PART")) {
+        } else if (*it == "PART") {
             foundParts = true;
-            int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);
-            if (i <= 0 || i > maxMinors) {
-                LOG(WARNING) << mId << " is ignoring partition " << i
-                        << " beyond max supported devices";
+
+            if (++it == split.end()) continue;
+            int i = 0;
+            if (!android::base::ParseInt(*it, &i, 1, maxMinors)) {
+                LOG(WARNING) << "Invalid partition number " << *it;
                 continue;
             }
             dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
 
             if (table == Table::kMbr) {
-                const char* type = strtok(nullptr, kSgdiskToken);
+                if (++it == split.end()) continue;
+                int type = 0;
+                if (!android::base::ParseInt("0x" + *it, &type)) {
+                    LOG(WARNING) << "Invalid partition type " << *it;
+                    continue;
+                }
 
-                switch (strtol(type, nullptr, 16)) {
+                switch (type) {
                 case 0x06: // FAT16
                 case 0x0b: // W95 FAT32 (LBA)
                 case 0x0c: // W95 FAT32 (LBA)
@@ -347,12 +372,14 @@
                     break;
                 }
             } else if (table == Table::kGpt) {
-                const char* typeGuid = strtok(nullptr, kSgdiskToken);
-                const char* partGuid = strtok(nullptr, kSgdiskToken);
+                if (++it == split.end()) continue;
+                auto typeGuid = *it;
+                if (++it == split.end()) continue;
+                auto partGuid = *it;
 
-                if (!strcasecmp(typeGuid, kGptBasicData)) {
+                if (android::base::EqualsIgnoreCase(typeGuid, kGptBasicData)) {
                     createPublicVolume(partDevice);
-                } else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {
+                } else if (android::base::EqualsIgnoreCase(typeGuid, kGptAndroidExpand)) {
                     createPrivateVolume(partDevice, partGuid);
                 }
             }
@@ -365,14 +392,16 @@
 
         std::string fsType;
         std::string unused;
-        if (ReadMetadataUntrusted(mDevPath, fsType, unused, unused) == OK) {
+        if (ReadMetadataUntrusted(mDevPath, &fsType, &unused, &unused) == OK) {
             createPublicVolume(mDevice);
         } else {
             LOG(WARNING) << mId << " failed to identify, giving up";
         }
     }
 
-    notifyEvent(ResponseCode::DiskScanned);
+    auto listener = VolumeManager::Instance()->getListener();
+    if (listener) listener->onDiskScanned(getId());
+
     mJustPartitioned = false;
     return OK;
 }
@@ -534,16 +563,6 @@
     return OK;
 }
 
-void Disk::notifyEvent(int event) {
-    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
-            getId().c_str(), false);
-}
-
-void Disk::notifyEvent(int event, const std::string& value) {
-    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
-            StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
-}
-
 int Disk::getMaxMinors() {
     // Figure out maximum partition devices supported
     unsigned int majorId = major(mDevice);
@@ -554,7 +573,7 @@
             LOG(ERROR) << "Failed to read max minors";
             return -errno;
         }
-        return atoi(tmp.c_str());
+        return std::stoi(tmp);
     }
     case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:
     case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
@@ -570,7 +589,7 @@
             LOG(ERROR) << "Failed to read max minors";
             return -errno;
         }
-        return atoi(tmp.c_str());
+        return std::stoi(tmp);
     }
     default: {
         if (isVirtioBlkDevice(majorId)) {
diff --git a/Disk.h b/model/Disk.h
similarity index 97%
rename from Disk.h
rename to model/Disk.h
index 77ec7df..63acf6a 100644
--- a/Disk.h
+++ b/model/Disk.h
@@ -79,9 +79,6 @@
     status_t partitionPrivate();
     status_t partitionMixed(int8_t ratio);
 
-    void notifyEvent(int msg);
-    void notifyEvent(int msg, const std::string& value);
-
 private:
     /* ID that uniquely references this disk */
     std::string mId;
diff --git a/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
similarity index 92%
rename from EmulatedVolume.cpp
rename to model/EmulatedVolume.cpp
index 21b290a..d40cf3f 100644
--- a/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -21,6 +21,7 @@
 #include <android-base/logging.h>
 #include <cutils/fs.h>
 #include <private/android_filesystem_config.h>
+#include <utils/Timers.h>
 
 #include <fcntl.h>
 #include <stdlib.h>
@@ -100,9 +101,16 @@
         return -errno;
     }
 
+    nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
     while (before == GetDevice(mFuseWrite)) {
         LOG(VERBOSE) << "Waiting for FUSE to spin up...";
         usleep(50000); // 50ms
+
+        nsecs_t now = systemTime(SYSTEM_TIME_BOOTTIME);
+        if (nanoseconds_to_milliseconds(now - start) > 5000) {
+            LOG(WARNING) << "Timed out while waiting for FUSE to spin up";
+            return -ETIMEDOUT;
+        }
     }
     /* sdcardfs will have exited already. FUSE will still be running */
     TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, WNOHANG));
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 95%
rename from PrivateVolume.cpp
rename to model/PrivateVolume.cpp
index e66e04d..48d041b 100644
--- a/PrivateVolume.cpp
+++ b/model/PrivateVolume.cpp
@@ -20,7 +20,6 @@
 #include "EmulatedVolume.h"
 #include "Utils.h"
 #include "VolumeManager.h"
-#include "ResponseCode.h"
 #include "cryptfs.h"
 
 #include <android-base/stringprintf.h>
@@ -54,10 +53,11 @@
 }
 
 status_t PrivateVolume::readMetadata() {
-    status_t res = ReadMetadata(mDmDevPath, mFsType, mFsUuid, mFsLabel);
-    notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
-    notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
-    notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
+    status_t res = ReadMetadata(mDmDevPath, &mFsType, &mFsUuid, &mFsLabel);
+
+    auto listener = getListener();
+    if (listener) listener->onVolumeMetadataChanged(getId(), mFsType, mFsUuid, mFsLabel);
+
     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 92%
rename from PublicVolume.cpp
rename to model/PublicVolume.cpp
index f976c4a..98c897f 100644
--- a/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -18,12 +18,12 @@
 #include "PublicVolume.h"
 #include "Utils.h"
 #include "VolumeManager.h"
-#include "ResponseCode.h"
 
 #include <android-base/stringprintf.h>
 #include <android-base/logging.h>
 #include <cutils/fs.h>
 #include <private/android_filesystem_config.h>
+#include <utils/Timers.h>
 
 #include <fcntl.h>
 #include <stdlib.h>
@@ -52,10 +52,11 @@
 }
 
 status_t PublicVolume::readMetadata() {
-    status_t res = ReadMetadataUntrusted(mDevPath, mFsType, mFsUuid, mFsLabel);
-    notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
-    notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
-    notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
+    status_t res = ReadMetadataUntrusted(mDevPath, &mFsType, &mFsUuid, &mFsLabel);
+
+    auto listener = getListener();
+    if (listener) listener->onVolumeMetadataChanged(getId(), mFsType, mFsUuid, mFsLabel);
+
     return res;
 }
 
@@ -186,9 +187,16 @@
         return -errno;
     }
 
+    nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
     while (before == GetDevice(mFuseWrite)) {
         LOG(VERBOSE) << "Waiting for FUSE to spin up...";
         usleep(50000); // 50ms
+
+        nsecs_t now = systemTime(SYSTEM_TIME_BOOTTIME);
+        if (nanoseconds_to_milliseconds(now - start) > 5000) {
+            LOG(WARNING) << "Timed out while waiting for FUSE to spin up";
+            return -ETIMEDOUT;
+        }
     }
     /* sdcardfs will have exited already. FUSE will still be running */
     TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, WNOHANG));
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 86%
rename from VolumeBase.cpp
rename to model/VolumeBase.cpp
index 3f27d87..429f134 100644
--- a/VolumeBase.cpp
+++ b/model/VolumeBase.cpp
@@ -17,7 +17,6 @@
 #include "Utils.h"
 #include "VolumeBase.h"
 #include "VolumeManager.h"
-#include "ResponseCode.h"
 
 #include <android-base/stringprintf.h>
 #include <android-base/logging.h>
@@ -44,7 +43,9 @@
 
 void VolumeBase::setState(State state) {
     mState = state;
-    notifyEvent(ResponseCode::VolumeStateChanged, StringPrintf("%d", mState));
+
+    auto listener = getListener();
+    if (listener) listener->onVolumeStateChanged(getId(), static_cast<int32_t>(mState));
 }
 
 status_t VolumeBase::setDiskId(const std::string& diskId) {
@@ -114,7 +115,10 @@
     }
 
     mPath = path;
-    notifyEvent(ResponseCode::VolumePathChanged, mPath);
+
+    auto listener = getListener();
+    if (listener) listener->onVolumePathChanged(getId(), mPath);
+
     return OK;
 }
 
@@ -125,20 +129,19 @@
     }
 
     mInternalPath = internalPath;
-    notifyEvent(ResponseCode::VolumeInternalPathChanged, mInternalPath);
+
+    auto listener = getListener();
+    if (listener) listener->onVolumeInternalPathChanged(getId(), mInternalPath);
+
     return OK;
 }
 
-void VolumeBase::notifyEvent(int event) {
-    if (mSilent) return;
-    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
-            getId().c_str(), false);
-}
-
-void VolumeBase::notifyEvent(int event, const std::string& value) {
-    if (mSilent) return;
-    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
-            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) {
@@ -163,8 +166,11 @@
 
     mCreated = true;
     status_t res = doCreate();
-    notifyEvent(ResponseCode::VolumeCreated,
-            StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
+
+    auto listener = getListener();
+    if (listener) listener->onVolumeCreated(getId(),
+            static_cast<int32_t>(mType), mDiskId, mPartGuid);
+
     setState(State::kUnmounted);
     return res;
 }
@@ -183,7 +189,10 @@
         setState(State::kRemoved);
     }
 
-    notifyEvent(ResponseCode::VolumeDestroyed);
+
+    auto listener = getListener();
+    if (listener) listener->onVolumeDestroyed(getId());
+
     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..4aa8b02 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>
@@ -114,8 +115,7 @@
     status_t setPath(const std::string& path);
     status_t setInternalPath(const std::string& internalPath);
 
-    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 */
diff --git a/prepare_dir.cpp b/prepare_dir.cpp
new file mode 100644
index 0000000..1d91458
--- /dev/null
+++ b/prepare_dir.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+/*
+ * Tool to create a directory with the right SELinux context applied, or
+ * apply the context if it's absent. Also fixes mode, uid, gid.
+ */
+
+#include <string>
+#include <vector>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <android-base/logging.h>
+
+#include <cutils/fs.h>
+#include <selinux/android.h>
+
+void usage(const char* progname) {
+    fprintf(stderr, "Usage: %s --mode MODE --uid UID --gid GID -- <path>\n", progname);
+}
+
+bool small_int(const std::string& s) {
+    return !s.empty() && s.size() < 7 && s.find_first_not_of("0123456789") == std::string::npos;
+}
+
+int main(int argc, const char* const argv[]) {
+    setenv("ANDROID_LOG_TAGS", "*:v", 1);
+    android::base::InitLogging(const_cast<char**>(argv));
+    std::vector<std::string> args(argv + 1, argv + argc);
+    // Enforce exact format of arguments. You can always loosen but you can never tighten :)
+    if (args.size() != 8 || args[0] != "--mode" || !small_int(args[1]) || args[2] != "--uid" ||
+        !small_int(args[3]) || args[4] != "--gid" || !small_int(args[5]) || args[6] != "--") {
+        usage(argv[0]);
+        return -1;
+    }
+    mode_t mode = (mode_t)stoi(args[1], nullptr, 8);
+    uid_t uid = (uid_t)stoi(args[3]);
+    gid_t gid = (gid_t)stoi(args[5]);
+    const char* path = args[7].c_str();
+
+    struct selabel_handle* sehandle = selinux_android_file_context_handle();
+    char* secontext = nullptr;
+    if (sehandle) {
+        if (selabel_lookup(sehandle, &secontext, path, S_IFDIR) == 0) {
+            setfscreatecon(secontext);
+        }
+    }
+
+    if (fs_prepare_dir(path, mode, uid, gid) != 0) {
+        return -1;
+    }
+    if (secontext) {
+        char* oldsecontext = nullptr;
+        if (lgetfilecon(path, &oldsecontext) < 0) {
+            PLOG(ERROR) << "Unable to read secontext for: " << path;
+            return -1;
+        }
+        if (strcmp(secontext, oldsecontext) != 0) {
+            LOG(INFO) << "Relabelling from " << oldsecontext << " to " << secontext << ": " << path;
+            if (lsetfilecon(path, secontext) != 0) {
+                PLOG(ERROR) << "Relabelling failed for: " << path;
+            }
+        }
+    }
+    return 0;
+}
diff --git a/secontext.h b/secontext.h
index 08ad48e..f5339c8 100644
--- a/secontext.h
+++ b/secontext.h
@@ -18,8 +18,6 @@
 
 #include <selinux/selinux.h>
 
-__BEGIN_DECLS
 security_context_t secontextFsck();
-__END_DECLS
 
 #endif
diff --git a/tests/Android.mk b/tests/Android.mk
index 5b8ff09..49da010 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -5,11 +5,16 @@
 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_SRC_FILES := \
+	cryptfs_test.cpp \
+	Utils_test.cpp \
+	VolumeManager_test.cpp \
+
 LOCAL_MODULE := vold_tests
 LOCAL_MODULE_TAGS := eng tests
 
@@ -37,4 +42,5 @@
 LOCAL_MODULE := vold_cryptfs_scrypt_hidlization_equivalence_test
 LOCAL_MODULE_TAGS := eng tests
 
+LOCAL_CFLAGS := -Wall -Werror
 include $(BUILD_NATIVE_TEST)
diff --git a/tests/CryptfsScryptHidlizationEquivalence_test.cpp b/tests/CryptfsScryptHidlizationEquivalence_test.cpp
index 91ddb2b..3a6c029 100644
--- a/tests/CryptfsScryptHidlizationEquivalence_test.cpp
+++ b/tests/CryptfsScryptHidlizationEquivalence_test.cpp
@@ -369,6 +369,7 @@
     return keymaster_compatibility_cryptfs_scrypt();
 }
 
+#if 0
 /* Create a new keymaster key and store it in this footer */
 static int keymaster_create_key_new(struct crypt_mnt_ftr *ftr)
 {
@@ -390,6 +391,7 @@
     }
     return 0;
 }
+#endif
 
 /* This signs the given object using the keymaster key. */
 static int keymaster_sign_object_new(struct crypt_mnt_ftr *ftr,
diff --git a/tests/Utils_test.cpp b/tests/Utils_test.cpp
new file mode 100644
index 0000000..ab9809e
--- /dev/null
+++ b/tests/Utils_test.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "../Utils.h"
+
+namespace android {
+namespace vold {
+
+class UtilsTest : public testing::Test {
+};
+
+TEST_F(UtilsTest, FindValueTest) {
+    std::string tmp;
+
+    ASSERT_FALSE(FindValue("", "KEY", &tmp));
+    ASSERT_FALSE(FindValue("BADKEY=\"VALUE\"", "KEY", &tmp));
+
+    ASSERT_TRUE(FindValue("KEY=\"VALUE\"", "KEY", &tmp));
+    ASSERT_EQ("VALUE", tmp);
+
+    ASSERT_TRUE(FindValue("FOO=\"BAR\" KEY=\"VALUE VALUE\" BAR=\"BAZ\"", "KEY", &tmp));
+    ASSERT_EQ("VALUE VALUE", tmp);
+
+    ASSERT_TRUE(FindValue("BADKEY=\"VALUE\" KEY=\"BAZ\"", "KEY", &tmp));
+    ASSERT_EQ("BAZ", tmp);
+}
+
+}
+}
diff --git a/tests/VolumeManager_test.cpp b/tests/VolumeManager_test.cpp
index c0c1fa5..f661d49 100644
--- a/tests/VolumeManager_test.cpp
+++ b/tests/VolumeManager_test.cpp
@@ -34,31 +34,4 @@
     }
 };
 
-TEST_F(VolumeManagerTest, AsecHashTests) {
-    char buffer[MD5_ASCII_LENGTH_PLUS_NULL];
-    char* dst = reinterpret_cast<char*>(&buffer);
-
-    const char* src1 = "";
-    const char* exp1 = "d41d8cd98f00b204e9800998ecf8427e";
-
-    EXPECT_TRUE(VolumeManager::asecHash(exp1, (char*)NULL, sizeof(buffer)) == NULL && errno == ESPIPE)
-            << "Should return NULL and set errno to ESPIPE when destination buffer is NULL";
-    EXPECT_TRUE(VolumeManager::asecHash(exp1, dst, 0) == NULL && errno == ESPIPE)
-            << "Should return NULL and set errno to ESPIPE when destination buffer length is 0";
-    EXPECT_TRUE(VolumeManager::asecHash((const char*)NULL, dst, sizeof(buffer)) == NULL && errno == ESPIPE)
-            << "Should return NULL and set errno to ESPIPE when source buffer is NULL";
-
-    EXPECT_FALSE(VolumeManager::asecHash(src1, dst, sizeof(buffer)) == NULL)
-            << "Should not return NULL on valid source, destination, and destination size";
-    EXPECT_STREQ(exp1, dst)
-            << "MD5 summed output should match";
-
-    const char* src2 = "android";
-    const char* exp2 = "c31b32364ce19ca8fcd150a417ecce58";
-    EXPECT_FALSE(VolumeManager::asecHash(src2, dst, sizeof(buffer)) == NULL)
-            << "Should not return NULL on valid source, destination, and destination size";
-    EXPECT_STREQ(exp2, dst)
-            << "MD5 summed output should match";
-}
-
 }
diff --git a/tests/cryptfs_test.cpp b/tests/cryptfs_test.cpp
new file mode 100644
index 0000000..6875c0f
--- /dev/null
+++ b/tests/cryptfs_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "../cryptfs.h"
+
+namespace android {
+
+class CryptfsTest : public testing::Test {
+protected:
+    virtual void SetUp() {
+    }
+
+    virtual void TearDown() {
+    }
+};
+
+TEST_F(CryptfsTest, MatchMultiEntryTest) {
+    ASSERT_NE(0, match_multi_entry("foo", "foo", 0));
+    ASSERT_NE(0, match_multi_entry("foo_0", "foo", 0));
+    ASSERT_NE(0, match_multi_entry("foo_1", "foo", 0));
+    ASSERT_NE(0, match_multi_entry("foo_2", "foo", 0));
+
+    ASSERT_EQ(0, match_multi_entry("foo", "foo", 1));
+    ASSERT_EQ(0, match_multi_entry("foo_0", "foo", 1));
+    ASSERT_NE(0, match_multi_entry("foo_1", "foo", 1));
+    ASSERT_NE(0, match_multi_entry("foo_2", "foo", 1));
+
+    ASSERT_EQ(0, match_multi_entry("foo", "foo", 2));
+    ASSERT_EQ(0, match_multi_entry("foo_0", "foo", 2));
+    ASSERT_EQ(0, match_multi_entry("foo_1", "foo", 2));
+    ASSERT_NE(0, match_multi_entry("foo_2", "foo", 2));
+
+    ASSERT_EQ(0, match_multi_entry("food", "foo", 0));
+    ASSERT_EQ(0, match_multi_entry("foo", "food", 0));
+    ASSERT_EQ(0, match_multi_entry("foo", "bar", 0));
+    ASSERT_EQ(0, match_multi_entry("foo_2", "bar", 0));
+}
+
+}
diff --git a/vdc.cpp b/vdc.cpp
index 7ab5fcc..4e0c3a9 100644
--- a/vdc.cpp
+++ b/vdc.cpp
@@ -24,31 +24,44 @@
 #include <stdlib.h>
 #include <poll.h>
 
-#include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/time.h>
 #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>
 
 static void usage(char *progname);
-static int do_monitor(int sock, int stop_after_cmd);
-static int do_cmd(int sock, int argc, char **argv);
 
-static constexpr int kCommandTimeoutMs = 20 * 1000;
+static android::sp<android::IBinder> getServiceAggressive() {
+    android::sp<android::IBinder> res;
+    auto sm = android::defaultServiceManager();
+    auto name = android::String16("vold");
+    for (int i = 0; i < 500; i++) {
+        res = sm->checkService(name);
+        if (res) {
+            LOG(VERBOSE) << "Waited " << (i * 10) << "ms for vold";
+            break;
+        }
+        usleep(10000); // 10ms
+    }
+    return res;
+}
 
 int main(int argc, char **argv) {
     int sock;
-    int wait_for_socket;
+    int wait;
     char *progname;
 
     progname = argv[0];
 
+    setenv("ANDROID_LOG_TAGS", "*:v", 1);
     if (getppid() == 1) {
         // If init is calling us then it's during boot and we should log to kmsg
         android::base::InitLogging(argv, &android::base::KernelLogger);
@@ -56,126 +69,46 @@
         android::base::InitLogging(argv, &android::base::StderrLogger);
     }
 
-    wait_for_socket = argc > 1 && strcmp(argv[1], "--wait") == 0;
-    if (wait_for_socket) {
+    wait = argc > 1 && strcmp(argv[1], "--wait") == 0;
+    if (wait) {
         argv++;
         argc--;
     }
 
-    if (argc < 2) {
+    if (argc < 3) {
         usage(progname);
         exit(5);
     }
 
-    const char* sockname = "vold";
-    if (!strcmp(argv[1], "cryptfs")) {
-        sockname = "cryptd";
-    }
+    std::string arg1 = argv[1];
+    std::string arg2 = argv[2];
 
-    while ((sock = socket_local_client(sockname,
-                                 ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                 SOCK_STREAM)) < 0) {
-        if (!wait_for_socket) {
-            PLOG(ERROR) << "Error connecting to " << sockname;
-            exit(4);
-        } else {
-            usleep(10000);
-        }
+    android::sp<android::IBinder> binder = getServiceAggressive();
+    if (!binder) {
+        LOG(ERROR) << "Failed to obtain vold Binder";
+        exit(EINVAL);
     }
+    auto vold = android::interface_cast<android::os::IVold>(binder);
 
-    if (!strcmp(argv[1], "monitor")) {
-        exit(do_monitor(sock, 0));
+    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 == "cryptfs" && arg2 == "mountdefaultencrypted") {
+        exit(vold->mountDefaultEncrypted().isOk() ? 0 : ENOTTY);
+    } else if (arg1 == "volume" && arg2 == "shutdown") {
+        exit(vold->shutdown().isOk() ? 0 : ENOTTY);
     } else {
-        exit(do_cmd(sock, argc, argv));
+        LOG(ERROR) << "Raw commands are no longer supported";
+        exit(EINVAL);
     }
 }
 
-static int do_cmd(int sock, int argc, char **argv) {
-    int seq = getpid();
-
-    std::string cmd(android::base::StringPrintf("%d ", seq));
-    for (int i = 1; i < argc; i++) {
-        if (!strchr(argv[i], ' ')) {
-            cmd.append(argv[i]);
-        } else {
-            cmd.push_back('\"');
-            cmd.append(argv[i]);
-            cmd.push_back('\"');
-        }
-
-        if (i < argc - 1) {
-            cmd.push_back(' ');
-        }
-    }
-
-    if (TEMP_FAILURE_RETRY(write(sock, cmd.c_str(), cmd.length() + 1)) < 0) {
-        PLOG(ERROR) << "Failed to write command";
-        return errno;
-    }
-
-    return do_monitor(sock, seq);
-}
-
-static int do_monitor(int sock, int stop_after_seq) {
-    char buffer[4096];
-    int timeout = kCommandTimeoutMs;
-
-    if (stop_after_seq == 0) {
-        LOG(INFO) << "Connected to vold";
-        timeout = -1;
-    }
-
-    while (1) {
-        struct pollfd poll_sock = { sock, POLLIN, 0 };
-        int rc = TEMP_FAILURE_RETRY(poll(&poll_sock, 1, timeout));
-        if (rc == 0) {
-            LOG(ERROR) << "Timeout waiting for " << stop_after_seq;
-            return ETIMEDOUT;
-        } else if (rc < 0) {
-            PLOG(ERROR) << "Failed during poll";
-            return errno;
-        }
-
-        if (!(poll_sock.revents & POLLIN)) {
-            LOG(INFO) << "No data; trying again";
-            continue;
-        }
-
-        memset(buffer, 0, sizeof(buffer));
-        rc = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
-        if (rc == 0) {
-            LOG(ERROR) << "Lost connection, did vold crash?";
-            return ECONNRESET;
-        } else if (rc < 0) {
-            PLOG(ERROR) << "Error reading data";
-            return errno;
-        }
-
-        int offset = 0;
-        for (int i = 0; i < rc; i++) {
-            if (buffer[i] == '\0') {
-                char* res = buffer + offset;
-                fprintf(stdout, "%s\n", res);
-
-                int code = atoi(strtok(res, " "));
-                if (code >= 200 && code < 600) {
-                    int seq = atoi(strtok(nullptr, " "));
-                    if (seq == stop_after_seq) {
-                        if (code == 200) {
-                            return 0;
-                        } else {
-                            return code;
-                        }
-                    }
-                }
-
-                offset = i + 1;
-            }
-        }
-    }
-    return EIO;
-}
-
 static void usage(char *progname) {
     LOG(INFO) << "Usage: " << progname << " [--wait] <monitor>|<cmd> [arg1] [arg2...]";
 }
diff --git a/vold.rc b/vold.rc
index 87e2fd8..c27aeda 100644
--- a/vold.rc
+++ b/vold.rc
@@ -2,9 +2,6 @@
         --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
         --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
     class core
-    socket vold stream 0660 root mount
-    socket cryptd stream 0660 root mount
     ioprio be 2
     writepid /dev/cpuset/foreground/tasks
     shutdown critical
-
diff --git a/vold_prepare_subdirs b/vold_prepare_subdirs
new file mode 100644
index 0000000..cdea243
--- /dev/null
+++ b/vold_prepare_subdirs
@@ -0,0 +1,67 @@
+#!/system/bin/sh
+#
+# 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.
+
+# Set up or tear down subdirectories of vold-created directories.
+#
+# This is kept separate from vold because under the SELinux rules, it has privileges vold doesn't
+# have. In particular, prepare_dir sets SELinux labels on subdirectories based on file_contexts,
+# so this script has some relabelling privileges.
+
+
+set -e
+action="$1"
+dirtype="$2"
+volume_uuid="$3"
+user_id="$4"
+path="$5"
+
+case "$user_id" in
+    *[!0-9]* | '')
+        echo "Invalid user id"
+        exit -1
+        ;;
+esac
+
+if [ x"$volume_uuid" != x ] ; then
+    echo "Volume must be root volume"
+    exit -1;
+fi
+
+case "$dirtype" in
+    misc_de|misc_ce)
+        computed_path="/data/$dirtype/$user_id"
+        if [ x"$computed_path" != x"$path" ] ; then
+            echo "Parameter path didn't match computed path: " $computed_path
+            exit -1;
+        fi
+        case "$action" in
+            prepare)
+                /system/bin/prepare_dir --mode 700 --uid 0 --gid 0 -- "$computed_path"/vold
+                ;;
+            destroy)
+                rm -rf "$computed_path"/*
+                ;;
+            *)
+                echo "Unknown action: $action"
+                exit -1
+                ;;
+        esac
+        ;;
+    *)
+        echo "Unknown type: $dirtype"
+        exit -1
+        ;;
+esac