Merge "Revert "Revert "Revert "Revert "vold now prepares a subdirectory for face data."""""
diff --git a/Android.bp b/Android.bp
index 8f2ba34..fbff1aa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -31,6 +31,7 @@
         "libbootloader_message",
         "libfec",
         "libfec_rs",
+        "libfs_avb",
         "libfs_mgr",
         "libscrypt_static",
         "libsquashfs_utils",
@@ -95,6 +96,7 @@
     ],
 
     srcs: [
+        "AppFuseUtil.cpp",
         "Benchmark.cpp",
         "CheckEncryption.cpp",
         "Checkpoint.cpp",
@@ -129,16 +131,20 @@
         "model/PrivateVolume.cpp",
         "model/PublicVolume.cpp",
         "model/VolumeBase.cpp",
+        "model/StubVolume.cpp",
         "secontext.cpp",
     ],
     product_variables: {
         arc: {
             exclude_srcs: [
+                "AppFuseUtil.cpp",
                 "model/ObbVolume.cpp",
             ],
             static_libs: [
                 "arc_services_aidl",
+                "libarcappfuse",
                 "libarcobbvolume",
+                "libparcelfiledescriptor",
             ],
         },
         debuggable: {
@@ -163,7 +169,9 @@
         arc: {
             static_libs: [
                 "arc_services_aidl",
+                "libarcappfuse",
                 "libarcobbvolume",
+                "libparcelfiledescriptor",
             ],
         },
     },
diff --git a/AppFuseUtil.cpp b/AppFuseUtil.cpp
new file mode 100644
index 0000000..ba82ba5
--- /dev/null
+++ b/AppFuseUtil.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 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 "AppFuseUtil.h"
+
+#include <sys/mount.h>
+#include <utils/Errors.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "Utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+namespace {
+
+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 = StringPrintf("/mnt/appfuse/%d_%s", uid, name.c_str());
+    return android::OK;
+}
+
+static android::status_t Mount(int device_fd, const std::string& path) {
+    // Remove existing mount.
+    android::vold::ForceUnmount(path);
+
+    const auto opts = StringPrintf(
+        "fd=%i,"
+        "rootmode=40000,"
+        "default_permissions,"
+        "allow_other,"
+        "user_id=0,group_id=0,"
+        "context=\"u:object_r:app_fuse_file:s0\","
+        "fscontext=u:object_r:app_fusefs:s0",
+        device_fd);
+
+    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 RunCommand(const std::string& command, uid_t uid, const std::string& path,
+                                    int device_fd) {
+    if (DEBUG_APPFUSE) {
+        LOG(DEBUG) << "Run app fuse command " << command << " for the path " << path << " and uid "
+                   << uid;
+    }
+
+    if (command == "mount") {
+        return Mount(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.";
+            return -errno;
+        }
+        if (rmdir(path.c_str()) != 0) {
+            PLOG(ERROR) << "Failed to remove the mount directory.";
+            return -errno;
+        }
+        return android::OK;
+    } else {
+        LOG(ERROR) << "Unknown appfuse command " << command;
+        return -EPERM;
+    }
+
+    return android::OK;
+}
+
+}  // namespace
+
+int MountAppFuse(uid_t uid, 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 RunCommand("mount", uid, path, device_fd->get());
+}
+
+int UnmountAppFuse(uid_t uid, 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 RunCommand("unmount", uid, path, -1 /* device_fd */);
+}
+
+int OpenAppFuseFile(uid_t uid, int mountId, int fileId, int flags) {
+    std::string name = std::to_string(mountId);
+
+    // Check mount point name.
+    std::string mountPoint;
+    if (GetMountPath(uid, name, &mountPoint) != android::OK) {
+        LOG(ERROR) << "Invalid mount point name";
+        return -1;
+    }
+
+    std::string path = StringPrintf("%s/%d", mountPoint.c_str(), fileId);
+    return TEMP_FAILURE_RETRY(open(path.c_str(), flags));
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/AppFuseUtil.h b/AppFuseUtil.h
new file mode 100644
index 0000000..463c6d0
--- /dev/null
+++ b/AppFuseUtil.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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_APP_FUSE_UTIL_H_
+#define ANDROID_VOLD_APP_FUSE_UTIL_H_
+
+#include <android-base/unique_fd.h>
+
+#define DEBUG_APPFUSE 0
+
+namespace android {
+namespace vold {
+
+int MountAppFuse(uid_t uid, int mountId, android::base::unique_fd* device_fd);
+
+int UnmountAppFuse(uid_t uid, int mountId);
+
+int OpenAppFuseFile(uid_t uid, int mountId, int fileId, int flags);
+
+}  // namespace vold
+}  // namespace android
+
+#endif  // ANDROID_VOLD_APP_FUSE_UTIL_H_
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index 94a78eb..e238acf 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -82,6 +82,7 @@
 }
 
 Status cp_commitChanges() {
+    if (!cp_needsCheckpoint()) return Status::ok();
     // Must take action for list of mounted checkpointed things here
     // To do this, we walk the list of mounted file systems.
     // But we also need to get the matching fstab entries to see
@@ -104,14 +105,17 @@
 
         if (fs_mgr_is_checkpoint_fs(fstab_rec)) {
             if (!strcmp(fstab_rec->fs_type, "f2fs")) {
-                mount(mount_rec->blk_device, mount_rec->mount_point, "none",
-                      MS_REMOUNT | fstab_rec->flags, "checkpoint=enable");
+                if (mount(mount_rec->blk_device, mount_rec->mount_point, "none",
+                          MS_REMOUNT | fstab_rec->flags, "checkpoint=enable")) {
+                    return Status::fromExceptionCode(EINVAL, "Failed to remount");
+                }
             }
         } else if (fs_mgr_is_checkpoint_blk(fstab_rec)) {
-            setBowState(mount_rec->blk_device, "2");
+            if (!setBowState(mount_rec->blk_device, "2"))
+                return Status::fromExceptionCode(EINVAL, "Failed to set bow state");
         }
     }
-    if (android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
+    if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
         return Status::fromExceptionCode(errno, err_str.c_str());
     return Status::ok();
 }
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index 087b916..cf179c4 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -57,6 +57,7 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -172,8 +173,25 @@
     auto const current_path = get_ce_key_current_path(directory_path);
     if (to_fix != current_path) {
         LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path;
+        android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+            open(to_fix.c_str(), O_RDONLY | O_CLOEXEC)));
+        if (fd == -1) {
+            PLOG(ERROR) << "Failed to open " << to_fix;
+            return;
+        }
+        if (fsync(fd) == -1) {
+            if (errno == EROFS || errno == EINVAL) {
+                PLOG(WARNING) << "Skip fsync " << to_fix
+                              << " on a file system does not support synchronization";
+            } else {
+                PLOG(ERROR) << "Failed to fsync " << to_fix;
+                unlink(to_fix.c_str());
+                return;
+            }
+        }
         if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
             PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path;
+            return;
         }
     }
 }
diff --git a/Utils.cpp b/Utils.cpp
index 8af616d..57417d6 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -767,7 +767,7 @@
 }
 
 status_t UnmountTree(const std::string& prefix) {
-    FILE* fp = setmntent("/proc/mounts", "r");
+    FILE* fp = setmntent("/proc/mounts", "re");
     if (fp == NULL) {
         PLOG(ERROR) << "Failed to open /proc/mounts";
         return -errno;
@@ -818,7 +818,8 @@
                 result = -errno;
                 continue;
             }
-            std::unique_ptr<DIR, decltype(&closedir)> subdirp(fdopendir(subfd), closedir);
+            std::unique_ptr<DIR, decltype(&closedir)> subdirp(
+                android::base::Fdopendir(std::move(subfd)), closedir);
             if (!subdirp) {
                 PLOG(ERROR) << "Couldn't fdopendir " << name;
                 result = -errno;
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 2696e30..c2d4e0b 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -191,11 +191,11 @@
 }
 
 binder::Status checkArgumentSandboxId(const std::string& sandboxId) {
-    // sandboxId will be in either the format shared:<shared-user-id> or <package-name>
+    // sandboxId will be in either the format shared-<shared-user-id> or <package-name>
     // and <shared-user-id> name has same requirements as <package-name>.
     std::size_t nameStartIndex = 0;
-    if (android::base::StartsWith(sandboxId, "shared:")) {
-        nameStartIndex = 7;  // len("shared:")
+    if (android::base::StartsWith(sandboxId, "shared-")) {
+        nameStartIndex = 7;  // len("shared-")
     }
     return checkArgumentPackageName(sandboxId.substr(nameStartIndex));
 }
@@ -604,6 +604,29 @@
     return translate(VolumeManager::Instance()->destroyObb(volId));
 }
 
+binder::Status VoldNativeService::createStubVolume(
+    const std::string& sourcePath, const std::string& mountPath, const std::string& fsType,
+    const std::string& fsUuid, const std::string& fsLabel, std::string* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PATH(sourcePath);
+    CHECK_ARGUMENT_PATH(mountPath);
+    CHECK_ARGUMENT_HEX(fsUuid);
+    // Label limitation seems to be different between fs (including allowed characters), so checking
+    // is quite meaningless.
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->createStubVolume(sourcePath, mountPath, fsType,
+                                                                 fsUuid, fsLabel, _aidl_return));
+}
+
+binder::Status VoldNativeService::destroyStubVolume(const std::string& volId) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_ID(volId);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->destroyStubVolume(volId));
+}
+
 binder::Status VoldNativeService::fstrim(
     int32_t fstrimFlags, const android::sp<android::os::IVoldTaskListener>& listener) {
     ENFORCE_UID(AID_SYSTEM);
@@ -631,19 +654,36 @@
     return ok();
 }
 
-binder::Status VoldNativeService::mountAppFuse(int32_t uid, int32_t pid, int32_t mountId,
+binder::Status VoldNativeService::mountAppFuse(int32_t uid, 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));
+    return translate(VolumeManager::Instance()->mountAppFuse(uid, mountId, _aidl_return));
 }
 
-binder::Status VoldNativeService::unmountAppFuse(int32_t uid, int32_t pid, int32_t mountId) {
+binder::Status VoldNativeService::unmountAppFuse(int32_t uid, int32_t mountId) {
     ENFORCE_UID(AID_SYSTEM);
     ACQUIRE_LOCK;
 
-    return translate(VolumeManager::Instance()->unmountAppFuse(uid, pid, mountId));
+    return translate(VolumeManager::Instance()->unmountAppFuse(uid, mountId));
+}
+
+binder::Status VoldNativeService::openAppFuseFile(int32_t uid, int32_t mountId, int32_t fileId,
+                                                  int32_t flags,
+                                                  android::base::unique_fd* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    ACQUIRE_LOCK;
+
+    int fd = VolumeManager::Instance()->openAppFuseFile(uid, mountId, fileId, flags);
+    if (fd == -1) {
+        return error("Failed to open AppFuse file for uid: " + std::to_string(uid) +
+                     " mountId: " + std::to_string(mountId) + " fileId: " + std::to_string(fileId) +
+                     " flags: " + std::to_string(flags));
+    }
+
+    *_aidl_return = android::base::unique_fd(fd);
+    return ok();
 }
 
 binder::Status VoldNativeService::fdeCheckPassword(const std::string& password) {
diff --git a/VoldNativeService.h b/VoldNativeService.h
index aea0f1a..726de68 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -72,14 +72,21 @@
                              int32_t ownerGid, std::string* _aidl_return);
     binder::Status destroyObb(const std::string& volId);
 
+    binder::Status createStubVolume(const std::string& sourcePath, const std::string& mountPath,
+                                    const std::string& fsType, const std::string& fsUuid,
+                                    const std::string& fsLabel, std::string* _aidl_return);
+    binder::Status destroyStubVolume(const std::string& volId);
+
     binder::Status fstrim(int32_t fstrimFlags,
                           const android::sp<android::os::IVoldTaskListener>& listener);
     binder::Status runIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
     binder::Status abortIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
 
-    binder::Status mountAppFuse(int32_t uid, int32_t pid, int32_t mountId,
+    binder::Status mountAppFuse(int32_t uid, int32_t mountId,
                                 android::base::unique_fd* _aidl_return);
-    binder::Status unmountAppFuse(int32_t uid, int32_t pid, int32_t mountId);
+    binder::Status unmountAppFuse(int32_t uid, int32_t mountId);
+    binder::Status openAppFuseFile(int32_t uid, int32_t mountId, int32_t fileId, int32_t flags,
+                                   android::base::unique_fd* _aidl_return);
 
     binder::Status fdeCheckPassword(const std::string& password);
     binder::Status fdeRestart();
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index e6e37fa..0b2f1bf 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -51,6 +51,7 @@
 
 #include <fscrypt/fscrypt.h>
 
+#include "AppFuseUtil.h"
 #include "Devmapper.h"
 #include "FsCrypt.h"
 #include "Loop.h"
@@ -65,6 +66,7 @@
 #include "fs/Vfat.h"
 #include "model/EmulatedVolume.h"
 #include "model/ObbVolume.h"
+#include "model/StubVolume.h"
 
 using android::base::GetBoolProperty;
 using android::base::StartsWith;
@@ -97,6 +99,7 @@
 VolumeManager::VolumeManager() {
     mDebug = false;
     mNextObbId = 0;
+    mNextStubVolumeId = 0;
     // For security reasons, assume that a secure keyguard is
     // showing until we hear otherwise
     mSecureKeyguardShowing = true;
@@ -310,6 +313,11 @@
             return vol;
         }
     }
+    for (const auto& vol : mStubVolumes) {
+        if (vol->getId() == id) {
+            return vol;
+        }
+    }
     for (const auto& vol : mObbVolumes) {
         if (vol->getId() == id) {
             return vol;
@@ -1190,6 +1198,7 @@
     for (const auto& disk : mDisks) {
         disk->destroy();
     }
+    mStubVolumes.clear();
     mDisks.clear();
     mPendingDisks.clear();
     android::vold::sSleepOnUnmount = true;
@@ -1204,13 +1213,16 @@
     if (mInternalEmulated != nullptr) {
         mInternalEmulated->unmount();
     }
+    for (const auto& stub : mStubVolumes) {
+        stub->unmount();
+    }
     for (const auto& disk : mDisks) {
         disk->unmountAll();
     }
 
     // Worst case we might have some stale mounts lurking around, so
     // force unmount those just to be safe.
-    FILE* fp = setmntent("/proc/mounts", "r");
+    FILE* fp = setmntent("/proc/mounts", "re");
     if (fp == NULL) {
         PLOG(ERROR) << "Failed to open /proc/mounts";
         return -errno;
@@ -1252,145 +1264,6 @@
     }
 }
 
-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 = 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 = 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 = 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++;
@@ -1417,43 +1290,40 @@
     return android::OK;
 }
 
-int VolumeManager::mountAppFuse(uid_t uid, pid_t pid, int mountId, unique_fd* device_fd) {
-    std::string name = std::to_string(mountId);
+int VolumeManager::createStubVolume(const std::string& sourcePath, const std::string& mountPath,
+                                    const std::string& fsType, const std::string& fsUuid,
+                                    const std::string& fsLabel, std::string* outVolId) {
+    int id = mNextStubVolumeId++;
+    auto vol = std::shared_ptr<android::vold::VolumeBase>(
+        new android::vold::StubVolume(id, sourcePath, mountPath, fsType, fsUuid, fsLabel));
+    vol->create();
 
-    // 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());
+    mStubVolumes.push_back(vol);
+    *outVolId = vol->getId();
+    return android::OK;
 }
 
-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;
+int VolumeManager::destroyStubVolume(const std::string& volId) {
+    auto i = mStubVolumes.begin();
+    while (i != mStubVolumes.end()) {
+        if ((*i)->getId() == volId) {
+            (*i)->destroy();
+            i = mStubVolumes.erase(i);
+        } else {
+            ++i;
+        }
     }
+    return android::OK;
+}
 
-    return runCommandInNamespace("unmount", uid, pid, path, -1 /* device_fd */);
+int VolumeManager::mountAppFuse(uid_t uid, int mountId, unique_fd* device_fd) {
+    return android::vold::MountAppFuse(uid, mountId, device_fd);
+}
+
+int VolumeManager::unmountAppFuse(uid_t uid, int mountId) {
+    return android::vold::UnmountAppFuse(uid, mountId);
+}
+
+int VolumeManager::openAppFuseFile(uid_t uid, int mountId, int fileId, int flags) {
+    return android::vold::OpenAppFuseFile(uid, mountId, fileId, flags);
 }
diff --git a/VolumeManager.h b/VolumeManager.h
index 1cbc4bf..9e990d0 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -38,8 +38,6 @@
 #include "model/Disk.h"
 #include "model/VolumeBase.h"
 
-#define DEBUG_APPFUSE 0
-
 class VolumeManager {
   private:
     static VolumeManager* sInstance;
@@ -136,8 +134,14 @@
                   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);
+    int createStubVolume(const std::string& sourcePath, const std::string& mountPath,
+                         const std::string& fsType, const std::string& fsUuid,
+                         const std::string& fsLabel, std::string* outVolId);
+    int destroyStubVolume(const std::string& volId);
+
+    int mountAppFuse(uid_t uid, int mountId, android::base::unique_fd* device_fd);
+    int unmountAppFuse(uid_t uid, int mountId);
+    int openAppFuseFile(uid_t uid, int mountId, int fileId, int flags);
 
   private:
     VolumeManager();
@@ -181,6 +185,7 @@
     std::list<std::shared_ptr<android::vold::Disk>> mDisks;
     std::list<std::shared_ptr<android::vold::Disk>> mPendingDisks;
     std::list<std::shared_ptr<android::vold::VolumeBase>> mObbVolumes;
+    std::list<std::shared_ptr<android::vold::VolumeBase>> mStubVolumes;
 
     std::unordered_map<userid_t, int> mAddedUsers;
     std::unordered_set<userid_t> mStartedUsers;
@@ -196,6 +201,7 @@
     std::unordered_set<std::string> mVisibleVolumeIds;
 
     int mNextObbId;
+    int mNextStubVolumeId;
     bool mSecureKeyguardShowing;
 };
 
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index b1846d0..2f7430f 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -62,8 +62,8 @@
     void runIdleMaint(IVoldTaskListener listener);
     void abortIdleMaint(IVoldTaskListener listener);
 
-    FileDescriptor mountAppFuse(int uid, int pid, int mountId);
-    void unmountAppFuse(int uid, int pid, int mountId);
+    FileDescriptor mountAppFuse(int uid, int mountId);
+    void unmountAppFuse(int uid, int mountId);
 
     void fdeCheckPassword(@utf8InCpp String password);
     void fdeRestart();
@@ -114,6 +114,13 @@
     void restoreCheckpoint(@utf8InCpp String device);
     void markBootAttempt();
 
+    @utf8InCpp String createStubVolume(@utf8InCpp String sourcePath,
+            @utf8InCpp String mountPath, @utf8InCpp String fsType,
+            @utf8InCpp String fsUuid, @utf8InCpp String fsLabel);
+    void destroyStubVolume(@utf8InCpp String volId);
+
+    FileDescriptor openAppFuseFile(int uid, int mountId, int fileId, int flags);
+
     const int ENCRYPTION_FLAG_NO_UI = 4;
 
     const int ENCRYPTION_STATE_NONE = 1;
@@ -161,4 +168,5 @@
     const int VOLUME_TYPE_EMULATED = 2;
     const int VOLUME_TYPE_ASEC = 3;
     const int VOLUME_TYPE_OBB = 4;
+    const int VOLUME_TYPE_STUB = 5;
 }
diff --git a/model/StubVolume.cpp b/model/StubVolume.cpp
new file mode 100644
index 0000000..edd0861
--- /dev/null
+++ b/model/StubVolume.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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 "StubVolume.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+StubVolume::StubVolume(int id, const std::string& sourcePath, const std::string& mountPath,
+                       const std::string& fsType, const std::string& fsUuid,
+                       const std::string& fsLabel)
+    : VolumeBase(Type::kStub),
+      mSourcePath(sourcePath),
+      mMountPath(mountPath),
+      mFsType(fsType),
+      mFsUuid(fsUuid),
+      mFsLabel(fsLabel) {
+    setId(StringPrintf("stub:%d", id));
+}
+
+StubVolume::~StubVolume() {}
+
+status_t StubVolume::doCreate() {
+    return OK;
+}
+
+status_t StubVolume::doDestroy() {
+    return OK;
+}
+
+status_t StubVolume::doMount() {
+    auto listener = getListener();
+    if (listener) listener->onVolumeMetadataChanged(getId(), mFsType, mFsUuid, mFsLabel);
+    setInternalPath(mSourcePath);
+    setPath(mMountPath);
+    return OK;
+}
+
+status_t StubVolume::doUnmount() {
+    return OK;
+}
+
+// TODO: return error instead.
+status_t StubVolume::doFormat(const std::string& fsType) {
+    return OK;
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/model/StubVolume.h b/model/StubVolume.h
new file mode 100644
index 0000000..538cae9
--- /dev/null
+++ b/model/StubVolume.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 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_STUB_VOLUME_H
+#define ANDROID_VOLD_STUB_VOLUME_H
+
+#include "VolumeBase.h"
+
+namespace android {
+namespace vold {
+
+/*
+ * A vold representation of volumes managed from outside Android (e.g., ARC++).
+ *
+ * Used for the case when events such that mounting and unmounting are
+ * actually handled from outside vold, and vold only need to keep track on those
+ * vents instead of talking to kernel directly.
+ */
+class StubVolume : public VolumeBase {
+  public:
+    StubVolume(int id, const std::string& sourcePath, const std::string& mountPath,
+               const std::string& fsType, const std::string& fsUuid, const std::string& fsLabel);
+    virtual ~StubVolume();
+
+  protected:
+    status_t doCreate() override;
+    status_t doDestroy() override;
+    status_t doMount() override;
+    status_t doUnmount() override;
+    status_t doFormat(const std::string& fsType) override;
+
+  private:
+    const std::string mSourcePath;
+    const std::string mMountPath;
+    const std::string mFsType;
+    const std::string mFsUuid;
+    const std::string mFsLabel;
+
+    DISALLOW_COPY_AND_ASSIGN(StubVolume);
+};
+
+}  // namespace vold
+}  // namespace android
+
+#endif
diff --git a/model/VolumeBase.h b/model/VolumeBase.h
index a9fd42d..ea187bd 100644
--- a/model/VolumeBase.h
+++ b/model/VolumeBase.h
@@ -56,6 +56,7 @@
         kEmulated,
         kAsec,
         kObb,
+        kStub,
     };
 
     enum MountFlags {