summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Marshall <tdm@cyngn.com>2014-11-24 16:02:04 -0800
committerTom Marshall <tdm@cyngn.com>2015-11-25 15:34:31 -0800
commit3f092f7778ed608d454df4c3dc3b3f7cb4afde3b (patch)
tree326444388672880e6ab3bf72f434e13e1d80c25e
parent383f723fdb0ebba5078ccc2aabf87f0516215bf9 (diff)
downloadbootable_recovery-3f092f7778ed608d454df4c3dc3b3f7cb4afde3b.zip
bootable_recovery-3f092f7778ed608d454df4c3dc3b3f7cb4afde3b.tar.gz
bootable_recovery-3f092f7778ed608d454df4c3dc3b3f7cb4afde3b.tar.bz2
recovery: Awakening of MiniVold
A minimal vold client for recovery. Change-Id: Id25d955dc1861a910e5f5fc27d9a19e245d66833
-rw-r--r--Android.mk27
-rw-r--r--device.cpp16
-rw-r--r--device.h9
-rw-r--r--etc/init.rc37
-rw-r--r--fuse_sdcard_provider.cpp5
-rw-r--r--install.cpp21
-rw-r--r--mtdutils/mounts.c15
-rw-r--r--mtdutils/mounts.h1
-rw-r--r--recovery.cpp220
-rw-r--r--recovery_cmds.h2
-rw-r--r--roots.cpp112
-rw-r--r--roots.h11
-rw-r--r--ui.cpp23
-rw-r--r--ui.h8
-rw-r--r--voldclient.cpp466
-rw-r--r--voldclient.h100
16 files changed, 961 insertions, 112 deletions
diff --git a/Android.mk b/Android.mk
index 4bc29de..2f58841 100644
--- a/Android.mk
+++ b/Android.mk
@@ -44,12 +44,14 @@ LOCAL_SRC_FILES := \
ui.cpp \
verifier.cpp \
wear_ui.cpp \
+ voldclient.cpp
# External tools
LOCAL_SRC_FILES += \
../../system/core/toolbox/newfs_msdos.c \
../../system/core/toolbox/start.c \
- ../../system/core/toolbox/stop.c
+ ../../system/core/toolbox/stop.c \
+ ../../system/vold/vdc.c
LOCAL_MODULE := recovery
@@ -78,6 +80,7 @@ LOCAL_STATIC_LIBRARIES := \
libminipigz_static \
libzopfli \
libreboot_static \
+ libsdcard \
libminzip \
libz \
libmtdutils \
@@ -105,11 +108,15 @@ ifeq ($(TARGET_USE_MDTP), true)
LOCAL_CFLAGS += -DUSE_MDTP
endif
-ifeq ($(TARGET_RECOVERY_UI_LIB),)
+LOCAL_CFLAGS += -DUSE_EXT4 -DMINIVOLD
+LOCAL_C_INCLUDES += system/extras/ext4_utils system/core/fs_mgr/include external/fsck_msdos
+LOCAL_C_INCLUDES += system/vold
+
+#ifeq ($(TARGET_RECOVERY_UI_LIB),)
LOCAL_SRC_FILES += default_device.cpp
-else
- LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
-endif
+#else
+# LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
+#endif
LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_C_INCLUDES += external/boringssl/include
@@ -117,7 +124,9 @@ LOCAL_C_INCLUDES += external/boringssl/include
ifeq ($(ONE_SHOT_MAKEFILE),)
LOCAL_ADDITIONAL_DEPENDENCIES += \
fstools \
- recovery_mkshrc
+ recovery_mkshrc \
+ minivold \
+ recovery_sgdisk
endif
@@ -126,7 +135,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES += toybox_recovery_links
# Set up the static symlinks
RECOVERY_TOOLS := \
- gunzip gzip make_ext4fs reboot setup_adbd sh start stop toybox unzip zip
+ gunzip gzip make_ext4fs reboot setup_adbd sh start stop toybox unzip vdc zip
LOCAL_POST_INSTALL_CMD := \
$(hide) $(foreach t,$(RECOVERY_TOOLS),ln -sf recovery $(TARGET_RECOVERY_ROOT_OUT)/sbin/$(t);)
@@ -209,6 +218,7 @@ LOCAL_MODULE := verifier_test
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := tests
LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -DVERIFIER_TEST
LOCAL_SRC_FILES := \
verifier_test.cpp \
asn1_decoder.cpp \
@@ -220,6 +230,9 @@ LOCAL_STATIC_LIBRARIES := \
libminzip \
libcutils \
libc
+LOCAL_C_INCLUDES += \
+ system/core/fs_mgr/include \
+ system/vold
include $(BUILD_EXECUTABLE)
diff --git a/device.cpp b/device.cpp
index c77ebf6..46ee880 100644
--- a/device.cpp
+++ b/device.cpp
@@ -19,8 +19,7 @@
static const char* MENU_ITEMS[] = {
"Reboot system now",
"Reboot to bootloader",
- "Apply update from ADB",
- "Apply update from SD card",
+ "Apply update",
"Wipe data/factory reset",
"Wipe cache partition",
"Mount /system",
@@ -39,13 +38,12 @@ Device::BuiltinAction Device::InvokeMenuItem(int menu_position) {
switch (menu_position) {
case 0: return REBOOT;
case 1: return REBOOT_BOOTLOADER;
- case 2: return APPLY_ADB_SIDELOAD;
- case 3: return APPLY_SDCARD;
- case 4: return WIPE_DATA;
- case 5: return WIPE_CACHE;
- case 6: return MOUNT_SYSTEM;
- case 7: return VIEW_RECOVERY_LOGS;
- case 8: return SHUTDOWN;
+ case 2: return APPLY_UPDATE;
+ case 3: return WIPE_DATA;
+ case 4: return WIPE_CACHE;
+ case 5: return MOUNT_SYSTEM;
+ case 6: return VIEW_RECOVERY_LOGS;
+ case 7: return SHUTDOWN;
default: return NO_ACTION;
}
}
diff --git a/device.h b/device.h
index 1faad5f..d761d2a 100644
--- a/device.h
+++ b/device.h
@@ -19,7 +19,7 @@
#include "ui.h"
-class Device {
+class Device : public VoldWatcher {
public:
Device(RecoveryUI* ui) : ui_(ui) { }
virtual ~Device() { }
@@ -59,9 +59,9 @@ class Device {
enum BuiltinAction {
NO_ACTION = 0,
REBOOT = 1,
- APPLY_SDCARD = 2,
+ APPLY_UPDATE = 2,
// APPLY_CACHE was 3.
- APPLY_ADB_SIDELOAD = 4,
+ // APPLY_ADB_SIDELOAD was 4.
WIPE_DATA = 5,
WIPE_CACHE = 6,
REBOOT_BOOTLOADER = 7,
@@ -91,6 +91,7 @@ class Device {
static const int kHighlightDown = -3;
static const int kInvokeItem = -4;
static const int kGoBack = -5;
+ static const int kRefresh = -6;
// Called before and after we do a wipe data/factory reset operation,
// either via a reboot from the main system with the --wipe_data flag,
@@ -106,6 +107,8 @@ class Device {
// Called before reboot
virtual char const* GetRebootReason() { return ""; }
+ virtual void onVolumeChanged() { ui_->onVolumeChanged(); }
+
private:
RecoveryUI* ui_;
};
diff --git a/etc/init.rc b/etc/init.rc
index fd0b1dc..61e8316 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -21,6 +21,26 @@ on init
chown root shell /tmp
chmod 0775 /tmp
+ mkdir /mnt 0775 root system
+ mkdir /storage 0050 root sdcard_r
+ mount tmpfs tmpfs /storage mode=0050,uid=0,gid=1028
+
+ # See storage config details at http://source.android.com/tech/storage/
+ mkdir /mnt/shell 0700 shell shell
+
+ # Directory for putting things only root should see.
+ mkdir /mnt/secure 0700 root root
+
+ # Create private mountpoint so we can MS_MOVE from staging
+ mount tmpfs tmpfs /mnt/secure mode=0700,uid=0,gid=0
+
+ # Directory for staging bindmounts
+ mkdir /mnt/secure/staging 0700 root root
+
+ # Fuse public mount points.
+ mkdir /mnt/fuse 0700 root system
+ mount tmpfs tmpfs /mnt/fuse mode=0775,gid=1000
+
write /proc/sys/kernel/panic_on_oops 1
write /proc/sys/vm/max_map_count 1000000
@@ -100,6 +120,13 @@ service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
socket adbd stream 660 system system
seclabel u:r:adbd:s0
+service vold /sbin/minivold \
+ --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
+ socket vold stream 0660 root mount
+ socket cryptd stream 0660 root mount
+ ioprio be 2
+
# setup_adbd will start adb once it has checked the keys
on property:ro.debuggable=1
start setup_adbd
@@ -108,3 +135,13 @@ on property:service.adb.root=1
write /sys/class/android_usb/android0/enable 0
restart adbd
write /sys/class/android_usb/android0/enable 1
+
+on property:sys.storage.ums_enabled=1
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/functions adb,mass_storage
+ write /sys/class/android_usb/android0/enable 1
+
+on property:sys.storage.ums_enabled=0
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/functions adb
+ write /sys/class/android_usb/android0/enable ${service.adb.root}
diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp
index eb6454f..879f90b 100644
--- a/fuse_sdcard_provider.cpp
+++ b/fuse_sdcard_provider.cpp
@@ -118,11 +118,6 @@ void* start_sdcard_fuse(const char* path) {
}
}
- // The installation process expects to find the sdcard unmounted.
- // Unmount it with MNT_DETACH so that our open file continues to
- // work but new references see it as unmounted.
- umount2("/sdcard", MNT_DETACH);
-
return t;
}
diff --git a/install.cpp b/install.cpp
index 8f2f252..1bdf46b 100644
--- a/install.cpp
+++ b/install.cpp
@@ -255,6 +255,27 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount)
// Give verification half the progress bar...
ui->SetProgressType(RecoveryUI::DETERMINATE);
ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
+
+ // Resolve symlink in case legacy /sdcard path is used
+ // Requires: symlink uses absolute path
+ char new_path[PATH_MAX];
+ if (strlen(path) > 1) {
+ char *rest = strchr(path + 1, '/');
+ if (rest != NULL) {
+ int readlink_length;
+ int root_length = rest - path;
+ char *root = (char *)malloc(root_length + 1);
+ strncpy(root, path, root_length);
+ root[root_length] = 0;
+ readlink_length = readlink(root, new_path, PATH_MAX);
+ if (readlink_length > 0) {
+ strncpy(new_path + readlink_length, rest, PATH_MAX - readlink_length);
+ path = new_path;
+ }
+ free(root);
+ }
+ }
+
LOGI("Update location: %s\n", path);
// Map the update package into memory.
diff --git a/mtdutils/mounts.c b/mtdutils/mounts.c
index 6a9b03d..41efa37 100644
--- a/mtdutils/mounts.c
+++ b/mtdutils/mounts.c
@@ -153,6 +153,21 @@ unmount_mounted_volume(const MountedVolume *volume)
}
int
+unmount_mounted_volume_detach(const MountedVolume *volume)
+{
+ /* Intentionally pass NULL to umount if the caller tries
+ * to unmount a volume they already unmounted using this
+ * function.
+ */
+ int ret = umount2(volume->mount_point, MNT_DETACH);
+ if (ret == 0) {
+ free_volume_internals(volume, 1);
+ return 0;
+ }
+ return ret;
+}
+
+int
remount_read_only(const MountedVolume* volume)
{
return mount(volume->device, volume->mount_point, volume->filesystem,
diff --git a/mtdutils/mounts.h b/mtdutils/mounts.h
index d721355..c8318c0 100644
--- a/mtdutils/mounts.h
+++ b/mtdutils/mounts.h
@@ -31,6 +31,7 @@ const MountedVolume *
find_mounted_volume_by_mount_point(const char *mount_point);
int unmount_mounted_volume(const MountedVolume *volume);
+int unmount_mounted_volume_detach(const MountedVolume *volume);
int remount_read_only(const MountedVolume* volume);
diff --git a/recovery.cpp b/recovery.cpp
index b7641aa..9f45d9a 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -47,6 +47,9 @@
#include "ui.h"
#include "screen_ui.h"
#include "device.h"
+
+#include "voldclient.h"
+
#include "adb_install.h"
#include "adb.h"
#include "fuse_sideload.h"
@@ -81,7 +84,6 @@ static const char *LOG_FILE = "/cache/recovery/log";
static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
static const char *LOCALE_FILE = "/cache/recovery/last_locale";
static const char *CACHE_ROOT = "/cache";
-static const char *SDCARD_ROOT = "/sdcard";
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
@@ -94,6 +96,8 @@ char* stage = NULL;
char* reason = NULL;
bool modified_flash = false;
+#include "mtdutils/mounts.h"
+
/*
* The recovery tool communicates with the main system through /cache files.
* /cache/recovery/command - INPUT - command line for tool, one arg per line
@@ -589,7 +593,7 @@ static bool erase_volume(const char* volume) {
return (result == 0);
}
-static int
+int
get_menu_selection(const char* const * headers, const char* const * items,
int menu_only, int initial_selection, Device* device) {
// throw away keys pressed previously, so user doesn't
@@ -600,7 +604,7 @@ get_menu_selection(const char* const * headers, const char* const * items,
int selected = initial_selection;
int chosen_item = -1;
- while (chosen_item < 0 && chosen_item != Device::kGoBack) {
+ while (chosen_item < 0 && chosen_item != Device::kGoBack && chosen_item != Device::kRefresh) {
int key = ui->WaitKey();
int visible = ui->IsTextVisible();
@@ -615,6 +619,9 @@ get_menu_selection(const char* const * headers, const char* const * items,
} else if (key == -2) { // we are returning from ui_cancel_wait_key(): trigger a GO_BACK
return Device::kGoBack;
}
+ else if (key == -6) {
+ return Device::kRefresh;
+ }
int action = device->HandleMenuKey(key, visible);
@@ -634,6 +641,9 @@ get_menu_selection(const char* const * headers, const char* const * items,
case Device::kGoBack:
chosen_item = Device::kGoBack;
break;
+ case Device::kRefresh:
+ chosen_item = Device::kRefresh;
+ break;
}
} else if (!menu_only) {
chosen_item = action;
@@ -650,8 +660,6 @@ static int compare_string(const void* a, const void* b) {
// Returns a malloc'd path, or NULL.
static char* browse_directory(const char* path, Device* device) {
- ensure_path_mounted(path);
-
DIR* d = opendir(path);
if (d == NULL) {
LOGE("error opening %s: %s\n", path, strerror(errno));
@@ -716,15 +724,15 @@ static char* browse_directory(const char* path, Device* device) {
int chosen_item = 0;
while (true) {
chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device);
-
- char* item = zips[chosen_item];
- int item_len = strlen(item);
- if (chosen_item == 0) { // item 0 is always "../"
+ if (chosen_item == 0 || chosen_item == Device::kGoBack) {
// go up but continue browsing (if the caller is update_directory)
result = NULL;
break;
}
+ char* item = zips[chosen_item];
+ int item_len = strlen(item);
+
char new_path[PATH_MAX];
strlcpy(new_path, path, PATH_MAX);
strlcat(new_path, "/", PATH_MAX);
@@ -842,30 +850,82 @@ static void choose_recovery_file(Device* device) {
}
}
-static int apply_from_sdcard(Device* device, bool* wipe_cache) {
+static int apply_from_storage(Device* device, const std::string& id, bool* wipe_cache) {
modified_flash = true;
- if (ensure_path_mounted(SDCARD_ROOT) != 0) {
- ui->Print("\n-- Couldn't mount %s.\n", SDCARD_ROOT);
+ int status;
+
+ if (!vdc->volumeMount(id)) {
return INSTALL_ERROR;
}
- char* path = browse_directory(SDCARD_ROOT, device);
+ VolumeInfo vi = vdc->getVolume(id);
+
+ char* path = browse_directory(vi.mInternalPath.c_str(), device);
if (path == NULL) {
ui->Print("\n-- No package file selected.\n");
- ensure_path_unmounted(SDCARD_ROOT);
- return INSTALL_ERROR;
+ vdc->volumeUnmount(vi.mId);
+ return INSTALL_NONE;
}
ui->Print("\n-- Install %s ...\n", path);
set_sdcard_update_bootloader_message();
void* token = start_sdcard_fuse(path);
- int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache,
+ vdc->volumeUnmount(vi.mId, true);
+
+ status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache,
TEMPORARY_INSTALL_FILE, false);
finish_sdcard_fuse(token);
- ensure_path_unmounted(SDCARD_ROOT);
+ free(path);
+ return status;
+}
+
+static int
+show_apply_update_menu(Device* device) {
+ static const char* headers[] = { "Apply update", "", NULL };
+ char* menu_items[MAX_NUM_MANAGED_VOLUMES + 1 + 1];
+ std::vector<VolumeInfo> volumes = vdc->getVolumes();
+
+ const int item_sideload = 0;
+ int n, i;
+ std::vector<VolumeInfo>::iterator vitr;
+
+refresh:
+ menu_items[item_sideload] = strdup("Apply from ADB");
+
+ n = item_sideload + 1;
+ for (vitr = volumes.begin(); vitr != volumes.end(); ++vitr) {
+ menu_items[n] = (char*)malloc(256);
+ sprintf(menu_items[n], "Choose from %s", vitr->mLabel.c_str());
+ ++n;
+ }
+ menu_items[n] = NULL;
+
+ bool wipe_cache;
+ int status = INSTALL_ERROR;
+
+ for (;;) {
+ int chosen = get_menu_selection(headers, menu_items, 0, 0, device);
+ for (i = 0; i < n; ++i) {
+ free(menu_items[i]);
+ }
+ if (chosen == Device::kRefresh) {
+ goto refresh;
+ }
+ if (chosen == Device::kGoBack) {
+ break;
+ }
+ if (chosen == item_sideload) {
+ status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
+ }
+ else {
+ std::string id = volumes[chosen - 1].mId;
+ status = apply_from_storage(device, id, &wipe_cache);
+ }
+ }
+
return status;
}
@@ -901,77 +961,79 @@ prompt_and_wait(Device* device, int status) {
Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);
bool should_wipe_cache = false;
- switch (chosen_action) {
- case Device::NO_ACTION:
- break;
+ for (;;) {
+ switch (chosen_action) {
+ case Device::NO_ACTION:
+ break;
- case Device::REBOOT:
- case Device::SHUTDOWN:
- case Device::REBOOT_BOOTLOADER:
- return chosen_action;
+ case Device::REBOOT:
+ case Device::SHUTDOWN:
+ case Device::REBOOT_BOOTLOADER:
+ return chosen_action;
- case Device::WIPE_DATA:
- wipe_data(ui->IsTextVisible(), device);
- if (!ui->IsTextVisible()) return Device::NO_ACTION;
- break;
+ case Device::WIPE_DATA:
+ wipe_data(ui->IsTextVisible(), device);
+ if (!ui->IsTextVisible()) return Device::NO_ACTION;
+ break;
- case Device::WIPE_CACHE:
- wipe_cache(ui->IsTextVisible(), device);
- if (!ui->IsTextVisible()) return Device::NO_ACTION;
- break;
+ case Device::WIPE_CACHE:
+ wipe_cache(ui->IsTextVisible(), device);
+ if (!ui->IsTextVisible()) return Device::NO_ACTION;
+ break;
- case Device::APPLY_ADB_SIDELOAD:
- case Device::APPLY_SDCARD:
- {
- bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
- if (adb) {
- status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
- } else {
- status = apply_from_sdcard(device, &should_wipe_cache);
- }
+ case Device::APPLY_UPDATE:
+ {
+ status = show_apply_update_menu(device);
- if (status == INSTALL_SUCCESS && should_wipe_cache) {
- if (!wipe_cache(false, device)) {
- status = INSTALL_ERROR;
+ if (status == INSTALL_SUCCESS && should_wipe_cache) {
+ if (!wipe_cache(false, device)) {
+ status = INSTALL_ERROR;
+ }
}
- }
- if (status != INSTALL_SUCCESS) {
- ui->SetBackground(RecoveryUI::ERROR);
- ui->Print("Installation aborted.\n");
- copy_logs();
- } else if (!ui->IsTextVisible()) {
- return Device::NO_ACTION; // reboot if logs aren't visible
- } else {
- ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card");
+ if (status != INSTALL_SUCCESS) {
+ ui->SetBackground(RecoveryUI::ERROR);
+ ui->Print("Installation aborted.\n");
+ copy_logs();
+ } else if (!ui->IsTextVisible()) {
+ return Device::NO_ACTION; // reboot if logs aren't visible
+ } else {
+ ui->Print("\nInstall complete.\n");
}
+ break;
}
- break;
+ break;
- case Device::VIEW_RECOVERY_LOGS:
- choose_recovery_file(device);
- break;
+ case Device::VIEW_RECOVERY_LOGS:
+ choose_recovery_file(device);
+ break;
- case Device::MOUNT_SYSTEM:
- char system_root_image[PROPERTY_VALUE_MAX];
- property_get("ro.build.system_root_image", system_root_image, "");
-
- // For a system image built with the root directory (i.e.
- // system_root_image == "true"), we mount it to /system_root, and symlink /system
- // to /system_root/system to make adb shell work (the symlink is created through
- // the build system).
- // Bug: 22855115
- if (strcmp(system_root_image, "true") == 0) {
- if (ensure_path_mounted_at("/", "/system_root") != -1) {
- ui->Print("Mounted /system.\n");
- }
- } else {
- if (ensure_path_mounted("/system") != -1) {
- ui->Print("Mounted /system.\n");
+ case Device::MOUNT_SYSTEM:
+ char system_root_image[PROPERTY_VALUE_MAX];
+ property_get("ro.build.system_root_image", system_root_image, "");
+
+ // For a system image built with the root directory (i.e.
+ // system_root_image == "true"), we mount it to /system_root, and symlink /system
+ // to /system_root/system to make adb shell work (the symlink is created through
+ // the build system).
+ // Bug: 22855115
+ if (strcmp(system_root_image, "true") == 0) {
+ if (ensure_path_mounted_at("/", "/system_root") != -1) {
+ ui->Print("Mounted /system.\n");
+ }
+ } else {
+ if (ensure_path_mounted("/system") != -1) {
+ ui->Print("Mounted /system.\n");
+ }
}
- }
- break;
+ break;
+ }
+ if (status == Device::kRefresh) {
+ status = 0;
+ continue;
+ }
+ break;
}
}
}
@@ -1159,6 +1221,9 @@ main(int argc, char **argv) {
ui = device->GetUI();
gCurrentUI = ui;
+ vdc = new VoldClient(device);
+ vdc->start();
+
ui->SetLocale(locale);
ui->Init();
@@ -1286,6 +1351,11 @@ main(int argc, char **argv) {
// Save logs and clean up before rebooting or shutting down.
finish_recovery(send_intent);
+ vdc->unmountAll();
+ vdc->stop();
+
+ sync();
+
switch (after) {
case Device::SHUTDOWN:
ui->Print("Shutting down...\n");
diff --git a/recovery_cmds.h b/recovery_cmds.h
index 935f283..09a92d9 100644
--- a/recovery_cmds.h
+++ b/recovery_cmds.h
@@ -31,6 +31,7 @@ int pigz_main(int argc, char **argv);
int start_main(int argc, char **argv);
int stop_main(int argc, char **argv);
int mksh_main(int argc, char **argv);
+int vdc_main(int argc, char **argv);
int toybox_driver(int argc, char **argv);
@@ -54,6 +55,7 @@ static const struct recovery_cmd recovery_cmds[] = {
{ "start", start_main },
{ "stop", stop_main },
{ "sh", mksh_main },
+ { "vdc", vdc_main },
{ NULL, NULL },
};
diff --git a/roots.cpp b/roots.cpp
index 966c8b2..3dc604b 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
+#include <dirent.h>
#include <fs_mgr.h>
#include "mtdutils/mtdutils.h"
@@ -35,10 +36,30 @@ extern "C" {
#include "cryptfs.h"
}
+#include "voldclient.h"
+
static struct fstab *fstab = NULL;
extern struct selabel_handle *sehandle;
+static int mkdir_p(const char* path, mode_t mode)
+{
+ char dir[PATH_MAX];
+ char* p;
+ strcpy(dir, path);
+ for (p = strchr(&dir[1], '/'); p != NULL; p = strchr(p+1, '/')) {
+ *p = '\0';
+ if (mkdir(dir, mode) != 0 && errno != EEXIST) {
+ return -1;
+ }
+ *p = '/';
+ }
+ if (mkdir(dir, mode) != 0 && errno != EEXIST) {
+ return -1;
+ }
+ return 0;
+}
+
static void write_fstab_entry(Volume *v, FILE *file)
{
if (NULL != v && strcmp(v->fs_type, "mtd") != 0 && strcmp(v->fs_type, "emmc") != 0
@@ -53,6 +74,14 @@ static void write_fstab_entry(Volume *v, FILE *file)
}
}
+int get_num_volumes() {
+ return fstab->num_entries;
+}
+
+Volume* get_device_volumes() {
+ return fstab->recs;
+}
+
void load_volume_table()
{
int i;
@@ -98,6 +127,17 @@ Volume* volume_for_path(const char* path) {
return fs_mgr_get_entry_for_mount_point(fstab, path);
}
+Volume* volume_for_label(const char* label) {
+ int i;
+ for (i = 0; i < get_num_volumes(); i++) {
+ Volume* v = get_device_volumes() + i;
+ if (v->label && !strcmp(v->label, label)) {
+ return v;
+ }
+ }
+ return NULL;
+}
+
// Mount the volume specified by path at the given mount_point.
int ensure_path_mounted_at(const char* path, const char* mount_point) {
Volume* v = volume_for_path(path);
@@ -121,14 +161,16 @@ int ensure_path_mounted_at(const char* path, const char* mount_point) {
mount_point = v->mount_point;
}
- const MountedVolume* mv =
- find_mounted_volume_by_mount_point(mount_point);
- if (mv) {
- // volume is already mounted
- return 0;
+ if (!fs_mgr_is_voldmanaged(v)) {
+ const MountedVolume* mv =
+ find_mounted_volume_by_mount_point(mount_point);
+ if (mv) {
+ // volume is already mounted
+ return 0;
+ }
}
- mkdir(mount_point, 0755); // in case it doesn't already exist
+ mkdir_p(mount_point, 0755); // in case it doesn't already exist
if (strcmp(v->fs_type, "yaffs2") == 0) {
// mount an MTD partition as a YAFFS2 filesystem.
@@ -156,17 +198,49 @@ int ensure_path_mounted_at(const char* path, const char* mount_point) {
return -1;
}
+int ensure_volume_mounted(Volume* v) {
+ if (v == NULL) {
+ LOGE("cannot mount unknown volume\n");
+ return -1;
+ }
+ return ensure_path_mounted_at(v->mount_point, nullptr);
+}
+
int ensure_path_mounted(const char* path) {
// Mount at the default mount point.
return ensure_path_mounted_at(path, nullptr);
}
-int ensure_path_unmounted(const char* path) {
- Volume* v = volume_for_path(path);
+int ensure_path_unmounted(const char* path, bool detach /* = false */) {
+ Volume* v;
+ if (memcmp(path, "/storage/", 9) == 0) {
+ char label[PATH_MAX];
+ const char* p = path+9;
+ const char* q = strchr(p, '/');
+ memset(label, 0, sizeof(label));
+ if (q) {
+ memcpy(label, p, q-p);
+ }
+ else {
+ strcpy(label, p);
+ }
+ v = volume_for_label(label);
+ }
+ else {
+ v = volume_for_path(path);
+ }
if (v == NULL) {
LOGE("unknown volume for path [%s]\n", path);
return -1;
}
+ return ensure_volume_unmounted(v, detach);
+}
+
+int ensure_volume_unmounted(Volume* v, bool detach /* = false */) {
+ if (v == NULL) {
+ LOGE("cannot unmount unknown volume\n");
+ return -1;
+ }
if (strcmp(v->fs_type, "ramdisk") == 0) {
// the ramdisk is always mounted; you can't unmount it.
return -1;
@@ -186,7 +260,14 @@ int ensure_path_unmounted(const char* path) {
return 0;
}
- return unmount_mounted_volume(mv);
+ if (detach) {
+ result = unmount_mounted_volume_detach(mv);
+ }
+ else {
+ result = unmount_mounted_volume(mv);
+ }
+
+ return result;
}
static int exec_cmd(const char* path, char* const argv[]) {
@@ -224,6 +305,11 @@ int format_volume(const char* volume) {
return -1;
}
+ if (fs_mgr_is_voldmanaged(v)) {
+ LOGE("can't format vold volume \"%s\"", volume);
+ return -1;
+ }
+
if (strcmp(v->fs_type, "yaffs2") == 0 || strcmp(v->fs_type, "mtd") == 0) {
mtd_scan_partitions();
const MtdPartition* partition = mtd_find_partition_by_name(v->blk_device);
@@ -317,7 +403,13 @@ int setup_install_mounts() {
}
} else {
- if (ensure_path_unmounted(v->mount_point) != 0) {
+ // datamedia and anything managed by vold must be unmounted
+ // with the detach flag to ensure that FUSE works.
+ bool detach = false;
+ if (is_data_media() && strcmp(v->mount_point, "/data") == 0) {
+ detach = true;
+ }
+ if (ensure_volume_unmounted(v, detach) != 0) {
LOGE("failed to unmount %s\n", v->mount_point);
return -1;
}
diff --git a/roots.h b/roots.h
index 6e3b243..f3e1903 100644
--- a/roots.h
+++ b/roots.h
@@ -18,6 +18,7 @@
#define RECOVERY_ROOTS_H_
#include "common.h"
+#include <fs_mgr.h>
// Load and parse volume data from /etc/recovery.fstab.
void load_volume_table();
@@ -27,6 +28,7 @@ Volume* volume_for_path(const char* path);
// Make sure that the volume 'path' is on is mounted. Returns 0 on
// success (volume is mounted).
+int ensure_volume_mounted(Volume* v);
int ensure_path_mounted(const char* path);
// Similar to ensure_path_mounted, but allows one to specify the mount_point.
@@ -34,7 +36,8 @@ int ensure_path_mounted_at(const char* path, const char* mount_point);
// Make sure that the volume 'path' is on is unmounted. Returns 0 on
// success (volume is unmounted);
-int ensure_path_unmounted(const char* path);
+int ensure_volume_unmounted(Volume *v, bool detach=false);
+int ensure_path_unmounted(const char* path, bool detach=false);
// Reformat the given volume (must be the mount point only, eg
// "/cache"), no paths permitted. Attempts to unmount the volume if
@@ -45,4 +48,10 @@ int format_volume(const char* volume);
// mounted (/tmp and /cache) are mounted. Returns 0 on success.
int setup_install_mounts();
+int get_num_volumes();
+
+int is_data_media();
+
+#define MAX_NUM_MANAGED_VOLUMES 10
+
#endif // RECOVERY_ROOTS_H_
diff --git a/ui.cpp b/ui.cpp
index d88cbfe..659ceb2 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -38,6 +38,8 @@
#include "screen_ui.h"
#include "ui.h"
+#include "voldclient.h"
+
#define UI_WAIT_KEY_TIMEOUT_SEC 120
RecoveryUI::RecoveryUI()
@@ -46,6 +48,7 @@ RecoveryUI::RecoveryUI()
key_long_press(false),
key_down_count(0),
enable_reboot(true),
+ v_changed(0),
consecutive_power_keys(0),
last_key(-1),
has_power_key(false),
@@ -174,6 +177,9 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
break;
case RecoveryUI::REBOOT:
+#ifndef VERIFIER_TEST
+ vdc->unmountAll();
+#endif
if (reboot_enabled) {
property_set(ANDROID_RB_PROPERTY, "reboot,");
while(1) { pause(); }
@@ -217,6 +223,7 @@ void RecoveryUI::EnqueueKey(int key_code) {
int RecoveryUI::WaitKey() {
pthread_mutex_lock(&key_queue_mutex);
+ int timeouts = UI_WAIT_KEY_TIMEOUT_SEC;
// Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
// plugged in.
@@ -226,13 +233,18 @@ int RecoveryUI::WaitKey() {
gettimeofday(&now, nullptr);
timeout.tv_sec = now.tv_sec;
timeout.tv_nsec = now.tv_usec * 1000;
- timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
+ timeout.tv_sec += 1;
int rc = 0;
while (key_queue_len == 0 && rc != ETIMEDOUT) {
rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout);
+ if (VolumesChanged()) {
+ pthread_mutex_unlock(&key_queue_mutex);
+ return Device::kRefresh;
+ }
+ timeouts--;
}
- } while (IsUsbConnected() && key_queue_len == 0);
+ } while ((timeouts || IsUsbConnected()) && key_queue_len == 0);
int key = -1;
if (key_queue_len > 0) {
@@ -337,3 +349,10 @@ void RecoveryUI::SetEnableReboot(bool enabled) {
enable_reboot = enabled;
pthread_mutex_unlock(&key_queue_mutex);
}
+
+bool RecoveryUI::VolumesChanged() {
+ int ret = v_changed;
+ if (v_changed > 0)
+ v_changed = 0;
+ return ret == 1;
+}
diff --git a/ui.h b/ui.h
index ca72911..48eeda8 100644
--- a/ui.h
+++ b/ui.h
@@ -21,6 +21,8 @@
#include <pthread.h>
#include <time.h>
+#include "voldclient.h"
+
// Abstract class for controlling the user interface during recovery.
class RecoveryUI {
public:
@@ -121,6 +123,9 @@ class RecoveryUI {
// statements will be displayed.
virtual void EndMenu() = 0;
+ // Notify of volume state change
+ void onVolumeChanged() { v_changed = 1; }
+
protected:
void EnqueueKey(int key_code);
@@ -135,6 +140,7 @@ private:
int key_down_count; // under key_queue_mutex
bool enable_reboot; // under key_queue_mutex
int rel_sum;
+ int v_changed;
int consecutive_power_keys;
int last_key;
@@ -159,6 +165,8 @@ private:
bool IsUsbConnected();
+ bool VolumesChanged();
+
static void* time_key_helper(void* cookie);
void time_key(int key_code, int count);
};
diff --git a/voldclient.cpp b/voldclient.cpp
new file mode 100644
index 0000000..0c8462a
--- /dev/null
+++ b/voldclient.cpp
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2013 The CyanogenMod 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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+#include <string>
+#include <sstream>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#include "common.h"
+#include "roots.h"
+#include "voldclient.h"
+
+#include "VolumeBase.h"
+#include "ResponseCode.h"
+
+using namespace android::vold;
+
+VoldClient* vdc = NULL;
+
+static void* threadfunc(void* arg)
+{
+ VoldClient* self = (VoldClient*)arg;
+ self->run();
+ return NULL;
+}
+
+VoldClient::VoldClient(VoldWatcher* watcher /* = nullptr */) :
+ mRunning(false),
+ mSock(-1),
+ mSockMutex(PTHREAD_MUTEX_INITIALIZER),
+ mSockCond(PTHREAD_COND_INITIALIZER),
+ mInFlight(0),
+ mResult(0),
+ mWatcher(watcher),
+ mVolumeLock(PTHREAD_RWLOCK_INITIALIZER),
+ mVolumeChanged(false),
+ mEmulatedStorage(true)
+{
+}
+
+void VoldClient::start(void)
+{
+ mRunning = true;
+ pthread_create(&mThread, NULL, threadfunc, this);
+ while (mSock == -1) {
+ sleep(1);
+ }
+ while (mInFlight != 0) {
+ sleep(1);
+ }
+ LOGI("VoldClient initialized, storage is %s\n",
+ vdc->isEmulatedStorage() ? "emulated" : "physical");
+}
+
+void VoldClient::stop(void)
+{
+ if (mRunning) {
+ mRunning = false;
+ close(mSock);
+ mSock = -1;
+ void* retval;
+ pthread_join(mThread, &retval);
+ }
+}
+
+VolumeInfo VoldClient::getVolume(const std::string& id)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ VolumeInfo* info = getVolumeLocked(id);
+ pthread_rwlock_unlock(&mVolumeLock);
+ return *info;
+}
+
+bool VoldClient::reset(void)
+{
+ const char *cmd[2] = { "volume", "reset" };
+ return sendCommand(2, cmd);
+}
+
+bool VoldClient::mountAll(void)
+{
+ bool ret = true;
+ pthread_rwlock_rdlock(&mVolumeLock);
+ for (auto& info : mVolumes) {
+ if (info.mState == (int)VolumeBase::State::kUnmounted) {
+ if (!volumeMount(info.mId)) {
+ ret = false;
+ }
+ }
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+ return ret;
+}
+
+bool VoldClient::unmountAll(void)
+{
+ bool ret = true;
+ pthread_rwlock_rdlock(&mVolumeLock);
+ for (auto& info : mVolumes) {
+ if (info.mState == (int)VolumeBase::State::kMounted) {
+ if (!volumeUnmount(info.mId)) {
+ ret = false;
+ }
+ }
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+ return ret;
+}
+
+bool VoldClient::volumeMount(const std::string& id)
+{
+ // Special case for emulated storage
+ if (id == "emulated") {
+ pthread_rwlock_wrlock(&mVolumeLock);
+ VolumeInfo* info = getVolumeLocked(id);
+ if (!info) {
+ pthread_rwlock_unlock(&mVolumeLock);
+ return false;
+ }
+ info->mPath = "/storage/emulated";
+ info->mInternalPath = "/data/media";
+ pthread_rwlock_unlock(&mVolumeLock);
+ return ensure_path_mounted("/data") == 0;
+ }
+ const char *cmd[3] = { "volume", "mount", id.c_str() };
+ return sendCommand(3, cmd);
+}
+
+// NB: can only force or detach, not both
+bool VoldClient::volumeUnmount(const std::string& id, bool detach /* = false */)
+{
+ // Special case for emulated storage
+ if (id == "emulated") {
+ if (ensure_path_unmounted("/data", detach) != 0) {
+ return false;
+ }
+ return true;
+ }
+ const char *cmd[4] = { "volume", "unmount", id.c_str(), NULL };
+ int cmdlen = 3;
+ if (detach) {
+ cmd[3] = "detach";
+ cmdlen = 4;
+ }
+ return sendCommand(cmdlen, cmd);
+}
+
+bool VoldClient::volumeFormat(const std::string& id)
+{
+ const char* cmd[3] = { "volume", "format", id.c_str() };
+ return sendCommand(3, cmd);
+}
+
+void VoldClient::resetVolumeState(void)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ mVolumes.clear();
+ mVolumeChanged = false;
+ mEmulatedStorage = true;
+ pthread_rwlock_unlock(&mVolumeLock);
+ if (mWatcher) {
+ mWatcher->onVolumeChanged();
+ }
+ const char *cmd[2] = { "volume", "reset" };
+ sendCommand(2, cmd, false);
+}
+
+VolumeInfo* VoldClient::getVolumeLocked(const std::string& id)
+{
+ for (auto iter = mVolumes.begin(); iter != mVolumes.end(); ++iter) {
+ if (iter->mId == id) {
+ return &(*iter);
+ }
+ }
+ return nullptr;
+}
+
+bool VoldClient::sendCommand(unsigned int len, const char** command, bool wait /* = true */)
+{
+ char line[4096];
+ char* p;
+ unsigned int i;
+ size_t sz;
+ bool ret = true;
+
+ p = line;
+ p += sprintf(p, "0 "); /* 0 is a (now required) sequence number */
+ for (i = 0; i < len; i++) {
+ const char* cmd = command[i];
+ if (!cmd[0] || !strchr(cmd, ' '))
+ p += sprintf(p, "%s", cmd);
+ else
+ p += sprintf(p, "\"%s\"", cmd);
+ if (i < len - 1)
+ *p++ = ' ';
+ if (p >= line + sizeof(line)) {
+ LOGE("vold command line too long\n");
+ exit(1);
+ }
+ }
+
+ // only one writer at a time
+ pthread_mutex_lock(&mSockMutex);
+ if (write(mSock, line, (p - line) + 1) < 0) {
+ LOGE("Unable to send command to vold!\n");
+ pthread_mutex_unlock(&mSockMutex);
+ return false;
+ }
+ ++mInFlight;
+
+ if (wait) {
+ while (mInFlight) {
+ // wait for completion
+ pthread_cond_wait(&mSockCond, &mSockMutex);
+ }
+ ret = (mResult >= 200 && mResult < 300);
+ }
+ pthread_mutex_unlock(&mSockMutex);
+
+ return ret;
+}
+
+void VoldClient::handleCommandOkay(void)
+{
+ bool changed = false;
+ pthread_rwlock_wrlock(&mVolumeLock);
+ if (mVolumeChanged) {
+ mVolumeChanged = false;
+ changed = true;
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+ if (changed) {
+ mWatcher->onVolumeChanged();
+ }
+}
+
+void VoldClient::handleVolumeCreated(const std::string& id, const std::string& type,
+ const std::string& disk, const std::string& guid)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ // Ignore emulated storage if primary storage is physical
+ if (id == "emulated") {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.vold.primary_physical", value, "0");
+ if (value[0] == '1' || value[0] == 'y' || !strcmp(value, "true")) {
+ mEmulatedStorage = false;
+ return;
+ }
+ mEmulatedStorage = true;
+ }
+ VolumeInfo info;
+ info.mId = id;
+ mVolumes.push_back(info);
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumeStateChanged(const std::string& id, const std::string& state)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ auto info = getVolumeLocked(id);
+ if (info) {
+ info->mState = atoi(state.c_str());
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumeFsLabelChanged(const std::string& id, const std::string& label)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ auto info = getVolumeLocked(id);
+ if (info) {
+ info->mLabel = label;
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumePathChanged(const std::string& id, const std::string& path)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ auto info = getVolumeLocked(id);
+ if (info) {
+ info->mPath = path;
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumeInternalPathChanged(const std::string& id, const std::string& path)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ auto info = getVolumeLocked(id);
+ if (info) {
+ info->mInternalPath = path;
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumeDestroyed(const std::string& id)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ for (auto iter = mVolumes.begin(); iter != mVolumes.end(); ++iter) {
+ if (iter->mId == id) {
+ mVolumes.erase(iter);
+ break;
+ }
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+static std::vector<std::string> split(const std::string& line)
+{
+ std::vector<std::string> tokens;
+ const char* tok = line.c_str();
+
+ while (*tok) {
+ unsigned int toklen;
+ const char* next;
+ if (*tok == '"') {
+ ++tok;
+ const char* q = strchr(tok, '"');
+ if (!q) {
+ LOGE("vold line <%s> malformed\n", line.c_str());
+ exit(1);
+ }
+ toklen = q - tok;
+ next = q + 1;
+ if (*next) {
+ if (*next != ' ') {
+ LOGE("vold line <%s> malformed\n", line.c_str());
+ exit(0);
+ }
+ ++next;
+ }
+ }
+ else {
+ next = strchr(tok, ' ');
+ if (next) {
+ toklen = next - tok;
+ ++next;
+ }
+ else {
+ toklen = strlen(tok);
+ next = tok + toklen;
+ }
+ }
+ tokens.push_back(std::string(tok, toklen));
+ tok = next;
+ }
+
+ return tokens;
+}
+
+void VoldClient::dispatch(const std::string& line)
+{
+ std::vector<std::string> tokens = split(line);
+
+ switch (mResult) {
+ case ResponseCode::CommandOkay:
+ handleCommandOkay();
+ break;
+ case ResponseCode::VolumeCreated:
+ handleVolumeCreated(tokens[1], tokens[2], tokens[3], tokens[4]);
+ break;
+ case ResponseCode::VolumeStateChanged:
+ handleVolumeStateChanged(tokens[1], tokens[2]);
+ break;
+ case ResponseCode::VolumeFsLabelChanged:
+ handleVolumeFsLabelChanged(tokens[1], tokens[2]);
+ break;
+ case ResponseCode::VolumePathChanged:
+ handleVolumePathChanged(tokens[1], tokens[2]);
+ break;
+ case ResponseCode::VolumeInternalPathChanged:
+ handleVolumeInternalPathChanged(tokens[1], tokens[2]);
+ break;
+ case ResponseCode::VolumeDestroyed:
+ handleVolumeDestroyed(tokens[1]);
+ break;
+ }
+}
+
+void VoldClient::run(void)
+{
+ LOGI("VoldClient thread starting\n");
+ while (mRunning) {
+ if (mSock == -1) {
+ LOGI("Connecting to Vold...\n");
+ mSock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ if (mSock == -1) {
+ sleep(1);
+ continue;
+ }
+ resetVolumeState();
+ }
+
+ int rc;
+
+ struct timeval tv;
+ fd_set rfds;
+
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_usec = 100 * 1000;
+ FD_ZERO(&rfds);
+ FD_SET(mSock, &rfds);
+
+ rc = select(mSock + 1, &rfds, NULL, NULL, &tv);
+ if (rc <= 0) {
+ if (rc < 0 && errno != EINTR) {
+ LOGE("vdc: error in select (%s)\n", strerror(errno));
+ close(mSock);
+ mSock = -1;
+ }
+ continue;
+ }
+
+ char buf[4096];
+ memset(buf, 0, sizeof(buf));
+ rc = read(mSock, buf, sizeof(buf) - 1);
+ if (rc <= 0) {
+ LOGE("vdc: read failed: %s\n", (rc == 0 ? "EOF" : strerror(errno)));
+ close(mSock);
+ mSock = -1;
+ continue;
+ }
+
+ // dispatch each line of the response
+ int nread = rc;
+ int off = 0;
+ while (off < nread) {
+ char* eol = (char*)memchr(buf + off, 0, nread - off);
+ if (!eol) {
+ break;
+ }
+ mResult = atoi(buf + off);
+ dispatch(std::string(buf + off));
+ if (mResult >= 200 && mResult < 600) {
+ pthread_mutex_lock(&mSockMutex);
+ --mInFlight;
+ pthread_cond_signal(&mSockCond);
+ pthread_mutex_unlock(&mSockMutex);
+ }
+ off = (eol - buf) + 1;
+ }
+ }
+}
diff --git a/voldclient.h b/voldclient.h
new file mode 100644
index 0000000..c5f568e
--- /dev/null
+++ b/voldclient.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013 The CyanogenMod 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_CLIENT_H
+#define _VOLD_CLIENT_H
+
+#include <sys/types.h>
+#include <pthread.h>
+
+#include <string>
+#include <vector>
+
+class VoldWatcher {
+public:
+ virtual ~VoldWatcher(void) {}
+ virtual void onVolumeChanged(void) = 0;
+};
+
+class VolumeInfo {
+public:
+ std::string mId;
+ std::string mLabel;
+ std::string mPath;
+ std::string mInternalPath;
+ int mState;
+};
+
+class VoldClient
+{
+public:
+ VoldClient(VoldWatcher* watcher = nullptr);
+
+ void start(void);
+ void stop(void);
+
+ std::vector<VolumeInfo> getVolumes(void) { return mVolumes; }
+ VolumeInfo getVolume(const std::string& id);
+
+ bool isEmulatedStorage(void) { return mEmulatedStorage; }
+
+ bool reset(void);
+ bool mountAll(void);
+ bool unmountAll(void);
+
+ bool volumeMount(const std::string& id);
+ bool volumeUnmount(const std::string& id, bool detach = false);
+ bool volumeFormat(const std::string& id);
+ bool volumeAvailable(const std::string& id);
+
+private:
+ void resetVolumeState(void);
+
+ VolumeInfo* getVolumeLocked(const std::string& id);
+ bool sendCommand(unsigned int len, const char** command, bool wait = true);
+
+ void handleCommandOkay(void);
+ void handleVolumeCreated(const std::string& id, const std::string& type,
+ const std::string& disk, const std::string& guid);
+ void handleVolumeStateChanged(const std::string& id, const std::string& state);
+ void handleVolumeFsLabelChanged(const std::string& id, const std::string& label);
+ void handleVolumePathChanged(const std::string& id, const std::string& path);
+ void handleVolumeInternalPathChanged(const std::string& id, const std::string& path);
+ void handleVolumeDestroyed(const std::string& id);
+
+ void dispatch(const std::string& line);
+
+public:
+ void run(void); // INTERNAL
+
+private:
+ bool mRunning;
+ int mSock;
+ pthread_t mThread;
+ pthread_mutex_t mSockMutex;
+ pthread_cond_t mSockCond;
+ unsigned int mInFlight;
+ unsigned int mResult;
+ VoldWatcher* mWatcher;
+ pthread_rwlock_t mVolumeLock;
+ bool mVolumeChanged;
+ bool mEmulatedStorage;
+ std::vector<VolumeInfo> mVolumes;
+};
+
+extern VoldClient* vdc;
+
+#endif
+