aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk228
-rw-r--r--adb_install.cpp130
-rw-r--r--adb_install.h4
-rw-r--r--backup.cpp300
-rw-r--r--bootloader_message/Android.mk4
-rw-r--r--bootloader_message/include/bootloader_message/bootloader_message.h5
-rw-r--r--bu.cpp386
-rw-r--r--bu.h54
-rw-r--r--device.cpp163
-rw-r--r--device.h50
-rw-r--r--etc/init.rc59
-rw-r--r--etc/mkshrc70
-rw-r--r--fstools/Android.mk70
-rw-r--r--fstools/fstools.cpp23
-rw-r--r--fstools/fstools.h78
-rw-r--r--fuse_sdcard_provider.cpp6
-rw-r--r--fuse_sideload.cpp147
-rw-r--r--install.cpp151
-rw-r--r--install.h2
-rw-r--r--minui/Android.mk11
-rw-r--r--minui/events.cpp9
-rw-r--r--minui/graphics.cpp75
-rw-r--r--minui/graphics_fbdev.cpp6
-rw-r--r--minui/minui.h4
-rw-r--r--minui/resources.cpp2
-rw-r--r--mtdutils/mounts.c15
-rw-r--r--mtdutils/mounts.h1
-rw-r--r--mtdutils/mtdutils.c8
-rw-r--r--recovery.cpp689
-rw-r--r--recovery_cmds.h74
-rw-r--r--res-hdpi/images/font_log.pngbin0 -> 13408 bytes
-rw-r--r--res-hdpi/images/font_menu.pngbin0 -> 22354 bytes
-rw-r--r--res-hdpi/images/icon_header.pngbin0 -> 2977 bytes
-rw-r--r--res-hdpi/images/icon_sysbar_back.pngbin0 -> 762 bytes
-rw-r--r--res-hdpi/images/icon_sysbar_back_highlight.pngbin0 -> 2674 bytes
-rw-r--r--res-hdpi/images/icon_sysbar_home.pngbin0 -> 860 bytes
-rw-r--r--res-hdpi/images/icon_sysbar_home_highlight.pngbin0 -> 2852 bytes
-rw-r--r--res-mdpi/images/font_log.pngbin0 -> 7287 bytes
-rw-r--r--res-mdpi/images/font_menu.pngbin0 -> 13408 bytes
-rw-r--r--res-mdpi/images/icon_header.pngbin0 -> 1848 bytes
-rw-r--r--res-mdpi/images/icon_sysbar_back.pngbin0 -> 555 bytes
-rw-r--r--res-mdpi/images/icon_sysbar_back_highlight.pngbin0 -> 1491 bytes
-rw-r--r--res-mdpi/images/icon_sysbar_home.pngbin0 -> 576 bytes
-rw-r--r--res-mdpi/images/icon_sysbar_home_highlight.pngbin0 -> 1567 bytes
-rw-r--r--res-xhdpi/images/font_log.pngbin0 -> 22354 bytes
-rw-r--r--res-xhdpi/images/font_menu.pngbin0 -> 17287 bytes
-rw-r--r--res-xhdpi/images/icon_header.pngbin0 -> 4577 bytes
-rw-r--r--res-xhdpi/images/icon_sysbar_back.pngbin0 -> 946 bytes
-rw-r--r--res-xhdpi/images/icon_sysbar_back_highlight.pngbin0 -> 3481 bytes
-rw-r--r--res-xhdpi/images/icon_sysbar_home.pngbin0 -> 1150 bytes
-rw-r--r--res-xhdpi/images/icon_sysbar_home_highlight.pngbin0 -> 3788 bytes
-rw-r--r--res-xxhdpi/images/font_log.pngbin0 -> 17287 bytes
-rw-r--r--res-xxhdpi/images/font_menu.pngbin0 -> 65852 bytes
-rw-r--r--res-xxhdpi/images/icon_header.pngbin0 -> 7321 bytes
-rw-r--r--res-xxhdpi/images/icon_sysbar_back.pngbin0 -> 1450 bytes
-rw-r--r--res-xxhdpi/images/icon_sysbar_back_highlight.pngbin0 -> 4942 bytes
-rw-r--r--res-xxhdpi/images/icon_sysbar_home.pngbin0 -> 1759 bytes
-rw-r--r--res-xxhdpi/images/icon_sysbar_home_highlight.pngbin0 -> 5398 bytes
-rw-r--r--res-xxxhdpi/images/font_log.pngbin0 -> 60087 bytes
-rw-r--r--res-xxxhdpi/images/font_menu.pngbin0 -> 101284 bytes
-rw-r--r--res-xxxhdpi/images/icon_header.pngbin0 -> 10050 bytes
-rw-r--r--res-xxxhdpi/images/icon_sysbar_back.pngbin0 -> 1245 bytes
-rw-r--r--res-xxxhdpi/images/icon_sysbar_back_highlight.pngbin0 -> 8323 bytes
-rw-r--r--res-xxxhdpi/images/icon_sysbar_home.pngbin0 -> 1445 bytes
-rw-r--r--res-xxxhdpi/images/icon_sysbar_home_highlight.pngbin0 -> 8905 bytes
-rw-r--r--restore.cpp305
-rw-r--r--roots.cpp344
-rw-r--r--roots.h23
-rw-r--r--screen_ui.cpp353
-rw-r--r--screen_ui.h49
-rw-r--r--tests/Android.mk1
-rw-r--r--ui.cpp442
-rw-r--r--ui.h110
-rw-r--r--updater/Android.mk26
-rw-r--r--updater/blockimg.cpp2
-rw-r--r--updater/install.cpp102
-rw-r--r--voldclient.cpp466
-rw-r--r--voldclient.h100
-rw-r--r--wear_ui.cpp2
-rw-r--r--wear_ui.h3
80 files changed, 4728 insertions, 424 deletions
diff --git a/Android.mk b/Android.mk
index 589bff41..7537307b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,4 +1,5 @@
# Copyright (C) 2007 The Android Open Source Project
+# Copyright (C) 2015 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.
@@ -12,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+ifeq ($(call my-dir),$(call project-path-for,recovery))
+
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
@@ -41,17 +44,18 @@ LOCAL_SRC_FILES := \
verifier.cpp \
wear_ui.cpp \
wear_touch.cpp \
+ voldclient.cpp
+
+# External tools
+LOCAL_SRC_FILES += \
+ ../../system/core/toolbox/newfs_msdos.c \
+ ../../system/core/toolbox/start_stop.cpp \
+ ../../system/vold/vdc.cpp
LOCAL_MODULE := recovery
LOCAL_FORCE_STATIC_EXECUTABLE := true
-ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
-ifeq ($(HOST_OS),linux)
-LOCAL_REQUIRED_MODULES := mkfs.f2fs
-endif
-endif
-
RECOVERY_API_VERSION := 3
RECOVERY_FSTAB_VERSION := 2
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
@@ -62,43 +66,92 @@ LOCAL_C_INCLUDES += \
system/vold \
system/extras/ext4_utils \
system/core/adb \
+ external/e2fsprogs/lib
LOCAL_STATIC_LIBRARIES := \
libbatterymonitor \
libbootloader_message \
+ libminivold_static \
libext4_utils_static \
+ libmake_ext4fs_static \
+ libminizip_static \
+ libminiunz_static \
libsparse_static \
+ libfsck_msdos \
+ libminipigz_static \
+ libzopfli \
+ libreboot_static \
+ libsdcard \
libminzip \
libz \
libmtdutils \
libminadbd \
+ libtoybox_driver \
+ libmksh_static \
libfusesideload \
libminui \
libpng \
+ libf2fs_sparseblock \
+ libdiskconfig \
+ libsysutils \
libfs_mgr \
+ libcrypto_utils_static \
libcrypto_static \
libbase \
- libcutils \
libutils \
liblog \
+ liblogwrap \
libselinux \
+ libscrypt_static \
+ libnl \
+ libc++_static \
libm \
- libc
+ libc \
+ libext2_blkid \
+ libext2_uuid \
+ libfec \
+ libfec_rs \
+ libsquashfs_utils
LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-
-ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
- LOCAL_CFLAGS += -DUSE_EXT4
- LOCAL_C_INCLUDES += system/extras/ext4_utils
- LOCAL_STATIC_LIBRARIES += libext4_utils_static libz
-endif
+LOCAL_WHOLE_STATIC_LIBRARIES += libcutils
ifeq ($(AB_OTA_UPDATER),true)
LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
endif
+# OEMLOCK support requires a device specific liboemlock be supplied.
+# See comments in recovery.cpp for the API.
+ifeq ($(TARGET_HAVE_OEMLOCK), true)
+ LOCAL_CFLAGS += -DHAVE_OEMLOCK
+ LOCAL_STATIC_LIBRARIES += liboemlock
+endif
+
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+# Handling for EV_REL is disabled by default because some accelerometers
+# send EV_REL events. Actual EV_REL devices are rare on modern hardware
+# so it's cleaner just to disable it by default.
+ifneq ($(BOARD_RECOVERY_NEEDS_REL_INPUT),)
+ LOCAL_CFLAGS += -DBOARD_RECOVERY_NEEDS_REL_INPUT
+endif
+
+ifeq ($(TARGET_USE_MDTP), true)
+ LOCAL_CFLAGS += -DUSE_MDTP
+endif
+
+ifeq ($(BOARD_HAS_DOWNLOAD_MODE), true)
+ LOCAL_CFLAGS += -DDOWNLOAD_MODE
+endif
+
+ifeq ($(TARGET_BUILD_VARIANT),user)
+ LOCAL_CFLAGS += -DRELEASE_BUILD
+endif
+
+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
@@ -109,8 +162,149 @@ ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),)
LOCAL_REQUIRED_MODULES := recovery-persist recovery-refresh
endif
+LOCAL_C_INCLUDES += system/extras/ext4_utils
+LOCAL_C_INCLUDES += external/boringssl/include
+
+ifeq ($(ONE_SHOT_MAKEFILE),)
+LOCAL_ADDITIONAL_DEPENDENCIES += \
+ fstools \
+ recovery_mkshrc \
+ bu_recovery \
+ toybox_recovery_links
+
+endif
+
+TOYBOX_INSTLIST := $(HOST_OUT_EXECUTABLES)/toybox-instlist
+
+# Set up the static symlinks
+RECOVERY_TOOLS := \
+ gunzip gzip make_ext4fs minivold 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);)
+
+ifneq ($(TARGET_RECOVERY_DEVICE_MODULES),)
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_RECOVERY_DEVICE_MODULES)
+endif
+
include $(BUILD_EXECUTABLE)
+# Run toybox-instlist and generate the rest of the symlinks
+toybox_recovery_links: $(TOYBOX_INSTLIST)
+toybox_recovery_links: TOYBOX_BINARY := $(TARGET_RECOVERY_ROOT_OUT)/sbin/toybox
+toybox_recovery_links:
+ @echo -e ${CL_CYN}"Generate Toybox links:"${CL_RST} $$($(TOYBOX_INSTLIST))
+ @mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/sbin
+ $(hide) $(TOYBOX_INSTLIST) | xargs -I'{}' ln -sf toybox '$(TARGET_RECOVERY_ROOT_OUT)/sbin/{}'
+
+# mkshrc
+include $(CLEAR_VARS)
+LOCAL_MODULE := recovery_mkshrc
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_SRC_FILES := etc/mkshrc
+LOCAL_MODULE_STEM := mkshrc
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bu_recovery
+LOCAL_MODULE_STEM := bu
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SRC_FILES := \
+ bu.cpp \
+ backup.cpp \
+ restore.cpp \
+ roots.cpp \
+ voldclient.cpp
+LOCAL_CFLAGS += -DMINIVOLD
+LOCAL_CFLAGS += -Wno-unused-parameter
+#ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
+ LOCAL_CFLAGS += -DUSE_EXT4
+ LOCAL_C_INCLUDES += system/extras/ext4_utils
+ LOCAL_STATIC_LIBRARIES += libext4_utils_static libz
+#endif
+LOCAL_STATIC_LIBRARIES += \
+ libsparse_static \
+ libz \
+ libmtdutils \
+ libminadbd \
+ libminui \
+ libfs_mgr \
+ libtar \
+ libcrypto_utils_static \
+ libcrypto_static \
+ libselinux \
+ libutils \
+ libcutils \
+ liblog \
+ libm \
+ libc \
+ libext2_blkid \
+ libext2_uuid
+
+LOCAL_C_INCLUDES += \
+ system/core/fs_mgr/include \
+ system/core/include \
+ system/core/libcutils \
+ system/vold \
+ external/libtar \
+ external/libtar/listhash \
+ external/openssl/include \
+ external/zlib \
+ bionic/libc/bionic \
+ external/e2fsprogs/lib
+
+
+include $(BUILD_EXECUTABLE)
+
+# make_ext4fs
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmake_ext4fs_static
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Dmain=make_ext4fs_main
+LOCAL_SRC_FILES := \
+ ../../system/extras/ext4_utils/make_ext4fs_main.c \
+ ../../system/core/libcutils/canned_fs_config.c
+LOCAL_STATIC_LIBRARIES += libselinux
+include $(BUILD_STATIC_LIBRARY)
+
+# Minizip static library
+include $(CLEAR_VARS)
+LOCAL_MODULE := libminizip_static
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Dmain=minizip_main -D__ANDROID__ -DIOAPI_NO_64 -Wno-unknown-attributes
+LOCAL_C_INCLUDES := external/zlib
+LOCAL_SRC_FILES := \
+ ../../external/zlib/src/contrib/minizip/ioapi.c \
+ ../../external/zlib/src/contrib/minizip/minizip.c \
+ ../../external/zlib/src/contrib/minizip/zip.c
+include $(BUILD_STATIC_LIBRARY)
+
+# Miniunz static library
+include $(CLEAR_VARS)
+LOCAL_MODULE := libminiunz_static
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Dmain=miniunz_main -D__ANDROID__ -DIOAPI_NO_64 -Wno-unknown-attributes
+LOCAL_C_INCLUDES := external/zlib bionic/libc/include
+LOCAL_SRC_FILES := \
+ ../../external/zlib/src/contrib/minizip/ioapi.c \
+ ../../external/zlib/src/contrib/minizip/miniunz.c \
+ ../../external/zlib/src/contrib/minizip/unzip.c
+LOCAL_STATIC_LIBRARIES += libc
+include $(BUILD_STATIC_LIBRARY)
+
+# Reboot static library
+include $(CLEAR_VARS)
+LOCAL_MODULE := libreboot_static
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Dmain=reboot_main
+LOCAL_SRC_FILES := ../../system/core/reboot/reboot.c
+include $(BUILD_STATIC_LIBRARY)
+
+
# recovery-persist (system partition dynamic executable run after /data mounts)
# ===============================
include $(CLEAR_VARS)
@@ -140,7 +334,8 @@ LOCAL_SRC_FILES := \
asn1_decoder.cpp \
verifier.cpp \
ui.cpp
-LOCAL_STATIC_LIBRARIES := libcrypto_static
+LOCAL_C_INCLUDES := system/core/fs_mgr/include
+LOCAL_STATIC_LIBRARIES := libcrypto_utils_static libcrypto_static
include $(BUILD_STATIC_LIBRARY)
include \
@@ -157,3 +352,6 @@ include \
$(LOCAL_PATH)/uncrypt/Android.mk \
$(LOCAL_PATH)/updater/Android.mk \
$(LOCAL_PATH)/update_verifier/Android.mk \
+ $(LOCAL_PATH)/fstools/Android.mk
+
+endif
diff --git a/adb_install.cpp b/adb_install.cpp
index 4aed9d4b..69858960 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -34,6 +34,7 @@
#include "fuse_sideload.h"
static RecoveryUI* ui = NULL;
+static pthread_t sideload_thread;
static void
set_usb_driver(bool enabled) {
@@ -66,69 +67,80 @@ maybe_restart_adbd() {
}
}
+struct sideload_data {
+ bool* wipe_cache;
+ const char* install_file;
+ bool cancel;
+ int result;
+};
+
+static struct sideload_data sideload_data;
+
// How long (in seconds) we wait for the host to start sending us a
// package, before timing out.
#define ADB_INSTALL_TIMEOUT 300
-int
-apply_from_adb(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) {
- modified_flash = true;
-
- ui = ui_;
-
- stop_adbd();
- set_usb_driver(true);
-
- ui->Print("\n\nNow send the package you want to apply\n"
- "to the device with \"adb sideload <filename>\"...\n");
-
+void *adb_sideload_thread(void* v) {
pid_t child;
if ((child = fork()) == 0) {
execl("/sbin/recovery", "recovery", "--adbd", NULL);
_exit(-1);
}
+ time_t start_time = time(NULL);
+ time_t now = start_time;
+
// FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host
// connects and starts serving a package. Poll for its
// appearance. (Note that inotify doesn't work with FUSE.)
- int result = INSTALL_ERROR;
- int status;
- bool waited = false;
+ int result = INSTALL_NONE;
+ int status = -1;
struct stat st;
- for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
- if (waitpid(child, &status, WNOHANG) != 0) {
+ while (now - start_time < ADB_INSTALL_TIMEOUT) {
+ /*
+ * Exit if either:
+ * - The adb child process dies, or
+ * - The ui tells us to cancel
+ */
+ if (kill(child, 0) != 0) {
result = INSTALL_ERROR;
- waited = true;
break;
}
- if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
- if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) {
- sleep(1);
- continue;
- } else {
- ui->Print("\nTimed out waiting for package.\n\n");
- result = INSTALL_ERROR;
- kill(child, SIGKILL);
- break;
- }
+ if (sideload_data.cancel) {
+ break;
+ }
+
+ status = stat(FUSE_SIDELOAD_HOST_PATHNAME, &st);
+ if (status == 0) {
+ break;
}
- result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false, 0);
- break;
+ if (errno != ENOENT && errno != ENOTCONN) {
+ ui->Print("\nError %s waiting for package\n\n", strerror(errno));
+ result = INSTALL_ERROR;
+ break;
+ }
+
+ sleep(1);
+ now = time(NULL);
}
- if (!waited) {
- // Calling stat() on this magic filename signals the minadbd
- // subprocess to shut down.
- stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+ if (status == 0) {
+ // Signal UI thread that we can no longer cancel
+ ui->CancelWaitKey();
- // TODO(dougz): there should be a way to cancel waiting for a
- // package (by pushing some button combo on the device). For now
- // you just have to 'adb sideload' a file that's not a valid
- // package, like "/dev/null".
- waitpid(child, &status, 0);
+ result = install_package(FUSE_SIDELOAD_HOST_PATHNAME,
+ sideload_data.wipe_cache,
+ sideload_data.install_file,
+ false, 0);
+
+ sideload_data.result = result;
}
+ // Ensure adb exits
+ kill(child, SIGTERM);
+ waitpid(child, &status, 0);
+
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
if (WEXITSTATUS(status) == 3) {
ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
@@ -137,8 +149,44 @@ apply_from_adb(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) {
}
}
- set_usb_driver(false);
+ LOGI("sideload thread finished\n");
+ return NULL;
+}
+
+void
+start_sideload(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) {
+ modified_flash = true;
+
+ ui = ui_;
+
+ stop_adbd();
+ set_usb_driver(true);
+
+ ui->Print("\n\nNow send the package you want to apply\n"
+ "to the device with \"adb sideload <filename>\"...\n");
+
+ sideload_data.wipe_cache = wipe_cache;
+ sideload_data.install_file = install_file;
+ sideload_data.cancel = false;
+ sideload_data.result = INSTALL_NONE;
+
+ pthread_create(&sideload_thread, NULL, &adb_sideload_thread, NULL);
+}
+
+void stop_sideload() {
+ sideload_data.cancel = true;
+}
+
+int wait_sideload() {
+ set_perf_mode(true);
+
+ pthread_join(sideload_thread, NULL);
+
+ ui->FlushKeys();
+
maybe_restart_adbd();
- return result;
+ set_perf_mode(false);
+
+ return sideload_data.result;
}
diff --git a/adb_install.h b/adb_install.h
index efad436f..7c9d7bcb 100644
--- a/adb_install.h
+++ b/adb_install.h
@@ -19,6 +19,8 @@
class RecoveryUI;
-int apply_from_adb(RecoveryUI* h, bool* wipe_cache, const char* install_file);
+void start_sideload(RecoveryUI* h, bool* wipe_cache, const char* install_file);
+void stop_sideload();
+int wait_sideload();
#endif
diff --git a/backup.cpp b/backup.cpp
new file mode 100644
index 00000000..28b023b0
--- /dev/null
+++ b/backup.cpp
@@ -0,0 +1,300 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/vfs.h>
+#include <time.h>
+
+#include "cutils/properties.h"
+
+#include "roots.h"
+
+#include "bu.h"
+
+#include "voldclient.h"
+
+using namespace android;
+
+static int append_sod(const char* opt_hash)
+{
+ const char* key;
+ char value[PROPERTY_VALUE_MAX];
+ int len;
+ char buf[PROP_LINE_LEN];
+ char sodbuf[PROP_LINE_LEN*10];
+ char* p = sodbuf;
+
+ key = "hash.name";
+ strcpy(value, opt_hash);
+ p += sprintf(p, "%s=%s\n", key, value);
+
+ key = "ro.product.device";
+ property_get(key, value, "");
+ p += sprintf(p, "%s=%s\n", key, value);
+
+ for (int i = 0; i < MAX_PART; ++i) {
+ partspec* part = part_get(i);
+ if (!part)
+ break;
+ if (!volume_is_mountable(part->vol) ||
+ volume_is_readonly(part->vol) ||
+ volume_is_verity(part->vol)) {
+ int fd = open(part->vol->blk_device, O_RDONLY);
+ part->size = part->used = lseek64(fd, 0, SEEK_END);
+ close(fd);
+ }
+ else {
+ if (ensure_path_mounted(part->path) == 0) {
+ struct statfs stfs;
+ memset(&stfs, 0, sizeof(stfs));
+ if (statfs(part->path, &stfs) == 0) {
+ part->size = (stfs.f_blocks) * stfs.f_bsize;
+ part->used = (stfs.f_blocks - stfs.f_bfree) * stfs.f_bsize;
+ }
+ else {
+ logmsg("Failed to statfs %s: %s\n", part->path, strerror(errno));
+ }
+ ensure_path_unmounted(part->path);
+ }
+ else {
+ int fd = open(part->vol->blk_device, O_RDONLY);
+ part->size = part->used = lseek64(fd, 0, SEEK_END);
+ close(fd);
+ }
+ }
+ p += sprintf(p, "fs.%s.size=%llu\n", part->name, part->size);
+ p += sprintf(p, "fs.%s.used=%llu\n", part->name, part->used);
+ }
+
+ int rc = tar_append_file_contents(tar, "SOD", 0600,
+ getuid(), getgid(), sodbuf, p-sodbuf);
+ return rc;
+}
+
+static int append_eod(const char* opt_hash)
+{
+ char buf[PROP_LINE_LEN];
+ char eodbuf[PROP_LINE_LEN*10];
+ char* p = eodbuf;
+ int n;
+
+ p += sprintf(p, "hash.datalen=%u\n", hash_datalen);
+
+ unsigned char digest[HASH_MAX_LENGTH];
+ char hexdigest[HASH_MAX_STRING_LENGTH];
+
+ if (!strcasecmp(opt_hash, "sha1")) {
+ SHA1_Final(digest, &sha_ctx);
+ for (n = 0; n < SHA_DIGEST_LENGTH; ++n) {
+ sprintf(hexdigest+2*n, "%02x", digest[n]);
+ }
+ p += sprintf(p, "hash.value=%s\n", hexdigest);
+ }
+ else { // default to md5
+ MD5_Final(digest, &md5_ctx);
+ for (n = 0; n < MD5_DIGEST_LENGTH; ++n) {
+ sprintf(hexdigest+2*n, "%02x", digest[n]);
+ }
+ p += sprintf(p, "hash.value=%s\n", hexdigest);
+ }
+
+ int rc = tar_append_file_contents(tar, "EOD", 0600,
+ getuid(), getgid(), eodbuf, p-eodbuf);
+ return rc;
+}
+
+static int do_backup_tree(const String8& path)
+{
+ int rc = 0;
+ bool path_is_data = !strcmp(path.string(), "/data");
+ DIR *dp;
+
+ dp = opendir(path.string());
+ struct dirent *de;
+ while ((de = readdir(dp)) != NULL) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+ continue;
+ }
+ if (path_is_data && !strcmp(de->d_name, "media") && vdc->isEmulatedStorage()) {
+ logmsg("do_backup_tree: skipping datamedia\n");
+ continue;
+ }
+ struct stat st;
+ String8 filepath(path);
+ filepath += "/";
+ filepath += de->d_name;
+
+ memset(&st, 0, sizeof(st));
+ rc = lstat(filepath.string(), &st);
+ if (rc != 0) {
+ logmsg("do_backup_tree: path=%s, lstat failed, rc=%d\n", path.string(), rc);
+ break;
+ }
+
+ if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) {
+ logmsg("do_backup_tree: path=%s, ignoring special file\n", path.string());
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ rc = tar_append_file(tar, filepath.string(), filepath.string());
+ if (rc != 0) {
+ logmsg("do_backup_tree: path=%s, tar_append_file failed, rc=%d\n", path.string(), rc);
+ break;
+ }
+ rc = do_backup_tree(filepath);
+ if (rc != 0) {
+ logmsg("do_backup_tree: path=%s, recursion failed, rc=%d\n", path.string(), rc);
+ break;
+ }
+ }
+ else {
+ rc = tar_append_file(tar, filepath.string(), filepath.string());
+ if (rc != 0) {
+ logmsg("do_backup_tree: path=%s, tar_append_file failed, rc=%d\n", path.string(), rc);
+ break;
+ }
+ }
+ }
+ closedir(dp);
+ return rc;
+}
+
+static int tar_append_device_contents(TAR* t, const char* devname, const char* savename)
+{
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ if (lstat(devname, &st) != 0) {
+ logmsg("tar_append_device_contents: lstat %s failed\n", devname);
+ return -1;
+ }
+ st.st_mode = 0644 | S_IFREG;
+
+ int fd = open(devname, O_RDONLY);
+ if (fd < 0) {
+ logmsg("tar_append_device_contents: open %s failed\n", devname);
+ return -1;
+ }
+ st.st_size = lseek64(fd, 0, SEEK_END);
+ close(fd);
+
+ th_set_from_stat(t, &st);
+ th_set_path(t, savename);
+ if (th_write(t) != 0) {
+ logmsg("tar_append_device_contents: th_write failed\n");
+ return -1;
+ }
+ if (tar_append_regfile(t, devname) != 0) {
+ logmsg("tar_append_device_contents: tar_append_regfile %s failed\n", devname);
+ return -1;
+ }
+ return 0;
+}
+
+int do_backup(int argc, char **argv)
+{
+ int rc = 1;
+ int n;
+ int i;
+
+ int len;
+ int written;
+
+ const char* opt_compress = "gzip";
+ const char* opt_hash = "md5";
+
+ int optidx = 0;
+ while (optidx < argc && argv[optidx][0] == '-' && argv[optidx][1] == '-') {
+ char* optname = &argv[optidx][2];
+ ++optidx;
+ char* optval = strchr(optname, '=');
+ if (optval) {
+ *optval = '\0';
+ ++optval;
+ }
+ else {
+ if (optidx >= argc) {
+ logmsg("No argument to --%s\n", optname);
+ return -1;
+ }
+ optval = argv[optidx];
+ ++optidx;
+ }
+ if (!strcmp(optname, "compress")) {
+ opt_compress = optval;
+ logmsg("do_backup: compress=%s\n", opt_compress);
+ }
+ else if (!strcmp(optname, "hash")) {
+ opt_hash = optval;
+ logmsg("do_backup: hash=%s\n", opt_hash);
+ }
+ else {
+ logmsg("do_backup: invalid option name \"%s\"\n", optname);
+ return -1;
+ }
+ }
+ for (n = optidx; n < argc; ++n) {
+ const char* partname = argv[n];
+ if (*partname == '-')
+ ++partname;
+ if (part_add(partname) != 0) {
+ logmsg("Failed to add partition %s\n", partname);
+ return -1;
+ }
+ }
+
+ rc = create_tar(adb_ofd, opt_compress, "w");
+ if (rc != 0) {
+ logmsg("do_backup: cannot open tar stream\n");
+ return rc;
+ }
+
+ append_sod(opt_hash);
+
+ hash_name = strdup(opt_hash);
+
+ for (i = 0; i < MAX_PART; ++i) {
+ partspec* curpart = part_get(i);
+ if (!curpart)
+ break;
+
+ part_set(curpart);
+ if (!volume_is_mountable(curpart->vol) ||
+ volume_is_readonly(curpart->vol) ||
+ volume_is_verity(curpart->vol)) {
+ rc = tar_append_device_contents(tar, curpart->vol->blk_device, curpart->name);
+ }
+ else {
+ if (ensure_path_mounted(curpart->path) != 0) {
+ rc = tar_append_device_contents(tar, curpart->vol->blk_device, curpart->name);
+ if (rc != 0) {
+ logmsg("do_backup: cannot backup %s\n", curpart->path);
+ continue;
+ }
+ }
+ String8 path(curpart->path);
+ rc = do_backup_tree(path);
+ ensure_path_unmounted(curpart->path);
+ }
+ }
+
+ free(hash_name);
+ hash_name = NULL;
+
+ append_eod(opt_hash);
+
+ tar_append_eof(tar);
+
+ if (opt_compress)
+ gzflush(gzf, Z_FINISH);
+
+ logmsg("backup complete: rc=%d\n", rc);
+
+ return rc;
+}
+
diff --git a/bootloader_message/Android.mk b/bootloader_message/Android.mk
index 815ac67d..20889a53 100644
--- a/bootloader_message/Android.mk
+++ b/bootloader_message/Android.mk
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+ifneq ($(BOARD_PROVIDES_BOOTLOADER_MESSAGE),true)
+
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
@@ -22,3 +24,5 @@ LOCAL_STATIC_LIBRARIES := libbase libfs_mgr
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_STATIC_LIBRARY)
+
+endif
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index c63aeca6..dc588b92 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -25,8 +25,13 @@
// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
// are not configurable without changing all of them.
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = BOARD_RECOVERY_BLDRMSG_OFFSET;
+static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024 + BOOTLOADER_MESSAGE_OFFSET_IN_MISC;
+#else
static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
+#endif
/* Bootloader Message
*
diff --git a/bu.cpp b/bu.cpp
new file mode 100644
index 00000000..f647c39b
--- /dev/null
+++ b/bu.cpp
@@ -0,0 +1,386 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/vfs.h>
+
+#include <cutils/properties.h>
+#include <cutils/log.h>
+
+#include <selinux/label.h>
+
+#include "roots.h"
+
+#include "bu.h"
+
+#include "voldclient.h"
+
+#define PATHNAME_RC "/tmp/burc"
+
+#define PATHNAME_XCOMP_ENABLE "/sys/fs/xcomp/enable"
+
+using namespace std;
+
+using namespace android;
+
+struct selabel_handle *sehandle;
+
+int adb_ifd;
+int adb_ofd;
+TAR* tar;
+gzFile gzf;
+
+char* hash_name;
+size_t hash_datalen;
+SHA_CTX sha_ctx;
+MD5_CTX md5_ctx;
+
+void
+ui_print(const char* format, ...) {
+ char buffer[256];
+
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+ va_end(ap);
+
+ fputs(buffer, stdout);
+}
+
+void logmsg(const char *fmt, ...)
+{
+ char msg[1024];
+ FILE* fp;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+
+ fp = fopen("/tmp/bu.log", "a");
+ if (fp) {
+ fprintf(fp, "[%d] %s", getpid(), msg);
+ fclose(fp);
+ }
+}
+
+static int xcomp_enable_get(void)
+{
+ int val = 0;
+ int fd;
+ char buf[12+1+1];
+
+ fd = open(PATHNAME_XCOMP_ENABLE, O_RDONLY);
+ if (fd < 0)
+ return 0;
+ memset(buf, 0, sizeof(buf));
+ if (read(fd, buf, sizeof(buf)) > 0) {
+ val = atoi(buf);
+ }
+ close(fd);
+ return val;
+}
+
+static void xcomp_enable_set(int val)
+{
+ int fd;
+ char buf[12+1+1];
+ int len;
+
+ fd = open(PATHNAME_XCOMP_ENABLE, O_RDWR);
+ if (fd < 0)
+ return;
+ len = sprintf(buf, "%d\n", val);
+ write(fd, buf, len);
+ close(fd);
+}
+
+static partspec partlist[MAX_PART];
+static partspec* curpart;
+
+int part_add(const char* name)
+{
+ Volume* vol = NULL;
+ char* path = NULL;
+ int i;
+
+ path = (char*)malloc(1+strlen(name)+1);
+ sprintf(path, "/%s", name);
+ vol = volume_for_path(path);
+ if (vol == NULL || vol->fs_type == NULL) {
+ logmsg("missing vol info for %s, ignoring\n", name);
+ goto err;
+ }
+
+ for (i = 0; i < MAX_PART; ++i) {
+ if (partlist[i].name == NULL) {
+ partlist[i].name = strdup(name);
+ partlist[i].path = path;
+ partlist[i].vol = vol;
+ logmsg("part_add: i=%d, name=%s, path=%s\n", i, name, path);
+ return 0;
+ }
+ if (strcmp(partlist[i].name, name) == 0) {
+ logmsg("duplicate partition %s, ignoring\n", name);
+ goto err;
+ }
+ }
+
+err:
+ free(path);
+ return -1;
+}
+
+partspec* part_get(int i)
+{
+ if (i >= 0 && i < MAX_PART) {
+ if (partlist[i].name != NULL) {
+ return &partlist[i];
+ }
+ }
+ return NULL;
+}
+
+partspec* part_find(const char* name)
+{
+ for (int i = 0; i < MAX_PART; ++i) {
+ if (partlist[i].name && !strcmp(name, partlist[i].name)) {
+ return &partlist[i];
+ }
+ }
+ return NULL;
+}
+
+void part_set(partspec* part)
+{
+ curpart = part;
+ curpart->off = 0;
+}
+
+int update_progress(uint64_t off)
+{
+ static time_t last_time = 0;
+ static int last_pct = 0;
+ if (curpart) {
+ curpart->off += off;
+ time_t now = time(NULL);
+ int pct = min(100, (int)((uint64_t)100*curpart->off/curpart->used));
+ if (now != last_time && pct != last_pct) {
+ char msg[256];
+ sprintf(msg, "%s: %d%% complete", curpart->name, pct);
+ ui_print(msg);
+ last_time = now;
+ last_pct = pct;
+ }
+ }
+ return 0;
+}
+
+static int tar_cb_open(const char* path, int mode, ...)
+{
+ errno = EINVAL;
+ return -1;
+}
+
+static int tar_cb_close(int fd)
+{
+ return 0;
+}
+
+static ssize_t tar_cb_read(int fd, void* buf, size_t len)
+{
+ ssize_t nread;
+ nread = ::read(fd, buf, len);
+ if (nread > 0 && hash_name) {
+ SHA1_Update(&sha_ctx, (u_char*)buf, nread);
+ MD5_Update(&md5_ctx, buf, nread);
+ hash_datalen += nread;
+ }
+ update_progress(nread);
+ return nread;
+}
+
+static ssize_t tar_cb_write(int fd, const void* buf, size_t len)
+{
+ ssize_t written = 0;
+
+ if (hash_name) {
+ SHA1_Update(&sha_ctx, (u_char*)buf, len);
+ MD5_Update(&md5_ctx, buf, len);
+ hash_datalen += len;
+ }
+
+ while (len > 0) {
+ ssize_t n = ::write(fd, buf, len);
+ if (n < 0) {
+ logmsg("tar_cb_write: error: n=%d\n", n);
+ return n;
+ }
+ if (n == 0)
+ break;
+ buf = (const char *)buf + n;
+ len -= n;
+ written += n;
+ }
+ update_progress(written);
+ return written;
+}
+
+static tartype_t tar_io = {
+ tar_cb_open,
+ tar_cb_close,
+ tar_cb_read,
+ tar_cb_write
+};
+
+static ssize_t tar_gz_cb_read(int fd, void* buf, size_t len)
+{
+ int nread;
+ nread = gzread(gzf, buf, len);
+ if (nread > 0 && hash_name) {
+ SHA1_Update(&sha_ctx, (u_char*)buf, nread);
+ MD5_Update(&md5_ctx, buf, nread);
+ hash_datalen += nread;
+ }
+ update_progress(nread);
+ return nread;
+}
+
+static ssize_t tar_gz_cb_write(int fd, const void* buf, size_t len)
+{
+ ssize_t written = 0;
+
+ if (hash_name) {
+ SHA1_Update(&sha_ctx, (u_char*)buf, len);
+ MD5_Update(&md5_ctx, buf, len);
+ hash_datalen += len;
+ }
+
+ while (len > 0) {
+ ssize_t n = gzwrite(gzf, buf, len);
+ if (n < 0) {
+ logmsg("tar_gz_cb_write: error: n=%d\n", n);
+ return n;
+ }
+ if (n == 0)
+ break;
+ buf = (const char *)buf + n;
+ len -= n;
+ written += n;
+ }
+ update_progress(written);
+ return written;
+}
+
+static tartype_t tar_io_gz = {
+ tar_cb_open,
+ tar_cb_close,
+ tar_gz_cb_read,
+ tar_gz_cb_write
+};
+
+int create_tar(int fd, const char* compress, const char* mode)
+{
+ int rc = -1;
+
+ SHA1_Init(&sha_ctx);
+ MD5_Init(&md5_ctx);
+
+ if (!compress || strcasecmp(compress, "none") == 0) {
+ rc = tar_fdopen(&tar, fd, "foobar", &tar_io,
+ 0, /* oflags: unused */
+ 0, /* mode: unused */
+ TAR_GNU | TAR_STORE_SELINUX /* options */);
+ }
+ else if (strcasecmp(compress, "gzip") == 0) {
+ gzf = gzdopen(fd, mode);
+ if (gzf != NULL) {
+ rc = tar_fdopen(&tar, 0, "foobar", &tar_io_gz,
+ 0, /* oflags: unused */
+ 0, /* mode: unused */
+ TAR_GNU | TAR_STORE_SELINUX /* options */);
+ }
+ }
+ return rc;
+}
+
+static void do_exit(int rc)
+{
+ char rcstr[80];
+ int len;
+ len = sprintf(rcstr, "%d\n", rc);
+
+ unlink(PATHNAME_RC);
+ int fd = open(PATHNAME_RC, O_RDWR|O_CREAT, 0644);
+ write(fd, rcstr, len);
+ close(fd);
+ exit(rc);
+}
+
+int main(int argc, char **argv)
+{
+ int n;
+ int rc = 1;
+ int xcomp_enable;
+
+ const char* logfile = "/tmp/recovery.log";
+ adb_ifd = dup(STDIN_FILENO);
+ adb_ofd = dup(STDOUT_FILENO);
+ freopen(logfile, "a", stdout); setbuf(stdout, NULL);
+ freopen(logfile, "a", stderr); setbuf(stderr, NULL);
+
+ logmsg("bu: invoked with %d args\n", argc);
+
+ if (argc < 2) {
+ logmsg("Not enough args (%d)\n", argc);
+ do_exit(1);
+ }
+
+ // progname args...
+ int optidx = 1;
+ const char* opname = argv[optidx++];
+
+ struct selinux_opt seopts[] = {
+ { SELABEL_OPT_PATH, "/file_contexts" }
+ };
+ sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+
+ xcomp_enable = xcomp_enable_get();
+ xcomp_enable_set(0);
+
+ load_volume_table();
+ vdc = new VoldClient();
+ vdc->start();
+
+ if (!strcmp(opname, "backup")) {
+ ui_print("Backup in progress...");
+ rc = do_backup(argc-optidx, &argv[optidx]);
+ }
+ else if (!strcmp(opname, "restore")) {
+ ui_print("Restore in progress...");
+ rc = do_restore(argc-optidx, &argv[optidx]);
+ }
+ else {
+ logmsg("Unknown operation %s\n", opname);
+ rc = 1;
+ }
+
+ xcomp_enable_set(xcomp_enable);
+
+ close(adb_ofd);
+ close(adb_ifd);
+
+ sleep(1);
+
+ logmsg("bu exiting\n");
+
+ do_exit(rc);
+
+ return rc;
+}
diff --git a/bu.h b/bu.h
new file mode 100644
index 00000000..15ab7454
--- /dev/null
+++ b/bu.h
@@ -0,0 +1,54 @@
+#include <utils/String8.h>
+
+#include <lib/libtar.h>
+#include <zlib.h>
+
+extern "C" {
+#include <openssl/sha.h>
+#include <openssl/md5.h>
+#ifndef MD5_DIGEST_STRING_LENGTH
+#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH*2+1)
+#endif
+#ifndef SHA_DIGEST_STRING_LENGTH
+#define SHA_DIGEST_STRING_LENGTH (SHA_DIGEST_LENGTH*2+1)
+#endif
+}
+
+#define HASH_MAX_LENGTH SHA_DIGEST_LENGTH
+#define HASH_MAX_STRING_LENGTH SHA_DIGEST_STRING_LENGTH
+
+#define PROP_LINE_LEN (PROPERTY_KEY_MAX+1+PROPERTY_VALUE_MAX+1+1)
+
+extern int adb_ifd;
+extern int adb_ofd;
+extern TAR* tar;
+extern gzFile gzf;
+
+extern char* hash_name;
+extern size_t hash_datalen;
+extern SHA_CTX sha_ctx;
+extern MD5_CTX md5_ctx;
+
+struct partspec {
+ char* name;
+ char* path;
+ Volume* vol;
+ uint64_t size;
+ uint64_t used;
+ uint64_t off;
+};
+#define MAX_PART 8
+
+extern void logmsg(const char* fmt, ...);
+
+extern int part_add(const char* name);
+extern partspec* part_get(int i);
+extern partspec* part_find(const char* name);
+extern void part_set(partspec* part);
+
+extern int update_progress(uint64_t off);
+
+extern int create_tar(int fd, const char* compress, const char* mode);
+
+extern int do_backup(int argc, char** argv);
+extern int do_restore(int argc, char** argv);
diff --git a/device.cpp b/device.cpp
index e717dddf..ba229621 100644
--- a/device.cpp
+++ b/device.cpp
@@ -16,48 +16,141 @@
#include "device.h"
-static const char* MENU_ITEMS[] = {
- "Reboot system now",
- "Reboot to bootloader",
- "Apply update from ADB",
- "Apply update from SD card",
- "Wipe data/factory reset",
+enum menu_action_type {
+ ACTION_NONE,
+ ACTION_SUBMENU,
+ ACTION_INVOKE
+};
+
+struct menu_entry;
+struct menu {
+ const char** names;
+ const menu_entry* entries;
+};
+
+union menu_action {
+ const menu* submenu;
+ Device::BuiltinAction action;
+};
+
+struct menu_entry {
+ menu_action_type action_type;
+ const menu_action action;
+};
+
+static const char* WIPE_MENU_NAMES[] = {
+#ifndef RELEASE_BUILD
+ "Wipe data (keep media)",
+#endif
+ "Full factory reset",
#ifndef AB_OTA_UPDATER
"Wipe cache partition",
#endif // !AB_OTA_UPDATER
+ nullptr
+};
+static const menu_entry WIPE_MENU_ENTRIES[] = {
+#ifndef RELEASE_BUILD
+ { ACTION_INVOKE, { .action = Device::WIPE_DATA } },
+#endif
+ { ACTION_INVOKE, { .action = Device::WIPE_FULL } },
+#ifndef AB_OTA_UPDATER
+ { ACTION_INVOKE, { .action = Device::WIPE_CACHE } },
+#endif // !AB_OTA_UPDATER
+ { ACTION_NONE, { .action = Device::NO_ACTION } }
+};
+static const menu WIPE_MENU = {
+ WIPE_MENU_NAMES,
+ WIPE_MENU_ENTRIES
+};
+
+static const char* ADVANCED_MENU_NAMES[] = {
+ "Reboot recovery",
+#ifdef DOWNLOAD_MODE
+ "Reboot to download mode",
+#else
+ "Reboot to bootloader",
+#endif
+#ifndef RELEASE_BUILD
"Mount /system",
+ "Wipe system partition",
+#endif
"View recovery logs",
"Run graphics test",
"Power off",
- NULL,
+ nullptr
+};
+static const menu_entry ADVANCED_MENU_ENTRIES[] = {
+ { ACTION_INVOKE, { .action = Device::REBOOT_RECOVERY } },
+#ifdef DOWNLOAD_MODE
+ { ACTION_INVOKE, { .action = Device::REBOOT_BOOTLOADER } },
+#else
+ { ACTION_INVOKE, { .action = Device::REBOOT_BOOTLOADER } },
+#endif
+#ifndef RELEASE_BUILD
+ { ACTION_INVOKE, { .action = Device::MOUNT_SYSTEM } },
+ { ACTION_INVOKE, { .action = Device::WIPE_SYSTEM } },
+#endif
+ { ACTION_INVOKE, { .action = Device::VIEW_RECOVERY_LOGS } },
+ { ACTION_INVOKE, { .action = Device::RUN_GRAPHICS_TEST } },
+ { ACTION_INVOKE, { .action = Device::SHUTDOWN } },
+ { ACTION_NONE, { .action = Device::NO_ACTION } }
+};
+static const menu ADVANCED_MENU = {
+ ADVANCED_MENU_NAMES,
+ ADVANCED_MENU_ENTRIES
};
-static const Device::BuiltinAction MENU_ACTIONS[] = {
- Device::REBOOT,
- Device::REBOOT_BOOTLOADER,
- Device::APPLY_ADB_SIDELOAD,
- Device::APPLY_SDCARD,
- Device::WIPE_DATA,
-#ifndef AB_OTA_UPDATER
- Device::WIPE_CACHE,
-#endif // !AB_OTA_UPDATER
- Device::MOUNT_SYSTEM,
- Device::VIEW_RECOVERY_LOGS,
- Device::RUN_GRAPHICS_TEST,
- Device::SHUTDOWN,
+static const char* MAIN_MENU_NAMES[] = {
+ "Reboot system now",
+ "Apply update",
+ "Factory reset",
+ "Advanced",
+ nullptr
+};
+static const menu_entry MAIN_MENU_ENTRIES[] = {
+ { ACTION_INVOKE, { .action = Device::REBOOT } },
+ { ACTION_INVOKE, { .action = Device::APPLY_UPDATE } },
+ { ACTION_SUBMENU, { .submenu = &WIPE_MENU } },
+ { ACTION_SUBMENU, { .submenu = &ADVANCED_MENU } },
+ { ACTION_NONE, { .action = Device::NO_ACTION } }
+};
+static const menu MAIN_MENU = {
+ MAIN_MENU_NAMES,
+ MAIN_MENU_ENTRIES
};
-static_assert(sizeof(MENU_ITEMS) / sizeof(MENU_ITEMS[0]) ==
- sizeof(MENU_ACTIONS) / sizeof(MENU_ACTIONS[0]) + 1,
- "MENU_ITEMS and MENU_ACTIONS should have the same length, "
- "except for the extra NULL entry in MENU_ITEMS.");
+Device::Device(RecoveryUI* ui) :
+ ui_(ui) {
+ menu_stack.push(&MAIN_MENU);
+}
const char* const* Device::GetMenuItems() {
- return MENU_ITEMS;
+ const menu* m = menu_stack.top();
+ return m->names;
}
Device::BuiltinAction Device::InvokeMenuItem(int menu_position) {
- return menu_position < 0 ? NO_ACTION : MENU_ACTIONS[menu_position];
+ if (menu_position < 0) {
+ if (menu_position == Device::kGoBack) {
+ if (menu_stack.size() > 1) {
+ menu_stack.pop();
+ }
+ }
+ return NO_ACTION;
+ }
+ const menu* m = menu_stack.top();
+ const menu_entry* entry = m->entries + menu_position;
+ if (entry->action_type == ACTION_SUBMENU) {
+ menu_stack.push(entry->action.submenu);
+ return NO_ACTION;
+ }
+ return entry->action.action;
+}
+
+void Device::GoHome() {
+ while (menu_stack.size() > 1) {
+ menu_stack.pop();
+ }
}
int Device::HandleMenuKey(int key, int visible) {
@@ -65,19 +158,37 @@ int Device::HandleMenuKey(int key, int visible) {
return kNoAction;
}
+ if (key & KEY_FLAG_ABS) {
+ return key;
+ }
+
switch (key) {
+ case KEY_RIGHTSHIFT:
case KEY_DOWN:
case KEY_VOLUMEDOWN:
+ case KEY_MENU:
return kHighlightDown;
+ case KEY_LEFTSHIFT:
case KEY_UP:
case KEY_VOLUMEUP:
+ case KEY_SEARCH:
return kHighlightUp;
case KEY_ENTER:
case KEY_POWER:
+ case BTN_MOUSE:
+ case KEY_SEND:
return kInvokeItem;
+ case KEY_BACKSPACE:
+ case KEY_BACK:
+ return kGoBack;
+
+ case KEY_HOME:
+ case KEY_HOMEPAGE:
+ return kGoHome;
+
default:
// If you have all of the above buttons, any other buttons
// are ignored. Otherwise, any button cycles the highlight.
diff --git a/device.h b/device.h
index 5017782e..b6f1a2e8 100644
--- a/device.h
+++ b/device.h
@@ -19,9 +19,15 @@
#include "ui.h"
-class Device {
+#include <stack>
+
+#define KEY_FLAG_ABS 0x8000
+
+struct menu;
+
+class Device : public VoldWatcher {
public:
- Device(RecoveryUI* ui) : ui_(ui) { }
+ explicit Device(RecoveryUI* ui);
virtual ~Device() { }
// Called to obtain the UI object that should be used to display
@@ -57,18 +63,19 @@ class Device {
virtual int HandleMenuKey(int key, int visible);
enum BuiltinAction {
- NO_ACTION = 0,
- REBOOT = 1,
- APPLY_SDCARD = 2,
- // APPLY_CACHE was 3.
- APPLY_ADB_SIDELOAD = 4,
- WIPE_DATA = 5,
- WIPE_CACHE = 6,
- REBOOT_BOOTLOADER = 7,
- SHUTDOWN = 8,
- VIEW_RECOVERY_LOGS = 9,
- MOUNT_SYSTEM = 10,
- RUN_GRAPHICS_TEST = 11,
+ NO_ACTION,
+ REBOOT,
+ APPLY_UPDATE,
+ WIPE_DATA,
+ WIPE_FULL,
+ WIPE_CACHE,
+ REBOOT_RECOVERY,
+ REBOOT_BOOTLOADER,
+ SHUTDOWN,
+ VIEW_RECOVERY_LOGS,
+ MOUNT_SYSTEM,
+ RUN_GRAPHICS_TEST,
+ WIPE_SYSTEM,
};
// Return the list of menu items (an array of strings,
@@ -87,10 +94,15 @@ class Device {
// actually perform it here and return NO_ACTION.
virtual BuiltinAction InvokeMenuItem(int menu_position);
+ virtual void GoHome();
+
static const int kNoAction = -1;
static const int kHighlightUp = -2;
static const int kHighlightDown = -3;
static const int kInvokeItem = -4;
+ static const int kGoBack = -5;
+ static const int kGoHome = -6;
+ static const int kRefresh = -7;
// 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,
@@ -103,8 +115,18 @@ class Device {
virtual bool PreWipeData() { return true; }
virtual bool PostWipeData() { return true; }
+ virtual bool PreWipeMedia() { return true; }
+ virtual bool PostWipeMedia() { return true; }
+
+ // Called before reboot
+ virtual char const* GetRebootReason() { return ""; }
+
+ virtual void onVolumeChanged() { ui_->onVolumeChanged(); }
+
private:
RecoveryUI* ui_;
+
+ std::stack<const menu*> menu_stack;
};
// The device-specific library must define this function (or the
diff --git a/etc/init.rc b/etc/init.rc
index 5915b8d8..a58fa70c 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -19,15 +19,38 @@ on init
mkdir /data
mkdir /cache
mkdir /sideload
+ mkdir /firmware
mount tmpfs tmpfs /tmp
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
on fs
+ mount pstore pstore /sys/fs/pstore
+
mkdir /dev/usb-ffs 0770 shell shell
mkdir /dev/usb-ffs/adb 0770 shell shell
mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
@@ -52,6 +75,9 @@ on boot
on load_system_props_action
load_system_props
+on load_persist_props_action
+ load_persist_props
+
on firmware_mounts_complete
rm /dev/.booting
@@ -67,6 +93,9 @@ on late-init
# issued fs triggers have completed.
trigger load_system_props_action
+ # Vendor init lives here
+ trigger load_persist_props_action
+
# Remove a file to wake up anything waiting for firmware
trigger firmware_mounts_complete
@@ -87,18 +116,40 @@ service healthd /sbin/healthd -r
service recovery /sbin/recovery
seclabel u:r:recovery:s0
+service setup_adbd /sbin/setup_adbd
+ oneshot
+ seclabel u:r:recovery:s0
+ disabled
+
service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
disabled
socket adbd stream 660 system system
seclabel u:r:adbd:s0
-# Always start adbd on userdebug and eng builds
+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
+ setenv BLKID_FILE /tmp/vold_blkid.tab
+ seclabel u:r:vold:s0
+
+# setup_adbd will start adb once it has checked the keys
on property:ro.debuggable=1
- write /sys/class/android_usb/android0/enable 1
- start adbd
+ start setup_adbd
-# Restart adbd so it can run as root
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/etc/mkshrc b/etc/mkshrc
new file mode 100644
index 00000000..b2923c6f
--- /dev/null
+++ b/etc/mkshrc
@@ -0,0 +1,70 @@
+# Copyright (c) 2010, 2012, 2013, 2014
+# Thorsten Glaser <tg@mirbsd.org>
+# This file is provided under the same terms as mksh.
+#-
+# Minimal /system/etc/mkshrc for Android
+#
+# Support: https://launchpad.net/mksh
+
+: ${HOME:=/}
+: ${HOSTNAME:=$(getprop ro.product.device)}
+: ${HOSTNAME:=android}
+: ${MKSH:=/sbin/sh}
+: ${SHELL:=$MKSH}
+: ${TERM:=linux}
+: ${TMPDIR:=/tmp}
+: ${USER:=$(id -un)}
+export HOME HOSTNAME MKSH SHELL TERM TMPDIR USER
+
+if (( USER_ID )); then PS1='$'; else PS1='#'; fi
+PS4='[$EPOCHREALTIME] '; PS1='${|
+ local e=$?
+
+ (( e )) && REPLY+="$e|"
+
+ return $e
+}$USER@$HOSTNAME:${PWD:-?} '"$PS1 "
+
+function hd {
+ local -Uui16 -Z11 pos=0
+ local -Uui16 -Z5 hv=2147483647
+ local dasc line i
+
+ cat "$@" | { set +U; if read -arN -1 line; then
+ typeset -i1 'line[*]'
+ i=0
+ while (( i < ${#line[*]} )); do
+ hv=${line[i++]}
+ if (( (pos & 15) == 0 )); then
+ (( pos )) && print -r -- "$dasc|"
+ print -n "${pos#16#} "
+ dasc=' |'
+ fi
+ print -n "${hv#16#} "
+ if (( (hv < 32) || (hv > 126) )); then
+ dasc+=.
+ else
+ dasc+=${line[i-1]#1#}
+ fi
+ (( (pos++ & 15) == 7 )) && print -n -- '- '
+ done
+ while (( pos & 15 )); do
+ print -n ' '
+ (( (pos++ & 15) == 7 )) && print -n -- '- '
+ done
+ (( hv == 2147483647 )) || print -r -- "$dasc|"
+ fi; }
+}
+
+function setenv {
+ eval export "\"$1\""'="$2"'
+}
+
+for p in ~/bin; do
+ [[ -d $p/. ]] || continue
+ [[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH
+done
+
+unset p
+
+: place customisations above this line
diff --git a/fstools/Android.mk b/fstools/Android.mk
new file mode 100644
index 00000000..680e5725
--- /dev/null
+++ b/fstools/Android.mk
@@ -0,0 +1,70 @@
+LOCAL_PATH := $(call my-dir)
+
+# This is a multi-call static binary which contains the
+# GPL filesystem tools.
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fstools
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := fstools.cpp
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+ libfuse_static
+
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+ libntfs-3g_static \
+ libntfs3g_fsck_static \
+ libntfs3g_mkfs_main \
+ libntfs3g_mount_static
+
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+ libext2fs \
+ libe2fsck_static \
+ libmke2fs_static \
+ libtune2fs
+
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+ libf2fs_static \
+ libf2fs_fsck_static \
+ libf2fs_mkfs_static
+
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+ libsgdisk_static
+
+LOCAL_STATIC_LIBRARIES := \
+ libext2_blkid \
+ libext2_uuid \
+ libext2_profile \
+ libext2_quota \
+ libext2_com_err \
+ libext2_e2p \
+ libc++_static \
+ libc \
+ libm \
+ libselinux
+
+FSTOOLS_LINKS := \
+ e2fsck mke2fs tune2fs fsck.ext4 mkfs.ext4 \
+ fsck.ntfs mkfs.ntfs mount.ntfs \
+ mkfs.f2fs fsck.f2fs
+
+ifeq ($(TARGET_USES_EXFAT),true)
+LOCAL_CFLAGS += -DHAVE_EXFAT
+LOCAL_WHOLE_STATIC_LIBRARIES += \
+ libexfat_static \
+ libexfat_fsck_static \
+ libexfat_mkfs_static \
+ libexfat_mount_static
+FSTOOLS_LINKS += \
+ fsck.exfat mkfs.exfat mount.exfat
+endif
+
+FSTOOLS_LINKS += \
+ sgdisk
+
+LOCAL_POST_INSTALL_CMD := \
+ $(hide) $(foreach t,$(FSTOOLS_LINKS),ln -sf fstools $(TARGET_RECOVERY_ROOT_OUT)/sbin/$(t);)
+include $(BUILD_EXECUTABLE)
+
diff --git a/fstools/fstools.cpp b/fstools/fstools.cpp
new file mode 100644
index 00000000..b1cc7adf
--- /dev/null
+++ b/fstools/fstools.cpp
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+
+extern "C" {
+#include "fstools.h"
+}
+
+
+int
+main(int argc, char **argv) {
+
+ // Handle alternative invocations
+ char* command = argv[0];
+ char* stripped = strrchr(argv[0], '/');
+ if (stripped)
+ command = stripped + 1;
+
+ if (strcmp(command, "fstools") != 0) {
+ struct fstools_cmd cmd = get_command(command);
+ if (cmd.name)
+ return cmd.main_func(argc, argv);
+ }
+ return -1;
+}
diff --git a/fstools/fstools.h b/fstools/fstools.h
new file mode 100644
index 00000000..d99b3825
--- /dev/null
+++ b/fstools/fstools.h
@@ -0,0 +1,78 @@
+/*
+ * 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 _FSTOOLS_CMDS_H
+#define _FSTOOLS_CMDS_H
+
+#include <stdio.h>
+#include <string.h>
+
+int e2fsck_main(int argc, char **argv);
+int mke2fs_main(int argc, char **argv);
+int tune2fs_main(int argc, char **argv);
+
+#ifdef HAVE_EXFAT
+int fsck_exfat_main(int argc, char **argv);
+int mkfs_exfat_main(int argc, char **argv);
+int mount_exfat_main(int argc, char **argv);
+#endif
+
+int fsck_ntfs3g_main(int argc, char **argv);
+int mkfs_ntfs3g_main(int argc, char **argv);
+int mount_ntfs3g_main(int argc, char **argv);
+
+int mkfs_f2fs_main(int argc, char **argv);
+int fsck_f2fs_main(int argc, char **argv);
+int fibmap_main(int argc, char **argv);
+
+int sgdisk_main(int argc, char **argv);
+
+struct fstools_cmd {
+ const char *name;
+ int (*main_func)(int argc, char **argv);
+};
+
+static const struct fstools_cmd fstools_cmds[] = {
+ { "e2fsck", e2fsck_main },
+ { "mke2fs", mke2fs_main },
+ { "tune2fs", tune2fs_main },
+ { "fsck.ext4", e2fsck_main },
+ { "mkfs.ext4", mke2fs_main },
+#ifdef HAVE_EXFAT
+ { "fsck.exfat", fsck_exfat_main },
+ { "mkfs.exfat", mkfs_exfat_main },
+ { "mount.exfat", mount_exfat_main },
+#endif
+ { "fsck.ntfs", fsck_ntfs3g_main },
+ { "mkfs.ntfs", mkfs_ntfs3g_main },
+ { "mount.ntfs", mount_ntfs3g_main },
+ { "mkfs.f2fs", mkfs_f2fs_main },
+ { "fsck.f2fs", fsck_f2fs_main },
+ { "sgdisk", sgdisk_main },
+ { NULL, NULL },
+};
+
+struct fstools_cmd get_command(char* command) {
+ int i;
+
+ for (i = 0; fstools_cmds[i].name; i++) {
+ if (strcmp(command, fstools_cmds[i].name) == 0)
+ break;
+ }
+
+ return fstools_cmds[i];
+}
+#endif
diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp
index df963127..27e442c4 100644
--- a/fuse_sdcard_provider.cpp
+++ b/fuse_sdcard_provider.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
@@ -79,10 +80,5 @@ bool start_sdcard_fuse(const char* path) {
vtab.read_block = read_block_file;
vtab.close = close_file;
- // 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 run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size) == 0;
}
diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp
index 1725e882..31b0b968 100644
--- a/fuse_sideload.cpp
+++ b/fuse_sideload.cpp
@@ -66,10 +66,10 @@
#include "fuse_sideload.h"
#define PACKAGE_FILE_ID (FUSE_ROOT_ID+1)
-#define EXIT_FLAG_ID (FUSE_ROOT_ID+2)
#define NO_STATUS 1
-#define NO_STATUS_EXIT 2
+
+#define INSTALL_REQUIRED_MEMORY (100*1024*1024)
struct fuse_data {
int ffd; // file descriptor for the fuse socket
@@ -85,7 +85,7 @@ struct fuse_data {
uid_t uid;
gid_t gid;
- uint32_t curr_block; // cache the block most recently read from the host
+ uint32_t curr_block; // cache the block most recently used
uint8_t* block_data;
uint8_t* extra_block; // another block of storage for reads that
@@ -93,8 +93,80 @@ struct fuse_data {
uint8_t* hashes; // SHA-256 hash of each block (all zeros
// if block hasn't been read yet)
+
+ // Block cache
+ uint32_t block_cache_max_size; // Max allowed block cache size
+ uint32_t block_cache_size; // Current block cache size
+ uint8_t** block_cache; // Block cache data
};
+static uint64_t free_memory() {
+ uint64_t mem = 0;
+ FILE* fp = fopen("/proc/meminfo", "r");
+ if (fp) {
+ char buf[256];
+ char* linebuf = buf;
+ size_t buflen = sizeof(buf);
+ while (getline(&linebuf, &buflen, fp) > 0) {
+ char* key = buf;
+ char* val = strchr(buf, ':');
+ *val = '\0';
+ ++val;
+ if (strcmp(key, "MemFree") == 0) {
+ mem += strtoul(val, NULL, 0) * 1024;
+ }
+ if (strcmp(key, "Buffers") == 0) {
+ mem += strtoul(val, NULL, 0) * 1024;
+ }
+ if (strcmp(key, "Cached") == 0) {
+ mem += strtoul(val, NULL, 0) * 1024;
+ }
+ }
+ fclose(fp);
+ }
+ return mem;
+}
+
+static int block_cache_fetch(struct fuse_data* fd, uint32_t block)
+{
+ if (fd->block_cache == NULL) {
+ return -1;
+ }
+ if (fd->block_cache[block] == NULL) {
+ return -1;
+ }
+ memcpy(fd->block_data, fd->block_cache[block], fd->block_size);
+ return 0;
+}
+
+static void block_cache_enter(struct fuse_data* fd, uint32_t block)
+{
+ if (!fd->block_cache)
+ return;
+ if (fd->block_cache_size == fd->block_cache_max_size) {
+ // Evict a block from the cache. Since the file is typically read
+ // sequentially, start looking from the block behind the current
+ // block and proceed backward.
+ int n;
+ for (n = fd->curr_block - 1; n != (int)fd->curr_block; --n) {
+ if (n < 0) {
+ n = fd->file_blocks - 1;
+ }
+ if (fd->block_cache[n]) {
+ free(fd->block_cache[n]);
+ fd->block_cache[n] = NULL;
+ fd->block_cache_size--;
+ break;
+ }
+ }
+ }
+
+ fd->block_cache[block] = (uint8_t*)malloc(fd->block_size);
+ memcpy(fd->block_cache[block], fd->block_data, fd->block_size);
+
+ fd->block_cache_size++;
+}
+
static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len)
{
struct fuse_out_header hdr;
@@ -180,14 +252,12 @@ static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct f
fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555);
} else if (hdr->nodeid == PACKAGE_FILE_ID) {
fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
- } else if (hdr->nodeid == EXIT_FLAG_ID) {
- fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
} else {
return -ENOENT;
}
fuse_reply(fd, hdr->unique, &out, sizeof(out));
- return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
+ return NO_STATUS;
}
static int handle_lookup(void* data, struct fuse_data* fd,
@@ -202,21 +272,15 @@ static int handle_lookup(void* data, struct fuse_data* fd,
out.nodeid = PACKAGE_FILE_ID;
out.generation = PACKAGE_FILE_ID;
fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444);
- } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, reinterpret_cast<const char*>(data),
- sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) {
- out.nodeid = EXIT_FLAG_ID;
- out.generation = EXIT_FLAG_ID;
- fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0);
} else {
return -ENOENT;
}
fuse_reply(fd, hdr->unique, &out, sizeof(out));
- return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS;
+ return NO_STATUS;
}
static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) {
- if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM;
if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
struct fuse_open_out out;
@@ -247,6 +311,11 @@ static int fetch_block(struct fuse_data* fd, uint32_t block) {
return 0;
}
+ if (block_cache_fetch(fd, block) == 0) {
+ fd->curr_block = block;
+ return 0;
+ }
+
size_t fetch_size = fd->block_size;
if (block * fd->block_size + fetch_size > fd->file_size) {
// If we're reading the last (partial) block of the file,
@@ -286,6 +355,7 @@ static int fetch_block(struct fuse_data* fd, uint32_t block) {
}
memcpy(blockhash, hash, SHA256_DIGEST_LENGTH);
+ block_cache_enter(fd, block);
return 0;
}
@@ -362,6 +432,12 @@ static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_he
return NO_STATUS;
}
+static volatile int terminated = 0;
+static void sig_term(int sig)
+{
+ terminated = 1;
+}
+
int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
uint64_t file_size, uint32_t block_size)
{
@@ -388,6 +464,9 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
fd.block_size = block_size;
fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1);
+ uint64_t mem = free_memory();
+ uint64_t avail = mem - (INSTALL_REQUIRED_MEMORY + fd.file_blocks * sizeof(uint8_t*));
+
if (fd.file_blocks > (1<<18)) {
fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks);
result = -1;
@@ -419,6 +498,24 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
goto done;
}
+ fd.block_cache_max_size = 0;
+ fd.block_cache_size = 0;
+ fd.block_cache = NULL;
+ if (mem > avail) {
+ uint32_t max_size = avail / fd.block_size;
+ if (max_size > fd.file_blocks) {
+ max_size = fd.file_blocks;
+ }
+ // The cache must be at least 1% of the file size or two blocks,
+ // whichever is larger.
+ if (max_size >= fd.file_blocks/100 && max_size >= 2) {
+ fd.block_cache_max_size = max_size;
+ fd.block_cache = (uint8_t**)calloc(fd.file_blocks, sizeof(uint8_t*));
+ }
+ }
+
+ signal(SIGTERM, sig_term);
+
fd.ffd = open("/dev/fuse", O_RDWR);
if (fd.ffd < 0) {
perror("open /dev/fuse");
@@ -439,7 +536,17 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
goto done;
}
uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8];
- for (;;) {
+ while (!terminated) {
+ fd_set fds;
+ struct timeval tv;
+ FD_ZERO(&fds);
+ FD_SET(fd.ffd, &fds);
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ int rc = select(fd.ffd+1, &fds, NULL, NULL, &tv);
+ if (rc <= 0) {
+ continue;
+ }
ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
if (len == -1) {
perror("read request");
@@ -494,11 +601,6 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
break;
}
- if (result == NO_STATUS_EXIT) {
- result = 0;
- break;
- }
-
if (result != NO_STATUS) {
struct fuse_out_header outhdr;
outhdr.len = sizeof(outhdr);
@@ -517,6 +619,13 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
}
if (fd.ffd) close(fd.ffd);
+ if (fd.block_cache) {
+ uint32_t n;
+ for (n = 0; n < fd.file_blocks; ++n) {
+ free(fd.block_cache[n]);
+ }
+ free(fd.block_cache);
+ }
free(fd.hashes);
free(fd.block_data);
free(fd.extra_block);
diff --git a/install.cpp b/install.cpp
index d30890a9..a24ef6f1 100644
--- a/install.cpp
+++ b/install.cpp
@@ -23,6 +23,8 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <setjmp.h>
+#include <sys/mount.h>
#include <chrono>
#include <limits>
@@ -48,6 +50,8 @@
#include "ui.h"
#include "verifier.h"
+#include "cutils/properties.h"
+
extern RecoveryUI* ui;
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
@@ -297,6 +301,12 @@ update_binary_command(const char* path, ZipArchive* zip, int retry_count,
}
#endif // !AB_OTA_UPDATER
+static jmp_buf jb;
+static void sig_bus(int sig)
+{
+ longjmp(jb, 1);
+}
+
// If the package contains an update binary, extract it and run it.
static int
try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache,
@@ -438,22 +448,112 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache,
return INSTALL_RETRY;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
+ if (WEXITSTATUS(status) != 7) {
+ LOGE("Installation error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
+ } else {
+ LOGE("Failed to install %s\n", path);
+ LOGE("Please take note of all the above lines for reports\n");
+ }
return INSTALL_ERROR;
}
return INSTALL_SUCCESS;
}
+#ifdef USE_MDTP
+static int
+mdtp_update()
+{
+ const char** args = (const char**)malloc(sizeof(char*) * 2);
+
+ if (args == NULL) {
+ LOGE("Failed to allocate memory for MDTP FOTA app arguments\n");
+ return 0;
+ }
+
+ args[0] = "/sbin/mdtp_fota";
+ args[1] = NULL;
+ int status = 0;
+
+ ui->Print("Running MDTP integrity verification and update...\n");
+
+ /* Make sure system partition is mounted, so MDTP can process its content. */
+ status = mount("/dev/block/bootdevice/by-name/system", "/system", "ext4",
+ MS_NOATIME | MS_NODEV | MS_NODIRATIME |
+ MS_RDONLY, "");
+
+ if (status) {
+ LOGE("Failed to mount the system partition, error=%s.\n", strerror(errno));
+ free(args);
+ return 0;
+ }
+
+ status = mount("/dev/block/bootdevice/by-name/modem", "/firmware", "vfat",
+ MS_NOATIME | MS_NODEV | MS_NODIRATIME |
+ MS_RDONLY, "");
+
+ if (status) {
+ LOGE("Failed to mount the modem (firmware) partition, error=%s.\n", strerror(errno));
+ free(args);
+ return 0;
+ }
+
+ status = 0;
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ execv(args[0], (char* const*)args);
+ LOGE("Can't run %s (%s)\n", args[0], strerror(errno));
+ _exit(-1);
+ }
+ if (pid > 0) {
+ LOGE("Waiting for MDTP FOTA to complete...\n");
+ pid = waitpid(pid, &status, 0);
+ LOGE("MDTP FOTA completed, status: %d\n", status);
+ }
+
+ /* Leave the system partition unmounted before we finish. */
+ umount("/system");
+ umount("/firmware");
+
+ free(args);
+
+ return (status > 0) ? 1 : 0;
+}
+#endif /* USE_MDTP */
+
static int
really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
std::vector<std::string>& log_buffer, int retry_count)
{
+ int ret = 0;
+
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->Print("Finding update package...\n");
// 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.
@@ -464,6 +564,7 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
ensure_path_mounted(path+1);
} else {
ensure_path_mounted(path);
+ remount_no_selinux(path);
}
}
@@ -473,10 +574,13 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
return INSTALL_CORRUPT;
}
+ set_perf_mode(true);
+
// Verify package.
if (!verify_package(map.addr, map.length)) {
log_buffer.push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
sysReleaseMap(&map);
+ set_perf_mode(false);
return INSTALL_CORRUPT;
}
@@ -488,6 +592,7 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
log_buffer.push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
sysReleaseMap(&map);
+ set_perf_mode(false);
return INSTALL_CORRUPT;
}
@@ -497,13 +602,25 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
ui->Print("Retry attempt: %d\n", retry_count);
}
ui->SetEnableReboot(false);
- int result = try_update_binary(path, &zip, wipe_cache, log_buffer, retry_count);
+ ret = try_update_binary(path, &zip, wipe_cache, log_buffer, retry_count);
ui->SetEnableReboot(true);
ui->Print("\n");
sysReleaseMap(&map);
- return result;
+#ifdef USE_MDTP
+ /* If MDTP update failed, return an error such that recovery will not finish. */
+ if (ret == INSTALL_SUCCESS) {
+ if (!mdtp_update()) {
+ ui->Print("Unable to verify integrity of /system for MDTP, update aborted.\n");
+ return INSTALL_ERROR;
+ }
+ ui->Print("Successfully verified integrity of /system for MDTP.\n");
+ }
+#endif /* USE_MDTP */
+
+ set_perf_mode(false);
+ return ret;
}
int
@@ -513,9 +630,11 @@ install_package(const char* path, bool* wipe_cache, const char* install_file,
modified_flash = true;
auto start = std::chrono::system_clock::now();
- int result;
+ int result = 0;
std::vector<std::string> log_buffer;
- if (setup_install_mounts() != 0) {
+ if (needs_mount == true)
+ result = setup_install_mounts();
+ if (result != 0) {
LOGE("failed to set up expected mounts for install; aborting\n");
result = INSTALL_ERROR;
} else {
@@ -569,9 +688,20 @@ bool verify_package(const unsigned char* package_data, size_t package_size) {
// Verify package.
ui->Print("Verifying update package...\n");
auto t0 = std::chrono::system_clock::now();
- int err = verify_file(const_cast<unsigned char*>(package_data), package_size, loadedKeys);
- std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
- ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
+ int err;
+
+ // Because we mmap() the update file which is backed by FUSE, we get
+ // SIGBUS when the host aborts the transfer. We handle this by using
+ // setjmp/longjmp.
+ signal(SIGBUS, sig_bus);
+ if (setjmp(jb) == 0) {
+ err = verify_file(const_cast<unsigned char*>(package_data), package_size, loadedKeys);
+ std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
+ ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
+ } else {
+ err = VERIFY_FAILURE;
+ }
+ signal(SIGBUS, SIG_DFL);
if (err != VERIFY_SUCCESS) {
LOGE("Signature verification failed\n");
LOGE("error: %d\n", kZipVerificationFailure);
@@ -579,3 +709,8 @@ bool verify_package(const unsigned char* package_data, size_t package_size) {
}
return true;
}
+
+void
+set_perf_mode(bool enable) {
+ property_set("recovery.perf.mode", enable ? "1" : "0");
+}
diff --git a/install.h b/install.h
index 14de2255..30827b29 100644
--- a/install.h
+++ b/install.h
@@ -38,4 +38,6 @@ bool verify_package(const unsigned char* package_data, size_t package_size);
// Return true if succeed, otherwise return false.
bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data);
+void set_perf_mode(bool enable);
+
#endif // RECOVERY_INSTALL_H_
diff --git a/minui/Android.mk b/minui/Android.mk
index 3057f452..a8d0aa41 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -27,6 +27,9 @@ endif
ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888)
LOCAL_CFLAGS += -DRECOVERY_RGBX
endif
+ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBA_8888)
+ LOCAL_CFLAGS += -DRECOVERY_RGBX
+endif
ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),BGRA_8888)
LOCAL_CFLAGS += -DRECOVERY_BGRA
endif
@@ -37,6 +40,14 @@ else
LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0
endif
+ifneq ($(BOARD_RECOVERY_NEEDS_FBIOPAN_DISPLAY),)
+ LOCAL_CFLAGS += -DBOARD_RECOVERY_NEEDS_FBIOPAN_DISPLAY
+endif
+
+ifneq ($(BOARD_RECOVERY_NEEDS_REL_INPUT),)
+ LOCAL_CFLAGS += -DBOARD_RECOVERY_NEEDS_REL_INPUT
+endif
+
include $(BUILD_STATIC_LIBRARY)
# Used by OEMs for factory test images.
diff --git a/minui/events.cpp b/minui/events.cpp
index 3b2262a4..85afe471 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -78,8 +78,13 @@ int ev_init(ev_callback input_cb, void* data) {
continue;
}
- // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
- if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
+ // We assume that only EV_KEY, EV_SW, and EV_ABS event types are ever needed.
+ // EV_REL should be enabled explicitly in device tree.
+#ifdef BOARD_RECOVERY_NEEDS_REL_INPUT
+ if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits) && !test_bit(EV_ABS, ev_bits)) {
+#else
+ if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_SW, ev_bits) && !test_bit(EV_ABS, ev_bits)) {
+#endif
close(fd);
continue;
}
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index c0eea9e3..1f255311 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -41,6 +41,20 @@ struct GRFont {
int cheight;
};
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+typedef struct {
+ char name[80];
+ GRFont* font;
+} font_item;
+
+static font_item gr_fonts[] = {
+ { "menu", NULL },
+ { "log", NULL },
+};
+
static GRFont* gr_font = NULL;
static minui_backend* gr_backend = NULL;
@@ -103,6 +117,36 @@ static void text_blend(unsigned char* src_p, int src_row_bytes,
}
}
+static int rainbow_index = 0;
+static int rainbow_enabled = 0;
+static int rainbow_colors[] = { 255, 0, 0, // red
+ 255, 127, 0, // orange
+ 255, 255, 0, // yellow
+ 0, 255, 0, // green
+ 60, 80, 255, // blue
+ 143, 0, 255 }; // violet
+static int num_rb_colors =
+ (sizeof(rainbow_colors)/sizeof(rainbow_colors[0])) / 3;
+
+static void rainbow(int col) {
+ int rainbow_color = ((rainbow_index + col) % num_rb_colors) * 3;
+ gr_color(rainbow_colors[rainbow_color], rainbow_colors[rainbow_color+1],
+ rainbow_colors[rainbow_color+2], 255);
+}
+
+void set_rainbow_mode(int enabled) {
+ rainbow_enabled = enabled;
+}
+
+void move_rainbow(int x) {
+ rainbow_index += x;
+ if (rainbow_index < 0) {
+ rainbow_index = num_rb_colors - 1;
+ } else if (rainbow_index >= num_rb_colors) {
+ rainbow_index = 0;
+ }
+}
+
void gr_text(int x, int y, const char *s, bool bold)
{
GRFont* font = gr_font;
@@ -116,6 +160,8 @@ void gr_text(int x, int y, const char *s, bool bold)
unsigned char ch;
while ((ch = *s++)) {
+ if (rainbow_enabled) rainbow(x / font->cwidth);
+
if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break;
if (ch < ' ' || ch > '~') {
@@ -229,6 +275,17 @@ void gr_fill(int x1, int y1, int x2, int y2)
}
}
+void gr_set_font(const char* name)
+{
+ unsigned int idx;
+ for (idx = 0; idx < ARRAY_SIZE(gr_fonts); ++idx) {
+ if (strcmp(name, gr_fonts[idx].name) == 0) {
+ gr_font = gr_fonts[idx].font;
+ break;
+ }
+ }
+}
+
void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
if (source == NULL) return;
@@ -267,11 +324,14 @@ unsigned int gr_get_height(GRSurface* surface) {
return surface->height;
}
-static void gr_init_font(void)
+static void gr_init_one_font(int idx)
{
- gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
+ char name[80];
+ GRFont* gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
+ snprintf(name, sizeof(name), "font_%s", gr_fonts[idx].name);
+ gr_fonts[idx].font = gr_font;
- int res = res_create_alpha_surface("font", &(gr_font->texture));
+ int res = res_create_alpha_surface(name, &(gr_font->texture));
if (res == 0) {
// The font image should be a 96x2 array of character images. The
// columns are the printable ASCII characters 0x20 - 0x7f. The
@@ -303,6 +363,15 @@ static void gr_init_font(void)
}
}
+static void gr_init_font()
+{
+ unsigned int idx;
+ for (idx = 0; idx < ARRAY_SIZE(gr_fonts); ++idx) {
+ gr_init_one_font(idx);
+ }
+ gr_font = gr_fonts[0].font;
+}
+
#if 0
// Exercises many of the gr_*() functions; useful for testing.
static void gr_test() {
diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp
index 0788f755..b017ff21 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -73,9 +73,15 @@ static void set_displayed_framebuffer(unsigned n)
vi.yres_virtual = gr_framebuffer[0].height * 2;
vi.yoffset = n * gr_framebuffer[0].height;
vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
+
if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
perror("active fb swap failed");
}
+#ifdef BOARD_RECOVERY_NEEDS_FBIOPAN_DISPLAY
+ if (ioctl(fb_fd, FBIOPAN_DISPLAY, &vi) < 0) {
+ perror("pan failed");
+ }
+#endif
displayed_buffer = n;
}
diff --git a/minui/minui.h b/minui/minui.h
index fb0bbe10..2d5e8483 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -45,6 +45,7 @@ void gr_fb_blank(bool blank);
void gr_clear(); // clear entire surface to current color
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
void gr_fill(int x1, int y1, int x2, int y2);
+void gr_set_font(const char* name);
void gr_text(int x, int y, const char *s, bool bold);
void gr_texticon(int x, int y, GRSurface* icon);
int gr_measure(const char *s);
@@ -122,4 +123,7 @@ int res_create_localized_alpha_surface(const char* name, const char* locale,
// functions.
void res_free_surface(GRSurface* surface);
+void set_rainbow_mode(int enabled);
+void move_rainbow(int x);
+
#endif
diff --git a/minui/resources.cpp b/minui/resources.cpp
index 40d3c2c8..1aa00b01 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -268,7 +268,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps,
printf(" found fps = %d\n", *fps);
}
- if (frames <= 0 || fps <= 0) {
+ if (*frames <= 0 || *fps <= 0) {
printf("bad number of frames (%d) and/or FPS (%d)\n", *frames, *fps);
result = -10;
goto exit;
diff --git a/mtdutils/mounts.c b/mtdutils/mounts.c
index 6a9b03d3..41efa376 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 d721355b..c8318c0f 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/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index cd4f52cd..ed74d0f4 100644
--- a/mtdutils/mtdutils.c
+++ b/mtdutils/mtdutils.c
@@ -28,6 +28,9 @@
#include "mtdutils.h"
+static const char mtdprefix[] = "/dev/block/mtd/by-name/";
+#define MTD_BASENAME_OFFSET (sizeof(mtdprefix)-1+1)
+
struct MtdPartition {
int device_index;
unsigned int size;
@@ -144,7 +147,7 @@ mtd_scan_partitions()
p->device_index = mtdnum;
p->size = mtdsize;
p->erase_size = mtderasesize;
- p->name = strdup(mtdname);
+ asprintf(&p->name, "%s%s", mtdprefix, mtdname);
if (p->name == NULL) {
errno = ENOMEM;
goto bail;
@@ -183,6 +186,9 @@ mtd_find_partition_by_name(const char *name)
if (strcmp(p->name, name) == 0) {
return p;
}
+ if (strcmp(p->name+MTD_BASENAME_OFFSET, name) == 0) {
+ return p;
+ }
}
}
}
diff --git a/recovery.cpp b/recovery.cpp
index 0f0b978e..4bad568b 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -31,6 +31,8 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/mount.h>
+#include <fs_mgr.h>
#include <time.h>
#include <unistd.h>
@@ -67,14 +69,56 @@
#include "unique_fd.h"
#include "screen_ui.h"
+#include "voldclient.h"
+
+extern "C" {
+#include "recovery_cmds.h"
+}
+
+#define UFS_DEV_SDCARD_BLK_PATH "/dev/block/mmcblk0p1"
+
struct selabel_handle *sehandle;
+#ifdef HAVE_OEMLOCK
+
+/*
+ * liboemlock must supply the following C symbols:
+ *
+ * - int oemlock_get()
+ *
+ * Returns the current state of the OEM lock, if available.
+ * -1: Not available and/or error
+ * 0: Unlocked
+ * 1: Locked
+ *
+ * - int oemlock_set(int lock)
+ *
+ * Sets the state of the OEM lock. The "lock" parameter will be set
+ * to 0 for unlock and 1 for lock.
+ *
+ * Returns 0 on success, -1 on error
+ */
+extern "C" {
+int oemlock_get();
+int oemlock_set(int lock);
+}
+
+enum OemLockOp {
+ OEM_LOCK_NONE,
+ OEM_LOCK_UNLOCK
+};
+
+static OemLockOp oem_lock = OEM_LOCK_NONE;
+
+#endif
+
static const struct option OPTIONS[] = {
{ "send_intent", required_argument, NULL, 'i' },
{ "update_package", required_argument, NULL, 'u' },
{ "retry_count", required_argument, NULL, 'n' },
{ "wipe_data", no_argument, NULL, 'w' },
{ "wipe_cache", no_argument, NULL, 'c' },
+ { "wipe_media", no_argument, NULL, 'm' },
{ "show_text", no_argument, NULL, 't' },
{ "sideload", no_argument, NULL, 's' },
{ "sideload_auto_reboot", no_argument, NULL, 'a' },
@@ -105,7 +149,6 @@ static const char *CONVERT_FBE_DIR = "/tmp/convert_fbe";
static const char *CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe";
static const char *CACHE_ROOT = "/cache";
static const char *DATA_ROOT = "/data";
-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";
@@ -128,6 +171,10 @@ char* reason = NULL;
bool modified_flash = false;
static bool has_cache = false;
+extern "C" int toybox_driver(int argc, char **argv);
+
+#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
@@ -190,6 +237,48 @@ static bool has_cache = false;
static const int MAX_ARG_LENGTH = 4096;
static const int MAX_ARGS = 100;
+#ifdef USE_MDTP
+
+#define MAX_CMD_LINE_LEN (2048)
+
+static int is_mdtp_activated()
+{
+ char cmdline[MAX_CMD_LINE_LEN];
+ char *ptr;
+ int fd;
+ int mdtp_activated = 0;
+
+ fd = open("/proc/cmdline", O_RDONLY);
+ if (fd >= 0) {
+ int n = read(fd, cmdline, sizeof(cmdline) - 1);
+ if (n < 0)
+ n = 0;
+ /* get rid of trailing newline, it happens */
+ if (n > 0 && cmdline[n-1] == '\n') n--;
+ cmdline[n] = 0;
+ close(fd);
+ } else {
+ cmdline[0] = 0;
+ }
+
+ /* Look for the string "mdtp" in kernel cmdline, indicating that MDTP is activated.*/
+ ptr = cmdline;
+ while (ptr && *ptr) {
+ char *x = strchr(ptr, ' ');
+ if (x != 0)
+ *x++ = 0;
+ if (!strcmp(ptr,"mdtp")) {
+ mdtp_activated = 1;
+ break;
+ }
+
+ ptr = x;
+ }
+
+ return mdtp_activated;
+}
+#endif /* USE_MDTP */
+
// open a given path, mounting partitions as necessary
FILE* fopen_path(const char *path, const char *mode) {
if (ensure_path_mounted(path) != 0) {
@@ -335,6 +424,14 @@ get_args(int *argc, char ***argv) {
(*argv)[0] = strdup(arg);
for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
if ((arg = strtok(NULL, "\n")) == NULL) break;
+ // Arguments that may only be passed in BCB
+#ifdef HAVE_OEMLOCK
+ if (strcmp(arg, "--oemunlock") == 0) {
+ oem_lock = OEM_LOCK_UNLOCK;
+ --*argc;
+ continue;
+ }
+#endif
(*argv)[*argc] = strdup(arg);
}
LOGI("Got arguments from boot message\n");
@@ -575,16 +672,16 @@ typedef struct _saved_log_file {
struct _saved_log_file* next;
} saved_log_file;
-static bool erase_volume(const char* volume) {
+static bool erase_volume(const char* volume, bool force = false) {
bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
bool is_data = (strcmp(volume, DATA_ROOT) == 0);
+ saved_log_file* head = NULL;
+
ui->SetBackground(RecoveryUI::ERASING);
ui->SetProgressType(RecoveryUI::INDETERMINATE);
- saved_log_file* head = NULL;
-
- if (is_cache) {
+ if (!force && is_cache) {
// If we're reformatting /cache, we load any past logs
// (i.e. "/cache/recovery/last_*") and the current log
// ("/cache/recovery/log") into memory, so we can restore them after
@@ -631,7 +728,9 @@ static bool erase_volume(const char* volume) {
ui->Print("Formatting %s...\n", volume);
- ensure_path_unmounted(volume);
+ if (volume[0] == '/') {
+ ensure_path_unmounted(volume);
+ }
int result;
@@ -652,10 +751,10 @@ static bool erase_volume(const char* volume) {
remove(CONVERT_FBE_FILE);
rmdir(CONVERT_FBE_DIR);
} else {
- result = format_volume(volume);
+ result = format_volume(volume, force);
}
- if (is_cache) {
+ if (!force && is_cache) {
while (head) {
FILE* f = fopen_path(head->name, "wb");
if (f) {
@@ -678,21 +777,37 @@ static bool erase_volume(const char* volume) {
copy_logs();
}
+ ui->SetBackground(RecoveryUI::NONE);
+ ui->SetProgressType(RecoveryUI::EMPTY);
+
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
// accidentally trigger menu items.
ui->FlushKeys();
+ // Count items to detect valid values for absolute selection
+ int header_count = 0;
+ if (headers) {
+ while (headers[header_count] != NULL)
+ ++header_count;
+ }
+ int item_count = 0;
+ while (items[item_count] != NULL)
+ ++item_count;
+
ui->StartMenu(headers, items, initial_selection);
int selected = initial_selection;
int chosen_item = -1;
- while (chosen_item < 0) {
+ while (chosen_item < 0 &&
+ chosen_item != Device::kGoBack &&
+ chosen_item != Device::kGoHome &&
+ chosen_item != Device::kRefresh) {
int key = ui->WaitKey();
int visible = ui->IsTextVisible();
@@ -704,10 +819,29 @@ get_menu_selection(const char* const * headers, const char* const * items,
ui->EndMenu();
return 0; // XXX fixme
}
+ } else if (key == -2) { // we are returning from ui_cancel_wait_key(): no action
+ return Device::kNoAction;
+ }
+ else if (key == -6) {
+ return Device::kRefresh;
}
int action = device->HandleMenuKey(key, visible);
+ if (action >= 0) {
+ action &= ~KEY_FLAG_ABS;
+ if (action < header_count || action >= header_count + item_count) {
+ action = Device::kNoAction;
+ }
+ else {
+ // Absolute selection. Update selected item and give some
+ // feedback in the UI by selecting the item for a short time.
+ selected = ui->SelectMenu(action, true);
+ action = Device::kInvokeItem;
+ usleep(50*1000);
+ }
+ }
+
if (action < 0) {
switch (action) {
case Device::kHighlightUp:
@@ -721,6 +855,15 @@ get_menu_selection(const char* const * headers, const char* const * items,
break;
case Device::kNoAction:
break;
+ case Device::kGoBack:
+ chosen_item = Device::kGoBack;
+ break;
+ case Device::kGoHome:
+ chosen_item = Device::kGoHome;
+ break;
+ case Device::kRefresh:
+ chosen_item = Device::kRefresh;
+ break;
}
} else if (!menu_only) {
chosen_item = action;
@@ -728,6 +871,9 @@ get_menu_selection(const char* const * headers, const char* const * items,
}
ui->EndMenu();
+ if (chosen_item == Device::kGoHome) {
+ device->GoHome();
+ }
return chosen_item;
}
@@ -737,8 +883,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));
@@ -797,21 +941,26 @@ static char* browse_directory(const char* path, Device* device) {
z_size += d_size;
zips[z_size] = NULL;
- const char* headers[] = { "Choose a package to install:", path, NULL };
+ const char* headers[] = { path, NULL };
char* result;
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 == Device::kGoHome) {
+ // go up and stop browsing
+ result = strdup("");
+ break;
+ }
+ 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);
@@ -844,7 +993,7 @@ static bool yes_no(Device* device, const char* question1, const char* question2)
}
// Return true on success.
-static bool wipe_data(int should_confirm, Device* device) {
+static bool wipe_data(int should_confirm, Device* device, bool force = false) {
if (should_confirm && !yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!")) {
return false;
}
@@ -852,15 +1001,39 @@ static bool wipe_data(int should_confirm, Device* device) {
modified_flash = true;
ui->Print("\n-- Wiping data...\n");
- bool success =
+ bool success;
+retry:
+ success =
device->PreWipeData() &&
- erase_volume("/data") &&
+ erase_volume("/data", force) &&
(has_cache ? erase_volume("/cache") : true) &&
device->PostWipeData();
+ if (!success && !force) {
+ if (!should_confirm || yes_no(device, "Wipe failed, format instead?", " THIS CAN NOT BE UNDONE!")) {
+ force = true;
+ goto retry;
+ }
+ }
ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
return success;
}
+static bool wipe_media(int should_confirm, Device* device) {
+ if (should_confirm && !yes_no(device, "Wipe all user media?", " THIS CAN NOT BE UNDONE!")) {
+ return false;
+ }
+
+ modified_flash = true;
+
+ ui->Print("\n-- Wiping media...\n");
+ bool success =
+ device->PreWipeMedia() &&
+ erase_volume("media") &&
+ device->PostWipeMedia();
+ ui->Print("Media wipe %s.\n", success ? "complete" : "failed");
+ return success;
+}
+
// Return true on success.
static bool wipe_cache(bool should_confirm, Device* device) {
if (!has_cache) {
@@ -880,6 +1053,20 @@ static bool wipe_cache(bool should_confirm, Device* device) {
return success;
}
+// Return true on success.
+static bool wipe_system(Device* device) {
+ if (!yes_no(device, "Wipe system?", " THIS CAN NOT BE UNDONE!")) {
+ return false;
+ }
+
+ modified_flash = true;
+
+ ui->Print("\n-- Wiping system...\n");
+ bool success = erase_volume("/system");
+ ui->Print("System wipe %s.\n", success ? "complete" : "failed");
+ return success;
+}
+
// Secure-wipe a given partition. It uses BLKSECDISCARD, if supported.
// Otherwise, it goes with BLKDISCARD (if device supports BLKDISCARDZEROES) or
// BLKZEROOUT.
@@ -1067,7 +1254,9 @@ static void choose_recovery_file(Device* device) {
while (true) {
int chosen_item = get_menu_selection(headers, entries, 1, 0, device);
- if (strcmp(entries[chosen_item], "Back") == 0) break;
+ if (chosen_item == Device::kGoHome) break;
+ if (chosen_item == Device::kGoBack) break;
+ if (chosen_item >= 0 && strcmp(entries[chosen_item], "Back") == 0) break;
ui->ShowFile(entries[chosen_item]);
}
@@ -1108,25 +1297,78 @@ static void run_graphics_test(Device* device) {
ui->ShowText(true);
}
+static int is_ufs_dev()
+{
+ char bootdevice[PROPERTY_VALUE_MAX] = {0};
+ property_get("ro.boot.bootdevice", bootdevice, "N/A");
+ ui->Print("ro.boot.bootdevice is: %s\n",bootdevice);
+ if (strlen(bootdevice) < strlen(".ufshc") + 1)
+ return 0;
+ return (!strncmp(&bootdevice[strlen(bootdevice) - strlen(".ufshc")],
+ ".ufshc",
+ sizeof(".ufshc")));
+}
+
+static int do_sdcard_mount_for_ufs()
+{
+ int rc = 0;
+ ui->Print("Update via sdcard on UFS dev.Mounting card\n");
+ Volume *v = volume_for_path("/sdcard");
+ if (v == NULL) {
+ ui->Print("Unknown volume for /sdcard.Check fstab\n");
+ goto error;
+ }
+ if (strncmp(v->fs_type, "vfat", sizeof("vfat"))) {
+ ui->Print("Unsupported format on the sdcard: %s\n",
+ v->fs_type);
+ goto error;
+ }
+ rc = mount(UFS_DEV_SDCARD_BLK_PATH,
+ v->mount_point,
+ v->fs_type,
+ v->flags,
+ v->fs_options);
+ if (rc) {
+ ui->Print("Failed to mount sdcard : %s\n",
+ strerror(errno));
+ goto error;
+ }
+ ui->Print("Done mounting sdcard\n");
+ return 0;
+error:
+ return -1;
+}
+
// How long (in seconds) we wait for the fuse-provided package file to
// appear, before timing out.
#define SDCARD_INSTALL_TIMEOUT 10
-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);
- return INSTALL_ERROR;
+ if (is_ufs_dev()) {
+ if (do_sdcard_mount_for_ufs() != 0) {
+ return INSTALL_ERROR;
+ }
+ } else {
+ if (!vdc->volumeMount(id)) {
+ return INSTALL_ERROR;
+ }
}
- char* path = browse_directory(SDCARD_ROOT, device);
- if (path == NULL) {
+ VolumeInfo vi = vdc->getVolume(id);
+
+ char* path = browse_directory(vi.mInternalPath.c_str(), device);
+ if (path == NULL || *path == '\0') {
ui->Print("\n-- No package file selected.\n");
- ensure_path_unmounted(SDCARD_ROOT);
- return INSTALL_ERROR;
+ vdc->volumeUnmount(vi.mId);
+ free(path);
+ return INSTALL_NONE;
}
+ ui->ClearText();
+ ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
+
ui->Print("\n-- Install %s ...\n", path);
set_sdcard_update_bootloader_message();
@@ -1145,7 +1387,10 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {
int result = INSTALL_ERROR;
int status;
bool waited = false;
- for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
+ time_t start_time = time(NULL);
+ time_t now = start_time;
+
+ while (now - start_time < SDCARD_INSTALL_TIMEOUT) {
if (waitpid(child, &status, WNOHANG) == -1) {
result = INSTALL_ERROR;
waited = true;
@@ -1154,8 +1399,9 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {
struct stat sb;
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) {
- if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
+ if (errno == ENOENT) {
sleep(1);
+ now = time(NULL);
continue;
} else {
LOGE("Timed out waiting for the fuse-provided package.\n");
@@ -1165,6 +1411,8 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {
}
}
+ vdc->volumeUnmount(vi.mId, true);
+
result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache,
TEMPORARY_INSTALL_FILE, false, 0/*retry_count*/);
break;
@@ -1183,10 +1431,71 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {
LOGE("Error exit from the fuse process: %d\n", WEXITSTATUS(status));
}
- ensure_path_unmounted(SDCARD_ROOT);
return result;
}
+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;
+
+ 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::kGoHome) {
+ return INSTALL_NONE;
+ }
+ if (chosen == Device::kGoBack) {
+ return INSTALL_NONE;
+ }
+ if (chosen == item_sideload) {
+ static const char* headers[] = { "ADB Sideload",
+ NULL
+ };
+ static const char* list[] = { "Cancel sideload", NULL };
+
+ start_sideload(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
+ int item = get_menu_selection(headers, list, 0, 0, device);
+ if (item != Device::kNoAction) {
+ stop_sideload();
+ }
+ status = wait_sideload();
+ }
+ else {
+ std::string id = volumes[chosen - 1].mId;
+ status = apply_from_storage(device, id, &wipe_cache);
+ }
+
+ if (status != INSTALL_SUCCESS && status != INSTALL_NONE) {
+ ui->Print("Install failed");
+ }
+
+ return status;
+}
+
// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION
// means to take the default, which is to reboot or shutdown depending
// on if the --shutdown_after flag was passed to recovery.
@@ -1197,7 +1506,7 @@ prompt_and_wait(Device* device, int status) {
switch (status) {
case INSTALL_SUCCESS:
case INSTALL_NONE:
- ui->SetBackground(RecoveryUI::NO_COMMAND);
+ ui->SetBackground(RecoveryUI::NONE);
break;
case INSTALL_ERROR:
@@ -1215,81 +1524,101 @@ 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_RECOVERY:
+ 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_FULL:
+ wipe_data(ui->IsTextVisible(), device, true);
+ 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::WIPE_CACHE:
+ wipe_cache(ui->IsTextVisible(), device);
+ if (!ui->IsTextVisible()) return Device::NO_ACTION;
+ break;
- if (status == INSTALL_SUCCESS && should_wipe_cache) {
- if (!wipe_cache(false, device)) {
- status = INSTALL_ERROR;
+ 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) {
- 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 >= 0 && status != INSTALL_NONE) {
+ 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;
- case Device::VIEW_RECOVERY_LOGS:
- choose_recovery_file(device);
- break;
+ case Device::VIEW_RECOVERY_LOGS:
+ choose_recovery_file(device);
+ break;
- case Device::RUN_GRAPHICS_TEST:
- run_graphics_test(device);
- break;
+ case Device::RUN_GRAPHICS_TEST:
+ run_graphics_test(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");
+ case Device::MOUNT_SYSTEM:
+#ifdef USE_MDTP
+ if (is_mdtp_activated()) {
+ ui->Print("Mounting /system forbidden by MDTP.\n");
}
- } else {
- if (ensure_path_mounted("/system") != -1) {
- ui->Print("Mounted /system.\n");
+ else
+#endif
+ {
+ 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;
+ case Device::WIPE_SYSTEM:
+ wipe_system(device);
+ break;
+ }
+ if (status == Device::kRefresh) {
+ status = 0;
+ continue;
+ }
+ break;
}
}
}
@@ -1318,6 +1647,40 @@ load_locale_from_cache() {
}
}
+static const char *key_src = "/data/misc/adb/adb_keys";
+static const char *key_dest = "/adb_keys";
+
+
+static void
+setup_adbd() {
+ struct stat f;
+ // Mount /data and copy adb_keys to root if it exists
+ ensure_path_mounted("/data");
+ if (stat(key_src, &f) == 0) {
+ FILE *file_src = fopen(key_src, "r");
+ if (file_src == NULL) {
+ LOGE("Can't open %s\n", key_src);
+ } else {
+ FILE *file_dest = fopen(key_dest, "w");
+ if (file_dest == NULL) {
+ LOGE("Can't open %s\n", key_dest);
+ } else {
+ char buf[4096];
+ while (fgets(buf, sizeof(buf), file_src)) fputs(buf, file_dest);
+ check_and_fclose(file_dest, key_dest);
+
+ // Enable secure adbd
+ property_set("ro.adb.secure", "1");
+ }
+ check_and_fclose(file_src, key_src);
+ }
+ }
+ ensure_path_unmounted("/data");
+
+ // Trigger (re)start of adb daemon
+ property_set("service.adb.root", "1");
+}
+
static RecoveryUI* gCurrentUI = NULL;
void
@@ -1493,6 +1856,28 @@ static ssize_t logrotate(
return __android_log_pmsg_file_write(logId, prio, name.c_str(), buf, len);
}
+static int write_file(const char *path, const char *value)
+{
+ int fd, ret, len;
+
+ fd = open(path, O_WRONLY|O_CREAT, 0622);
+ if (fd < 0)
+ return -errno;
+
+ len = strlen(value);
+
+ do {
+ ret = write(fd, value, len);
+ } while (ret < 0 && errno == EINTR);
+
+ close(fd);
+ if (ret < 0) {
+ return -errno;
+ } else {
+ return 0;
+ }
+}
+
int main(int argc, char **argv) {
// Take last pmsg contents and rewrite it to the current pmsg session.
static const char filter[] = "recovery/";
@@ -1518,6 +1903,37 @@ int main(int argc, char **argv) {
return 0;
}
+ // Handle alternative invocations
+ char* command = argv[0];
+ char* stripped = strrchr(argv[0], '/');
+ if (stripped)
+ command = stripped + 1;
+
+ if (strcmp(command, "recovery") != 0) {
+ struct recovery_cmd cmd = get_command(command);
+ if (cmd.name)
+ return cmd.main_func(argc, argv);
+
+ if (!strcmp(command, "setup_adbd")) {
+ load_volume_table();
+ setup_adbd();
+ return 0;
+ }
+ if (strstr(argv[0], "start")) {
+ property_set("ctl.start", argv[1]);
+ return 0;
+ }
+ if (strstr(argv[0], "stop")) {
+ property_set("ctl.stop", argv[1]);
+ return 0;
+ }
+ return toybox_driver(argc, argv);
+ }
+
+ // Clear umask for packages that copy files out to /tmp and then over
+ // to /system without properly setting all permissions (eg. gapps).
+ umask(0);
+
time_t start = time(NULL);
// redirect_stdio should be called only in non-sideload mode. Otherwise
@@ -1537,6 +1953,7 @@ int main(int argc, char **argv) {
bool should_wipe_cache = false;
bool should_wipe_ab = false;
size_t wipe_package_size = 0;
+ bool should_wipe_media = false;
bool show_text = false;
bool sideload = false;
bool sideload_auto_reboot = false;
@@ -1544,6 +1961,8 @@ int main(int argc, char **argv) {
bool shutdown_after = false;
int retry_count = 0;
bool security_update = false;
+ int status = INSTALL_SUCCESS;
+ bool mount_required = true;
int arg;
int option_index;
@@ -1554,6 +1973,7 @@ int main(int argc, char **argv) {
case 'u': update_package = optarg; break;
case 'w': should_wipe_data = true; break;
case 'c': should_wipe_cache = true; break;
+ case 'm': should_wipe_media = true; break;
case 't': show_text = true; break;
case 's': sideload = true; break;
case 'a': sideload = true; sideload_auto_reboot = true; break;
@@ -1597,6 +2017,9 @@ int main(int argc, char **argv) {
ui = device->GetUI();
gCurrentUI = ui;
+ vdc = new VoldClient(device);
+ vdc->start();
+
ui->SetLocale(locale);
ui->Init();
// Set background string to "installing security update" for security update,
@@ -1611,6 +2034,9 @@ int main(int argc, char **argv) {
ui->SetBackground(RecoveryUI::NONE);
if (show_text) ui->ShowText(true);
+ /*enable the backlight*/
+ write_file("/sys/class/leds/lcd-backlight/brightness", "128");
+
struct selinux_opt seopts[] = {
{ SELABEL_OPT_PATH, "/file_contexts" }
};
@@ -1646,16 +2072,38 @@ int main(int argc, char **argv) {
else
printf("modified_path allocation failed\n");
}
+ if (!strncmp("/sdcard", update_package, 7)) {
+ //If this is a UFS device lets mount the sdcard ourselves.Depending
+ //on if the device is UFS or EMMC based the path to the sdcard
+ //device changes so we cannot rely on the block dev path from
+ //recovery.fstab
+ if (is_ufs_dev()) {
+ if(do_sdcard_mount_for_ufs() != 0) {
+ status = INSTALL_ERROR;
+ goto error;
+ }
+ mount_required = false;
+ } else {
+ ui->Print("Update via sdcard on EMMC dev. Using path from fstab\n");
+ }
+ }
}
printf("\n");
-
property_list(print_property, NULL);
printf("\n");
- ui->Print("Supported API: %d\n", RECOVERY_API_VERSION);
-
- int status = INSTALL_SUCCESS;
-
+#ifdef HAVE_OEMLOCK
+ if (oem_lock == OEM_LOCK_UNLOCK) {
+ device->PreWipeData();
+ if (erase_volume("/data", true)) status = INSTALL_ERROR;
+ if (should_wipe_cache && erase_volume("/cache", true)) status = INSTALL_ERROR;
+ device->PostWipeData();
+ if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n");
+ if (oemlock_set(0)) status = INSTALL_ERROR;
+ // Force reboot regardless of actual status
+ status = INSTALL_SUCCESS;
+ } else
+#endif
if (update_package != NULL) {
// It's not entirely true that we will modify the flash. But we want
// to log the update attempt since update_package is non-NULL.
@@ -1675,7 +2123,7 @@ int main(int argc, char **argv) {
status = INSTALL_SKIPPED;
} else {
status = install_package(update_package, &should_wipe_cache,
- TEMPORARY_INSTALL_FILE, true, retry_count);
+ TEMPORARY_INSTALL_FILE, mount_required, retry_count);
if (status == INSTALL_SUCCESS && should_wipe_cache) {
wipe_cache(false, device);
}
@@ -1708,7 +2156,7 @@ int main(int argc, char **argv) {
}
}
} else if (should_wipe_data) {
- if (!wipe_data(false, device)) {
+ if (!wipe_data(false, device, should_wipe_media)) {
status = INSTALL_ERROR;
}
} else if (should_wipe_cache) {
@@ -1719,6 +2167,10 @@ int main(int argc, char **argv) {
if (!wipe_ab_device(wipe_package_size)) {
status = INSTALL_ERROR;
}
+ } else if (should_wipe_media) {
+ if (!wipe_media(false, device)) {
+ status = INSTALL_ERROR;
+ }
} else if (sideload) {
// 'adb reboot sideload' acts the same as user presses key combinations
// to enter the sideload mode. When 'sideload-auto-reboot' is used, text
@@ -1729,7 +2181,8 @@ int main(int argc, char **argv) {
if (!sideload_auto_reboot) {
ui->ShowText(true);
}
- status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
+ start_sideload(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
+ status = wait_sideload();
if (status == INSTALL_SUCCESS && should_wipe_cache) {
if (!wipe_cache(false, device)) {
status = INSTALL_ERROR;
@@ -1743,14 +2196,10 @@ int main(int argc, char **argv) {
status = INSTALL_NONE; // No command specified
ui->SetBackground(RecoveryUI::NO_COMMAND);
- // http://b/17489952
- // If this is an eng or userdebug build, automatically turn on the
- // text display if no command is specified.
- if (is_ro_debuggable()) {
- ui->ShowText(true);
- }
+ // Always show menu if no command is specified
+ ui->ShowText(true);
}
-
+error:
if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) {
copy_logs();
ui->SetBackground(RecoveryUI::ERROR);
@@ -1768,20 +2217,40 @@ int main(int argc, char **argv) {
// Save logs and clean up before rebooting or shutting down.
finish_recovery(send_intent);
+ vdc->unmountAll();
+ vdc->stop();
+
+ sync();
+
+ write_file("/sys/class/leds/lcd-backlight/brightness", "0");
+ gr_fb_blank(true);
+
switch (after) {
case Device::SHUTDOWN:
ui->Print("Shutting down...\n");
property_set(ANDROID_RB_PROPERTY, "shutdown,");
break;
+ case Device::REBOOT_RECOVERY:
+ ui->Print("Rebooting recovery...\n");
+ property_set(ANDROID_RB_PROPERTY, "reboot,recovery");
+ break;
+
case Device::REBOOT_BOOTLOADER:
+#ifdef DOWNLOAD_MODE
+ ui->Print("Rebooting to download mode...\n");
+ property_set(ANDROID_RB_PROPERTY, "reboot,download");
+#else
ui->Print("Rebooting to bootloader...\n");
property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
+#endif
break;
default:
+ char reason[PROPERTY_VALUE_MAX];
+ snprintf(reason, PROPERTY_VALUE_MAX, "reboot,%s", device->GetRebootReason());
ui->Print("Rebooting...\n");
- property_set(ANDROID_RB_PROPERTY, "reboot,");
+ property_set(ANDROID_RB_PROPERTY, reason);
break;
}
while (true) {
diff --git a/recovery_cmds.h b/recovery_cmds.h
new file mode 100644
index 00000000..8fc4d3e1
--- /dev/null
+++ b/recovery_cmds.h
@@ -0,0 +1,74 @@
+/*
+ * 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 _RECOVERY_CMDS_H
+#define _RECOVERY_CMDS_H
+
+#include <stdio.h>
+#include <string.h>
+
+int vold_main(int argc, char **argv);
+int minizip_main(int argc, char **argv);
+int miniunz_main(int argc, char **argv);
+int make_ext4fs_main(int argc, char **argv);
+int reboot_main(int argc, char **argv);
+int poweroff_main(int argc, char **argv);
+int fsck_msdos_main(int argc, char **argv);
+int newfs_msdos_main(int argc, char **argv);
+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);
+
+struct recovery_cmd {
+ const char *name;
+ int (*main_func)(int argc, char **argv);
+};
+
+static const struct recovery_cmd recovery_cmds[] = {
+ { "minivold", vold_main },
+ { "minizip", minizip_main },
+ { "make_ext4fs", make_ext4fs_main },
+ { "reboot", reboot_main },
+ { "poweroff", reboot_main },
+ { "fsck_msdos", fsck_msdos_main },
+ { "newfs_msdos", newfs_msdos_main },
+ { "pigz", pigz_main },
+ { "gzip", pigz_main },
+ { "gunzip", pigz_main },
+ { "zip", minizip_main },
+ { "unzip", miniunz_main },
+ { "start", start_main },
+ { "stop", stop_main },
+ { "sh", mksh_main },
+ { "vdc", vdc_main },
+ { NULL, NULL },
+};
+
+struct recovery_cmd get_command(char* command) {
+ int i;
+
+ for (i = 0; recovery_cmds[i].name; i++) {
+ if (strcmp(command, recovery_cmds[i].name) == 0)
+ break;
+ }
+
+ return recovery_cmds[i];
+}
+#endif
diff --git a/res-hdpi/images/font_log.png b/res-hdpi/images/font_log.png
new file mode 100644
index 00000000..59b990ae
--- /dev/null
+++ b/res-hdpi/images/font_log.png
Binary files differ
diff --git a/res-hdpi/images/font_menu.png b/res-hdpi/images/font_menu.png
new file mode 100644
index 00000000..d1aad6da
--- /dev/null
+++ b/res-hdpi/images/font_menu.png
Binary files differ
diff --git a/res-hdpi/images/icon_header.png b/res-hdpi/images/icon_header.png
new file mode 100644
index 00000000..1abd92d3
--- /dev/null
+++ b/res-hdpi/images/icon_header.png
Binary files differ
diff --git a/res-hdpi/images/icon_sysbar_back.png b/res-hdpi/images/icon_sysbar_back.png
new file mode 100644
index 00000000..5aa8e62f
--- /dev/null
+++ b/res-hdpi/images/icon_sysbar_back.png
Binary files differ
diff --git a/res-hdpi/images/icon_sysbar_back_highlight.png b/res-hdpi/images/icon_sysbar_back_highlight.png
new file mode 100644
index 00000000..58365041
--- /dev/null
+++ b/res-hdpi/images/icon_sysbar_back_highlight.png
Binary files differ
diff --git a/res-hdpi/images/icon_sysbar_home.png b/res-hdpi/images/icon_sysbar_home.png
new file mode 100644
index 00000000..b37422da
--- /dev/null
+++ b/res-hdpi/images/icon_sysbar_home.png
Binary files differ
diff --git a/res-hdpi/images/icon_sysbar_home_highlight.png b/res-hdpi/images/icon_sysbar_home_highlight.png
new file mode 100644
index 00000000..ed0ccfaf
--- /dev/null
+++ b/res-hdpi/images/icon_sysbar_home_highlight.png
Binary files differ
diff --git a/res-mdpi/images/font_log.png b/res-mdpi/images/font_log.png
new file mode 100644
index 00000000..bcbe242d
--- /dev/null
+++ b/res-mdpi/images/font_log.png
Binary files differ
diff --git a/res-mdpi/images/font_menu.png b/res-mdpi/images/font_menu.png
new file mode 100644
index 00000000..59b990ae
--- /dev/null
+++ b/res-mdpi/images/font_menu.png
Binary files differ
diff --git a/res-mdpi/images/icon_header.png b/res-mdpi/images/icon_header.png
new file mode 100644
index 00000000..c5fe5726
--- /dev/null
+++ b/res-mdpi/images/icon_header.png
Binary files differ
diff --git a/res-mdpi/images/icon_sysbar_back.png b/res-mdpi/images/icon_sysbar_back.png
new file mode 100644
index 00000000..81e46375
--- /dev/null
+++ b/res-mdpi/images/icon_sysbar_back.png
Binary files differ
diff --git a/res-mdpi/images/icon_sysbar_back_highlight.png b/res-mdpi/images/icon_sysbar_back_highlight.png
new file mode 100644
index 00000000..d173042e
--- /dev/null
+++ b/res-mdpi/images/icon_sysbar_back_highlight.png
Binary files differ
diff --git a/res-mdpi/images/icon_sysbar_home.png b/res-mdpi/images/icon_sysbar_home.png
new file mode 100644
index 00000000..d9e3a43e
--- /dev/null
+++ b/res-mdpi/images/icon_sysbar_home.png
Binary files differ
diff --git a/res-mdpi/images/icon_sysbar_home_highlight.png b/res-mdpi/images/icon_sysbar_home_highlight.png
new file mode 100644
index 00000000..ef65d610
--- /dev/null
+++ b/res-mdpi/images/icon_sysbar_home_highlight.png
Binary files differ
diff --git a/res-xhdpi/images/font_log.png b/res-xhdpi/images/font_log.png
new file mode 100644
index 00000000..d1aad6da
--- /dev/null
+++ b/res-xhdpi/images/font_log.png
Binary files differ
diff --git a/res-xhdpi/images/font_menu.png b/res-xhdpi/images/font_menu.png
new file mode 100644
index 00000000..6aead738
--- /dev/null
+++ b/res-xhdpi/images/font_menu.png
Binary files differ
diff --git a/res-xhdpi/images/icon_header.png b/res-xhdpi/images/icon_header.png
new file mode 100644
index 00000000..7ec2745a
--- /dev/null
+++ b/res-xhdpi/images/icon_header.png
Binary files differ
diff --git a/res-xhdpi/images/icon_sysbar_back.png b/res-xhdpi/images/icon_sysbar_back.png
new file mode 100644
index 00000000..415715ec
--- /dev/null
+++ b/res-xhdpi/images/icon_sysbar_back.png
Binary files differ
diff --git a/res-xhdpi/images/icon_sysbar_back_highlight.png b/res-xhdpi/images/icon_sysbar_back_highlight.png
new file mode 100644
index 00000000..237af6ab
--- /dev/null
+++ b/res-xhdpi/images/icon_sysbar_back_highlight.png
Binary files differ
diff --git a/res-xhdpi/images/icon_sysbar_home.png b/res-xhdpi/images/icon_sysbar_home.png
new file mode 100644
index 00000000..425c0dc5
--- /dev/null
+++ b/res-xhdpi/images/icon_sysbar_home.png
Binary files differ
diff --git a/res-xhdpi/images/icon_sysbar_home_highlight.png b/res-xhdpi/images/icon_sysbar_home_highlight.png
new file mode 100644
index 00000000..f7b6ce37
--- /dev/null
+++ b/res-xhdpi/images/icon_sysbar_home_highlight.png
Binary files differ
diff --git a/res-xxhdpi/images/font_log.png b/res-xxhdpi/images/font_log.png
new file mode 100644
index 00000000..6aead738
--- /dev/null
+++ b/res-xxhdpi/images/font_log.png
Binary files differ
diff --git a/res-xxhdpi/images/font_menu.png b/res-xxhdpi/images/font_menu.png
new file mode 100644
index 00000000..f3b54b38
--- /dev/null
+++ b/res-xxhdpi/images/font_menu.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_header.png b/res-xxhdpi/images/icon_header.png
new file mode 100644
index 00000000..6f3a4296
--- /dev/null
+++ b/res-xxhdpi/images/icon_header.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_sysbar_back.png b/res-xxhdpi/images/icon_sysbar_back.png
new file mode 100644
index 00000000..0b2e866e
--- /dev/null
+++ b/res-xxhdpi/images/icon_sysbar_back.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_sysbar_back_highlight.png b/res-xxhdpi/images/icon_sysbar_back_highlight.png
new file mode 100644
index 00000000..03cb8a1f
--- /dev/null
+++ b/res-xxhdpi/images/icon_sysbar_back_highlight.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_sysbar_home.png b/res-xxhdpi/images/icon_sysbar_home.png
new file mode 100644
index 00000000..73488413
--- /dev/null
+++ b/res-xxhdpi/images/icon_sysbar_home.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_sysbar_home_highlight.png b/res-xxhdpi/images/icon_sysbar_home_highlight.png
new file mode 100644
index 00000000..a9814959
--- /dev/null
+++ b/res-xxhdpi/images/icon_sysbar_home_highlight.png
Binary files differ
diff --git a/res-xxxhdpi/images/font_log.png b/res-xxxhdpi/images/font_log.png
new file mode 100644
index 00000000..ec8d899b
--- /dev/null
+++ b/res-xxxhdpi/images/font_log.png
Binary files differ
diff --git a/res-xxxhdpi/images/font_menu.png b/res-xxxhdpi/images/font_menu.png
new file mode 100644
index 00000000..d7c326ad
--- /dev/null
+++ b/res-xxxhdpi/images/font_menu.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_header.png b/res-xxxhdpi/images/icon_header.png
new file mode 100644
index 00000000..3a723079
--- /dev/null
+++ b/res-xxxhdpi/images/icon_header.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_sysbar_back.png b/res-xxxhdpi/images/icon_sysbar_back.png
new file mode 100644
index 00000000..f630553d
--- /dev/null
+++ b/res-xxxhdpi/images/icon_sysbar_back.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_sysbar_back_highlight.png b/res-xxxhdpi/images/icon_sysbar_back_highlight.png
new file mode 100644
index 00000000..92110933
--- /dev/null
+++ b/res-xxxhdpi/images/icon_sysbar_back_highlight.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_sysbar_home.png b/res-xxxhdpi/images/icon_sysbar_home.png
new file mode 100644
index 00000000..9ee96ced
--- /dev/null
+++ b/res-xxxhdpi/images/icon_sysbar_home.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_sysbar_home_highlight.png b/res-xxxhdpi/images/icon_sysbar_home_highlight.png
new file mode 100644
index 00000000..d63ecb03
--- /dev/null
+++ b/res-xxxhdpi/images/icon_sysbar_home_highlight.png
Binary files differ
diff --git a/restore.cpp b/restore.cpp
new file mode 100644
index 00000000..8c15f6f8
--- /dev/null
+++ b/restore.cpp
@@ -0,0 +1,305 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/vfs.h>
+
+#include <cutils/properties.h>
+
+#include <lib/libtar.h>
+#include <zlib.h>
+
+#include "roots.h"
+
+#include "bu.h"
+
+using namespace android;
+
+static int verify_sod()
+{
+ const char* key;
+ char value[PROPERTY_VALUE_MAX];
+ char sodbuf[PROP_LINE_LEN*10];
+ size_t len;
+
+ len = sizeof(sodbuf);
+ if (tar_extract_file_contents(tar, sodbuf, &len) != 0) {
+ logmsg("tar_verify_sod: failed to extract file\n");
+ return -1;
+ }
+
+ int partidx = 0;
+
+ char val_hashname[PROPERTY_VALUE_MAX];
+ memset(val_hashname, 0, sizeof(val_hashname));
+ char val_product[PROPERTY_VALUE_MAX];
+ memset(val_product, 0, sizeof(val_product));
+ char* p = sodbuf;
+ char* q;
+ while ((q = strchr(p, '\n')) != NULL) {
+ char* key = p;
+ *q = '\0';
+ logmsg("verify_sod: line=%s\n", p);
+ p = q+1;
+ char* val = strchr(key, '=');
+ if (val) {
+ *val = '\0';
+ ++val;
+ if (strcmp(key, "hash.name") == 0) {
+ strncpy(val_hashname, val, sizeof(val_hashname));
+ }
+ if (strcmp(key, "ro.product.device") == 0) {
+ strncpy(val_product, val, sizeof(val_product));
+ }
+ if (strncmp(key, "fs.", 3) == 0) {
+ char* name = key+3;
+ char* attr = strchr(name, '.');
+ if (attr) {
+ *attr = '\0';
+ ++attr;
+ part_add(name);
+ struct partspec* part = part_find(name);
+ if (!strcmp(attr, "size")) {
+ part->size = strtoul(val, NULL, 0);
+ }
+ if (!strcmp(attr, "used")) {
+ part->used = strtoul(val, NULL, 0);
+ }
+ }
+ }
+ }
+ }
+
+ if (!val_hashname[0]) {
+ logmsg("verify_sod: did not find hash.name\n");
+ return -1;
+ }
+ hash_name = strdup(val_hashname);
+
+ if (!val_product[0]) {
+ logmsg("verify_sod: did not find ro.product.device\n");
+ return -1;
+ }
+ key = "ro.product.device";
+ property_get(key, value, "");
+ if (strcmp(val_product, value) != 0) {
+ logmsg("verify_sod: product does not match\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int verify_eod(size_t actual_hash_datalen,
+ SHA_CTX* actual_sha_ctx, MD5_CTX* actual_md5_ctx)
+{
+ int rc = -1;
+ char eodbuf[PROP_LINE_LEN*10];
+ size_t len;
+
+ len = sizeof(eodbuf);
+ if (tar_extract_file_contents(tar, eodbuf, &len) != 0) {
+ logmsg("verify_eod: failed to extract file\n");
+ return -1;
+ }
+
+ size_t reported_datalen = 0;
+ char reported_hash[HASH_MAX_STRING_LENGTH];
+ memset(reported_hash, 0, sizeof(reported_hash));
+ char* p = eodbuf;
+ char* q;
+ while ((q = strchr(p, '\n')) != NULL) {
+ char* key = p;
+ *q = '\0';
+ logmsg("verify_eod: line=%s\n", p);
+ p = q+1;
+ char* val = strchr(key, '=');
+ if (val) {
+ *val = '\0';
+ ++val;
+ if (strcmp(key, "hash.datalen") == 0) {
+ reported_datalen = strtoul(val, NULL, 0);
+ }
+ if (strcmp(key, "hash.value") == 0) {
+ memset(reported_hash, 0, sizeof(reported_hash));
+ strncpy(reported_hash, val, sizeof(reported_hash));
+ }
+ }
+ }
+
+ unsigned char digest[HASH_MAX_LENGTH];
+ char hexdigest[HASH_MAX_STRING_LENGTH];
+
+ int n;
+ if (hash_name != NULL && !strcasecmp(hash_name, "sha1")) {
+ SHA1_Final(digest, actual_sha_ctx);
+ for (n = 0; n < SHA_DIGEST_LENGTH; ++n) {
+ sprintf(hexdigest+2*n, "%02x", digest[n]);
+ }
+ }
+ else { // default to md5
+ MD5_Final(digest, actual_md5_ctx);
+ for (n = 0; n < MD5_DIGEST_LENGTH; ++n) {
+ sprintf(hexdigest+2*n, "%02x", digest[n]);
+ }
+ }
+
+ logmsg("verify_eod: expected=%d,%s\n", actual_hash_datalen, hexdigest);
+
+ logmsg("verify_eod: reported=%d,%s\n", reported_datalen, reported_hash);
+
+ if ((reported_datalen == actual_hash_datalen) &&
+ (memcmp(hexdigest, reported_hash, strlen(hexdigest)) == 0)) {
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int do_restore_tree()
+{
+ int rc = 0;
+ ssize_t len;
+ const char* compress = "none";
+ char buf[512];
+ char rootpath[] = "/";
+
+ logmsg("do_restore_tree: enter\n");
+
+ len = recv(adb_ifd, buf, sizeof(buf), MSG_PEEK);
+ if (len < 0) {
+ logmsg("do_restore_tree: peek failed (%d:%s)\n", rc, strerror(errno));
+ return -1;
+ }
+ if (len < 2) {
+ logmsg("do_restore_tree: peek returned %d\n", len);
+ return -1;
+ }
+ if (buf[0] == 0x1f && buf[1] == 0x8b) {
+ logmsg("do_restore_tree: is gzip\n");
+ compress = "gzip";
+ }
+
+ create_tar(adb_ifd, compress, "r");
+
+ size_t save_hash_datalen;
+ SHA_CTX save_sha_ctx;
+ MD5_CTX save_md5_ctx;
+
+ char cur_mount[PATH_MAX];
+ cur_mount[0] = '\0';
+ while (1) {
+ save_hash_datalen = hash_datalen;
+ memcpy(&save_sha_ctx, &sha_ctx, sizeof(SHA_CTX));
+ memcpy(&save_md5_ctx, &md5_ctx, sizeof(MD5_CTX));
+ rc = th_read(tar);
+ if (rc != 0) {
+ if (rc == 1) { // EOF
+ rc = 0;
+ }
+ break;
+ }
+ char* pathname = th_get_pathname(tar);
+ logmsg("do_restore_tree: extract %s\n", pathname);
+ if (pathname[0] == '/') {
+ const char* mntend = strchr(&pathname[1], '/');
+ if (!mntend) {
+ mntend = pathname + strlen(pathname);
+ }
+ if (memcmp(cur_mount, pathname, mntend-pathname) != 0) {
+ // New mount
+ if (cur_mount[0]) {
+ logmsg("do_restore_tree: unmounting %s\n", cur_mount);
+ ensure_path_unmounted(cur_mount);
+ }
+ memcpy(cur_mount, pathname, mntend-pathname);
+ cur_mount[mntend-pathname] = '\0';
+
+ // XXX: Assume paths are not interspersed
+ logmsg("do_restore_tree: switching to %s\n", cur_mount);
+ rc = ensure_path_unmounted(cur_mount);
+ if (rc != 0) {
+ logmsg("do_restore_tree: cannot unmount %s\n", cur_mount);
+ break;
+ }
+ logmsg("do_restore_tree: formatting %s\n", cur_mount);
+ rc = format_volume(cur_mount);
+ if (rc != 0) {
+ logmsg("do_restore_tree: cannot format %s\n", cur_mount);
+ break;
+ }
+ rc = ensure_path_mounted(cur_mount, true);
+ if (rc != 0) {
+ logmsg("do_restore_tree: cannot mount %s\n", cur_mount);
+ break;
+ }
+ partspec* curpart = part_find(&cur_mount[1]);
+ part_set(curpart);
+ }
+ rc = tar_extract_file(tar, pathname);
+ if (rc != 0) {
+ logmsg("do_restore_tree: failed to restore %s", pathname);
+ }
+ }
+ else if (!strcmp(pathname, "SOD")) {
+ rc = verify_sod();
+ logmsg("do_restore_tree: tar_verify_sod returned %d\n", rc);
+ }
+ else if (!strcmp(pathname, "EOD")) {
+ rc = verify_eod(save_hash_datalen, &save_sha_ctx, &save_md5_ctx);
+ logmsg("do_restore_tree: tar_verify_eod returned %d\n", rc);
+ }
+ else {
+ char mnt[PATH_MAX];
+ snprintf(mnt, sizeof(mnt), "/%s", pathname);
+ Volume* vol = volume_for_path(mnt);
+ if (vol != NULL && vol->fs_type != NULL) {
+ partspec* curpart = part_find(pathname);
+ part_set(curpart);
+ rc = tar_extract_file(tar, vol->blk_device);
+ }
+ else {
+ logmsg("do_restore_tree: cannot find volume for %s\n", mnt);
+ }
+ }
+ free(pathname);
+ if (rc != 0) {
+ logmsg("extract failed, rc=%d\n", rc);
+ break;
+ }
+ }
+
+ if (cur_mount[0]) {
+ logmsg("do_restore_tree: unmounting %s\n", cur_mount);
+ ensure_path_unmounted(cur_mount);
+ }
+
+ tar_close(tar);
+
+ return rc;
+}
+
+int do_restore(int argc, char **argv)
+{
+ int rc = 1;
+ int n;
+
+ char buf[256];
+ int len;
+ int written;
+
+ rc = do_restore_tree();
+ logmsg("do_restore: rc=%d\n", rc);
+
+ free(hash_name);
+ hash_name = NULL;
+
+ return rc;
+}
+
diff --git a/roots.cpp b/roots.cpp
index f361cb8c..162b0dfc 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"
@@ -33,10 +34,53 @@
#include "wipe.h"
#include "cryptfs.h"
+#include "voldclient.h"
+#include <blkid/blkid.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
+ && strcmp(v->fs_type, "bml") != 0 && !fs_mgr_is_voldmanaged(v)
+ && strncmp(v->blk_device, "/", 1) == 0
+ && strncmp(v->mount_point, "/", 1) == 0) {
+
+ fprintf(file, "%s ", v->blk_device);
+ fprintf(file, "%s ", v->mount_point);
+ fprintf(file, "%s ", v->fs_type);
+ fprintf(file, "%s 0 0\n", v->fs_options == NULL ? "defaults" : v->fs_options);
+ }
+}
+
+int get_num_volumes() {
+ return fstab->num_entries;
+}
+
+Volume* get_device_volumes() {
+ return fstab->recs;
+}
+
void load_volume_table()
{
int i;
@@ -56,22 +100,84 @@ void load_volume_table()
return;
}
+ // Create a boring /etc/fstab so tools like Busybox work
+ FILE *file = fopen("/etc/fstab", "w");
+ if (file == NULL) {
+ LOGW("Unable to create /etc/fstab!\n");
+ return;
+ }
+
printf("recovery filesystem table\n");
printf("=========================\n");
for (i = 0; i < fstab->num_entries; ++i) {
Volume* v = &fstab->recs[i];
printf(" %d %s %s %s %lld\n", i, v->mount_point, v->fs_type,
v->blk_device, v->length);
+
+ write_fstab_entry(v, file);
}
+
+ fclose(file);
+
printf("\n");
}
+bool volume_is_mountable(Volume *v)
+{
+ return (fs_mgr_is_voldmanaged(v) ||
+ !strcmp(v->fs_type, "yaffs2") ||
+ !strcmp(v->fs_type, "ext4") ||
+ !strcmp(v->fs_type, "f2fs") ||
+ !strcmp(v->fs_type, "vfat"));
+}
+
+bool volume_is_readonly(Volume *v)
+{
+ return (v->flags & MS_RDONLY);
+}
+
+bool volume_is_verity(Volume *v)
+{
+ return fs_mgr_is_verified(v);
+}
+
Volume* volume_for_path(const char* path) {
- return fs_mgr_get_entry_for_mount_point(fstab, path);
+ Volume *rec = fs_mgr_get_entry_for_mount_point(fstab, path);
+
+ if (rec == NULL)
+ return rec;
+
+ if (strcmp(rec->fs_type, "ext4") == 0 || strcmp(rec->fs_type, "f2fs") == 0 ||
+ strcmp(rec->fs_type, "vfat") == 0) {
+ char *detected_fs_type = blkid_get_tag_value(NULL, "TYPE", rec->blk_device);
+
+ if (detected_fs_type == NULL)
+ return rec;
+
+ Volume *fetched_rec = rec;
+ while (rec != NULL && strcmp(rec->fs_type, detected_fs_type) != 0)
+ rec = fs_mgr_get_entry_for_mount_point_after(rec, fstab, path);
+
+ if (rec == NULL)
+ return fetched_rec;
+ }
+
+ return rec;
+}
+
+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) {
+int ensure_path_mounted_at(const char* path, const char* mount_point, bool force_rw) {
Volume* v = volume_for_path(path);
if (v == NULL) {
LOGE("unknown volume for path [%s]\n", path);
@@ -93,14 +199,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.
@@ -114,10 +222,17 @@ int ensure_path_mounted_at(const char* path, const char* mount_point) {
}
return mtd_mount_partition(partition, mount_point, v->fs_type, 0);
} else if (strcmp(v->fs_type, "ext4") == 0 ||
+ strcmp(v->fs_type, "f2fs") == 0 ||
strcmp(v->fs_type, "squashfs") == 0 ||
strcmp(v->fs_type, "vfat") == 0) {
+ unsigned long mntflags = v->flags;
+ if (!force_rw) {
+ if ((v->flags & MS_RDONLY) || fs_mgr_is_verified(v)) {
+ mntflags |= MS_RDONLY;
+ }
+ }
result = mount(v->blk_device, mount_point, v->fs_type,
- v->flags, v->fs_options);
+ mntflags, v->fs_options);
if (result == 0) return 0;
LOGE("failed to mount %s (%s)\n", mount_point, strerror(errno));
@@ -128,17 +243,76 @@ int ensure_path_mounted_at(const char* path, const char* mount_point) {
return -1;
}
-int ensure_path_mounted(const char* path) {
+int ensure_volume_mounted(Volume* v, bool force_rw) {
+ if (v == NULL) {
+ LOGE("cannot mount unknown volume\n");
+ return -1;
+ }
+ return ensure_path_mounted_at(v->mount_point, nullptr, force_rw);
+}
+
+int remount_no_selinux(const char* path) {
+ int ret;
+
+ char *old_fs_options;
+ char *new_fs_options;
+
+ char se_context[] = ",context=u:object_r:app_data_file:s0";
+ Volume *v;
+
+ // Backup original mount options
+ v = volume_for_path(path);
+ old_fs_options = v->fs_options;
+
+ // Add SELinux mount override
+ asprintf(&new_fs_options, "%s%s", v->fs_options, se_context);
+ v->fs_options = new_fs_options;
+
+ ensure_path_unmounted(path);
+ ret = ensure_path_mounted(path);
+
+ // Restore original mount options
+ v->fs_options = old_fs_options;
+ free(new_fs_options);
+
+ return ret;
+}
+
+int ensure_path_mounted(const char* path, bool force_rw) {
// Mount at the default mount point.
- return ensure_path_mounted_at(path, nullptr);
+ return ensure_path_mounted_at(path, nullptr, force_rw);
}
-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;
@@ -158,7 +332,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[]) {
@@ -175,7 +356,61 @@ static int exec_cmd(const char* path, char* const argv[]) {
return WEXITSTATUS(status);
}
-int format_volume(const char* volume, const char* directory) {
+static int rmtree_except(const char* path, const char* except)
+{
+ char pathbuf[PATH_MAX];
+ int rc = 0;
+ DIR* dp = opendir(path);
+ if (dp == NULL) {
+ return -1;
+ }
+ struct dirent* de;
+ while ((de = readdir(dp)) != NULL) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+ if (except && !strcmp(de->d_name, except))
+ continue;
+ struct stat st;
+ snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, de->d_name);
+ rc = lstat(pathbuf, &st);
+ if (rc != 0) {
+ LOGE("Failed to stat %s\n", pathbuf);
+ break;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ rc = rmtree_except(pathbuf, NULL);
+ if (rc != 0)
+ break;
+ rc = rmdir(pathbuf);
+ }
+ else {
+ rc = unlink(pathbuf);
+ }
+ if (rc != 0) {
+ LOGI("Failed to remove %s: %s\n", pathbuf, strerror(errno));
+ break;
+ }
+ }
+ closedir(dp);
+ return rc;
+}
+
+int format_volume(const char* volume, const char* directory, bool force) {
+ if (strcmp(volume, "media") == 0) {
+ if (!vdc->isEmulatedStorage()) {
+ return 0;
+ }
+ if (ensure_path_mounted("/data") != 0) {
+ LOGE("format_volume failed to mount /data\n");
+ return -1;
+ }
+ remount_no_selinux("/data");
+ int rc = 0;
+ rc = rmtree_except("/data/media", NULL);
+ ensure_path_unmounted("/data");
+ return rc;
+ }
+
Volume* v = volume_for_path(volume);
if (v == NULL) {
LOGE("unknown volume \"%s\"\n", volume);
@@ -191,11 +426,52 @@ int format_volume(const char* volume, const char* directory) {
return -1;
}
+ if (!force && strcmp(volume, "/data") == 0 && vdc->isEmulatedStorage()) {
+ if (ensure_path_mounted("/data") == 0) {
+ remount_no_selinux("/data");
+ // Preserve .layout_version to avoid "nesting bug"
+ LOGI("Preserving layout version\n");
+ unsigned char layout_buf[256];
+ ssize_t layout_buflen = -1;
+ int fd;
+ fd = open("/data/.layout_version", O_RDONLY);
+ if (fd != -1) {
+ layout_buflen = read(fd, layout_buf, sizeof(layout_buf));
+ close(fd);
+ }
+
+ int rc = rmtree_except("/data", "media");
+
+ // Restore .layout_version
+ if (layout_buflen > 0) {
+ LOGI("Restoring layout version\n");
+ fd = open("/data/.layout_version", O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd != -1) {
+ write(fd, layout_buf, layout_buflen);
+ close(fd);
+ }
+ }
+
+ ensure_path_unmounted(volume);
+
+ return rc;
+ }
+ else {
+ LOGE("format_volume failed to mount /data\n");
+ return -1;
+ }
+ }
+
if (ensure_path_unmounted(volume) != 0) {
LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
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);
@@ -243,24 +519,24 @@ int format_volume(const char* volume, const char* directory) {
if (strcmp(v->fs_type, "ext4") == 0) {
result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory);
} else { /* Has to be f2fs because we checked earlier. */
- if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) {
- LOGE("format_volume: crypt footer + negative length (%zd) not supported on %s\n", length, v->fs_type);
- return -1;
- }
+ char bytes_reserved[20], num_sectors[20];
+ const char* f2fs_argv[6] = {"mkfs.f2fs", "-t1"};
if (length < 0) {
- LOGE("format_volume: negative length (%zd) not supported on %s\n", length, v->fs_type);
- return -1;
- }
- char *num_sectors;
- if (asprintf(&num_sectors, "%zd", length / 512) <= 0) {
- LOGE("format_volume: failed to create %s command for %s\n", v->fs_type, v->blk_device);
- return -1;
+ snprintf(bytes_reserved, sizeof(bytes_reserved), "%zd", -length);
+ f2fs_argv[2] = "-r";
+ f2fs_argv[3] = bytes_reserved;
+ f2fs_argv[4] = v->blk_device;
+ f2fs_argv[5] = NULL;
+ } else {
+ /* num_sectors can be zero which mean whole device space */
+ snprintf(num_sectors, sizeof(num_sectors), "%zd", length / 512);
+ f2fs_argv[2] = v->blk_device;
+ f2fs_argv[3] = num_sectors;
+ f2fs_argv[4] = NULL;
}
const char *f2fs_path = "/sbin/mkfs.f2fs";
- const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, NULL};
result = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
- free(num_sectors);
}
if (result != 0) {
LOGE("format_volume: make %s failed on %s with %d(%s)\n", v->fs_type, v->blk_device, result, strerror(errno));
@@ -273,8 +549,8 @@ int format_volume(const char* volume, const char* directory) {
return -1;
}
-int format_volume(const char* volume) {
- return format_volume(volume, NULL);
+int format_volume(const char* volume, bool force) {
+ return format_volume(volume, NULL, force);
}
int setup_install_mounts() {
@@ -293,7 +569,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 (vdc->isEmulatedStorage() && 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 a14b7d97..8ce08924 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,28 +28,40 @@ 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_path_mounted(const char* path);
+int ensure_volume_mounted(Volume* v, bool force_rw=false);
+int ensure_path_mounted(const char* path, bool force_rw=false);
+// Above, plus override SELinux default context
+int remount_no_selinux(const char* path);
// Similar to ensure_path_mounted, but allows one to specify the mount_point.
-int ensure_path_mounted_at(const char* path, const char* mount_point);
+int ensure_path_mounted_at(const char* path, const char* mount_point, bool force_rw=false);
// 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
// it is mounted.
-int format_volume(const char* volume);
+int format_volume(const char* volume, bool force = false);
// Reformat the given volume (must be the mount point only, eg
// "/cache"), no paths permitted. Attempts to unmount the volume if
// it is mounted.
// Copies 'directory' to root of the newly formatted volume
-int format_volume(const char* volume, const char* directory);
+int format_volume(const char* volume, const char* directory, bool force = false);
// Ensure that all and only the volumes that packages expect to find
// mounted (/tmp and /cache) are mounted. Returns 0 on success.
int setup_install_mounts();
+int get_num_volumes();
+
+bool volume_is_mountable(Volume *v);
+bool volume_is_readonly(Volume *v);
+bool volume_is_verity(Volume *v);
+
+#define MAX_NUM_MANAGED_VOLUMES 10
+
#endif // RECOVERY_ROOTS_H_
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 2a0769e4..a4dd390b 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -40,6 +40,7 @@
#include "minui/minui.h"
#include "screen_ui.h"
#include "ui.h"
+#include "cutils/properties.h"
#define TEXT_INDENT 4
@@ -60,6 +61,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() :
progressScopeSize(0),
progress(0),
pagesIdentical(false),
+ log_text_cols_(0),
+ log_text_rows_(0),
text_cols_(0),
text_rows_(0),
text_(nullptr),
@@ -69,17 +72,30 @@ ScreenRecoveryUI::ScreenRecoveryUI() :
show_text(false),
show_text_ever(false),
menu_(nullptr),
+ menu_headers_(nullptr),
+ header_items(0),
show_menu(false),
menu_items(0),
menu_sel(0),
+ menu_show_start_(0),
+ sysbar_state(0),
file_viewer_text_(nullptr),
+ progressCondition(PTHREAD_COND_INITIALIZER),
intro_frames(0),
loop_frames(0),
animation_fps(30), // TODO: there's currently no way to infer this.
stage(-1),
max_stage(-1),
updateMutex(PTHREAD_MUTEX_INITIALIZER),
- rtl_locale(false) {
+ rtl_locale(false),
+ rainbow(false),
+ wrap_count(0) {
+
+ headerIcon = nullptr;
+ sysbarBackIcon = nullptr;
+ sysbarBackHighlightIcon = nullptr;
+ sysbarHomeIcon = nullptr;
+ sysbarHomeHighlightIcon = nullptr;
}
GRSurface* ScreenRecoveryUI::GetCurrentFrame() {
@@ -231,13 +247,13 @@ void ScreenRecoveryUI::SetColor(UIElement e) {
break;
case MENU:
case MENU_SEL_BG:
- gr_color(0, 106, 157, 255);
+ gr_color(84, 81, 80, 255);
break;
case MENU_SEL_BG_ACTIVE:
- gr_color(0, 156, 100, 255);
+ gr_color(138, 135, 134, 255);
break;
case MENU_SEL_FG:
- gr_color(255, 255, 255, 255);
+ gr_color(22, 124, 128, 255);
break;
case LOG:
gr_color(196, 196, 196, 255);
@@ -280,63 +296,129 @@ static const char* LONG_PRESS_HELP[] = {
NULL
};
+int ScreenRecoveryUI::draw_header_icon()
+{
+ GRSurface* surface = headerIcon;
+ int iw = header_width_;
+ int ih = header_height_;
+ int ix = (gr_fb_width() - iw) / 2;
+ int iy = 0;
+ gr_blit(surface, 0, 0, iw, ih, ix, iy);
+ return ih;
+}
+
+void ScreenRecoveryUI::draw_menu_item(int textrow, const char *text, int selected)
+{
+ if (selected) {
+ SetColor(MENU_SEL_BG);
+ gr_fill(0, (textrow) * char_height_,
+ gr_fb_width(), (textrow+3) * char_height_ - 1);
+ SetColor(MENU_SEL_FG);
+ gr_text(4, (textrow+1) * char_height_ - 1, text, 0);
+ SetColor(MENU);
+ }
+ else {
+ SetColor(MENU);
+ gr_text(4, (textrow+1) * char_height_ - 1, text, 0);
+ }
+}
+
+void ScreenRecoveryUI::draw_sysbar()
+{
+ GRSurface* surface;
+ int sw = gr_fb_width();
+ int sh = gr_fb_height();
+ int iw;
+ int ih;
+ SetColor(TEXT_FILL);
+ gr_fill(0, sh - sysbar_height_, sw, sh);
+
+ // Left third is back button
+ if (!HasBackKey()) {
+ if (sysbar_state & SYSBAR_BACK) {
+ surface = sysbarBackHighlightIcon;
+ }
+ else {
+ surface = sysbarBackIcon;
+ }
+ iw = gr_get_width(surface);
+ ih = gr_get_height(surface);
+ gr_blit(surface, 0, 0, iw, ih,
+ 1 * (sw / 6) - (iw / 2), sh - ih);
+ }
+
+ // Middle third is home button
+ if (!HasHomeKey()) {
+ if (sysbar_state & SYSBAR_HOME) {
+ surface = sysbarHomeHighlightIcon;
+ }
+ else {
+ surface = sysbarHomeIcon;
+ }
+ iw = gr_get_width(surface);
+ ih = gr_get_height(surface);
+ gr_blit(surface, 0, 0, iw, ih,
+ 3 * (sw / 6) - (iw / 2), sh - ih);
+ }
+}
+
// Redraw everything on the screen. Does not flip pages.
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::draw_screen_locked() {
- if (!show_text) {
- draw_background_locked();
- draw_foreground_locked();
- } else {
- gr_color(0, 0, 0, 255);
- gr_clear();
+ draw_background_locked();
+
+ if (show_text) {
+ if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
+ size_t y = currentIcon == INSTALLING_UPDATE
+ ? 3 * gr_fb_height() / 4 : header_height_ + 4;
+
+ SetColor(LOG);
+ int cx, cy;
+ gr_set_font("log");
+ gr_font_size(&cx, &cy);
+ // display from the bottom up, until we hit the top of the
+ // screen or we've displayed the entire text buffer.
+ size_t ty, count;
+ int row = (text_row_ + log_text_rows_ - 1) % log_text_rows_;
+ for (ty = gr_fb_height() - cy, count = 0;
+ ty > y + 2 && count < log_text_rows_;
+ ty -= (cy + 2), ++count) {
+ gr_text(4, ty, text_[row], 0);
+ --row;
+ if (row < 0) row = log_text_rows_ - 1;
+ }
+ gr_set_font("menu");
+ return;
+ }
- int y = 0;
if (show_menu) {
- char recovery_fingerprint[PROPERTY_VALUE_MAX];
- property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, "");
-
- SetColor(INFO);
- DrawTextLine(TEXT_INDENT, &y, "Android Recovery", true);
- for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
- DrawTextLine(TEXT_INDENT, &y, chunk.c_str(), false);
- }
- DrawTextLines(TEXT_INDENT, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
-
- SetColor(HEADER);
- DrawTextLines(TEXT_INDENT, &y, menu_headers_);
-
- SetColor(MENU);
- DrawHorizontalRule(&y);
- y += 4;
- for (int i = 0; i < menu_items; ++i) {
- if (i == menu_sel) {
- // Draw the highlight bar.
- SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG);
- gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2);
- // Bold white text for the selected item.
- SetColor(MENU_SEL_FG);
- gr_text(4, y, menu_[i], true);
- SetColor(MENU);
- } else {
- gr_text(4, y, menu_[i], false);
+ int i, y;
+ draw_header_icon();
+ draw_sysbar();
+
+ // Divider
+ y = text_first_row_ * char_height_;
+ SetColor(MENU_SEL_FG);
+ gr_fill(0, y - 1, gr_fb_width(), y);
+
+ if (header_items > 0) {
+ for (i = 0; i < header_items; ++i) {
+ draw_menu_item(text_first_row_ + 3*i,
+ menu_headers_[i], false);
}
- y += char_height_ + 4;
+ y = (text_first_row_ + 3*header_items) * char_height_;
+ SetColor(MENU_SEL_FG);
+ gr_fill(0, y - 1, gr_fb_width(), y);
+ }
+ int nr_items = menu_items - menu_show_start_;
+ if (header_items + nr_items > max_menu_rows_)
+ nr_items = max_menu_rows_ - header_items;
+ for (i = 0; i < nr_items; ++i) {
+ const char* text = menu_[menu_show_start_ + i];
+ draw_menu_item(text_first_row_ + 3 * (header_items + i),
+ menu_[menu_show_start_ + i],
+ ((menu_show_start_ + i) == menu_sel));
}
- DrawHorizontalRule(&y);
- }
-
- // display from the bottom up, until we hit the top of the
- // screen, the bottom of the menu, or we've displayed the
- // entire text buffer.
- SetColor(LOG);
- int row = (text_top_ + text_rows_ - 1) % text_rows_;
- size_t count = 0;
- for (int ty = gr_fb_height() - char_height_;
- ty >= y && count < text_rows_;
- ty -= char_height_, ++count) {
- gr_text(0, ty, text_[row], false);
- --row;
- if (row < 0) row = text_rows_ - 1;
}
}
}
@@ -344,20 +426,9 @@ void ScreenRecoveryUI::draw_screen_locked() {
// Redraw everything on the screen and flip the screen (make it visible).
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::update_screen_locked() {
- draw_screen_locked();
- gr_flip();
-}
-
-// Updates only the progress bar, if possible, otherwise redraws the screen.
-// Should only be called with updateMutex locked.
-void ScreenRecoveryUI::update_progress_locked() {
- if (show_text || !pagesIdentical) {
- draw_screen_locked(); // Must redraw the whole screen
- pagesIdentical = true;
- } else {
- draw_foreground_locked(); // Draw only the progress bar and overlays
- }
- gr_flip();
+ update_waiting = true;
+ pthread_cond_signal(&progressCondition);
+ LOGV("%s: %p\n", __func__, __builtin_return_address(0));
}
// Keeps the progress bar updated, even when the process is otherwise busy.
@@ -366,17 +437,26 @@ void* ScreenRecoveryUI::ProgressThreadStartRoutine(void* data) {
return nullptr;
}
+void ScreenRecoveryUI::OMGRainbows()
+{
+ rainbow = rainbow ? false : true;
+ set_rainbow_mode(rainbow);
+ property_set("sys.rainbow.recovery", rainbow ? "1" : "0");
+}
+
void ScreenRecoveryUI::ProgressThreadLoop() {
double interval = 1.0 / animation_fps;
while (true) {
- double start = now();
pthread_mutex_lock(&updateMutex);
+ if (progressBarType == EMPTY && !update_waiting)
+ pthread_cond_wait(&progressCondition, &updateMutex);
bool redraw = false;
+ double start = now();
// update the installation animation, if active
// skip this if we have a text overlay (too expensive to update)
- if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && !show_text) {
+ if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING)) {
if (!intro_done) {
if (current_frame == intro_frames - 1) {
intro_done = true;
@@ -403,9 +483,22 @@ void ScreenRecoveryUI::ProgressThreadLoop() {
}
}
- if (redraw) update_progress_locked();
+ if (update_waiting || !pagesIdentical) {
+ LOGV("call draw_screen_locked\n");
+ draw_screen_locked();
+ if (!update_waiting)
+ pagesIdentical = true;
+ }
+ if (redraw) {
+ LOGV("call draw_foreground_locked\n");
+ draw_foreground_locked();
+ }
+ gr_flip();
+
+ update_waiting = false;
pthread_mutex_unlock(&updateMutex);
+
double end = now();
// minimum of 20ms delay between frames
double delay = interval - (end-start);
@@ -457,19 +550,41 @@ void ScreenRecoveryUI::Init() {
// Are we the large variant of our base layout?
if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
+ gr_set_font("log");
+ gr_font_size(&log_char_width_, &log_char_height_);
+ gr_set_font("menu");
gr_font_size(&char_width_, &char_height_);
- text_rows_ = gr_fb_height() / char_height_;
- text_cols_ = gr_fb_width() / char_width_;
-
- text_ = Alloc2d(text_rows_, text_cols_ + 1);
- file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
- menu_ = Alloc2d(text_rows_, text_cols_ + 1);
text_col_ = text_row_ = 0;
text_top_ = 1;
LoadBitmap("icon_error", &error_icon);
+ LoadBitmap("icon_header", &headerIcon);
+ LoadBitmap("icon_sysbar_back", &sysbarBackIcon);
+ LoadBitmap("icon_sysbar_back_highlight", &sysbarBackHighlightIcon);
+ LoadBitmap("icon_sysbar_home", &sysbarHomeIcon);
+ LoadBitmap("icon_sysbar_home_highlight", &sysbarHomeHighlightIcon);
+
+ header_height_ = gr_get_height(headerIcon);
+ header_width_ = gr_get_width(headerIcon);
+
+ sysbar_height_ = gr_get_height(sysbarBackIcon);
+
+ text_rows_ = (gr_fb_height() - sysbar_height_) / char_height_;
+ text_cols_ = gr_fb_width() / char_width_;
+
+ log_text_rows_ = (gr_fb_height() - sysbar_height_) / log_char_height_;
+ log_text_cols_ = gr_fb_width() / log_char_width_;
+
+ text_ = Alloc2d(log_text_rows_, log_text_cols_ + 1);
+ file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
+ menu_ = Alloc2d(text_rows_, text_cols_ + 1);
+
+ text_first_row_ = (header_height_ / char_height_) + 1;
+ menu_item_start_ = text_first_row_ * char_height_;
+ max_menu_rows_ = (text_rows_ - text_first_row_) / 3;
+
LoadBitmap("progress_empty", &progressBarEmpty);
LoadBitmap("progress_fill", &progressBarFill);
@@ -563,7 +678,8 @@ void ScreenRecoveryUI::SetProgressType(ProgressType type) {
progressScopeStart = 0;
progressScopeSize = 0;
progress = 0;
- update_progress_locked();
+
+ update_screen_locked();
pthread_mutex_unlock(&updateMutex);
}
@@ -575,7 +691,8 @@ void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
progressScopeTime = now();
progressScopeDuration = seconds;
progress = 0;
- update_progress_locked();
+
+ update_screen_locked();
pthread_mutex_unlock(&updateMutex);
}
@@ -589,7 +706,7 @@ void ScreenRecoveryUI::SetProgress(float fraction) {
float scale = width * progressScopeSize;
if ((int) (progress * scale) != (int) (fraction * scale)) {
progress = fraction;
- update_progress_locked();
+ update_screen_locked();
}
}
pthread_mutex_unlock(&updateMutex);
@@ -611,13 +728,13 @@ void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap)
}
pthread_mutex_lock(&updateMutex);
- if (text_rows_ > 0 && text_cols_ > 0) {
+ if (log_text_rows_ > 0 && log_text_cols_ > 0) {
for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
- if (*ptr == '\n' || text_col_ >= text_cols_) {
+ if (*ptr == '\n' || text_col_ >= log_text_cols_) {
text_[text_row_][text_col_] = '\0';
text_col_ = 0;
- text_row_ = (text_row_ + 1) % text_rows_;
- if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
+ text_row_ = (text_row_ + 1) % log_text_rows_;
+ if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % log_text_rows_;
}
if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
}
@@ -658,8 +775,8 @@ void ScreenRecoveryUI::ClearText() {
text_col_ = 0;
text_row_ = 0;
text_top_ = 1;
- for (size_t i = 0; i < text_rows_; ++i) {
- memset(text_[i], 0, text_cols_ + 1);
+ for (size_t i = 0; i < log_text_rows_; ++i) {
+ memset(text_[i], 0, log_text_cols_ + 1);
}
pthread_mutex_unlock(&updateMutex);
}
@@ -739,11 +856,29 @@ void ScreenRecoveryUI::ShowFile(const char* filename) {
text_top_ = old_text_top;
}
+void ScreenRecoveryUI::SetSysbarState(int state)
+{
+ if (HasBackKey()) {
+ state &= ~SYSBAR_BACK;
+ }
+ if (HasHomeKey()) {
+ state &= ~SYSBAR_HOME;
+ }
+ sysbar_state = state;
+ Redraw();
+}
+
void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
int initial_selection) {
pthread_mutex_lock(&updateMutex);
if (text_rows_ > 0 && text_cols_ > 0) {
+ header_items = 0;
menu_headers_ = headers;
+ if (menu_headers_) {
+ while (menu_headers_[header_items]) {
+ ++header_items;
+ }
+ }
size_t i = 0;
for (; i < text_rows_ && items[i] != nullptr; ++i) {
strncpy(menu_[i], items[i], text_cols_ - 1);
@@ -752,22 +887,59 @@ void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const
menu_items = i;
show_menu = true;
menu_sel = initial_selection;
+ if (menu_show_start_ <= menu_sel - max_menu_rows_ ||
+ menu_show_start_ > menu_sel) {
+ menu_show_start_ = menu_sel;
+ }
update_screen_locked();
}
pthread_mutex_unlock(&updateMutex);
}
-int ScreenRecoveryUI::SelectMenu(int sel) {
+int ScreenRecoveryUI::SelectMenu(int sel, bool abs /* = false */) {
+ int wrapped = 0;
pthread_mutex_lock(&updateMutex);
+ if (abs) {
+ sel += menu_show_start_ - header_items;
+ }
if (show_menu) {
int old_sel = menu_sel;
menu_sel = sel;
// Wrap at top and bottom.
- if (menu_sel < 0) menu_sel = menu_items - 1;
- if (menu_sel >= menu_items) menu_sel = 0;
-
+ if (rainbow) {
+ if (menu_sel > old_sel) {
+ move_rainbow(1);
+ } else if (menu_sel < old_sel) {
+ move_rainbow(-1);
+ }
+ }
+ if (menu_sel < 0) {
+ wrapped = -1;
+ menu_sel = menu_items + menu_sel;
+ }
+ if (menu_sel >= menu_items) {
+ wrapped = 1;
+ menu_sel = menu_sel - menu_items;
+ }
+ if (menu_sel < menu_show_start_ && menu_show_start_ > 0) {
+ menu_show_start_ = menu_sel;
+ }
+ if (menu_sel - menu_show_start_ >= max_menu_rows_ - header_items) {
+ menu_show_start_ = menu_sel - (max_menu_rows_ - header_items) + 1;
+ }
sel = menu_sel;
+ if (wrapped != 0) {
+ if (wrap_count / wrapped > 0) {
+ wrap_count += wrapped;
+ } else {
+ wrap_count = wrapped;
+ }
+ if (wrap_count / wrapped >= 5) {
+ wrap_count = 0;
+ OMGRainbows();
+ }
+ }
if (menu_sel != old_sel) update_screen_locked();
}
pthread_mutex_unlock(&updateMutex);
@@ -778,7 +950,6 @@ void ScreenRecoveryUI::EndMenu() {
pthread_mutex_lock(&updateMutex);
if (show_menu && text_rows_ > 0 && text_cols_ > 0) {
show_menu = false;
- update_screen_locked();
}
pthread_mutex_unlock(&updateMutex);
}
diff --git a/screen_ui.h b/screen_ui.h
index 89877577..caf6612b 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -23,11 +23,15 @@
#include "ui.h"
#include "minui/minui.h"
+#define SYSBAR_BACK 0x01
+#define SYSBAR_HOME 0x02
+
// Implementation of RecoveryUI appropriate for devices with a screen
// (shows an icon + a progress bar, text logging, menu, etc.)
class ScreenRecoveryUI : public RecoveryUI {
public:
ScreenRecoveryUI();
+ virtual ~ScreenRecoveryUI() { }
void Init();
void SetLocale(const char* locale);
@@ -53,10 +57,17 @@ class ScreenRecoveryUI : public RecoveryUI {
void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3);
void ShowFile(const char* filename);
+ // sysbar
+ int GetSysbarHeight() { return gr_get_height(sysbarBackHighlightIcon); }
+ int GetSysbarState() { return sysbar_state; }
+ void SetSysbarState(int state);
+
// menu display
+ virtual int MenuItemStart() const { return menu_item_start_; }
+ virtual int MenuItemHeight() const { return 3 * char_height_; }
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection);
- int SelectMenu(int sel);
+ int SelectMenu(int sel, bool abs = false);
void EndMenu();
void KeyLongPress(int);
@@ -90,6 +101,11 @@ class ScreenRecoveryUI : public RecoveryUI {
GRSurface** introFrames;
GRSurface** loopFrames;
+ GRSurface* headerIcon;
+ GRSurface* sysbarBackIcon;
+ GRSurface* sysbarBackHighlightIcon;
+ GRSurface* sysbarHomeIcon;
+ GRSurface* sysbarHomeHighlightIcon;
GRSurface* progressBarEmpty;
GRSurface* progressBarFill;
GRSurface* stageMarkerEmpty;
@@ -103,6 +119,7 @@ class ScreenRecoveryUI : public RecoveryUI {
// true when both graphics pages are the same (except for the progress bar).
bool pagesIdentical;
+ size_t log_text_cols_, log_text_rows_;
size_t text_cols_, text_rows_;
// Log text overlay, displayed when a magic key is pressed.
@@ -114,13 +131,22 @@ class ScreenRecoveryUI : public RecoveryUI {
char** menu_;
const char* const* menu_headers_;
+ int header_items;
bool show_menu;
int menu_items, menu_sel;
+ int menu_show_start_;
+ int max_menu_rows_;
+
+ int sysbar_state;
+
// An alternate text screen, swapped with 'text_' when we're viewing a log file.
char** file_viewer_text_;
+ int menu_item_start_;
+
pthread_t progress_thread_;
+ pthread_cond_t progressCondition;
// Number of intro frames and loop frames in the animation.
int intro_frames;
@@ -131,16 +157,29 @@ class ScreenRecoveryUI : public RecoveryUI {
int stage, max_stage;
- int char_width_;
- int char_height_;
pthread_mutex_t updateMutex;
bool rtl_locale;
void draw_background_locked();
void draw_foreground_locked();
+
+ bool rainbow;
+ int wrap_count;
+
+ int log_char_height_, log_char_width_;
+ int char_height_, char_width_;
+
+ int header_height_, header_width_;
+ int sysbar_height_;
+ int text_first_row_;
+
+ bool update_waiting;
+
+ int draw_header_icon();
+ void draw_menu_item(int textrow, const char *text, int selected);
+ void draw_sysbar();
void draw_screen_locked();
void update_screen_locked();
- void update_progress_locked();
GRSurface* GetCurrentFrame();
GRSurface* GetCurrentText();
@@ -165,6 +204,8 @@ class ScreenRecoveryUI : public RecoveryUI {
void DrawHorizontalRule(int* y);
void DrawTextLine(int x, int* y, const char* line, bool bold);
void DrawTextLines(int x, int* y, const char* const* lines);
+
+ void OMGRainbows();
};
#endif // RECOVERY_UI_H
diff --git a/tests/Android.mk b/tests/Android.mk
index a66991b2..4509c420 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -49,6 +49,7 @@ LOCAL_STATIC_LIBRARIES := \
libmtdutils \
libbase \
libverifier \
+ libcrypto_utils_static \
libcrypto_static \
libminui \
libminzip \
diff --git a/ui.cpp b/ui.cpp
index 2efb759d..89f55ff6 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -30,6 +30,7 @@
#include <cutils/properties.h>
#include <cutils/android_reboot.h>
+#include <cutils/properties.h>
#include "common.h"
#include "roots.h"
@@ -38,19 +39,29 @@
#include "screen_ui.h"
#include "ui.h"
+#include "voldclient.h"
+
#define UI_WAIT_KEY_TIMEOUT_SEC 120
+/* Some extra input defines */
+#ifndef ABS_MT_ANGLE
+#define ABS_MT_ANGLE 0x38
+#endif
+
RecoveryUI::RecoveryUI()
: key_queue_len(0),
key_last_down(-1),
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),
has_up_key(false),
- has_down_key(false) {
+ has_down_key(false),
+ has_back_key(false),
+ has_home_key(false) {
pthread_mutex_init(&key_queue_mutex, nullptr);
pthread_cond_init(&key_queue_cond, nullptr);
memset(key_pressed, 0, sizeof(key_pressed));
@@ -63,6 +74,12 @@ void RecoveryUI::OnKeyDetected(int key_code) {
has_down_key = true;
} else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) {
has_up_key = true;
+ } else if (key_code == KEY_BACK) {
+ has_back_key = true;
+ LOGI("Detected back key, disabling virtual back button\n");
+ } else if (key_code == KEY_HOMEPAGE || key_code == KEY_HOME) {
+ has_home_key = true;
+ LOGI("Detected home key, disabling virtual home button\n");
}
}
@@ -81,6 +98,7 @@ static void* InputThreadLoop(void*) {
}
void RecoveryUI::Init() {
+ calibrate_swipe();
ev_init(InputCallback, this);
ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
@@ -94,31 +112,45 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
return -1;
}
- if (ev.type == EV_SYN) {
- return 0;
- } else if (ev.type == EV_REL) {
- if (ev.code == REL_Y) {
- // accumulate the up or down motion reported by
- // the trackball. When it exceeds a threshold
- // (positive or negative), fake an up/down
- // key event.
- rel_sum += ev.value;
- if (rel_sum > 3) {
- ProcessKey(KEY_DOWN, 1); // press down key
- ProcessKey(KEY_DOWN, 0); // and release it
- rel_sum = 0;
- } else if (rel_sum < -3) {
- ProcessKey(KEY_UP, 1); // press up key
- ProcessKey(KEY_UP, 0); // and release it
- rel_sum = 0;
- }
+ input_device* dev = NULL;
+ int n;
+ for (n = 0; n < MAX_NR_INPUT_DEVICES; ++n) {
+ if (input_devices[n].fd == fd) {
+ dev = &input_devices[n];
+ break;
}
- } else {
- rel_sum = 0;
+ if (input_devices[n].fd == -1) {
+ dev = &input_devices[n];
+ memset(dev, 0, sizeof(input_device));
+ dev->fd = fd;
+ dev->tracking_id = -1;
+ calibrate_touch(dev);
+ setup_vkeys(dev);
+ break;
+ }
+ }
+ if (!dev) {
+ LOGE("input_callback: no more available input devices\n");
+ return -1;
+ }
+
+ if (ev.type != EV_REL) {
+ dev->rel_sum = 0;
}
- if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
- ProcessKey(ev.code, ev.value);
+ switch (ev.type) {
+ case EV_SYN:
+ ProcessSyn(dev, ev.code, ev.value);
+ break;
+ case EV_ABS:
+ ProcessAbs(dev, ev.code, ev.value);
+ break;
+ case EV_REL:
+ ProcessRel(dev, ev.code, ev.value);
+ break;
+ case EV_KEY:
+ ProcessKey(dev, ev.code, ev.value);
+ break;
}
return 0;
@@ -136,11 +168,14 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
// a key is registered.
//
// updown == 1 for key down events; 0 for key up events
-void RecoveryUI::ProcessKey(int key_code, int updown) {
+void RecoveryUI::ProcessKey(input_device* dev, int key_code, int updown) {
bool register_key = false;
bool long_press = false;
bool reboot_enabled;
+ if (key_code > KEY_MAX)
+ return;
+
pthread_mutex_lock(&key_queue_mutex);
key_pressed[key_code] = updown;
if (updown) {
@@ -174,6 +209,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 (true) { pause(); }
@@ -187,6 +225,146 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
}
}
+void RecoveryUI::ProcessSyn(input_device* dev, int code, int value) {
+ /*
+ * Type A device release:
+ * 1. Lack of position update
+ * 2. BTN_TOUCH | ABS_PRESSURE | SYN_MT_REPORT
+ * 3. SYN_REPORT
+ *
+ * Type B device release:
+ * 1. ABS_MT_TRACKING_ID == -1 for "first" slot
+ * 2. SYN_REPORT
+ */
+
+ if (code == SYN_MT_REPORT) {
+ if (!dev->in_touch && (dev->saw_pos_x && dev->saw_pos_y)) {
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type a press\n");
+#endif
+ handle_press(dev);
+ }
+ dev->saw_mt_report = true;
+ return;
+ }
+ if (code == SYN_REPORT) {
+ if (dev->in_touch) {
+ handle_gestures(dev);
+ }
+ else {
+ if (dev->saw_tracking_id) {
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type b press\n");
+#endif
+ handle_press(dev);
+ }
+ }
+
+ /* Detect release */
+ if (dev->saw_mt_report) {
+ if (dev->in_touch && !dev->saw_pos_x && !dev->saw_pos_y) {
+ /* type A release */
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type a release\n");
+#endif
+ handle_release(dev);
+ dev->slot_first = 0;
+ }
+ }
+ else {
+ if (dev->in_touch && dev->saw_tracking_id && dev->tracking_id == -1 &&
+ dev->slot_current == dev->slot_first) {
+ /* type B release */
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type b release\n");
+#endif
+ handle_release(dev);
+ dev->slot_first = 0;
+ }
+ }
+
+ dev->saw_pos_x = dev->saw_pos_y = false;
+ dev->saw_mt_report = dev->saw_tracking_id = false;
+ }
+}
+
+void RecoveryUI::ProcessAbs(input_device* dev, int code, int value) {
+ if (code == ABS_MT_SLOT) {
+ dev->slot_current = value;
+ if (dev->slot_first == -1) {
+ dev->slot_first = value;
+ }
+ return;
+ }
+ if (code == ABS_MT_TRACKING_ID) {
+ /*
+ * Some devices send an initial ABS_MT_SLOT event before switching
+ * to type B events, so discard any type A state related to slot.
+ */
+ dev->saw_tracking_id = true;
+ dev->slot_first = dev->slot_current = 0;
+
+ if (value != dev->tracking_id) {
+ dev->tracking_id = value;
+ if (dev->tracking_id < 0) {
+ dev->slot_nr_active--;
+ }
+ else {
+ dev->slot_nr_active++;
+ }
+ }
+ return;
+ }
+ /*
+ * For type A devices, we "lock" onto the first coordinates by ignoring
+ * position updates from the time we see a SYN_MT_REPORT until the next
+ * SYN_REPORT
+ *
+ * For type B devices, we "lock" onto the first slot seen until all slots
+ * are released
+ */
+ if (dev->slot_nr_active == 0) {
+ /* type A */
+ if (dev->saw_pos_x && dev->saw_pos_y) {
+ return;
+ }
+ }
+ else {
+ if (dev->slot_current != dev->slot_first) {
+ return;
+ }
+ }
+ if (code == ABS_MT_POSITION_X) {
+ dev->saw_pos_x = true;
+ dev->touch_pos.x = value * fb_dimensions.x / (dev->touch_max.x - dev->touch_min.x);
+ }
+ else if (code == ABS_MT_POSITION_Y) {
+ dev->saw_pos_y = true;
+ dev->touch_pos.y = value * fb_dimensions.y / (dev->touch_max.y - dev->touch_min.y);
+ }
+}
+
+void RecoveryUI::ProcessRel(input_device* dev, int code, int value) {
+#ifdef BOARD_RECOVERY_NEEDS_REL_INPUT
+ if (code == REL_Y) {
+ // accumulate the up or down motion reported by
+ // the trackball. When it exceeds a threshold
+ // (positive or negative), fake an up/down
+ // key event.
+ dev->rel_sum += value;
+ if (dev->rel_sum > 3) {
+ ProcessKey(dev, KEY_DOWN, 1); // press down key
+ ProcessKey(dev, KEY_DOWN, 0); // and release it
+ dev->rel_sum = 0;
+ } else if (dev->rel_sum < -3) {
+ ProcessKey(dev, KEY_UP, 1); // press up key
+ ProcessKey(dev, KEY_UP, 0); // and release it
+ dev->rel_sum = 0;
+ }
+ }
+#endif
+}
+
void* RecoveryUI::time_key_helper(void* cookie) {
key_timer_t* info = (key_timer_t*) cookie;
info->ui->time_key(info->key_code, info->count);
@@ -205,6 +383,196 @@ void RecoveryUI::time_key(int key_code, int count) {
if (long_press) KeyLongPress(key_code);
}
+void RecoveryUI::calibrate_touch(input_device* dev) {
+ fb_dimensions.x = gr_fb_width();
+ fb_dimensions.y = gr_fb_height();
+
+ struct input_absinfo info;
+ memset(&info, 0, sizeof(info));
+ if (ioctl(dev->fd, EVIOCGABS(ABS_MT_POSITION_X), &info) == 0) {
+ dev->touch_min.x = info.minimum;
+ dev->touch_max.x = info.maximum;
+ dev->touch_pos.x = info.value;
+ }
+ memset(&info, 0, sizeof(info));
+ if (ioctl(dev->fd, EVIOCGABS(ABS_MT_POSITION_Y), &info) == 0) {
+ dev->touch_min.y = info.minimum;
+ dev->touch_max.y = info.maximum;
+ dev->touch_pos.y = info.value;
+ }
+#ifdef DEBUG_TOUCH
+ LOGI("calibrate_touch: fd=%d, (%d,%d)-(%d,%d) pos (%d,%d)\n", dev->fd,
+ dev->touch_min.x, dev->touch_min.y,
+ dev->touch_max.x, dev->touch_max.y,
+ dev->touch_pos.x, dev->touch_pos.y);
+#endif
+}
+
+void RecoveryUI::setup_vkeys(input_device* dev) {
+ int n;
+ char name[256];
+ char path[PATH_MAX];
+ char buf[64*MAX_NR_VKEYS];
+
+ for (n = 0; n < MAX_NR_VKEYS; ++n) {
+ dev->virtual_keys[n].keycode = -1;
+ }
+
+ memset(name, 0, sizeof(name));
+ if (ioctl(dev->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
+ LOGI("setup_vkeys: no vkeys\n");
+ return;
+ }
+ sprintf(path, "/sys/board_properties/virtualkeys.%s", name);
+ int vkfd = open(path, O_RDONLY);
+ if (vkfd < 0) {
+ LOGI("setup_vkeys: could not open %s\n", path);
+ return;
+ }
+ ssize_t len = read(vkfd, buf, sizeof(buf));
+ close(vkfd);
+ if (len <= 0) {
+ LOGE("setup_vkeys: could not read %s\n", path);
+ return;
+ }
+ buf[len] = '\0';
+
+ char* p = buf;
+ char* endp;
+ for (n = 0; n < MAX_NR_VKEYS && p < buf+len && *p == '0'; ++n) {
+ int val[6];
+ int f;
+ for (f = 0; *p && f < 6; ++f) {
+ val[f] = strtol(p, &endp, 0);
+ if (p == endp)
+ break;
+ p = endp+1;
+ }
+ if (f != 6 || val[0] != 0x01)
+ break;
+ dev->virtual_keys[n].keycode = val[1];
+ dev->virtual_keys[n].min.x = val[2] - val[4]/2;
+ dev->virtual_keys[n].min.y = val[3] - val[5]/2;
+ dev->virtual_keys[n].max.x = val[2] + val[4]/2;
+ dev->virtual_keys[n].max.y = val[3] + val[5]/2;
+
+#ifdef DEBUG_TOUCH
+ LOGI("vkey: fd=%d, [%d]=(%d,%d)-(%d,%d)\n", dev->fd,
+ dev->virtual_keys[n].keycode,
+ dev->virtual_keys[n].min.x, dev->virtual_keys[n].min.y,
+ dev->virtual_keys[n].max.x, dev->virtual_keys[n].max.y);
+#endif
+ }
+}
+
+void RecoveryUI::calibrate_swipe() {
+ char strvalue[PROPERTY_VALUE_MAX];
+ int intvalue;
+ property_get("ro.sf.lcd_density", strvalue, "160");
+ intvalue = atoi(strvalue);
+ int screen_density = (intvalue >= 160 ? intvalue : 160);
+ min_swipe_px.x = screen_density * 50 / 100; // Roughly 0.5in
+ min_swipe_px.y = screen_density * 30 / 100; // Roughly 0.3in
+#ifdef DEBUG_TOUCH
+ LOGI("calibrate_swipe: density=%d, min_swipe=(%d,%d)\n",
+ screen_density, min_swipe_px.x, min_swipe_px.y);
+#endif
+}
+
+void RecoveryUI::handle_press(input_device* dev) {
+ dev->touch_start = dev->touch_track = dev->touch_pos;
+ dev->in_touch = true;
+ dev->in_swipe = false;
+ if (dev->touch_pos.y >= gr_fb_height() - GetSysbarHeight()) {
+ SetSysbarState(1 << (3 * dev->touch_pos.x / gr_fb_width()));
+ }
+ else {
+ SetSysbarState(0);
+ }
+}
+
+void RecoveryUI::handle_release(input_device* dev) {
+ struct point diff = dev->touch_pos - dev->touch_start;
+ bool in_touch = dev->in_touch;
+ bool in_swipe = dev->in_swipe;
+
+ dev->in_touch = dev->in_swipe = false;
+
+ if (!in_swipe) {
+ int n;
+ for (n = 0; dev->virtual_keys[n].keycode != -1 && n < MAX_NR_VKEYS; ++n) {
+ vkey* vk = &dev->virtual_keys[n];
+ if (dev->touch_start.x >= vk->min.x && dev->touch_start.x < vk->max.x &&
+ dev->touch_start.y >= vk->min.y && dev->touch_start.y < vk->max.y) {
+#ifdef DEBUG_TOUCH
+ LOGI("handle_release: vkey %d\n", vk->keycode);
+#endif
+ EnqueueKey(vk->keycode);
+ return;
+ }
+ }
+
+ int sysbar_state = GetSysbarState();
+ SetSysbarState(0);
+ if (sysbar_state == SYSBAR_BACK) {
+ ProcessKey(dev, KEY_BACK, 1);
+ ProcessKey(dev, KEY_BACK, 0);
+ return;
+ }
+ if (sysbar_state == SYSBAR_HOME) {
+ ProcessKey(dev, KEY_HOME, 1);
+ ProcessKey(dev, KEY_HOME, 0);
+ return;
+ }
+ }
+
+ if (in_swipe) {
+ if (abs(diff.x) > abs(diff.y)) {
+ if (abs(diff.x) > min_swipe_px.x) {
+ int key = (diff.x > 0 ? KEY_ENTER : KEY_BACK);
+ ProcessKey(dev, key, 1);
+ ProcessKey(dev, key, 0);
+ }
+ }
+ else {
+ /* Vertical swipe, handled realtime */
+ }
+ }
+ else {
+ int sel, start_menu_pos;
+ // Make sure touch pos is not less than menu start pos.
+ // No need to check if beyond end of menu items, since
+ // that is checked by get_menu_selection().
+ start_menu_pos = MenuItemStart();
+ if (dev->touch_pos.y >= start_menu_pos) {
+ sel = (dev->touch_pos.y - start_menu_pos)/MenuItemHeight();
+ EnqueueKey(KEY_FLAG_ABS | sel);
+ }
+ }
+}
+
+void RecoveryUI::handle_gestures(input_device* dev) {
+ struct point diff;
+ diff = dev->touch_pos - dev->touch_start;
+
+ if (abs(diff.x) > abs(diff.y)) {
+ if (abs(diff.x) > min_swipe_px.x) {
+ /* Horizontal swipe, handle it on release */
+ dev->in_swipe = true;
+ }
+ }
+ else {
+ diff.y = dev->touch_pos.y - dev->touch_track.y;
+ if (abs(diff.y) > MenuItemHeight()) {
+ dev->in_swipe = true;
+ dev->touch_track = dev->touch_pos;
+ int key = (diff.y < 0) ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
+ ProcessKey(dev, key, 1);
+ ProcessKey(dev, key, 0);
+ }
+ }
+}
+
void RecoveryUI::EnqueueKey(int key_code) {
pthread_mutex_lock(&key_queue_mutex);
const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
@@ -217,6 +585,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 +595,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) {
@@ -243,6 +617,15 @@ int RecoveryUI::WaitKey() {
return key;
}
+void RecoveryUI::CancelWaitKey()
+{
+ pthread_mutex_lock(&key_queue_mutex);
+ key_queue[key_queue_len] = -2;
+ key_queue_len++;
+ pthread_cond_signal(&key_queue_cond);
+ pthread_mutex_unlock(&key_queue_mutex);
+}
+
bool RecoveryUI::IsUsbConnected() {
int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
if (fd < 0) {
@@ -337,3 +720,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 82d95a34..6e4681ed 100644
--- a/ui.h
+++ b/ui.h
@@ -21,6 +21,74 @@
#include <pthread.h>
#include <time.h>
+#include "voldclient.h"
+
+#define MAX_NR_INPUT_DEVICES 8
+#define MAX_NR_VKEYS 8
+
+/*
+ * Simple representation of a (x,y) coordinate with convenience operators
+ */
+struct point {
+ point() : x(0), y(0) {}
+ point operator+(const point& rhs) const {
+ point tmp;
+ tmp.x = x + rhs.x;
+ tmp.y = y + rhs.y;
+ return tmp;
+ }
+ point operator-(const point& rhs) const {
+ point tmp;
+ tmp.x = x - rhs.x;
+ tmp.y = y - rhs.y;
+ return tmp;
+ }
+
+ int x;
+ int y;
+};
+
+/*
+ * Virtual key representation. Valid when keycode != -1.
+ */
+struct vkey {
+ vkey() : keycode(-1) {}
+ int keycode;
+ point min;
+ point max;
+};
+
+/*
+ * Input device representation. Valid when fd != -1.
+ * This holds all information and state related to a given input device.
+ */
+struct input_device {
+ input_device() : fd(-1) {}
+
+ int fd;
+ vkey virtual_keys[MAX_NR_VKEYS];
+ point touch_min;
+ point touch_max;
+
+ int rel_sum; // Accumulated relative movement
+
+ bool saw_pos_x; // Did sequence have ABS_MT_POSITION_X?
+ bool saw_pos_y; // Did sequence have ABS_MT_POSITION_Y?
+ bool saw_mt_report; // Did sequence have SYN_MT_REPORT?
+ bool saw_tracking_id; // Did sequence have SYN_TRACKING_ID?
+ bool in_touch; // Are we in a touch event?
+ bool in_swipe; // Are we in a swipe event?
+
+ point touch_pos; // Current touch coordinates
+ point touch_start; // Coordinates of touch start
+ point touch_track; // Last tracked coordinates
+
+ int slot_nr_active;
+ int slot_first;
+ int slot_current;
+ int tracking_id;
+};
+
// Abstract class for controlling the user interface during recovery.
class RecoveryUI {
public:
@@ -69,12 +137,16 @@ class RecoveryUI {
virtual void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3) = 0;
virtual void ShowFile(const char* filename) = 0;
+ virtual void ClearText() = 0;
// --- key handling ---
// Wait for a key and return it. May return -1 after timeout.
virtual int WaitKey();
+ // Cancel a WaitKey()
+ virtual void CancelWaitKey();
+
virtual bool IsKeyPressed(int key);
virtual bool IsLongPress();
@@ -82,6 +154,9 @@ class RecoveryUI {
// of phones and tablets, false otherwise.
virtual bool HasThreeButtons();
+ virtual bool HasBackKey() const { return has_back_key; }
+ virtual bool HasHomeKey() const { return has_home_key; }
+
// Erase any queued-up keys.
virtual void FlushKeys();
@@ -108,6 +183,13 @@ class RecoveryUI {
// --- menu display ---
+ virtual int MenuItemStart() const = 0;
+ virtual int MenuItemHeight() const = 0;
+
+ virtual int GetSysbarHeight() = 0;
+ virtual int GetSysbarState() = 0;
+ virtual void SetSysbarState(int state) = 0;
+
// Display some header text followed by a menu of items, which appears
// at the top of the screen (in place of any scrolling ui_print()
// output, if necessary).
@@ -116,12 +198,15 @@ class RecoveryUI {
// Set the menu highlight to the given index, wrapping if necessary.
// Returns the actual item selected.
- virtual int SelectMenu(int sel) = 0;
+ virtual int SelectMenu(int sel, bool abs = false) = 0;
// End menu mode, resetting the text overlay so that ui_print()
// statements will be displayed.
virtual void EndMenu() = 0;
+ // Notify of volume state change
+ void onVolumeChanged() { v_changed = 1; }
+
protected:
void EnqueueKey(int key_code);
@@ -136,6 +221,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;
@@ -143,6 +229,13 @@ private:
bool has_power_key;
bool has_up_key;
bool has_down_key;
+ bool has_back_key;
+ bool has_home_key;
+
+ input_device input_devices[MAX_NR_INPUT_DEVICES];
+
+ point fb_dimensions;
+ point min_swipe_px;
struct key_timer_t {
RecoveryUI* ui;
@@ -156,12 +249,25 @@ private:
static int InputCallback(int fd, uint32_t epevents, void* data);
int OnInputEvent(int fd, uint32_t epevents);
- void ProcessKey(int key_code, int updown);
+ void ProcessKey(input_device* dev, int key_code, int updown);
+ void ProcessSyn(input_device* dev, int code, int value);
+ void ProcessAbs(input_device* dev, int code, int value);
+ void ProcessRel(input_device* dev, int code, int value);
bool IsUsbConnected();
+ bool VolumesChanged();
+
static void* time_key_helper(void* cookie);
void time_key(int key_code, int count);
+
+ void process_touch(int fd, struct input_event *ev);
+ void calibrate_touch(input_device* dev);
+ void setup_vkeys(input_device* dev);
+ void calibrate_swipe();
+ void handle_press(input_device* dev);
+ void handle_release(input_device* dev);
+ void handle_gestures(input_device* dev);
};
#endif // RECOVERY_UI_H
diff --git a/updater/Android.mk b/updater/Android.mk
index d7aa613e..e3bd34dc 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -33,15 +33,24 @@ LOCAL_CLANG := true
LOCAL_SRC_FILES := $(updater_src_files)
-LOCAL_STATIC_LIBRARIES += libfec libfec_rs libext4_utils_static libsquashfs_utils libcrypto_static
+LOCAL_STATIC_LIBRARIES += libfec \
+ libfec_rs \
+ libext4_utils_static \
+ libsquashfs_utils \
+ libcrypto_utils_static \
+ libcrypto_static
-ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
-LOCAL_CFLAGS += -DUSE_EXT4
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_STATIC_LIBRARIES += \
libsparse_static \
libz
+
+LOCAL_C_INCLUDES += external/e2fsprogs/lib
+LOCAL_STATIC_LIBRARIES += libext2_blkid libext2_uuid
+
+ifeq ($(BOARD_SUPPRESS_EMMC_WIPE),true)
+ LOCAL_CFLAGS += -DSUPPRESS_EMMC_WIPE
endif
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
@@ -72,7 +81,11 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
# any subsidiary static libraries required for your registered
# extension libs.
+ifeq ($(TARGET_ARCH),arm64)
+inc := $(call intermediates-dir-for,PACKAGING,updater_extensions,,,32)/register.inc
+else
inc := $(call intermediates-dir-for,PACKAGING,updater_extensions)/register.inc
+endif
# Encode the value of TARGET_RECOVERY_UPDATER_LIBS into the filename of the dependency.
# So if TARGET_RECOVERY_UPDATER_LIBS is changed, a new dependency file will be generated.
@@ -94,13 +107,18 @@ $(inc) : $(inc_dep_file)
$(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@;)
$(hide) echo "}" >> $@
-$(call intermediates-dir-for,EXECUTABLES,updater,,,$(TARGET_PREFER_32_BIT))/updater.o : $(inc)
+ifeq ($(TARGET_ARCH),arm64)
+$(call intermediates-dir-for,EXECUTABLES,updater,,,32)/updater.o : $(inc)
+else
+$(call intermediates-dir-for,EXECUTABLES,updater)/updater.o : $(inc)
+endif
LOCAL_C_INCLUDES += $(dir $(inc))
inc :=
inc_dep_file :=
LOCAL_MODULE := updater
+LOCAL_32_BIT_ONLY := true
LOCAL_FORCE_STATIC_EXECUTABLE := true
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index a80180a9..7b7e3bbf 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -1334,10 +1334,12 @@ static int PerformCommandErase(CommandParameters& params) {
// length in bytes
blocks[1] = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * (uint64_t) BLOCKSIZE;
+#ifndef SUPPRESS_EMMC_WIPE
if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) {
fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
return -1;
}
+#endif
}
}
diff --git a/updater/install.cpp b/updater/install.cpp
index 005f9f97..e4ad9b2b 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -33,6 +33,7 @@
#include <sys/xattr.h>
#include <linux/xattr.h>
#include <inttypes.h>
+#include <blkid/blkid.h>
#include <memory>
#include <vector>
@@ -56,11 +57,8 @@
#include "updater.h"
#include "install.h"
#include "tune2fs.h"
-
-#ifdef USE_EXT4
#include "make_ext4fs.h"
#include "wipe.h"
-#endif
// Send over the buffer to recovery though the command pipe.
static void uiPrint(State* state, const std::string& buffer) {
@@ -189,6 +187,16 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
}
result = mount_point;
} else {
+ char *detected_fs_type = blkid_get_tag_value(NULL, "TYPE", location);
+ if (detected_fs_type) {
+ uiPrintf(state, "detected filesystem %s for %s\n",
+ detected_fs_type, location);
+ fs_type = detected_fs_type;
+ } else {
+ uiPrintf(state, "could not detect filesystem for %s, assuming %s\n",
+ location, fs_type);
+ }
+
if (mount(location, mount_point, fs_type,
MS_NOATIME | MS_NODEV | MS_NODIRATIME,
has_mount_options ? mount_options : "") < 0) {
@@ -361,7 +369,6 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
goto done;
}
result = location;
-#ifdef USE_EXT4
} else if (strcmp(fs_type, "ext4") == 0) {
int status = make_ext4fs(location, atoll(fs_size), mount_point, sehandle);
if (status != 0) {
@@ -389,7 +396,6 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
goto done;
}
result = location;
-#endif
} else {
printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"",
name, fs_type, partition_type);
@@ -966,6 +972,74 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(strdup(value));
}
+//Check to confirm if this is the same hardware as the one the package was
+//generated on or not. 32 vs 64 bit variants are upgrade compatible but have
+//names such as msmWXYZ msmWXYZ_32 vs msmWXYZ_64.Input to this
+//function is the BuildProp value that gets stored in the update package
+//at the time it it created.
+Value* ConfirmDevVariant(const char* name, State* state, int argc, Expr* argv[])
+{
+ //ro.product.device that was on the build that the update package was made
+ //from
+ char* package_dev_variant;
+ //ro.product.device on the current hardware
+ char current_dev_variant[PROPERTY_VALUE_MAX];
+ int comparison_len;
+ int package_dev_variant_len;
+ int current_dev_variant_len;
+ if (argc != 1) {
+ return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
+ }
+ package_dev_variant = Evaluate(state, argv[0]);
+ if (!package_dev_variant) goto error;
+ property_get("ro.product.device", current_dev_variant, "n/a");
+ if (!strncmp(current_dev_variant,"n/a",3)) {
+ ErrorAbort(state, "Failed to get valid ro.product.device");
+ goto error;
+ }
+ package_dev_variant_len = strlen(package_dev_variant);
+ current_dev_variant_len = strlen(current_dev_variant);
+ //Ensure device variant lengths are atleast 3 characters long
+ if ((package_dev_variant_len < 3) || (current_dev_variant_len < 3)) {
+ ErrorAbort(state, "Device Variant length is less than 3 characters");
+ goto error;
+ }
+ //Length of the largest string - 3(for _32/64)
+ comparison_len =
+ (package_dev_variant_len >= current_dev_variant_len ?
+ package_dev_variant_len :
+ current_dev_variant_len) - 3;
+ //Complete match
+ if (!strncmp(current_dev_variant, package_dev_variant,
+ strlen(current_dev_variant)))
+ goto success;
+ //Match except for the last 3 char's of either string which are _32 or _64
+ if (!strncmp(current_dev_variant, package_dev_variant, comparison_len)) {
+ if (package_dev_variant_len >= current_dev_variant_len) {
+ if (!strncmp(&package_dev_variant[package_dev_variant_len-3],
+ "_32", 3) ||
+ !strncmp(&package_dev_variant[package_dev_variant_len-3],
+ "_64", 3))
+ goto success;
+ } else {
+ if (!strncmp(&current_dev_variant[current_dev_variant_len-3],
+ "_32", 3) ||
+ !strncmp(&current_dev_variant[current_dev_variant_len-3],
+ "_64", 3))
+ goto success;
+ }
+ ErrorAbort(state, "Invalid target for update package");
+ goto error;
+ }
+success:
+ free(package_dev_variant);
+ return StringValue(strdup("OK"));
+error:
+ if (package_dev_variant) {
+ free(package_dev_variant);
+ }
+ return StringValue(strdup("ERROR"));
+}
// file_getprop(file, key)
//
@@ -1433,10 +1507,11 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
// current package (because nothing has cleared the copy of the
// arguments stored in the BCB).
//
-// The argument is the partition name passed to the android reboot
-// property. It can be "recovery" to boot from the recovery
-// partition, or "" (empty string) to boot from the regular boot
-// partition.
+// The first argument is the block device for the misc partition
+// ("/misc" in the fstab). The second argument is the argument
+// passed to the android reboot property. It can be "recovery" to
+// boot from the recovery partition, or "" (empty string) to boot
+// from the regular boot partition.
Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 2) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
@@ -1452,6 +1527,7 @@ Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) {
memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command));
FILE* f = fopen(filename, "r+b");
fseek(f, offsetof(struct bootloader_message, command), SEEK_SET);
+ fseek(f, BOOTLOADER_MESSAGE_OFFSET_IN_MISC, SEEK_CUR);
ota_fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f);
fclose(f);
free(filename);
@@ -1464,6 +1540,11 @@ Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) {
property_set(ANDROID_RB_PROPERTY, buffer);
sleep(5);
+ // Attempt to reboot using older methods in case the recovery
+ // that we are updating does not support init reboots
+ android_reboot(ANDROID_RB_RESTART, 0, 0);
+
+ sleep(5);
free(property);
ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name);
return NULL;
@@ -1494,6 +1575,7 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
// package installation.
FILE* f = fopen(filename, "r+b");
fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
+ fseek(f, BOOTLOADER_MESSAGE_OFFSET_IN_MISC, SEEK_CUR);
int to_write = strlen(stagestr)+1;
int max_size = sizeof(((struct bootloader_message*)0)->stage);
if (to_write > max_size) {
@@ -1520,6 +1602,7 @@ Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
char buffer[sizeof(((struct bootloader_message*)0)->stage)];
FILE* f = fopen(filename, "rb");
fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
+ fseek(f, BOOTLOADER_MESSAGE_OFFSET_IN_MISC, SEEK_CUR);
ota_fread(buffer, sizeof(buffer), 1, f);
fclose(f);
buffer[sizeof(buffer)-1] = '\0';
@@ -1640,4 +1723,5 @@ void RegisterInstallFunctions() {
RegisterFunction("enable_reboot", EnableRebootFn);
RegisterFunction("tune2fs", Tune2FsFn);
+ RegisterFunction("get_device_compatible", ConfirmDevVariant);
}
diff --git a/voldclient.cpp b/voldclient.cpp
new file mode 100644
index 00000000..0c8462a5
--- /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 00000000..c5f568e9
--- /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
+
diff --git a/wear_ui.cpp b/wear_ui.cpp
index b437fd0a..158276f5 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -430,7 +430,7 @@ void WearRecoveryUI::StartMenu(const char* const * headers, const char* const *
pthread_mutex_unlock(&updateMutex);
}
-int WearRecoveryUI::SelectMenu(int sel) {
+int WearRecoveryUI::SelectMenu(int sel, bool abs /* = false */) {
int old_sel;
pthread_mutex_lock(&updateMutex);
if (show_menu > 0) {
diff --git a/wear_ui.h b/wear_ui.h
index e2d6fe07..3cb0643e 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -22,6 +22,7 @@
class WearRecoveryUI : public ScreenRecoveryUI {
public:
WearRecoveryUI();
+ virtual ~WearRecoveryUI() { }
void Init();
// overall recovery state ("background image")
@@ -48,7 +49,7 @@ class WearRecoveryUI : public ScreenRecoveryUI {
// menu display
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection);
- int SelectMenu(int sel);
+ int SelectMenu(int sel, bool abs = false);
void EndMenu();
void Redraw();