summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk242
-rw-r--r--adb_install.cpp130
-rw-r--r--adb_install.h4
-rw-r--r--applypatch/Android.mk9
-rw-r--r--applypatch/imgdiff.c62
-rw-r--r--backup.cpp308
-rw-r--r--bootloader.cpp16
-rw-r--r--bu.cpp394
-rw-r--r--bu.h54
-rw-r--r--device.cpp153
-rw-r--r--device.h47
-rw-r--r--edify/Android.mk2
-rw-r--r--etc/init.rc57
-rw-r--r--etc/mkshrc70
-rw-r--r--fstools/Android.mk69
-rw-r--r--fstools/fstools.cpp23
-rw-r--r--fstools/fstools.h78
-rw-r--r--fuse_sdcard_provider.cpp (renamed from fuse_sdcard_provider.c)63
-rw-r--r--fuse_sdcard_provider.h6
-rw-r--r--fuse_sideload.cpp (renamed from fuse_sideload.c)160
-rw-r--r--fuse_sideload.h8
-rw-r--r--install.cpp141
-rw-r--r--install.h2
-rw-r--r--messagesocket.cpp116
-rw-r--r--messagesocket.h40
-rw-r--r--minadbd/Android.mk1
-rw-r--r--minadbd/services.cpp6
-rw-r--r--minui/Android.mk8
-rw-r--r--minui/events.cpp21
-rw-r--r--minui/graphics.cpp75
-rw-r--r--minui/graphics_fbdev.cpp6
-rw-r--r--minui/minui.h5
-rw-r--r--minzip/Android.mk8
-rw-r--r--minzip/Hash.c4
-rw-r--r--minzip/SysUtil.c108
-rw-r--r--minzip/Zip.c106
-rw-r--r--minzip/Zip.h12
-rw-r--r--mtdutils/Android.mk2
-rw-r--r--mtdutils/mounts.c15
-rw-r--r--mtdutils/mounts.h1
-rw-r--r--mtdutils/mtdutils.c16
-rw-r--r--recovery.cpp694
-rw-r--r--recovery_cmds.h74
l---------res-560dpi1
-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 -> 3502 bytes
-rw-r--r--res-hdpi/images/icon_headless.pngbin0 -> 1155 bytes
-rw-r--r--res-hdpi/images/icon_info.pngbin0 -> 3745 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 -> 2279 bytes
-rw-r--r--res-mdpi/images/icon_headless.pngbin0 -> 734 bytes
-rw-r--r--res-mdpi/images/icon_info.pngbin0 -> 3745 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 -> 5519 bytes
-rw-r--r--res-xhdpi/images/icon_headless.pngbin0 -> 1536 bytes
-rw-r--r--res-xhdpi/images/icon_info.pngbin0 -> 3745 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 -> 8674 bytes
-rw-r--r--res-xxhdpi/images/icon_headless.pngbin0 -> 2395 bytes
-rw-r--r--res-xxhdpi/images/icon_info.pngbin0 -> 3745 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 -> 12172 bytes
-rw-r--r--res-xxxhdpi/images/icon_headless.pngbin0 -> 3300 bytes
-rw-r--r--res-xxxhdpi/images/icon_info.pngbin0 -> 3745 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.cpp313
-rw-r--r--roots.cpp356
-rw-r--r--roots.h30
-rw-r--r--screen_ui.cpp553
-rw-r--r--screen_ui.h67
-rw-r--r--tests/Android.mk1
-rw-r--r--ui.cpp552
-rw-r--r--ui.h127
-rw-r--r--uncrypt/Android.mk2
-rw-r--r--uncrypt/uncrypt.cpp2
-rw-r--r--updater/Android.mk29
-rw-r--r--updater/blockimg.c101
-rw-r--r--updater/install.c115
-rw-r--r--verifier_test.cpp24
-rw-r--r--voldclient.cpp466
-rw-r--r--voldclient.h100
-rw-r--r--wear_ui.cpp10
-rw-r--r--wear_ui.h4
107 files changed, 5603 insertions, 666 deletions
diff --git a/Android.mk b/Android.mk
index b31f730..2aeefde 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,13 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH := $(call my-dir)
+ifeq ($(call my-dir),$(call project-path-for,recovery))
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := fuse_sideload.c
-
+LOCAL_SRC_FILES := fuse_sideload.cpp
+LOCAL_CLANG := true
LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
@@ -34,71 +36,269 @@ LOCAL_SRC_FILES := \
asn1_decoder.cpp \
bootloader.cpp \
device.cpp \
- fuse_sdcard_provider.c \
+ fuse_sdcard_provider.cpp \
install.cpp \
recovery.cpp \
roots.cpp \
screen_ui.cpp \
+ messagesocket.cpp \
ui.cpp \
verifier.cpp \
wear_ui.cpp \
+ voldclient.cpp
+
+# External tools
+LOCAL_SRC_FILES += \
+ ../../system/core/toolbox/newfs_msdos.c \
+ ../../system/core/toolbox/start.c \
+ ../../system/core/toolbox/stop.c \
+ ../../system/vold/vdc.c
LOCAL_MODULE := recovery
LOCAL_FORCE_STATIC_EXECUTABLE := true
-ifeq ($(HOST_OS),linux)
-LOCAL_REQUIRED_MODULES := mkfs.f2fs
-endif
-
RECOVERY_API_VERSION := 3
RECOVERY_FSTAB_VERSION := 2
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CLANG := true
LOCAL_C_INCLUDES += \
system/vold \
system/extras/ext4_utils \
system/core/adb \
+ external/e2fsprogs/lib
LOCAL_STATIC_LIBRARIES := \
+ 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 \
libmincrypt \
libminadbd \
+ libtoybox_driver \
+ libmksh_static \
libfusesideload \
libminui \
libpng \
+ libf2fs_sparseblock \
+ libdiskconfig \
+ libsysutils \
libfs_mgr \
+ libsquashfs_utils \
libbase \
libcutils \
+ libutils \
liblog \
+ liblogwrap \
libselinux \
- libstdc++ \
+ libcrypto_static \
+ libscrypt_static \
+ libnl \
+ libc++_static \
libm \
- libc
+ libc \
+ libext2_blkid \
+ libext2_uuid
-ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
- LOCAL_CFLAGS += -DUSE_EXT4
- LOCAL_C_INCLUDES += system/extras/ext4_utils
- LOCAL_STATIC_LIBRARIES += libext4_utils_static libz
+# 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
+
+ifneq ($(BOARD_RECOVERY_BLDRMSG_OFFSET),)
+ LOCAL_CFLAGS += -DBOARD_RECOVERY_BLDRMSG_OFFSET=$(BOARD_RECOVERY_BLDRMSG_OFFSET)
+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
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
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: TOY_LIST=$(shell $(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} $(TOY_LIST)
+ @mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/sbin
+ $(hide) $(foreach t,$(TOY_LIST),ln -sf toybox $(TARGET_RECOVERY_ROOT_OUT)/sbin/$(t);)
+
+# 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 \
+ messagesocket.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_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/extras/ext4_utils/canned_fs_config.c
+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
+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
+LOCAL_C_INCLUDES := external/zlib
+LOCAL_SRC_FILES := \
+ ../../external/zlib/src/contrib/minizip/ioapi.c \
+ ../../external/zlib/src/contrib/minizip/miniunz.c \
+ ../../external/zlib/src/contrib/minizip/unzip.c
+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)
+
+
# All the APIs for testing
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_MODULE := libverifier
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
@@ -106,22 +306,27 @@ LOCAL_SRC_FILES := \
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_MODULE := verifier_test
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := tests
LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -DVERIFIER_TEST
LOCAL_SRC_FILES := \
verifier_test.cpp \
asn1_decoder.cpp \
verifier.cpp \
- ui.cpp
+ ui.cpp \
+ messagesocket.cpp
LOCAL_STATIC_LIBRARIES := \
libmincrypt \
libminui \
libminzip \
libcutils \
- libstdc++ \
libc
+LOCAL_C_INCLUDES += \
+ system/core/fs_mgr/include \
+ system/vold
include $(BUILD_EXECUTABLE)
@@ -134,4 +339,7 @@ include $(LOCAL_PATH)/minui/Android.mk \
$(LOCAL_PATH)/edify/Android.mk \
$(LOCAL_PATH)/uncrypt/Android.mk \
$(LOCAL_PATH)/updater/Android.mk \
- $(LOCAL_PATH)/applypatch/Android.mk
+ $(LOCAL_PATH)/applypatch/Android.mk \
+ $(LOCAL_PATH)/fstools/Android.mk
+
+endif
diff --git a/adb_install.cpp b/adb_install.cpp
index e3b94ea..23bfb51 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;
- 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);
- 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);
+
+ 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 efad436..7c9d7bc 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/applypatch/Android.mk b/applypatch/Android.mk
index 4984093..eb3e458 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -13,8 +13,10 @@
# limitations under the License.
LOCAL_PATH := $(call my-dir)
+
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_SRC_FILES := applypatch.c bspatch.c freecache.c imgpatch.c utils.c
LOCAL_MODULE := libapplypatch
LOCAL_MODULE_TAGS := eng
@@ -25,28 +27,31 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
-LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
+LOCAL_SHARED_LIBRARIES += libz libcutils libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch_static
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
-LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
+LOCAL_STATIC_LIBRARIES += libz libcutils libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_SRC_FILES := imgdiff.c utils.c bsdiff.c
LOCAL_MODULE := imgdiff
LOCAL_FORCE_STATIC_EXECUTABLE := true
diff --git a/applypatch/imgdiff.c b/applypatch/imgdiff.c
index 3bac8be..3a2eb91 100644
--- a/applypatch/imgdiff.c
+++ b/applypatch/imgdiff.c
@@ -122,6 +122,7 @@
*/
#include <errno.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -192,8 +193,9 @@ unsigned char* ReadZip(const char* filename,
}
unsigned char* img = malloc(st.st_size);
+ size_t sz = (size_t) st.st_size;
FILE* f = fopen(filename, "rb");
- if (fread(img, 1, st.st_size, f) != st.st_size) {
+ if (fread(img, 1, sz, f) != sz) {
printf("failed to read \"%s\" %s\n", filename, strerror(errno));
fclose(f);
return NULL;
@@ -383,7 +385,8 @@ unsigned char* ReadImage(const char* filename,
unsigned char* img = malloc(st.st_size + 4);
FILE* f = fopen(filename, "rb");
- if (fread(img, 1, st.st_size, f) != st.st_size) {
+ size_t sz = (size_t) st.st_size;
+ if (fread(img, 1, sz, f) != sz) {
printf("failed to read \"%s\" %s\n", filename, strerror(errno));
fclose(f);
return NULL;
@@ -400,10 +403,11 @@ unsigned char* ReadImage(const char* filename,
*num_chunks = 0;
*chunks = NULL;
- while (pos < st.st_size) {
+ while (pos < sz) {
unsigned char* p = img+pos;
- if (st.st_size - pos >= 4 &&
+ bool processed_deflate = false;
+ if (sz - pos >= 4 &&
p[0] == 0x1f && p[1] == 0x8b &&
p[2] == 0x08 && // deflate compression
p[3] == 0x00) { // no header flags
@@ -455,18 +459,24 @@ unsigned char* ReadImage(const char* filename,
strm.next_out = curr->data + curr->len;
ret = inflate(&strm, Z_NO_FLUSH);
if (ret < 0) {
- printf("Error: inflate failed [%s] at file offset [%zu]\n"
- "imgdiff only supports gzip kernel compression,"
- " did you try CONFIG_KERNEL_LZO?\n",
- strm.msg, chunk_offset);
- free(img);
- return NULL;
+ if (!processed_deflate) {
+ // This is the first chunk, assume that it's just a spurious
+ // gzip header instead of a real one.
+ break;
+ }
+ printf("Error: inflate failed [%s] at file offset [%zu]\n"
+ "imgdiff only supports gzip kernel compression,"
+ " did you try CONFIG_KERNEL_LZO?\n",
+ strm.msg, chunk_offset);
+ free(img);
+ return NULL;
}
curr->len = allocated - strm.avail_out;
if (strm.avail_out == 0) {
allocated *= 2;
curr->data = realloc(curr->data, allocated);
}
+ processed_deflate = true;
} while (ret != Z_STREAM_END);
curr->deflate_len = st.st_size - strm.avail_in - pos;
@@ -493,7 +503,7 @@ unsigned char* ReadImage(const char* filename,
// the decompression.
size_t footer_size = Read4(p-4);
if (footer_size != curr[-2].len) {
- printf("Error: footer size %d != decompressed size %d\n",
+ printf("Error: footer size %zu != decompressed size %zu\n",
footer_size, curr[-2].len);
free(img);
return NULL;
@@ -623,7 +633,15 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
}
char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
- mkstemp(ptemp);
+ int fd = mkstemp(ptemp);
+
+ if (fd == -1) {
+ printf("MakePatch failed to create a temporary file: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+ close(fd); // temporary file is created and we don't need its file
+ // descriptor
int r = bsdiff(src->data, src->len, &(src->I), tgt->data, tgt->len, ptemp);
if (r != 0) {
@@ -639,8 +657,8 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
}
unsigned char* data = malloc(st.st_size);
-
- if (tgt->type == CHUNK_NORMAL && tgt->len <= st.st_size) {
+ size_t sz = (size_t) st.st_size;
+ if (tgt->type == CHUNK_NORMAL && tgt->len <= sz) {
unlink(ptemp);
tgt->type = CHUNK_RAW;
@@ -648,14 +666,14 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
return tgt->data;
}
- *size = st.st_size;
+ *size = sz;
FILE* f = fopen(ptemp, "rb");
if (f == NULL) {
printf("failed to open patch %s: %s\n", ptemp, strerror(errno));
return NULL;
}
- if (fread(data, 1, st.st_size, f) != st.st_size) {
+ if (fread(data, 1, sz, f) != sz) {
printf("failed to read patch %s: %s\n", ptemp, strerror(errno));
return NULL;
}
@@ -783,7 +801,7 @@ ImageChunk* FindChunkByName(const char* name,
void DumpChunks(ImageChunk* chunks, int num_chunks) {
int i;
for (i = 0; i < num_chunks; ++i) {
- printf("chunk %d: type %d start %d len %d\n",
+ printf("chunk %d: type %d start %zu len %zu\n",
i, chunks[i].type, chunks[i].start, chunks[i].len);
}
}
@@ -967,7 +985,7 @@ int main(int argc, char** argv) {
}
} else {
if (i == 1 && bonus_data) {
- printf(" using %d bytes of bonus data for chunk %d\n", bonus_size, i);
+ printf(" using %zu bytes of bonus data for chunk %d\n", bonus_size, i);
src_chunks[i].data = realloc(src_chunks[i].data, src_chunks[i].len + bonus_size);
memcpy(src_chunks[i].data+src_chunks[i].len, bonus_data, bonus_size);
src_chunks[i].len += bonus_size;
@@ -975,7 +993,7 @@ int main(int argc, char** argv) {
patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
}
- printf("patch %3d is %d bytes (of %d)\n",
+ printf("patch %3d is %zu bytes (of %zu)\n",
i, patch_size[i], tgt_chunks[i].source_len);
}
@@ -1012,7 +1030,7 @@ int main(int argc, char** argv) {
switch (tgt_chunks[i].type) {
case CHUNK_NORMAL:
- printf("chunk %3d: normal (%10d, %10d) %10d\n", i,
+ printf("chunk %3d: normal (%10zu, %10zu) %10zu\n", i,
tgt_chunks[i].start, tgt_chunks[i].len, patch_size[i]);
Write8(tgt_chunks[i].source_start, f);
Write8(tgt_chunks[i].source_len, f);
@@ -1021,7 +1039,7 @@ int main(int argc, char** argv) {
break;
case CHUNK_DEFLATE:
- printf("chunk %3d: deflate (%10d, %10d) %10d %s\n", i,
+ printf("chunk %3d: deflate (%10zu, %10zu) %10zu %s\n", i,
tgt_chunks[i].start, tgt_chunks[i].deflate_len, patch_size[i],
tgt_chunks[i].filename);
Write8(tgt_chunks[i].source_start, f);
@@ -1038,7 +1056,7 @@ int main(int argc, char** argv) {
break;
case CHUNK_RAW:
- printf("chunk %3d: raw (%10d, %10d)\n", i,
+ printf("chunk %3d: raw (%10zu, %10zu)\n", i,
tgt_chunks[i].start, tgt_chunks[i].len);
Write4(patch_size[i], f);
fwrite(patch_data[i], 1, patch_size[i], f);
diff --git a/backup.cpp b/backup.cpp
new file mode 100644
index 0000000..7792add
--- /dev/null
+++ b/backup.cpp
@@ -0,0 +1,308 @@
+#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"
+
+#include "messagesocket.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;
+ }
+ }
+
+ MessageSocket ms;
+ ms.ClientInit();
+ ms.Show("Backup in progress...");
+
+ 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);
+
+ ms.Dismiss();
+
+ logmsg("backup complete: rc=%d\n", rc);
+
+ return rc;
+}
+
diff --git a/bootloader.cpp b/bootloader.cpp
index 600d238..9e91b27 100644
--- a/bootloader.cpp
+++ b/bootloader.cpp
@@ -34,8 +34,8 @@ static int set_bootloader_message_block(const struct bootloader_message *in, con
int get_bootloader_message(struct bootloader_message *out) {
Volume* v = volume_for_path("/misc");
if (v == NULL) {
- LOGE("Cannot load volume /misc!\n");
- return -1;
+ LOGI("Cannot load volume /misc.\n");
+ return -1;
}
if (strcmp(v->fs_type, "mtd") == 0) {
return get_bootloader_message_mtd(out, v);
@@ -49,8 +49,8 @@ int get_bootloader_message(struct bootloader_message *out) {
int set_bootloader_message(const struct bootloader_message *in) {
Volume* v = volume_for_path("/misc");
if (v == NULL) {
- LOGE("Cannot load volume /misc!\n");
- return -1;
+ LOGI("Cannot load volume /misc.\n");
+ return -1;
}
if (strcmp(v->fs_type, "mtd") == 0) {
return set_bootloader_message_mtd(in, v);
@@ -168,6 +168,9 @@ static int get_bootloader_message_block(struct bootloader_message *out,
LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+ fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_SET);
+#endif
struct bootloader_message temp;
int count = fread(&temp, sizeof(temp), 1, f);
if (count != 1) {
@@ -185,11 +188,14 @@ static int get_bootloader_message_block(struct bootloader_message *out,
static int set_bootloader_message_block(const struct bootloader_message *in,
const Volume* v) {
wait_for_device(v->blk_device);
- FILE* f = fopen(v->blk_device, "wb");
+ FILE* f = fopen(v->blk_device, "rb+");
if (f == NULL) {
LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+ fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_SET);
+#endif
int count = fwrite(in, sizeof(*in), 1, f);
if (count != 1) {
LOGE("Failed writing %s\n(%s)\n", v->blk_device, strerror(errno));
diff --git a/bu.cpp b/bu.cpp
new file mode 100644
index 0000000..976d406
--- /dev/null
+++ b/bu.cpp
@@ -0,0 +1,394 @@
+#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 "messagesocket.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;
+
+static MessageSocket ms;
+
+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);
+ ms.Show(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();
+
+ ms.ClientInit();
+
+ if (!strcmp(opname, "backup")) {
+ ms.Show("Backup in progress...");
+ rc = do_backup(argc-optidx, &argv[optidx]);
+ }
+ else if (!strcmp(opname, "restore")) {
+ ms.Show("Restore in progress...");
+ rc = do_restore(argc-optidx, &argv[optidx]);
+ }
+ else {
+ logmsg("Unknown operation %s\n", opname);
+ rc = 1;
+ }
+
+ ms.Dismiss();
+
+ 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 0000000..15ab745
--- /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 fd1a987..95608d6 100644
--- a/device.cpp
+++ b/device.cpp
@@ -16,36 +16,133 @@
#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
+ "System reset (keep media)",
+#endif
+ "Full factory reset",
"Wipe cache partition",
+ nullptr
+};
+static const menu_entry WIPE_MENU_ENTRIES[] = {
+#ifndef RELEASE_BUILD
+ { ACTION_INVOKE, { .action = Device::WIPE_DATA } },
+#endif
+ { ACTION_INVOKE, { .action = Device::WIPE_FULL } },
+ { ACTION_INVOKE, { .action = Device::WIPE_CACHE } },
+ { 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",
+#endif
"View recovery logs",
"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 } },
+#endif
+ { ACTION_INVOKE, { .action = Device::VIEW_RECOVERY_LOGS } },
+ { ACTION_INVOKE, { .action = Device::SHUTDOWN } },
+ { ACTION_NONE, { .action = Device::NO_ACTION } }
};
+static const menu ADVANCED_MENU = {
+ ADVANCED_MENU_NAMES,
+ ADVANCED_MENU_ENTRIES
+};
+
+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
+};
+
+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) {
- switch (menu_position) {
- case 0: return REBOOT;
- case 1: return REBOOT_BOOTLOADER;
- case 2: return APPLY_ADB_SIDELOAD;
- case 3: return APPLY_SDCARD;
- case 4: return WIPE_DATA;
- case 5: return WIPE_CACHE;
- case 6: return MOUNT_SYSTEM;
- case 7: return VIEW_RECOVERY_LOGS;
- case 8: return SHUTDOWN;
- default: return NO_ACTION;
+ 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) {
@@ -53,19 +150,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 f74b6b0..98427a2 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,17 +63,17 @@ 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,
+ NO_ACTION,
+ REBOOT,
+ APPLY_UPDATE,
+ WIPE_DATA,
+ WIPE_FULL,
+ WIPE_CACHE,
+ REBOOT_RECOVERY,
+ REBOOT_BOOTLOADER,
+ SHUTDOWN,
+ VIEW_RECOVERY_LOGS,
+ MOUNT_SYSTEM,
};
// Return the list of menu items (an array of strings,
@@ -86,10 +92,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,
@@ -102,8 +113,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/edify/Android.mk b/edify/Android.mk
index 03c04e4..c366450 100644
--- a/edify/Android.mk
+++ b/edify/Android.mk
@@ -25,6 +25,7 @@ LOCAL_CFLAGS := $(edify_cflags) -g -O0
LOCAL_MODULE := edify
LOCAL_YACCFLAGS := -v
LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CLANG := true
include $(BUILD_HOST_EXECUTABLE)
@@ -38,5 +39,6 @@ LOCAL_SRC_FILES := $(edify_src_files)
LOCAL_CFLAGS := $(edify_cflags)
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE := libedify
+LOCAL_CLANG := true
include $(BUILD_STATIC_LIBRARY)
diff --git a/etc/init.rc b/etc/init.rc
index 4277277..e070438 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -5,7 +5,6 @@ on early-init
start healthd
on init
- export PATH /sbin:/system/bin
export ANDROID_ROOT /system
export ANDROID_DATA /data
export EXTERNAL_STORAGE /sdcard
@@ -22,6 +21,26 @@ on init
chown root shell /tmp
chmod 0775 /tmp
+ mkdir /mnt 0775 root system
+ mkdir /storage 0050 root sdcard_r
+ mount tmpfs tmpfs /storage mode=0050,uid=0,gid=1028
+
+ # See storage config details at http://source.android.com/tech/storage/
+ mkdir /mnt/shell 0700 shell shell
+
+ # Directory for putting things only root should see.
+ mkdir /mnt/secure 0700 root root
+
+ # Create private mountpoint so we can MS_MOVE from staging
+ mount tmpfs tmpfs /mnt/secure mode=0700,uid=0,gid=0
+
+ # Directory for staging bindmounts
+ mkdir /mnt/secure/staging 0700 root root
+
+ # Fuse public mount points.
+ mkdir /mnt/fuse 0700 root system
+ mount tmpfs tmpfs /mnt/fuse mode=0775,gid=1000
+
write /proc/sys/kernel/panic_on_oops 1
write /proc/sys/vm/max_map_count 1000000
@@ -50,6 +69,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
@@ -65,6 +87,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
@@ -85,18 +110,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 0000000..b2923c6
--- /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 0000000..73dc0f0
--- /dev/null
+++ b/fstools/Android.mk
@@ -0,0 +1,69 @@
+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
+
+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 0000000..b1cc7ad
--- /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 0000000..d99b382
--- /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.c b/fuse_sdcard_provider.cpp
index 4565c7b..8681425 100644
--- a/fuse_sdcard_provider.c
+++ b/fuse_sdcard_provider.cpp
@@ -21,6 +21,7 @@
#include <pthread.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
@@ -34,7 +35,7 @@ struct file_data {
};
static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
- struct file_data* fd = (struct file_data*)cookie;
+ file_data* fd = reinterpret_cast<file_data*>(cookie);
off64_t offset = ((off64_t) block) * fd->block_size;
if (TEMP_FAILURE_RETRY(lseek64(fd->fd, offset, SEEK_SET)) == -1) {
@@ -56,18 +57,18 @@ static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32
}
static void close_file(void* cookie) {
- struct file_data* fd = (struct file_data*)cookie;
+ file_data* fd = reinterpret_cast<file_data*>(cookie);
close(fd->fd);
}
struct token {
- pthread_t th;
+ pid_t pid;
const char* path;
int result;
};
static void* run_sdcard_fuse(void* cookie) {
- struct token* t = (struct token*)cookie;
+ token* t = reinterpret_cast<token*>(cookie);
struct stat sb;
if (stat(t->path, &sb) < 0) {
@@ -100,41 +101,45 @@ static void* run_sdcard_fuse(void* cookie) {
#define SDCARD_INSTALL_TIMEOUT 10
void* start_sdcard_fuse(const char* path) {
- struct token* t = malloc(sizeof(struct token));
+ token* t = new token;
t->path = path;
- pthread_create(&(t->th), NULL, run_sdcard_fuse, t);
-
- struct stat st;
- int i;
- for (i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
- if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
- if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
- sleep(1);
- continue;
- } else {
- return NULL;
- }
- }
+ if ((t->pid = fork()) < 0) {
+ free(t);
+ return NULL;
}
+ if (t->pid == 0) {
+ run_sdcard_fuse(t);
+ _exit(0);
+ }
+
+ time_t start_time = time(NULL);
+ time_t now = start_time;
- // 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);
+ while (now - start_time < SDCARD_INSTALL_TIMEOUT) {
+ struct stat st;
+ if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) == 0) {
+ break;
+ }
+ if (errno != ENOENT && errno != ENOTCONN) {
+ free(t);
+ t = NULL;
+ break;
+ }
+ sleep(1);
+ now = time(NULL);
+ }
return t;
}
void finish_sdcard_fuse(void* cookie) {
if (cookie == NULL) return;
- struct token* t = (struct token*)cookie;
+ token* t = reinterpret_cast<token*>(cookie);
- // Calling stat() on this magic filename signals the fuse
- // filesystem to shut down.
- struct stat st;
- stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+ kill(t->pid, SIGTERM);
+ int status;
+ waitpid(t->pid, &status, 0);
- pthread_join(t->th, NULL);
- free(t);
+ delete t;
}
diff --git a/fuse_sdcard_provider.h b/fuse_sdcard_provider.h
index dbfbcd5..dc2982c 100644
--- a/fuse_sdcard_provider.h
+++ b/fuse_sdcard_provider.h
@@ -17,13 +17,7 @@
#ifndef __FUSE_SDCARD_PROVIDER_H
#define __FUSE_SDCARD_PROVIDER_H
-#include <sys/cdefs.h>
-
-__BEGIN_DECLS
-
void* start_sdcard_fuse(const char* path);
void finish_sdcard_fuse(void* token);
-__END_DECLS
-
#endif
diff --git a/fuse_sideload.c b/fuse_sideload.cpp
index 48e6cc5..a53a756 100644
--- a/fuse_sideload.c
+++ b/fuse_sideload.cpp
@@ -65,10 +65,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
@@ -84,7 +84,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
@@ -92,8 +92,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;
@@ -116,7 +188,7 @@ static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, siz
}
static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
- const struct fuse_init_in* req = data;
+ const struct fuse_init_in* req = reinterpret_cast<const struct fuse_init_in*>(data);
struct fuse_init_out out;
size_t fuse_struct_size;
@@ -170,8 +242,7 @@ static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd,
attr->mode = mode;
}
-static int handle_getattr(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
- const struct fuse_getattr_in* req = data;
+static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) {
struct fuse_attr_out out;
memset(&out, 0, sizeof(out));
out.attr_valid = 10;
@@ -180,14 +251,12 @@ static int handle_getattr(void* data, struct fuse_data* fd, const struct fuse_in
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,
@@ -197,28 +266,20 @@ static int handle_lookup(void* data, struct fuse_data* fd,
out.entry_valid = 10;
out.attr_valid = 10;
- if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, data,
+ if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, reinterpret_cast<const char*>(data),
sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) {
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, 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) {
- const struct fuse_open_in* req = data;
-
- if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM;
+static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) {
if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT;
struct fuse_open_out out;
@@ -249,6 +310,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,
@@ -288,11 +354,12 @@ static int fetch_block(struct fuse_data* fd, uint32_t block) {
}
memcpy(blockhash, hash, SHA256_DIGEST_SIZE);
+ block_cache_enter(fd, block);
return 0;
}
static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
- const struct fuse_read_in* req = data;
+ const struct fuse_read_in* req = reinterpret_cast<const struct fuse_read_in*>(data);
struct fuse_out_header outhdr;
struct iovec vec[3];
int vec_used;
@@ -364,6 +431,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)
{
@@ -390,6 +463,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;
@@ -421,6 +497,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");
@@ -441,7 +535,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");
@@ -496,11 +600,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);
@@ -519,6 +618,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/fuse_sideload.h b/fuse_sideload.h
index f9e3bf0..71a09a6 100644
--- a/fuse_sideload.h
+++ b/fuse_sideload.h
@@ -17,16 +17,10 @@
#ifndef __FUSE_SIDELOAD_H
#define __FUSE_SIDELOAD_H
-#include <sys/cdefs.h>
-
-__BEGIN_DECLS
-
// define the filenames created by the sideload FUSE filesystem
#define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload"
#define FUSE_SIDELOAD_HOST_FILENAME "package.zip"
#define FUSE_SIDELOAD_HOST_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_FILENAME)
-#define FUSE_SIDELOAD_HOST_EXIT_FLAG "exit"
-#define FUSE_SIDELOAD_HOST_EXIT_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_EXIT_FLAG)
struct provider_vtab {
// read a block
@@ -39,6 +33,4 @@ struct provider_vtab {
int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
uint64_t file_size, uint32_t block_size);
-__END_DECLS
-
#endif
diff --git a/install.cpp b/install.cpp
index c7d382f..c557cd0 100644
--- a/install.cpp
+++ b/install.cpp
@@ -22,6 +22,8 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <setjmp.h>
+#include <sys/mount.h>
#include "common.h"
#include "install.h"
@@ -35,6 +37,8 @@
#include "verifier.h"
#include "ui.h"
+#include "cutils/properties.h"
+
extern RecoveryUI* ui;
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
@@ -46,6 +50,12 @@ static const float VERIFICATION_PROGRESS_FRACTION = 0.25;
static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
+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) {
@@ -164,9 +174,9 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) {
} else if (strcmp(command, "ui_print") == 0) {
char* str = strtok(NULL, "\n");
if (str) {
- ui->Print("%s", str);
+ ui->PrintOnScreenOnly("%s", str);
} else {
- ui->Print("\n");
+ ui->PrintOnScreenOnly("\n");
}
fflush(stdout);
} else if (strcmp(command, "wipe_cache") == 0) {
@@ -187,21 +197,101 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) {
int status;
waitpid(pid, &status, 0);
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. */
+ mkdir("/system", 0755);
+ 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 = 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");
+
+ free(args);
+
+ return (status > 0) ? 1 : 0;
+}
+#endif /* USE_MDTP */
+
static int
really_install_package(const char *path, bool* wipe_cache, bool needs_mount)
{
+ 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.
@@ -212,6 +302,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);
}
}
@@ -229,16 +320,31 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount)
}
LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
+ set_perf_mode(true);
+
ui->Print("Verifying update package...\n");
int err;
- err = verify_file(map.addr, map.length, loadedKeys, numKeys);
+
+ // 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(map.addr, map.length, loadedKeys, numKeys);
+ }
+ else {
+ err = VERIFY_FAILURE;
+ }
+ signal(SIGBUS, SIG_DFL);
+
free(loadedKeys);
LOGI("verify_file returned %d\n", err);
if (err != VERIFY_SUCCESS) {
LOGE("signature verification failed\n");
sysReleaseMap(&map);
- return INSTALL_CORRUPT;
+ ret = INSTALL_CORRUPT;
+ goto out;
}
/* Try to open the package.
@@ -248,20 +354,34 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount)
if (err != 0) {
LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
sysReleaseMap(&map);
- return INSTALL_CORRUPT;
+ ret = INSTALL_CORRUPT;
+ goto out;
}
/* Verify and install the contents of the package.
*/
ui->Print("Installing update...\n");
ui->SetEnableReboot(false);
- int result = try_update_binary(path, &zip, wipe_cache);
+ ret = try_update_binary(path, &zip, wipe_cache);
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 */
+
+out:
+ set_perf_mode(false);
+ return ret;
}
int
@@ -291,3 +411,8 @@ install_package(const char* path, bool* wipe_cache, const char* install_file,
}
return result;
}
+
+void
+set_perf_mode(bool enable) {
+ property_set("recovery.perf.mode", enable ? "1" : "0");
+}
diff --git a/install.h b/install.h
index 680499d..e6ce66e 100644
--- a/install.h
+++ b/install.h
@@ -30,6 +30,8 @@ enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE };
int install_package(const char* root_path, bool* wipe_cache,
const char* install_file, bool needs_mount);
+void set_perf_mode(bool enable);
+
#ifdef __cplusplus
}
#endif
diff --git a/messagesocket.cpp b/messagesocket.cpp
new file mode 100644
index 0000000..355a667
--- /dev/null
+++ b/messagesocket.cpp
@@ -0,0 +1,116 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include "messagesocket.h"
+
+static const char * const SOCKET_PATH = "/tmp/.dialog_sock";
+
+bool MessageSocket::ServerInit()
+{
+ int fd, rc;
+ unlink(SOCKET_PATH);
+ fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ return false;
+ }
+ struct sockaddr_un sa;
+ socklen_t salen;
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ strcpy(sa.sun_path, SOCKET_PATH);
+ rc = ::bind(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (rc != 0) {
+ ::close(fd);
+ return false;
+ }
+ rc = ::listen(fd, 5);
+ if (rc != 0) {
+ ::close(fd);
+ return false;
+ }
+ _sock = fd;
+ return true;
+}
+
+MessageSocket* MessageSocket::Accept()
+{
+ int clientfd;
+ struct sockaddr_un sa;
+ socklen_t salen;
+ memset(&sa, 0, sizeof(sa));
+ salen = sizeof(sa);
+ clientfd = ::accept(_sock, (struct sockaddr*)&sa, &salen);
+ if (clientfd < 0) {
+ return NULL;
+ }
+ return new MessageSocket(clientfd);
+}
+
+ssize_t MessageSocket::Read(void* buf, size_t len)
+{
+ //XXX: use fdopen/getline
+ ssize_t nread = ::read(_sock, buf, len);
+ if (nread > 0) {
+ char* p = (char*)memchr(buf, '\n', nread);
+ if (p) {
+ *p = '\0';
+ }
+ }
+ return nread;
+}
+
+bool MessageSocket::ClientInit()
+{
+ int fd, rc;
+ fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ return false;
+ }
+ struct sockaddr_un sa;
+ socklen_t salen;
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ strcpy(sa.sun_path, SOCKET_PATH);
+ rc = ::connect(fd, (struct sockaddr*)&sa, sizeof(sa));
+ if (rc != 0) {
+ ::close(fd);
+ return false;
+ }
+ _sock = fd;
+ return true;
+}
+
+bool MessageSocket::Show(const char* message)
+{
+ char buf[256];
+ sprintf(buf, "show %s", message);
+ return send_command(buf);
+}
+
+bool MessageSocket::Dismiss()
+{
+ return send_command("dismiss");
+}
+
+void MessageSocket::Close()
+{
+ if (_sock != -1) {
+ ::close(_sock);
+ _sock = -1;
+ }
+}
+
+bool MessageSocket::send_command(const char* command)
+{
+ char buf[256];
+ int len;
+ ssize_t written;
+ len = sprintf(buf, "dialog %s\n", command);
+ written = ::write(_sock, buf, len);
+ return (written == len);
+}
diff --git a/messagesocket.h b/messagesocket.h
new file mode 100644
index 0000000..5a4c67d
--- /dev/null
+++ b/messagesocket.h
@@ -0,0 +1,40 @@
+#ifndef MESSAGESOCKET_H
+#define MESSAGESOCKET_H
+
+class MessageSocket
+{
+private:
+ // Unimplemented
+ MessageSocket(const MessageSocket&);
+ MessageSocket& operator=(const MessageSocket&);
+
+public:
+ MessageSocket() : _sock(-1) {}
+ ~MessageSocket() { Close(); }
+ int fd() const { return _sock; }
+
+ bool ServerInit();
+ MessageSocket* Accept();
+ ssize_t Read(void* buf, size_t len);
+
+ bool ClientInit();
+ bool Show(const char* message);
+ bool Dismiss();
+
+ void Close();
+
+private:
+ explicit MessageSocket(int fd) : _sock(fd) {}
+
+ bool send_command(const char* command);
+
+ int _sock;
+};
+
+extern int dialog_server_init();
+extern int dialog_client_init();
+extern int dialog_accept(int fd);
+extern int dialog_show(int fd);
+extern int dialog_dismiss(int fd);
+
+#endif
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index a7a3e08..3db3b41 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -15,6 +15,7 @@ LOCAL_SRC_FILES := \
fuse_adb_provider.cpp \
services.cpp \
+LOCAL_CLANG := true
LOCAL_MODULE := libminadbd
LOCAL_CFLAGS := $(minadbd_cflags)
LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration
diff --git a/minadbd/services.cpp b/minadbd/services.cpp
index dd1fd7c..0eeeb20 100644
--- a/minadbd/services.cpp
+++ b/minadbd/services.cpp
@@ -44,13 +44,14 @@ void* service_bootstrap_func(void* x) {
}
static void sideload_host_service(int sfd, void* data) {
- const char* args = reinterpret_cast<const char*>(data);
+ char* args = reinterpret_cast<char*>(data);
int file_size;
int block_size;
if (sscanf(args, "%d:%d", &file_size, &block_size) != 2) {
printf("bad sideload-host arguments: %s\n", args);
exit(1);
}
+ free(args);
printf("sideload-host file size %d block size %d\n", file_size, block_size);
@@ -97,7 +98,8 @@ int service_to_fd(const char* name) {
// sideload-host).
exit(3);
} else if (!strncmp(name, "sideload-host:", 14)) {
- ret = create_service_thread(sideload_host_service, (void*)(name + 14));
+ char* arg = strdup(name + 14);
+ ret = create_service_thread(sideload_host_service, arg);
}
if (ret >= 0) {
close_on_exec(ret);
diff --git a/minui/Android.mk b/minui/Android.mk
index 97724fb..e74c7be 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,10 +40,15 @@ else
LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0
endif
+ifneq ($(BOARD_RECOVERY_NEEDS_FBIOPAN_DISPLAY),)
+ LOCAL_CFLAGS += -DBOARD_RECOVERY_NEEDS_FBIOPAN_DISPLAY
+endif
+
include $(BUILD_STATIC_LIBRARY)
# Used by OEMs for factory test images.
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_MODULE := libminui
LOCAL_WHOLE_STATIC_LIBRARIES += libminui
LOCAL_SHARED_LIBRARIES := libpng
diff --git a/minui/events.cpp b/minui/events.cpp
index 3b2262a..120baed 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -78,8 +78,8 @@ 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_REL, EV_SW, and EV_ABS event types are ever needed.
+ 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)) {
close(fd);
continue;
}
@@ -137,6 +137,23 @@ int ev_add_fd(int fd, ev_callback cb, void* data) {
return ret;
}
+int ev_del_fd(int fd)
+{
+ unsigned n;
+ for (n = 0; n < ev_count; ++n) {
+ if (ev_fdinfo[n].fd == fd) {
+ epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, fd, NULL);
+ if (n != ev_count-1) {
+ ev_fdinfo[n] = ev_fdinfo[ev_count-1];
+ }
+ ev_count--;
+ ev_misc_count--;
+ return 0;
+ }
+ }
+ return -1;
+}
+
void ev_exit(void) {
while (ev_count > 0) {
close(ev_fdinfo[--ev_count].fd);
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index c0eea9e..1f25531 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 0788f75..b017ff2 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 bdde083..063fc86 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);
@@ -67,6 +68,7 @@ typedef int (*ev_set_key_callback)(int code, int value, void* data);
int ev_init(ev_callback input_cb, void* data);
void ev_exit();
int ev_add_fd(int fd, ev_callback cb, void* data);
+int ev_del_fd(int fd);
void ev_iterate_available_keys(std::function<void(int)> f);
int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
@@ -120,4 +122,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/minzip/Android.mk b/minzip/Android.mk
index 045f355..1605a53 100644
--- a/minzip/Android.mk
+++ b/minzip/Android.mk
@@ -10,12 +10,16 @@ LOCAL_SRC_FILES := \
LOCAL_C_INCLUDES := \
external/zlib \
- external/safe-iop/include
+ external/safe-iop/include \
+ external/lzma/xz-embedded
LOCAL_STATIC_LIBRARIES := libselinux
+LOCAL_STATIC_LIBRARIES += libxz
LOCAL_MODULE := libminzip
-LOCAL_CFLAGS += -Wall
+LOCAL_CLANG := true
+
+LOCAL_CFLAGS += -Werror -Wall
include $(BUILD_STATIC_LIBRARY)
diff --git a/minzip/Hash.c b/minzip/Hash.c
index 8f8ed68..49bcb31 100644
--- a/minzip/Hash.c
+++ b/minzip/Hash.c
@@ -361,7 +361,7 @@ void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
{
const void* data = (const void*)mzHashIterData(&iter);
int count;
-
+
count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc);
numEntries++;
@@ -373,7 +373,7 @@ void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
totalProbe += count;
}
- LOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n",
+ LOGV("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n",
minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize,
(float) totalProbe / (float) numEntries);
}
diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c
index b1fb455..32bc4c0 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -3,93 +3,52 @@
*
* System utilities.
*/
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <unistd.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
-#include <sys/types.h>
#include <sys/stat.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <errno.h>
-#include <assert.h>
+#include <sys/types.h>
+#include <unistd.h>
#define LOG_TAG "sysutil"
#include "Log.h"
#include "SysUtil.h"
-static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
-{
- off_t start, end;
- size_t length;
-
- assert(start_ != NULL);
- assert(length_ != NULL);
-
- // TODO: isn't start always 0 for the single call site? just use fstat instead?
-
- start = TEMP_FAILURE_RETRY(lseek(fd, 0L, SEEK_CUR));
- end = TEMP_FAILURE_RETRY(lseek(fd, 0L, SEEK_END));
-
- if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1 ||
- start == (off_t) -1 || end == (off_t) -1) {
- LOGE("could not determine length of file\n");
- return -1;
- }
-
- length = end - start;
- if (length == 0) {
- LOGE("file is empty\n");
- return -1;
- }
-
- *start_ = start;
- *length_ = length;
-
- return 0;
-}
-
-/*
- * Map a file (from fd's current offset) into a private, read-only memory
- * segment. The file offset must be a multiple of the page size.
- *
- * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
- * value and does not disturb "pMap".
- */
-static int sysMapFD(int fd, MemMapping* pMap)
-{
- off_t start;
- size_t length;
- void* memPtr;
-
+static bool sysMapFD(int fd, MemMapping* pMap) {
assert(pMap != NULL);
- if (getFileStartAndLength(fd, &start, &length) < 0)
- return -1;
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ LOGE("fstat(%d) failed: %s\n", fd, strerror(errno));
+ return false;
+ }
- memPtr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, start);
+ void* memPtr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (memPtr == MAP_FAILED) {
- LOGW("mmap(%d, R, PRIVATE, %d, %d) failed: %s\n", (int) length,
- fd, (int) start, strerror(errno));
- return -1;
+ LOGE("mmap(%d, R, PRIVATE, %d, 0) failed: %s\n", (int) sb.st_size, fd, strerror(errno));
+ return false;
}
pMap->addr = memPtr;
- pMap->length = length;
+ pMap->length = sb.st_size;
pMap->range_count = 1;
pMap->ranges = malloc(sizeof(MappedRange));
if (pMap->ranges == NULL) {
LOGE("malloc failed: %s\n", strerror(errno));
- munmap(memPtr, length);
- return -1;
+ munmap(memPtr, pMap->length);
+ return false;
}
pMap->ranges[0].addr = memPtr;
- pMap->ranges[0].length = length;
+ pMap->ranges[0].length = sb.st_size;
- return 0;
+ return true;
}
static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
@@ -102,7 +61,7 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
unsigned int i;
if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) {
- LOGW("failed to read block device from header\n");
+ LOGE("failed to read block device from header\n");
return -1;
}
for (i = 0; i < sizeof(block_dev); ++i) {
@@ -113,9 +72,10 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
}
if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) {
- LOGW("failed to parse block map header\n");
+ LOGE("failed to parse block map header\n");
return -1;
}
+
if (blksize != 0) {
blocks = ((size-1) / blksize) + 1;
}
@@ -136,14 +96,14 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
unsigned char* reserve;
reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (reserve == MAP_FAILED) {
- LOGW("failed to reserve address space: %s\n", strerror(errno));
+ LOGE("failed to reserve address space: %s\n", strerror(errno));
free(pMap->ranges);
return -1;
}
int fd = open(block_dev, O_RDONLY);
if (fd < 0) {
- LOGW("failed to open block device %s: %s\n", block_dev, strerror(errno));
+ LOGE("failed to open block device %s: %s\n", block_dev, strerror(errno));
munmap(reserve, blocks * blksize);
free(pMap->ranges);
return -1;
@@ -155,7 +115,7 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
for (i = 0; i < range_count; ++i) {
size_t start, end;
if (fscanf(mapf, "%zu %zu\n", &start, &end) != 2) {
- LOGW("failed to parse range %d in block map\n", i);
+ LOGE("failed to parse range %d in block map\n", i);
success = false;
break;
}
@@ -168,7 +128,7 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize);
if (addr == MAP_FAILED) {
- LOGW("failed to map block %d: %s\n", i, strerror(errno));
+ LOGE("failed to map block %d: %s\n", i, strerror(errno));
success = false;
break;
}
@@ -206,12 +166,12 @@ int sysMapFile(const char* fn, MemMapping* pMap)
// A map of blocks
FILE* mapf = fopen(fn+1, "r");
if (mapf == NULL) {
- LOGV("Unable to open '%s': %s\n", fn+1, strerror(errno));
+ LOGE("Unable to open '%s': %s\n", fn+1, strerror(errno));
return -1;
}
if (sysMapBlockFile(mapf, pMap) != 0) {
- LOGW("Map of '%s' failed\n", fn);
+ LOGE("Map of '%s' failed\n", fn);
fclose(mapf);
return -1;
}
@@ -219,13 +179,13 @@ int sysMapFile(const char* fn, MemMapping* pMap)
fclose(mapf);
} else {
// This is a regular file.
- int fd = open(fn, O_RDONLY, 0);
- if (fd < 0) {
+ int fd = open(fn, O_RDONLY);
+ if (fd == -1) {
LOGE("Unable to open '%s': %s\n", fn, strerror(errno));
return -1;
}
- if (sysMapFD(fd, pMap) != 0) {
+ if (!sysMapFD(fd, pMap)) {
LOGE("Map of '%s' failed\n", fn);
close(fd);
return -1;
@@ -244,7 +204,7 @@ void sysReleaseMap(MemMapping* pMap)
int i;
for (i = 0; i < pMap->range_count; ++i) {
if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) {
- LOGW("munmap(%p, %d) failed: %s\n",
+ LOGE("munmap(%p, %d) failed: %s\n",
pMap->ranges[i].addr, (int)pMap->ranges[i].length, strerror(errno));
}
}
diff --git a/minzip/Zip.c b/minzip/Zip.c
index 40712e0..7823c38 100644
--- a/minzip/Zip.c
+++ b/minzip/Zip.c
@@ -5,6 +5,7 @@
*/
#include "safe_iop.h"
#include "zlib.h"
+#include "xz_config.h"
#include <errno.h>
#include <fcntl.h>
@@ -198,10 +199,10 @@ static bool parseZipArchive(ZipArchive* pArchive)
*/
val = get4LE(pArchive->addr);
if (val == ENDSIG) {
- LOGI("Found Zip archive, but it looks empty\n");
+ LOGW("Found Zip archive, but it looks empty\n");
goto bail;
} else if (val != LOCSIG) {
- LOGV("Not a Zip archive (found 0x%08x)\n", val);
+ LOGW("Not a Zip archive (found 0x%08x)\n", val);
goto bail;
}
@@ -217,7 +218,7 @@ static bool parseZipArchive(ZipArchive* pArchive)
ptr--;
}
if (ptr < (const unsigned char*) pArchive->addr) {
- LOGI("Could not find end-of-central-directory in Zip\n");
+ LOGW("Could not find end-of-central-directory in Zip\n");
goto bail;
}
@@ -429,7 +430,7 @@ int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive)
if (length < ENDHDR) {
err = -1;
- LOGV("File '%s' too small to be zip (%zd)\n", fileName, map.length);
+ LOGW("Archive %p is too small to be zip (%zd)\n", pArchive, length);
goto bail;
}
@@ -438,7 +439,7 @@ int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive)
if (!parseZipArchive(pArchive)) {
err = -1;
- LOGV("Parsing '%s' failed\n", fileName);
+ LOGW("Parsing archive %p failed\n", pArchive);
goto bail;
}
@@ -501,12 +502,73 @@ static bool processStoredEntry(const ZipArchive *pArchive,
return processFunction(pArchive->addr + pEntry->offset, pEntry->uncompLen, cookie);
}
+static bool processXZEntry(const ZipArchive *pArchive,
+ const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
+ void *cookie)
+{
+ unsigned char out[32*1024];
+ struct xz_buf b;
+ struct xz_dec *s;
+ enum xz_ret ret;
+
+ printf("ok!\n");
+ xz_crc32_init();
+ xz_crc64_init();
+ s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+ if (s == NULL) {
+ LOGE("XZ decompression alloc failed\n");
+ goto bail;
+ }
+
+ b.in = pArchive->addr + pEntry->offset;
+ b.in_pos = 0;
+ b.in_size = pEntry->compLen;
+ b.out = out;
+ b.out_pos = 0;
+ b.out_size = sizeof(out);
+
+ do {
+ ret = xz_dec_run(s, &b);
+
+ LOGVV("+++ b.in_pos = %zu b.out_pos = %zu ret=%d\n", b.in_pos, b.out_pos, ret);
+ if (b.out_pos == sizeof(out)) {
+ LOGVV("+++ processing %d bytes\n", b.out_pos);
+ bool err = processFunction(out, b.out_pos, cookie);
+ if (!err) {
+ LOGW("Process function elected to fail (in xz_dec)\n");
+ goto xz_bail;
+ }
+ b.out_pos = 0;
+ }
+
+ } while (ret == XZ_OK);
+
+ assert(ret == XZ_STREAM_END);
+
+ bool err = processFunction(out, b.out_pos, cookie);
+ if (!err) {
+ LOGW("Process function elected to fail (in xz_dec)\n");
+ goto xz_bail;
+ }
+
+
+xz_bail:
+ xz_dec_end(s);
+
+bail:
+ if (b.in_pos != (unsigned long)pEntry->compLen) {
+ LOGW("Size mismatch on file after xz_dec (%ld vs %zu)\n",
+ pEntry->compLen, b.in_pos);
+ //return false;
+ }
+ return true;
+}
+
static bool processDeflatedEntry(const ZipArchive *pArchive,
const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
void *cookie)
{
long result = -1;
- unsigned char readBuf[32 * 1024];
unsigned char procBuf[32 * 1024];
z_stream zstream;
int zerr;
@@ -549,7 +611,7 @@ static bool processDeflatedEntry(const ZipArchive *pArchive,
/* uncompress the data */
zerr = inflate(&zstream, Z_NO_FLUSH);
if (zerr != Z_OK && zerr != Z_STREAM_END) {
- LOGD("zlib inflate call failed (zerr=%d)\n", zerr);
+ LOGW("zlib inflate call failed (zerr=%d)\n", zerr);
goto z_bail;
}
@@ -603,7 +665,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive,
void *cookie)
{
bool ret = false;
- off_t oldOff;
switch (pEntry->compression) {
case STORED:
@@ -621,13 +682,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive,
return ret;
}
-static bool crcProcessFunction(const unsigned char *data, int dataLen,
- void *crc)
-{
- *(unsigned long *)crc = crc32(*(unsigned long *)crc, data, dataLen);
- return true;
-}
-
typedef struct {
char *buf;
int bufLen;
@@ -647,6 +701,26 @@ static bool copyProcessFunction(const unsigned char *data, int dataLen,
}
/*
+ * Similar to mzProcessZipEntryContents, but explicitly process the stream
+ * using XZ/LZMA before calling processFunction.
+ *
+ * This is a separate function for use by the updater. LZMA provides huge
+ * size reductions vs deflate, but isn't actually supported by the ZIP format.
+ * We need to process it using as little memory as possible.
+ */
+bool mzProcessZipEntryContentsXZ(const ZipArchive *pArchive,
+ const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
+ void *cookie)
+{
+ if (pEntry->compression == STORED) {
+ return processXZEntry(pArchive, pEntry, processFunction, cookie);
+ }
+ LOGE("Explicit XZ decoding of entry '%s' unsupported for type %d",
+ pEntry->fileName, pEntry->compression);
+ return false;
+}
+
+/*
* Read an entry into a buffer allocated by the caller.
*/
bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
@@ -1016,7 +1090,7 @@ bool mzExtractRecursive(const ZipArchive *pArchive,
if (callback != NULL) callback(targetFile, cookie);
}
- LOGD("Extracted %d file(s)\n", extractCount);
+ LOGV("Extracted %d file(s)\n", extractCount);
free(helper.buf);
free(zpath);
diff --git a/minzip/Zip.h b/minzip/Zip.h
index 86d8db5..92c20a0 100644
--- a/minzip/Zip.h
+++ b/minzip/Zip.h
@@ -114,6 +114,18 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive,
void *cookie);
/*
+ * Similar to mzProcessZipEntryContents, but explicitly process the stream
+ * using XZ/LZMA before calling processFunction.
+ *
+ * This is a separate function for use by the updater. LZMA provides huge
+ * size reductions vs deflate, but isn't actually supported by the ZIP format.
+ * We need to process it using as little memory as possible.
+ */
+bool mzProcessZipEntryContentsXZ(const ZipArchive *pArchive,
+ const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
+ void *cookie);
+
+/*
* Read an entry into a buffer allocated by the caller.
*/
bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk
index f04355b..b7d35c2 100644
--- a/mtdutils/Android.mk
+++ b/mtdutils/Android.mk
@@ -6,10 +6,12 @@ LOCAL_SRC_FILES := \
mounts.c
LOCAL_MODULE := libmtdutils
+LOCAL_CLANG := true
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_SRC_FILES := flash_image.c
LOCAL_MODULE := flash_image
LOCAL_MODULE_TAGS := eng
diff --git a/mtdutils/mounts.c b/mtdutils/mounts.c
index 6a9b03d..41efa37 100644
--- a/mtdutils/mounts.c
+++ b/mtdutils/mounts.c
@@ -153,6 +153,21 @@ unmount_mounted_volume(const MountedVolume *volume)
}
int
+unmount_mounted_volume_detach(const MountedVolume *volume)
+{
+ /* Intentionally pass NULL to umount if the caller tries
+ * to unmount a volume they already unmounted using this
+ * function.
+ */
+ int ret = umount2(volume->mount_point, MNT_DETACH);
+ if (ret == 0) {
+ free_volume_internals(volume, 1);
+ return 0;
+ }
+ return ret;
+}
+
+int
remount_read_only(const MountedVolume* volume)
{
return mount(volume->device, volume->mount_point, volume->filesystem,
diff --git a/mtdutils/mounts.h b/mtdutils/mounts.h
index d721355..c8318c0 100644
--- a/mtdutils/mounts.h
+++ b/mtdutils/mounts.h
@@ -31,6 +31,7 @@ const MountedVolume *
find_mounted_volume_by_mount_point(const char *mount_point);
int unmount_mounted_volume(const MountedVolume *volume);
+int unmount_mounted_volume_detach(const MountedVolume *volume);
int remount_read_only(const MountedVolume* volume);
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index cc30334..ed74d0f 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;
+ }
}
}
}
@@ -300,20 +306,20 @@ static int read_block(const MtdPartition *partition, int fd, char *data)
if (TEMP_FAILURE_RETRY(lseek64(fd, pos, SEEK_SET)) != pos ||
TEMP_FAILURE_RETRY(read(fd, data, size)) != size) {
printf("mtd: read error at 0x%08llx (%s)\n",
- pos, strerror(errno));
+ (long long)pos, strerror(errno));
} else if (ioctl(fd, ECCGETSTATS, &after)) {
printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno));
return -1;
} else if (after.failed != before.failed) {
printf("mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
- after.corrected - before.corrected,
- after.failed - before.failed, pos);
+ after.corrected - before.corrected,
+ after.failed - before.failed, (long long)pos);
// copy the comparison baseline for the next read.
memcpy(&before, &after, sizeof(struct mtd_ecc_stats));
} else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
fprintf(stderr,
"mtd: MEMGETBADBLOCK returned %d at 0x%08llx: %s\n",
- mgbb, pos, strerror(errno));
+ mgbb, (long long)pos, strerror(errno));
} else {
return 0; // Success!
}
diff --git a/recovery.cpp b/recovery.cpp
index b7a5458..c13a689 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -31,6 +31,8 @@
#include <time.h>
#include <unistd.h>
+#include <chrono>
+
#include <base/file.h>
#include <base/stringprintf.h>
@@ -45,18 +47,60 @@
#include "ui.h"
#include "screen_ui.h"
#include "device.h"
+
+#include "voldclient.h"
+
#include "adb_install.h"
#include "adb.h"
#include "fuse_sideload.h"
#include "fuse_sdcard_provider.h"
+extern "C" {
+#include "recovery_cmds.h"
+}
+
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' },
+ { "headless", no_argument, NULL, 'h' },
{ "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' },
@@ -75,7 +119,6 @@ static const char *LOG_FILE = "/cache/recovery/log";
static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
static const char *LOCALE_FILE = "/cache/recovery/last_locale";
static const char *CACHE_ROOT = "/cache";
-static const char *SDCARD_ROOT = "/sdcard";
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
@@ -88,6 +131,8 @@ char* stage = NULL;
char* reason = NULL;
bool modified_flash = false;
+#include "mtdutils/mounts.h"
+
/*
* The recovery tool communicates with the main system through /cache files.
* /cache/recovery/command - INPUT - command line for tool, one arg per line
@@ -151,8 +196,7 @@ static const int MAX_ARG_LENGTH = 4096;
static const int MAX_ARGS = 100;
// open a given path, mounting partitions as necessary
-FILE*
-fopen_path(const char *path, const char *mode) {
+FILE* fopen_path(const char *path, const char *mode) {
if (ensure_path_mounted(path) != 0) {
LOGE("Can't mount %s\n", path);
return NULL;
@@ -166,23 +210,102 @@ fopen_path(const char *path, const char *mode) {
return fp;
}
+// close a file, log an error if the error indicator is set
+static void check_and_fclose(FILE *fp, const char *name) {
+ fflush(fp);
+ if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno));
+ fclose(fp);
+}
+
bool is_ro_debuggable() {
char value[PROPERTY_VALUE_MAX+1];
return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1');
}
static void redirect_stdio(const char* filename) {
- // If these fail, there's not really anywhere to complain...
- freopen(filename, "a", stdout); setbuf(stdout, NULL);
- freopen(filename, "a", stderr); setbuf(stderr, NULL);
-}
+ int pipefd[2];
+ if (pipe(pipefd) == -1) {
+ LOGE("pipe failed: %s\n", strerror(errno));
-// close a file, log an error if the error indicator is set
-static void
-check_and_fclose(FILE *fp, const char *name) {
- fflush(fp);
- if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno));
- fclose(fp);
+ // Fall back to traditional logging mode without timestamps.
+ // If these fail, there's not really anywhere to complain...
+ freopen(filename, "a", stdout); setbuf(stdout, NULL);
+ freopen(filename, "a", stderr); setbuf(stderr, NULL);
+
+ return;
+ }
+
+ pid_t pid = fork();
+ if (pid == -1) {
+ LOGE("fork failed: %s\n", strerror(errno));
+
+ // Fall back to traditional logging mode without timestamps.
+ // If these fail, there's not really anywhere to complain...
+ freopen(filename, "a", stdout); setbuf(stdout, NULL);
+ freopen(filename, "a", stderr); setbuf(stderr, NULL);
+
+ return;
+ }
+
+ if (pid == 0) {
+ /// Close the unused write end.
+ close(pipefd[1]);
+
+ auto start = std::chrono::steady_clock::now();
+
+ // Child logger to actually write to the log file.
+ FILE* log_fp = fopen(filename, "a");
+ if (log_fp == nullptr) {
+ LOGE("fopen \"%s\" failed: %s\n", filename, strerror(errno));
+ close(pipefd[0]);
+ _exit(1);
+ }
+
+ FILE* pipe_fp = fdopen(pipefd[0], "r");
+ if (pipe_fp == nullptr) {
+ LOGE("fdopen failed: %s\n", strerror(errno));
+ check_and_fclose(log_fp, filename);
+ close(pipefd[0]);
+ _exit(1);
+ }
+
+ char* line = nullptr;
+ size_t len = 0;
+ while (getline(&line, &len, pipe_fp) != -1) {
+ auto now = std::chrono::steady_clock::now();
+ double duration = std::chrono::duration_cast<std::chrono::duration<double>>(
+ now - start).count();
+ if (line[0] == '\n') {
+ fprintf(log_fp, "[%12.6lf]\n", duration);
+ } else {
+ fprintf(log_fp, "[%12.6lf] %s", duration, line);
+ }
+ fflush(log_fp);
+ }
+
+ LOGE("getline failed: %s\n", strerror(errno));
+
+ free(line);
+ check_and_fclose(log_fp, filename);
+ close(pipefd[0]);
+ _exit(1);
+ } else {
+ // Redirect stdout/stderr to the logger process.
+ // Close the unused read end.
+ close(pipefd[0]);
+
+ setbuf(stdout, nullptr);
+ setbuf(stderr, nullptr);
+
+ if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
+ LOGE("dup2 stdout failed: %s\n", strerror(errno));
+ }
+ if (dup2(pipefd[1], STDERR_FILENO) == -1) {
+ LOGE("dup2 stderr failed: %s\n", strerror(errno));
+ }
+
+ close(pipefd[1]);
+ }
}
// command line args come from, in decreasing precedence:
@@ -213,6 +336,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");
@@ -326,14 +457,18 @@ static void rotate_logs(int max) {
ensure_path_mounted(LAST_KMSG_FILE);
for (int i = max-1; i >= 0; --i) {
- std::string old_log = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d",
- LAST_LOG_FILE, i);
+ std::string old_log = android::base::StringPrintf("%s", LAST_LOG_FILE);
+ if (i > 0) {
+ old_log += "." + std::to_string(i);
+ }
std::string new_log = android::base::StringPrintf("%s.%d", LAST_LOG_FILE, i+1);
// Ignore errors if old_log doesn't exist.
rename(old_log.c_str(), new_log.c_str());
- std::string old_kmsg = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d",
- LAST_KMSG_FILE, i);
+ std::string old_kmsg = android::base::StringPrintf("%s", LAST_KMSG_FILE);
+ if (i > 0) {
+ old_kmsg += "." + std::to_string(i);
+ }
std::string new_kmsg = android::base::StringPrintf("%s.%d", LAST_KMSG_FILE, i+1);
rename(old_kmsg.c_str(), new_kmsg.c_str());
}
@@ -417,15 +552,15 @@ 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);
+ 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
@@ -472,10 +607,12 @@ static bool erase_volume(const char* volume) {
ui->Print("Formatting %s...\n", volume);
- ensure_path_unmounted(volume);
- int result = format_volume(volume);
+ if (volume[0] == '/') {
+ ensure_path_unmounted(volume);
+ }
+ int result = format_volume(volume, force);
- if (is_cache) {
+ if (!force && is_cache) {
while (head) {
FILE* f = fopen_path(head->name, "wb");
if (f) {
@@ -498,21 +635,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();
@@ -524,10 +677,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:
@@ -541,6 +713,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;
@@ -548,6 +729,9 @@ get_menu_selection(const char* const * headers, const char* const * items,
}
ui->EndMenu();
+ if (chosen_item == Device::kGoHome) {
+ device->GoHome();
+ }
return chosen_item;
}
@@ -557,8 +741,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));
@@ -617,21 +799,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);
@@ -664,7 +851,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;
}
@@ -672,15 +859,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) &&
erase_volume("/cache") &&
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 (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) {
@@ -706,7 +917,10 @@ static void choose_recovery_file(Device* device) {
// Add LAST_KMSG_FILE + LAST_KMSG_FILE.x
for (int i = 0; i < KEEP_LOG_COUNT; i++) {
char* log_file;
- if (asprintf(&log_file, (i == 0) ? "%s" : "%s.%d", LAST_LOG_FILE, i) == -1) {
+ int ret;
+ ret = (i == 0) ? asprintf(&log_file, "%s", LAST_LOG_FILE) :
+ asprintf(&log_file, "%s.%d", LAST_LOG_FILE, i);
+ if (ret == -1) {
// memory allocation failure - return early. Should never happen.
return;
}
@@ -717,7 +931,9 @@ static void choose_recovery_file(Device* device) {
}
char* kmsg_file;
- if (asprintf(&kmsg_file, (i == 0) ? "%s" : "%s.%d", LAST_KMSG_FILE, i) == -1) {
+ ret = (i == 0) ? asprintf(&kmsg_file, "%s", LAST_KMSG_FILE) :
+ asprintf(&kmsg_file, "%s.%d", LAST_KMSG_FILE, i);
+ if (ret == -1) {
// memory allocation failure - return early. Should never happen.
return;
}
@@ -734,12 +950,11 @@ 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;
- // TODO: do we need to redirect? ShowFile could just avoid writing to stdio.
- redirect_stdio("/dev/null");
ui->ShowFile(entries[chosen_item]);
- redirect_stdio(TEMPORARY_LOG_FILE);
}
for (size_t i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) {
@@ -747,29 +962,101 @@ static void choose_recovery_file(Device* device) {
}
}
-static int apply_from_sdcard(Device* device, bool* wipe_cache) {
+static int apply_from_storage(Device* device, const std::string& id, bool* wipe_cache) {
modified_flash = true;
- if (ensure_path_mounted(SDCARD_ROOT) != 0) {
- ui->Print("\n-- Couldn't mount %s.\n", SDCARD_ROOT);
+ int status;
+
+ if (!vdc->volumeMount(id)) {
return INSTALL_ERROR;
}
- char* path = browse_directory(SDCARD_ROOT, device);
- 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");
- 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();
void* token = start_sdcard_fuse(path);
- int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache,
+ vdc->volumeUnmount(vi.mId, true);
+
+ status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache,
TEMPORARY_INSTALL_FILE, false);
finish_sdcard_fuse(token);
- ensure_path_unmounted(SDCARD_ROOT);
+ free(path);
+ return status;
+}
+
+static int
+show_apply_update_menu(Device* device) {
+ static const char* headers[] = { "Apply update", NULL };
+ char* menu_items[MAX_NUM_MANAGED_VOLUMES + 1 + 1];
+ std::vector<VolumeInfo> volumes = vdc->getVolumes();
+
+ const int item_sideload = 0;
+ int n, i;
+ std::vector<VolumeInfo>::iterator vitr;
+
+refresh:
+ menu_items[item_sideload] = strdup("Apply from ADB");
+
+ n = item_sideload + 1;
+ for (vitr = volumes.begin(); vitr != volumes.end(); ++vitr) {
+ menu_items[n] = (char*)malloc(256);
+ sprintf(menu_items[n], "Choose from %s", vitr->mLabel.c_str());
+ ++n;
+ }
+ menu_items[n] = NULL;
+
+ bool wipe_cache;
+ int status = INSTALL_ERROR;
+
+ 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->DialogShowErrorLog("Install failed");
+ }
+
return status;
}
@@ -783,12 +1070,12 @@ 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:
case INSTALL_CORRUPT:
- ui->SetBackground(RecoveryUI::ERROR);
+ ui->SetBackground(RecoveryUI::D_ERROR);
break;
}
ui->SetProgressType(RecoveryUI::EMPTY);
@@ -801,62 +1088,87 @@ 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;
+
+ case Device::APPLY_UPDATE:
+ {
+ status = show_apply_update_menu(device);
- if (status == INSTALL_SUCCESS && should_wipe_cache) {
- if (!wipe_cache(false, device)) {
- status = INSTALL_ERROR;
+ if (status == INSTALL_SUCCESS && should_wipe_cache) {
+ if (!wipe_cache(false, device)) {
+ status = INSTALL_ERROR;
+ }
}
- }
- if (status != INSTALL_SUCCESS) {
- ui->SetBackground(RecoveryUI::ERROR);
- ui->Print("Installation aborted.\n");
- copy_logs();
- } else if (!ui->IsTextVisible()) {
- return Device::NO_ACTION; // reboot if logs aren't visible
- } else {
- ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card");
+ if (status >= 0 && status != INSTALL_NONE) {
+ if (status != INSTALL_SUCCESS) {
+ ui->SetBackground(RecoveryUI::D_ERROR);
+ ui->Print("Installation aborted.\n");
+ copy_logs();
+ } else if (!ui->IsTextVisible()) {
+ return Device::NO_ACTION; // reboot if logs aren't visible
+ } else {
+ ui->Print("\nInstall complete.\n");
+ }
}
+ break;
}
- break;
+ break;
- case Device::VIEW_RECOVERY_LOGS:
- choose_recovery_file(device);
- break;
+ case Device::VIEW_RECOVERY_LOGS:
+ choose_recovery_file(device);
+ break;
- case Device::MOUNT_SYSTEM:
- if (ensure_path_mounted("/system") != -1) {
- ui->Print("Mounted /system.\n");
- }
- break;
+ case Device::MOUNT_SYSTEM:
+ char system_root_image[PROPERTY_VALUE_MAX];
+ property_get("ro.build.system_root_image", system_root_image, "");
+
+ // For a system image built with the root directory (i.e.
+ // system_root_image == "true"), we mount it to /system_root, and symlink /system
+ // to /system_root/system to make adb shell work (the symlink is created through
+ // the build system).
+ // Bug: 22855115
+ if (strcmp(system_root_image, "true") == 0) {
+ if (ensure_path_mounted_at("/", "/system_root") != -1) {
+ ui->Print("Mounted /system.\n");
+ }
+ } else {
+ if (ensure_path_mounted("/system") != -1) {
+ ui->Print("Mounted /system.\n");
+ }
+ }
+
+ break;
+ }
+ if (status == Device::kRefresh) {
+ status = 0;
+ continue;
+ }
+ break;
}
}
}
@@ -885,6 +1197,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
@@ -903,12 +1249,32 @@ ui_print(const char* format, ...) {
}
}
-int
-main(int argc, char **argv) {
- time_t start = time(NULL);
+extern "C" int toybox_driver(int argc, char **argv);
- redirect_stdio(TEMPORARY_LOG_FILE);
+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) {
// If this binary is started with the single argument "--adbd",
// instead of being the normal recovery binary, it turns into kind
// of a stripped-down version of adbd that only supports the
@@ -921,6 +1287,43 @@ 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
+ // we may have two logger instances with different timestamps.
+ redirect_stdio(TEMPORARY_LOG_FILE);
+
printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
load_volume_table();
@@ -930,9 +1333,11 @@ main(int argc, char **argv) {
const char *update_package = NULL;
bool should_wipe_data = false;
bool should_wipe_cache = false;
+ bool should_wipe_media = false;
bool show_text = false;
bool sideload = false;
bool sideload_auto_reboot = false;
+ bool headless = false;
bool just_exit = false;
bool shutdown_after = false;
@@ -941,8 +1346,10 @@ main(int argc, char **argv) {
switch (arg) {
case 'i': send_intent = optarg; break;
case 'u': update_package = optarg; break;
+ case 'h': headless = true; 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;
@@ -975,6 +1382,9 @@ main(int argc, char **argv) {
ui = device->GetUI();
gCurrentUI = ui;
+ vdc = new VoldClient(device);
+ vdc->start();
+
ui->SetLocale(locale);
ui->Init();
@@ -986,6 +1396,9 @@ 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" }
};
@@ -1011,11 +1424,15 @@ main(int argc, char **argv) {
if (strncmp(update_package, "CACHE:", 6) == 0) {
int len = strlen(update_package) + 10;
char* modified_path = (char*)malloc(len);
- strlcpy(modified_path, "/cache/", len);
- strlcat(modified_path, update_package+6, len);
- printf("(replacing path \"%s\" with \"%s\")\n",
- update_package, modified_path);
- update_package = modified_path;
+ if (modified_path) {
+ strlcpy(modified_path, "/cache/", len);
+ strlcat(modified_path, update_package+6, len);
+ printf("(replacing path \"%s\" with \"%s\")\n",
+ update_package, modified_path);
+ update_package = modified_path;
+ }
+ else
+ printf("modified_path allocation failed\n");
}
}
printf("\n");
@@ -1023,10 +1440,20 @@ main(int argc, char **argv) {
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) {
status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, true);
if (status == INSTALL_SUCCESS && should_wipe_cache) {
@@ -1043,13 +1470,17 @@ 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) {
if (!wipe_cache(false, device)) {
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
@@ -1060,7 +1491,8 @@ 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;
@@ -1074,21 +1506,25 @@ 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);
}
if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) {
copy_logs();
- ui->SetBackground(RecoveryUI::ERROR);
+ ui->SetBackground(RecoveryUI::D_ERROR);
}
Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
- if ((status != INSTALL_SUCCESS && !sideload_auto_reboot) || ui->IsTextVisible()) {
+ if (headless) {
+ ui->ShowText(true);
+ ui->SetHeadlessMode();
+ finish_recovery(NULL);
+ for (;;) {
+ pause();
+ }
+ }
+ else if ((status != INSTALL_SUCCESS && !sideload_auto_reboot) || ui->IsTextVisible()) {
Device::BuiltinAction temp = prompt_and_wait(device, status);
if (temp != Device::NO_ACTION) {
after = temp;
@@ -1098,20 +1534,40 @@ 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;
}
sleep(5); // should reboot before this finishes
diff --git a/recovery_cmds.h b/recovery_cmds.h
new file mode 100644
index 0000000..8fc4d3e
--- /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-560dpi b/res-560dpi
new file mode 120000
index 0000000..8576a9b
--- /dev/null
+++ b/res-560dpi
@@ -0,0 +1 @@
+res-xxhdpi \ No newline at end of file
diff --git a/res-hdpi/images/font_log.png b/res-hdpi/images/font_log.png
new file mode 100644
index 0000000..59b990a
--- /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 0000000..d1aad6d
--- /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 0000000..c6c35ad
--- /dev/null
+++ b/res-hdpi/images/icon_header.png
Binary files differ
diff --git a/res-hdpi/images/icon_headless.png b/res-hdpi/images/icon_headless.png
new file mode 100644
index 0000000..780836f
--- /dev/null
+++ b/res-hdpi/images/icon_headless.png
Binary files differ
diff --git a/res-hdpi/images/icon_info.png b/res-hdpi/images/icon_info.png
new file mode 100644
index 0000000..3993941
--- /dev/null
+++ b/res-hdpi/images/icon_info.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 0000000..5aa8e62
--- /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 0000000..5836504
--- /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 0000000..b37422d
--- /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 0000000..ed0ccfa
--- /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 0000000..bcbe242
--- /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 0000000..59b990a
--- /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 0000000..0fc5723
--- /dev/null
+++ b/res-mdpi/images/icon_header.png
Binary files differ
diff --git a/res-mdpi/images/icon_headless.png b/res-mdpi/images/icon_headless.png
new file mode 100644
index 0000000..5d134cd
--- /dev/null
+++ b/res-mdpi/images/icon_headless.png
Binary files differ
diff --git a/res-mdpi/images/icon_info.png b/res-mdpi/images/icon_info.png
new file mode 100644
index 0000000..3993941
--- /dev/null
+++ b/res-mdpi/images/icon_info.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 0000000..81e4637
--- /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 0000000..d173042
--- /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 0000000..d9e3a43
--- /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 0000000..ef65d61
--- /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 0000000..d1aad6d
--- /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 0000000..6aead73
--- /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 0000000..c04c7bb
--- /dev/null
+++ b/res-xhdpi/images/icon_header.png
Binary files differ
diff --git a/res-xhdpi/images/icon_headless.png b/res-xhdpi/images/icon_headless.png
new file mode 100644
index 0000000..af283e2
--- /dev/null
+++ b/res-xhdpi/images/icon_headless.png
Binary files differ
diff --git a/res-xhdpi/images/icon_info.png b/res-xhdpi/images/icon_info.png
new file mode 100644
index 0000000..3993941
--- /dev/null
+++ b/res-xhdpi/images/icon_info.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 0000000..415715e
--- /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 0000000..237af6a
--- /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 0000000..425c0dc
--- /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 0000000..f7b6ce3
--- /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 0000000..6aead73
--- /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 0000000..f3b54b3
--- /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 0000000..e690d8d
--- /dev/null
+++ b/res-xxhdpi/images/icon_header.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_headless.png b/res-xxhdpi/images/icon_headless.png
new file mode 100644
index 0000000..09a8234
--- /dev/null
+++ b/res-xxhdpi/images/icon_headless.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_info.png b/res-xxhdpi/images/icon_info.png
new file mode 100644
index 0000000..3993941
--- /dev/null
+++ b/res-xxhdpi/images/icon_info.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 0000000..0b2e866
--- /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 0000000..03cb8a1
--- /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 0000000..7348841
--- /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 0000000..a981495
--- /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 0000000..ec8d899
--- /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 0000000..d7c326a
--- /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 0000000..a35f300
--- /dev/null
+++ b/res-xxxhdpi/images/icon_header.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_headless.png b/res-xxxhdpi/images/icon_headless.png
new file mode 100644
index 0000000..a715e3f
--- /dev/null
+++ b/res-xxxhdpi/images/icon_headless.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_info.png b/res-xxxhdpi/images/icon_info.png
new file mode 100644
index 0000000..3993941
--- /dev/null
+++ b/res-xxxhdpi/images/icon_info.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 0000000..f630553
--- /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 0000000..9211093
--- /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 0000000..9ee96ce
--- /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 0000000..d63ecb0
--- /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 0000000..edfeff8
--- /dev/null
+++ b/restore.cpp
@@ -0,0 +1,313 @@
+#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"
+
+#include "messagesocket.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;
+
+ MessageSocket ms;
+ ms.ClientInit();
+ ms.Show("Restore in progress...");
+
+ rc = do_restore_tree();
+ logmsg("do_restore: rc=%d\n", rc);
+
+ ms.Dismiss();
+
+ free(hash_name);
+ hash_name = NULL;
+
+ return rc;
+}
+
diff --git a/roots.cpp b/roots.cpp
index 2bd457e..3977745 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
+#include <dirent.h>
#include <fs_mgr.h>
#include "mtdutils/mtdutils.h"
@@ -35,10 +36,53 @@ extern "C" {
#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;
@@ -58,21 +102,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;
}
-int ensure_path_mounted(const char* path) {
+// Mount the volume specified by path at the given 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);
@@ -90,14 +197,20 @@ int ensure_path_mounted(const char* path) {
return -1;
}
- const MountedVolume* mv =
- find_mounted_volume_by_mount_point(v->mount_point);
- if (mv) {
- // volume is already mounted
- return 0;
+ if (!mount_point) {
+ mount_point = v->mount_point;
}
- mkdir(v->mount_point, 0755); // in case it doesn't already exist
+ 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_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.
@@ -106,31 +219,102 @@ int ensure_path_mounted(const char* path) {
partition = mtd_find_partition_by_name(v->blk_device);
if (partition == NULL) {
LOGE("failed to find \"%s\" partition to mount at \"%s\"\n",
- v->blk_device, v->mount_point);
+ v->blk_device, mount_point);
return -1;
}
- return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0);
+ 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) {
- result = mount(v->blk_device, v->mount_point, v->fs_type,
- v->flags, v->fs_options);
+ 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,
+ mntflags, v->fs_options);
if (result == 0) return 0;
- LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));
+ LOGE("failed to mount %s (%s)\n", mount_point, strerror(errno));
return -1;
}
- LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point);
+ LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, mount_point);
return -1;
}
-int ensure_path_unmounted(const char* path) {
- Volume* v = volume_for_path(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, force_rw);
+}
+
+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;
@@ -150,7 +334,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[]) {
@@ -167,7 +358,61 @@ static int exec_cmd(const char* path, char* const argv[]) {
return WEXITSTATUS(status);
}
-int format_volume(const char* volume) {
+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, 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);
@@ -183,11 +428,52 @@ int format_volume(const char* volume) {
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);
@@ -235,24 +521,24 @@ int format_volume(const char* volume) {
if (strcmp(v->fs_type, "ext4") == 0) {
result = make_ext4fs(v->blk_device, length, volume, sehandle);
} 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));
@@ -281,7 +567,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 230d9de..32d35b3 100644
--- a/roots.h
+++ b/roots.h
@@ -18,10 +18,7 @@
#define RECOVERY_ROOTS_H_
#include "common.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <fs_mgr.h>
// Load and parse volume data from /etc/recovery.fstab.
void load_volume_table();
@@ -31,23 +28,34 @@ 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);
-// Make sure that the volume 'path' is on is mounted. Returns 0 on
+// 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, 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);
// 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();
-#ifdef __cplusplus
-}
-#endif
+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 ff95915..46f4add 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -30,16 +30,16 @@
#include <vector>
-#include "base/strings.h"
-#include "cutils/properties.h"
+#include <base/strings.h>
+#include <base/stringprintf.h>
+#include <cutils/properties.h>
+
#include "common.h"
#include "device.h"
#include "minui/minui.h"
#include "screen_ui.h"
#include "ui.h"
-
-static int char_width;
-static int char_height;
+#include "cutils/properties.h"
// Return the current time as a double (including fractions of a second).
static double now() {
@@ -58,6 +58,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),
@@ -66,20 +68,34 @@ ScreenRecoveryUI::ScreenRecoveryUI() :
text_top_(0),
show_text(false),
show_text_ever(false),
+ dialog_icon(NONE),
+ dialog_text(nullptr),
+ dialog_show_log(false),
menu_(nullptr),
+ menu_headers_(nullptr),
+ header_items(0),
show_menu(false),
menu_items(0),
menu_sel(0),
+ sysbar_state(0),
file_viewer_text_(nullptr),
animation_fps(20),
installing_frames(-1),
stage(-1),
- max_stage(-1) {
-
- for (int i = 0; i < 5; i++) {
+ max_stage(-1),
+ rainbow(false),
+ wrap_count(0) {
+
+ headerIcon = nullptr;
+ sysbarBackIcon = nullptr;
+ sysbarBackHighlightIcon = nullptr;
+ sysbarHomeIcon = nullptr;
+ sysbarHomeHighlightIcon = nullptr;
+ for (int i = 0; i < NR_ICONS; i++) {
backgroundIcon[i] = nullptr;
}
pthread_mutex_init(&updateMutex, nullptr);
+ pthread_cond_init(&progressCondition, NULL);
}
// Clear the screen and draw the currently selected background icon (if any).
@@ -101,14 +117,16 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) {
int textWidth = gr_get_width(text_surface);
int textHeight = gr_get_height(text_surface);
int stageHeight = gr_get_height(stageMarkerEmpty);
+ int availableHeight = icon == INSTALLING_UPDATE && !DialogShowing() && show_text
+ ? 3 * gr_fb_height() / 4 : gr_fb_height();
int sh = (max_stage >= 0) ? stageHeight : 0;
iconX = (gr_fb_width() - iconWidth) / 2;
- iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2;
+ iconY = (availableHeight - (iconHeight+textHeight+40+sh)) / 2;
int textX = (gr_fb_width() - textWidth) / 2;
- int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40;
+ int textY = ((availableHeight - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40;
gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
if (stageHeight > 0) {
@@ -122,6 +140,8 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) {
}
}
+ LOGV("textX=%d textY=%d iconX=%d iconY=%d", textX, textY, iconX, iconY);
+
gr_color(255, 255, 255, 255);
gr_texticon(textX, textY, text_surface);
}
@@ -130,7 +150,7 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) {
// Draw the progress bar (if any) on the screen. Does not flip pages.
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::draw_progress_locked() {
- if (currentIcon == ERROR) return;
+ if (currentIcon == D_ERROR) return;
if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
GRSurface* icon = installation[installingFrame];
@@ -142,8 +162,11 @@ void ScreenRecoveryUI::draw_progress_locked() {
int width = gr_get_width(progressBarEmpty);
int height = gr_get_height(progressBarEmpty);
- int dx = (gr_fb_width() - width)/2;
- int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
+ int bottomOfUsableHeight = show_text ? 3 * gr_fb_height() / 4 : gr_fb_height();
+ int bottomOfIcon = bottomOfUsableHeight / 2 + iconHeight / 2;
+
+ int dx = (gr_fb_width() - width) / 2;
+ int dy = bottomOfIcon + (bottomOfUsableHeight - bottomOfIcon) / 2 - height / 2;
// Erase behind the progress bar (in case this was a progress-only update)
gr_color(0, 0, 0, 255);
@@ -184,13 +207,13 @@ void ScreenRecoveryUI::SetColor(UIElement e) {
break;
case MENU:
case MENU_SEL_BG:
- gr_color(0, 106, 157, 255);
+ gr_color(106, 103, 102, 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(0, 177, 229, 255);
break;
case LOG:
gr_color(196, 196, 196, 255);
@@ -198,6 +221,9 @@ void ScreenRecoveryUI::SetColor(UIElement e) {
case TEXT_FILL:
gr_color(0, 0, 0, 160);
break;
+ case ERROR_TEXT:
+ gr_color(255, 0, 0, 255);
+ break;
default:
gr_color(255, 255, 255, 255);
break;
@@ -213,7 +239,7 @@ void ScreenRecoveryUI::DrawHorizontalRule(int* y) {
void ScreenRecoveryUI::DrawTextLine(int* y, const char* line, bool bold) {
gr_text(4, *y, line, bold);
- *y += char_height + 4;
+ *y += char_height_ + 4;
}
void ScreenRecoveryUI::DrawTextLines(int* y, const char* const* lines) {
@@ -233,63 +259,193 @@ 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);
+ }
+}
+
+void ScreenRecoveryUI::draw_dialog()
+{
+ int x, y, w, h;
+
+ if (dialog_icon == HEADLESS) {
+ return;
+ }
+ draw_header_icon();
+ draw_sysbar();
+
+ int iconHeight = gr_get_height(backgroundIcon[dialog_icon]);
+
+ x = (gr_fb_width()/2 - (char_width_ * strlen(dialog_text))/2);
+ if (dialog_show_log) {
+ y = gr_get_height(headerIcon) + char_height_;
+ }
+ else {
+ y = (gr_fb_height()/2 + iconHeight/2);
+ }
+
+ SetColor(ERROR_TEXT);
+ gr_text(x, y, dialog_text, 0);
+ y += char_height_ + 2;
+
+ if (dialog_show_log) {
+ int cx, cy;
+ gr_set_font("log");
+ gr_font_size(&cx, &cy);
+
+ size_t row;
+ for (row = 0; row < log_text_rows_; ++row) {
+ gr_text(2, y, text_[row], 0);
+ y += cy + 2;
+ }
+ gr_set_font("menu");
+ }
+
+ if (dialog_icon == D_ERROR) {
+ /*
+ * This could be improved...
+ *
+ * Draw rect around text "Okay".
+ * Text is centered horizontally.
+ * Bottom of text is 4 lines from bottom of screen.
+ * Rect width 4px
+ * Rect padding 8px
+ */
+ w = char_width_ * 4;
+ h = char_height_;
+ x = gr_fb_width()/2 - w/2;
+ y = gr_fb_height() - h - 4 * char_height_;
+ SetColor(HEADER);
+ gr_fill(x-(4+8), y-(4+8), x+w+(4+8), y+h+(4+8));
+ SetColor(MENU_SEL_BG);
+ gr_fill(x-8, y-8, x+w+8, y+h+8);
+ SetColor(MENU_SEL_FG);
+ gr_text(x, y, "Okay", 0);
+ }
+}
+
// 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(currentIcon);
- draw_progress_locked();
- } else {
- gr_color(0, 0, 0, 255);
- gr_clear();
+ draw_background_locked(currentIcon);
- int y = 0;
- if (show_menu) {
- char recovery_fingerprint[PROPERTY_VALUE_MAX];
- property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, "");
+ if (DialogShowing()) {
+ draw_dialog();
+ return;
+ }
- SetColor(INFO);
- DrawTextLine(&y, "Android Recovery", true);
- for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
- DrawTextLine(&y, chunk.c_str(), false);
- }
- DrawTextLines(&y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
-
- SetColor(HEADER);
- DrawTextLines(&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);
- }
- y += char_height + 4;
+ if (show_text) {
+ if (currentIcon == ERASING || currentIcon == INSTALLING_UPDATE || currentIcon == VIEWING_LOG) {
+ size_t y = currentIcon == INSTALLING_UPDATE ? 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_first_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;
}
- DrawHorizontalRule(&y);
+ gr_set_font("menu");
+ return;
}
- // 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;
+ if (show_menu) {
+ 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 = (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));
+ }
}
}
}
@@ -297,20 +453,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_progress_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.
@@ -319,20 +464,31 @@ 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);
- int redraw = 0;
+ bool redraw = false;
+ double start = now();
+
+ LOGV("loop %f show_text=%d progressBarType=%d waiting=%d\n", start, show_text, progressBarType, update_waiting );
// update the installation animation, if active
// skip this if we have a text overlay (too expensive to update)
if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) &&
- installing_frames > 0 && !show_text) {
+ installing_frames > 0) {
installingFrame = (installingFrame + 1) % installing_frames;
- redraw = 1;
+ redraw = true;
}
// move the progress bar forward on timed intervals, if configured
@@ -343,13 +499,26 @@ void ScreenRecoveryUI::ProgressThreadLoop() {
if (p > 1.0) p = 1.0;
if (p > progress) {
progress = p;
- redraw = 1;
+ redraw = true;
}
}
- 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_progress_locked\n");
+ draw_progress_locked();
+ }
+ gr_flip();
+
+ update_waiting = false;
pthread_mutex_unlock(&updateMutex);
+
double end = now();
// minimum of 20ms delay between frames
double delay = interval - (end-start);
@@ -391,23 +560,47 @@ static char** Alloc2d(size_t rows, size_t cols) {
void ScreenRecoveryUI::Init() {
gr_init();
- gr_font_size(&char_width, &char_height);
- text_rows_ = gr_fb_height() / char_height;
- text_cols_ = gr_fb_width() / char_width;
+ 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_col_ = text_row_ = 0;
+ text_top_ = 1;
+
+ 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_ = Alloc2d(text_rows_, text_cols_ + 1);
+ 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_col_ = text_row_ = 0;
- text_top_ = 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;
backgroundIcon[NONE] = nullptr;
LoadBitmapArray("icon_installing", &installing_frames, &installation);
backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : nullptr;
backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
- LoadBitmap("icon_error", &backgroundIcon[ERROR]);
- backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
+ LoadBitmap("icon_info", &backgroundIcon[D_INFO]);
+ LoadBitmap("icon_error", &backgroundIcon[D_ERROR]);
+ backgroundIcon[NO_COMMAND] = backgroundIcon[D_ERROR];
+ LoadBitmap("icon_headless", &backgroundIcon[HEADLESS]);
LoadBitmap("progress_empty", &progressBarEmpty);
LoadBitmap("progress_fill", &progressBarFill);
@@ -417,7 +610,7 @@ void ScreenRecoveryUI::Init() {
LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]);
LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]);
LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
- LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);
+ LoadLocalizedBitmap("error_text", &backgroundText[D_ERROR]);
pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
@@ -467,7 +660,8 @@ void ScreenRecoveryUI::SetProgressType(ProgressType type) {
progressScopeStart = 0;
progressScopeSize = 0;
progress = 0;
- update_progress_locked();
+
+ update_screen_locked();
pthread_mutex_unlock(&updateMutex);
}
@@ -479,7 +673,8 @@ void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
progressScopeTime = now();
progressScopeDuration = seconds;
progress = 0;
- update_progress_locked();
+
+ update_screen_locked();
pthread_mutex_unlock(&updateMutex);
}
@@ -493,7 +688,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);
@@ -506,23 +701,22 @@ void ScreenRecoveryUI::SetStage(int current, int max) {
pthread_mutex_unlock(&updateMutex);
}
-void ScreenRecoveryUI::Print(const char *fmt, ...) {
- char buf[256];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, 256, fmt, ap);
- va_end(ap);
+void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
+ std::string str;
+ android::base::StringAppendV(&str, fmt, ap);
- fputs(buf, stdout);
+ if (copy_to_stdout) {
+ fputs(str.c_str(), stdout);
+ }
pthread_mutex_lock(&updateMutex);
- if (text_rows_ > 0 && text_cols_ > 0) {
- for (const char* ptr = buf; *ptr != '\0'; ++ptr) {
- if (*ptr == '\n' || text_col_ >= text_cols_) {
+ if (log_text_rows_ > 0 && log_text_cols_ > 0) {
+ for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
+ 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;
}
@@ -532,6 +726,20 @@ void ScreenRecoveryUI::Print(const char *fmt, ...) {
pthread_mutex_unlock(&updateMutex);
}
+void ScreenRecoveryUI::Print(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ PrintV(fmt, true, ap);
+ va_end(ap);
+}
+
+void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ PrintV(fmt, false, ap);
+ va_end(ap);
+}
+
void ScreenRecoveryUI::PutChar(char ch) {
pthread_mutex_lock(&updateMutex);
if (ch != '\n') text_[text_row_][text_col_++] = ch;
@@ -549,8 +757,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);
}
@@ -560,13 +768,15 @@ void ScreenRecoveryUI::ShowFile(FILE* fp) {
offsets.push_back(ftell(fp));
ClearText();
+ SetBackground(RecoveryUI::VIEWING_LOG);
+
struct stat sb;
fstat(fileno(fp), &sb);
bool show_prompt = false;
while (true) {
if (show_prompt) {
- Print("--(%d%% of %d bytes)--",
+ PrintOnScreenOnly("--(%d%% of %d bytes)--",
static_cast<int>(100 * (double(ftell(fp)) / double(sb.st_size))),
static_cast<int>(sb.st_size));
Redraw();
@@ -630,11 +840,82 @@ void ScreenRecoveryUI::ShowFile(const char* filename) {
text_top_ = old_text_top;
}
+void ScreenRecoveryUI::DialogShowInfo(const char* text)
+{
+ pthread_mutex_lock(&updateMutex);
+ free(dialog_text);
+ dialog_text = strdup(text);
+ dialog_show_log = false;
+ dialog_icon = D_INFO;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void ScreenRecoveryUI::DialogShowError(const char* text)
+{
+ pthread_mutex_lock(&updateMutex);
+ free(dialog_text);
+ dialog_text = strdup(text);
+ dialog_show_log = false;
+ dialog_icon = D_ERROR;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void ScreenRecoveryUI::DialogShowErrorLog(const char* text)
+{
+ pthread_mutex_lock(&updateMutex);
+ free(dialog_text);
+ dialog_text = strdup(text);
+ dialog_show_log = true;
+ dialog_icon = D_ERROR;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void ScreenRecoveryUI::DialogDismiss()
+{
+ pthread_mutex_lock(&updateMutex);
+ free(dialog_text);
+ dialog_text = NULL;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void ScreenRecoveryUI::SetHeadlessMode()
+{
+ pthread_mutex_lock(&updateMutex);
+ free(dialog_text);
+ dialog_text = strdup("");
+ dialog_show_log = false;
+ dialog_icon = HEADLESS;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+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);
@@ -643,22 +924,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);
@@ -669,7 +987,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 ea05bf1..78fcb9b 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -23,6 +23,9 @@
#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 {
@@ -49,12 +52,28 @@ class ScreenRecoveryUI : public RecoveryUI {
// printing messages
void Print(const char* fmt, ...) __printflike(2, 3);
+ void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3);
void ShowFile(const char* filename);
+ void DialogShowInfo(const char* text);
+ void DialogShowError(const char* text);
+ void DialogShowErrorLog(const char* text);
+ int DialogShowing() const { return (dialog_text != NULL); }
+ bool DialogDismissable() const { return (dialog_icon == D_ERROR); }
+ void DialogDismiss();
+ void SetHeadlessMode();
+
+ // 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);
@@ -62,7 +81,7 @@ class ScreenRecoveryUI : public RecoveryUI {
void Redraw();
enum UIElement {
- HEADER, MENU, MENU_SEL_BG, MENU_SEL_BG_ACTIVE, MENU_SEL_FG, LOG, TEXT_FILL, INFO
+ HEADER, MENU, MENU_SEL_BG, MENU_SEL_BG_ACTIVE, MENU_SEL_FG, LOG, TEXT_FILL, INFO, ERROR_TEXT
};
void SetColor(UIElement e);
@@ -73,8 +92,15 @@ class ScreenRecoveryUI : public RecoveryUI {
bool rtl_locale;
pthread_mutex_t updateMutex;
- GRSurface* backgroundIcon[5];
- GRSurface* backgroundText[5];
+ pthread_cond_t progressCondition;
+
+ GRSurface* headerIcon;
+ GRSurface* sysbarBackIcon;
+ GRSurface* sysbarBackHighlightIcon;
+ GRSurface* sysbarHomeIcon;
+ GRSurface* sysbarHomeHighlightIcon;
+ GRSurface* backgroundIcon[NR_ICONS];
+ GRSurface* backgroundText[NR_ICONS];
GRSurface** installation;
GRSurface* progressBarEmpty;
GRSurface* progressBarFill;
@@ -89,6 +115,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.
@@ -98,14 +125,26 @@ class ScreenRecoveryUI : public RecoveryUI {
bool show_text;
bool show_text_ever; // has show_text ever been true?
+ Icon dialog_icon;
+ char *dialog_text;
+ bool dialog_show_log;
+
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_;
int animation_fps;
@@ -115,16 +154,32 @@ class ScreenRecoveryUI : public RecoveryUI {
int stage, max_stage;
+ 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;
+
void draw_background_locked(Icon icon);
void draw_progress_locked();
+ int draw_header_icon();
+ void draw_menu_item(int textrow, const char *text, int selected);
+ void draw_sysbar();
+ void draw_dialog();
void draw_screen_locked();
void update_screen_locked();
- void update_progress_locked();
static void* ProgressThreadStartRoutine(void* data);
void ProgressThreadLoop();
void ShowFile(FILE*);
+ void PrintV(const char*, bool, va_list);
void PutChar(char);
void ClearText();
@@ -135,6 +190,8 @@ class ScreenRecoveryUI : public RecoveryUI {
void LoadBitmap(const char* filename, GRSurface** surface);
void LoadBitmapArray(const char* filename, int* frames, GRSurface*** surface);
void LoadLocalizedBitmap(const char* filename, GRSurface** surface);
+
+ void OMGRainbows();
};
#endif // RECOVERY_UI_H
diff --git a/tests/Android.mk b/tests/Android.mk
index 02a272a..4ce00b4 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -17,6 +17,7 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_STATIC_LIBRARIES := libverifier
LOCAL_SRC_FILES := asn1_decoder_test.cpp
diff --git a/ui.cpp b/ui.cpp
index 1a0b079..d1eca91 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -27,8 +27,11 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
+#include <sys/epoll.h>
+#include <cutils/properties.h>
#include <cutils/android_reboot.h>
+#include <cutils/properties.h>
#include "common.h"
#include "roots.h"
@@ -37,19 +40,113 @@
#include "screen_ui.h"
#include "ui.h"
+#include "voldclient.h"
+
+#include "messagesocket.h"
+
#define UI_WAIT_KEY_TIMEOUT_SEC 120
+/* Some extra input defines */
+#ifndef ABS_MT_ANGLE
+#define ABS_MT_ANGLE 0x38
+#endif
+
+static int string_split(char* s, char** fields, int maxfields)
+{
+ int n = 0;
+ while (n+1 < maxfields) {
+ char* p = strchr(s, ' ');
+ if (!p)
+ break;
+ *p = '\0';
+ printf("string_split: field[%d]=%s\n", n, s);
+ fields[n++] = s;
+ s = p+1;
+ }
+ fields[n] = s;
+ printf("string_split: last field[%d]=%s\n", n, s);
+ return n+1;
+}
+
+struct ms_info {
+ RecoveryUI* ui;
+ MessageSocket* sock;
+};
+
+static int message_socket_client_event(int fd, uint32_t epevents, void *data)
+{
+ ms_info* info = (ms_info*)data;
+
+ printf("message_socket client event\n");
+ if (!(epevents & EPOLLIN)) {
+ return 0;
+ }
+
+ char buf[256];
+ ssize_t nread;
+ nread = info->sock->Read(buf, sizeof(buf));
+ if (nread <= 0) {
+ ev_del_fd(fd);
+ info->ui->DialogDismiss();
+ info->sock->Close();
+ delete info->sock;
+ delete info;
+ return 0;
+ }
+
+ printf("message_socket client message <%s>\n", buf);
+
+ // Parse the message. Right now we support:
+ // dialog show <string>
+ // dialog dismiss
+ char* fields[3];
+ int nfields;
+ nfields = string_split(buf, fields, 3);
+ printf("fields=%d\n", nfields);
+ if (nfields < 2)
+ return 0;
+ printf("field[0]=%s, field[1]=%s\n", fields[0], fields[1]);
+ if (strcmp(fields[0], "dialog") == 0) {
+ if (strcmp(fields[1], "show") == 0 && nfields > 2) {
+ info->ui->DialogShowInfo(fields[2]);
+ }
+ if (strcmp(fields[1], "dismiss") == 0) {
+ info->ui->DialogDismiss();
+ }
+ }
+
+ return 0;
+}
+
+static int message_socket_listen_event(int fd, uint32_t epevents, void *data)
+{
+ ms_info* info = (ms_info*)data;
+ MessageSocket* sock = info->sock->Accept();
+ printf("message_socket_listen_event: event on %d\n", fd);
+ if (sock) {
+ printf("message_socket client connected\n");
+ ms_info* clientinfo = new ms_info;
+ clientinfo->ui = info->ui;
+ clientinfo->sock = sock;
+ ev_add_fd(sock->fd(), message_socket_client_event, clientinfo);
+ }
+ return 0;
+}
+
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));
@@ -62,6 +159,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");
}
}
@@ -80,8 +183,15 @@ static void* InputThreadLoop(void*) {
}
void RecoveryUI::Init() {
+ calibrate_swipe();
ev_init(InputCallback, this);
+ message_socket.ServerInit();
+ ms_info* info = new ms_info;
+ info->ui = this;
+ info->sock = &message_socket;
+ ev_add_fd(message_socket.fd(), message_socket_listen_event, info);
+
ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
@@ -93,31 +203,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_KEY && ev.code <= KEY_MAX) {
- ProcessKey(ev.code, ev.value);
+ if (ev.type != EV_REL) {
+ dev->rel_sum = 0;
+ }
+
+ 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;
@@ -135,11 +259,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) {
@@ -173,8 +300,12 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
break;
case RecoveryUI::REBOOT:
+#ifndef VERIFIER_TEST
+ vdc->unmountAll();
+#endif
if (reboot_enabled) {
- android_reboot(ANDROID_RB_RESTART, 0, 0);
+ property_set(ANDROID_RB_PROPERTY, "reboot,");
+ while(1) { pause(); }
}
break;
@@ -185,6 +316,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) {
+ process_key(dev, KEY_DOWN, 1); // press down key
+ process_key(dev, KEY_DOWN, 0); // and release it
+ dev->rel_sum = 0;
+ } else if (dev->rel_sum < -3) {
+ process_key(dev, KEY_UP, 1); // press up key
+ process_key(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);
@@ -203,7 +474,212 @@ 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 (DialogShowing()) {
+ if (DialogDismissable() && !dev->in_swipe) {
+ DialogDismiss();
+ }
+ 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;
+ if (!DialogShowing()) {
+ 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) {
+ if (DialogShowing()) {
+ if (DialogDismissable()) {
+ DialogDismiss();
+ }
+ return;
+ }
pthread_mutex_lock(&key_queue_mutex);
const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
if (key_queue_len < queue_max) {
@@ -215,6 +691,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.
@@ -224,13 +701,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) {
@@ -241,6 +723,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) {
@@ -335,3 +826,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 4dcaa0f..e71dbe2 100644
--- a/ui.h
+++ b/ui.h
@@ -21,6 +21,75 @@
#include <pthread.h>
#include <time.h>
+#include "messagesocket.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:
@@ -37,7 +106,7 @@ class RecoveryUI {
virtual void SetLocale(const char* locale) = 0;
// Set the overall recovery state ("background image").
- enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR };
+ enum Icon { NONE, INSTALLING_UPDATE, VIEWING_LOG, ERASING, NO_COMMAND, D_INFO, D_ERROR, HEADLESS, NR_ICONS };
virtual void SetBackground(Icon icon) = 0;
// --- progress indicator ---
@@ -62,16 +131,30 @@ class RecoveryUI {
virtual bool WasTextEverVisible() = 0;
// Write a message to the on-screen log (shown if the user has
- // toggled on the text display).
+ // toggled on the text display). Print() will also dump the message
+ // to stdout / log file, while PrintOnScreenOnly() not.
virtual void Print(const char* fmt, ...) __printflike(2, 3) = 0;
+ virtual void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3) = 0;
virtual void ShowFile(const char* filename) = 0;
+ virtual void ClearText() = 0;
+
+ virtual void DialogShowInfo(const char* text) = 0;
+ virtual void DialogShowError(const char* text) = 0;
+ virtual void DialogShowErrorLog(const char* text) = 0;
+ virtual int DialogShowing() const = 0;
+ virtual bool DialogDismissable() const = 0;
+ virtual void DialogDismiss() = 0;
+ virtual void SetHeadlessMode() = 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();
@@ -79,6 +162,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();
@@ -105,6 +191,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).
@@ -113,12 +206,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);
@@ -133,6 +229,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;
@@ -140,6 +237,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;
@@ -149,16 +253,31 @@ private:
pthread_t input_thread_;
+ MessageSocket message_socket;
+
void OnKeyDetected(int key_code);
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/uncrypt/Android.mk b/uncrypt/Android.mk
index c7d4d37..e73c8f1 100644
--- a/uncrypt/Android.mk
+++ b/uncrypt/Android.mk
@@ -16,6 +16,8 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_CLANG := true
+
LOCAL_SRC_FILES := uncrypt.cpp
LOCAL_MODULE := uncrypt
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index 46da86d..2cf09a5 100644
--- a/uncrypt/uncrypt.cpp
+++ b/uncrypt/uncrypt.cpp
@@ -226,7 +226,7 @@ static int produce_block_map(const char* path, const char* map_file, const char*
int wfd = -1;
if (encrypted) {
- wfd = open(blk_dev, O_WRONLY | O_SYNC);
+ wfd = open(blk_dev, O_WRONLY);
if (wfd < 0) {
ALOGE("failed to open fd for writing: %s\n", strerror(errno));
return -1;
diff --git a/updater/Android.mk b/updater/Android.mk
index ff02a33..470eb5d 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -17,22 +17,32 @@ include $(CLEAR_VARS)
# needed only for OTA packages.)
LOCAL_MODULE_TAGS := eng
+LOCAL_CLANG := true
+
LOCAL_SRC_FILES := $(updater_src_files)
-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 += \
libext4_utils_static \
libsparse_static \
libz
+
+LOCAL_C_INCLUDES += external/e2fsprogs/lib
+LOCAL_STATIC_LIBRARIES += libext2_blkid libext2_uuid
+
+ifneq ($(BOARD_RECOVERY_BLDRMSG_OFFSET),)
+ LOCAL_CFLAGS += -DBOARD_RECOVERY_BLDRMSG_OFFSET=$(BOARD_RECOVERY_BLDRMSG_OFFSET)
+endif
+
+ifeq ($(BOARD_SUPPRESS_EMMC_WIPE),true)
+ LOCAL_CFLAGS += -DSUPPRESS_EMMC_WIPE
endif
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
-LOCAL_STATIC_LIBRARIES += libmincrypt libbz
-LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc
+LOCAL_STATIC_LIBRARIES += libmincrypt libbz libxz
+LOCAL_STATIC_LIBRARIES += libcutils liblog libc
LOCAL_STATIC_LIBRARIES += libselinux
tune2fs_static_libraries := \
libext2_com_err \
@@ -57,7 +67,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.
@@ -79,13 +93,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.c b/updater/blockimg.c
index b006d10..be56fbd 100644
--- a/updater/blockimg.c
+++ b/updater/blockimg.c
@@ -61,30 +61,91 @@ typedef struct {
int pos[0];
} RangeSet;
+#define RANGESET_MAX_POINTS \
+ ((int)((INT_MAX / sizeof(int)) - sizeof(RangeSet)))
+
static RangeSet* parse_range(char* text) {
char* save;
- int num;
- num = strtol(strtok_r(text, ",", &save), NULL, 0);
+ char* token;
+ int i, num;
+ long int val;
+ RangeSet* out = NULL;
+ size_t bufsize;
- RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int));
- if (out == NULL) {
- fprintf(stderr, "failed to allocate range of %zu bytes\n",
- sizeof(RangeSet) + num * sizeof(int));
- exit(1);
+ if (!text) {
+ goto err;
+ }
+
+ token = strtok_r(text, ",", &save);
+
+ if (!token) {
+ goto err;
}
+
+ val = strtol(token, NULL, 0);
+
+ if (val < 2 || val > RANGESET_MAX_POINTS) {
+ goto err;
+ } else if (val % 2) {
+ goto err; // must be even
+ }
+
+ num = (int) val;
+ bufsize = sizeof(RangeSet) + num * sizeof(int);
+
+ out = malloc(bufsize);
+
+ if (!out) {
+ fprintf(stderr, "failed to allocate range of %zu bytes\n", bufsize);
+ goto err;
+ }
+
out->count = num / 2;
out->size = 0;
- int i;
+
for (i = 0; i < num; ++i) {
- out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0);
- if (i%2) {
+ token = strtok_r(NULL, ",", &save);
+
+ if (!token) {
+ goto err;
+ }
+
+ val = strtol(token, NULL, 0);
+
+ if (val < 0 || val > INT_MAX) {
+ goto err;
+ }
+
+ out->pos[i] = (int) val;
+
+ if (i % 2) {
+ if (out->pos[i - 1] >= out->pos[i]) {
+ goto err; // empty or negative range
+ }
+
+ if (out->size > INT_MAX - out->pos[i]) {
+ goto err; // overflow
+ }
+
out->size += out->pos[i];
} else {
+ if (out->size < 0) {
+ goto err;
+ }
+
out->size -= out->pos[i];
}
}
+ if (out->size <= 0) {
+ goto err;
+ }
+
return out;
+
+err:
+ fprintf(stderr, "failed to parse range '%s'\n", text ? text : "NULL");
+ exit(1);
}
static int range_overlaps(RangeSet* r1, RangeSet* r2) {
@@ -135,11 +196,6 @@ static int write_all(int fd, const uint8_t* data, size_t size) {
written += w;
}
- if (fsync(fd) == -1) {
- fprintf(stderr, "fsync failed: %s\n", strerror(errno));
- return -1;
- }
-
return 0;
}
@@ -285,7 +341,12 @@ static bool receive_new_data(const unsigned char* data, int size, void* cookie)
static void* unzip_new_data(void* cookie) {
NewThreadInfo* nti = (NewThreadInfo*) cookie;
- mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
+ if (strncmp(".xz", nti->entry->fileName + (nti->entry->fileNameLen - 3), 3) == 0) {
+ mzProcessZipEntryContentsXZ(nti->za, nti->entry, receive_new_data, nti);
+ } else {
+ mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti);
+ }
+
return NULL;
}
@@ -671,7 +732,7 @@ static int WriteStash(const char* base, const char* id, int blocks, uint8_t* buf
fprintf(stderr, " writing %d blocks to %s\n", blocks, cn);
- fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, STASH_FILE_MODE));
+ fd = TEMP_FAILURE_RETRY(open(fn, O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE));
if (fd == -1) {
fprintf(stderr, "failed to create \"%s\": %s\n", fn, strerror(errno));
@@ -1465,10 +1526,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));
goto pceout;
}
+#endif
}
}
@@ -1726,6 +1789,10 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int argc,
}
if (params.canwrite) {
+ if (fsync(params.fd) == -1) {
+ fprintf(stderr, "fsync failed: %s\n", strerror(errno));
+ goto pbiudone;
+ }
fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
fflush(cmd_pipe);
}
diff --git a/updater/install.c b/updater/install.c
index 01a5dd2..19c22fb 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -33,6 +33,7 @@
#include <sys/xattr.h>
#include <linux/xattr.h>
#include <inttypes.h>
+#include <blkid/blkid.h>
#include "bootloader.h"
#include "applypatch/applypatch.h"
@@ -47,11 +48,8 @@
#include "updater.h"
#include "install.h"
#include "tune2fs.h"
-
-#ifdef USE_EXT4
#include "make_ext4fs.h"
#include "wipe.h"
-#endif
void uiPrint(State* state, char* buffer) {
char* line = strtok(buffer, "\n");
@@ -61,6 +59,11 @@ void uiPrint(State* state, char* buffer) {
line = strtok(NULL, "\n");
}
fprintf(ui->cmd_pipe, "ui_print\n");
+
+ // The recovery will only print the contents to screen for pipe command
+ // ui_print. We need to dump the contents to stderr (which has been
+ // redirected to the log file) directly.
+ fprintf(stderr, "%s", buffer);
}
__attribute__((__format__(printf, 2, 3))) __nonnull((2))
@@ -165,6 +168,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) {
@@ -332,7 +345,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) {
@@ -360,7 +372,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);
@@ -931,6 +942,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)
//
@@ -974,7 +1053,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
goto done;
}
- if (fread(buffer, 1, st.st_size, f) != st.st_size) {
+ if (fread(buffer, 1, st.st_size, f) != (size_t)st.st_size) {
ErrorAbort(state, "%s: failed to read %lld bytes from %s",
name, (long long)st.st_size+1, filename);
fclose(f);
@@ -1416,10 +1495,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, "%s() expects 2 args, got %d", name, argc);
@@ -1435,6 +1515,9 @@ 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);
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+ fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_CUR);
+#endif
fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f);
fclose(f);
free(filename);
@@ -1447,6 +1530,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, "%s() failed to reboot", name);
return NULL;
@@ -1477,6 +1565,9 @@ 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);
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+ fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_CUR);
+#endif
int to_write = strlen(stagestr)+1;
int max_size = sizeof(((struct bootloader_message*)0)->stage);
if (to_write > max_size) {
@@ -1503,6 +1594,9 @@ 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);
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+ fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_CUR);
+#endif
fread(buffer, sizeof(buffer), 1, f);
fclose(f);
buffer[sizeof(buffer)-1] = '\0';
@@ -1622,4 +1716,5 @@ void RegisterInstallFunctions() {
RegisterFunction("enable_reboot", EnableRebootFn);
RegisterFunction("tune2fs", Tune2FsFn);
+ RegisterFunction("get_device_compatible", ConfirmDevVariant);
}
diff --git a/verifier_test.cpp b/verifier_test.cpp
index 82546ed..a9d42c0 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -141,12 +141,34 @@ class FakeUI : public RecoveryUI {
vfprintf(stderr, fmt, ap);
va_end(ap);
}
+ void PrintOnScreenOnly(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
void ShowFile(const char*) { }
+ void ClearText() {}
+
+ virtual void DialogShowInfo(const char* text) {}
+ virtual void DialogShowError(const char* text) {}
+ virtual void DialogShowErrorLog(const char* text) {}
+ virtual int DialogShowing() const { return 0; }
+ bool DialogDismissable() const { return false; }
+ virtual void DialogDismiss() {}
+ virtual void SetHeadlessMode() {}
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection) { }
- int SelectMenu(int sel) { return 0; }
+ int SelectMenu(int sel, bool abs = false) { return 0; }
void EndMenu() { }
+
+ virtual int MenuItemStart() const { return 0; }
+ virtual int MenuItemHeight() const { return 0; }
+
+ virtual int GetSysbarHeight() { return 0; }
+ virtual int GetSysbarState() { return 0; }
+ virtual void SetSysbarState(int state) {}
};
void
diff --git a/voldclient.cpp b/voldclient.cpp
new file mode 100644
index 0000000..0c8462a
--- /dev/null
+++ b/voldclient.cpp
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+#include <string>
+#include <sstream>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#include "common.h"
+#include "roots.h"
+#include "voldclient.h"
+
+#include "VolumeBase.h"
+#include "ResponseCode.h"
+
+using namespace android::vold;
+
+VoldClient* vdc = NULL;
+
+static void* threadfunc(void* arg)
+{
+ VoldClient* self = (VoldClient*)arg;
+ self->run();
+ return NULL;
+}
+
+VoldClient::VoldClient(VoldWatcher* watcher /* = nullptr */) :
+ mRunning(false),
+ mSock(-1),
+ mSockMutex(PTHREAD_MUTEX_INITIALIZER),
+ mSockCond(PTHREAD_COND_INITIALIZER),
+ mInFlight(0),
+ mResult(0),
+ mWatcher(watcher),
+ mVolumeLock(PTHREAD_RWLOCK_INITIALIZER),
+ mVolumeChanged(false),
+ mEmulatedStorage(true)
+{
+}
+
+void VoldClient::start(void)
+{
+ mRunning = true;
+ pthread_create(&mThread, NULL, threadfunc, this);
+ while (mSock == -1) {
+ sleep(1);
+ }
+ while (mInFlight != 0) {
+ sleep(1);
+ }
+ LOGI("VoldClient initialized, storage is %s\n",
+ vdc->isEmulatedStorage() ? "emulated" : "physical");
+}
+
+void VoldClient::stop(void)
+{
+ if (mRunning) {
+ mRunning = false;
+ close(mSock);
+ mSock = -1;
+ void* retval;
+ pthread_join(mThread, &retval);
+ }
+}
+
+VolumeInfo VoldClient::getVolume(const std::string& id)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ VolumeInfo* info = getVolumeLocked(id);
+ pthread_rwlock_unlock(&mVolumeLock);
+ return *info;
+}
+
+bool VoldClient::reset(void)
+{
+ const char *cmd[2] = { "volume", "reset" };
+ return sendCommand(2, cmd);
+}
+
+bool VoldClient::mountAll(void)
+{
+ bool ret = true;
+ pthread_rwlock_rdlock(&mVolumeLock);
+ for (auto& info : mVolumes) {
+ if (info.mState == (int)VolumeBase::State::kUnmounted) {
+ if (!volumeMount(info.mId)) {
+ ret = false;
+ }
+ }
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+ return ret;
+}
+
+bool VoldClient::unmountAll(void)
+{
+ bool ret = true;
+ pthread_rwlock_rdlock(&mVolumeLock);
+ for (auto& info : mVolumes) {
+ if (info.mState == (int)VolumeBase::State::kMounted) {
+ if (!volumeUnmount(info.mId)) {
+ ret = false;
+ }
+ }
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+ return ret;
+}
+
+bool VoldClient::volumeMount(const std::string& id)
+{
+ // Special case for emulated storage
+ if (id == "emulated") {
+ pthread_rwlock_wrlock(&mVolumeLock);
+ VolumeInfo* info = getVolumeLocked(id);
+ if (!info) {
+ pthread_rwlock_unlock(&mVolumeLock);
+ return false;
+ }
+ info->mPath = "/storage/emulated";
+ info->mInternalPath = "/data/media";
+ pthread_rwlock_unlock(&mVolumeLock);
+ return ensure_path_mounted("/data") == 0;
+ }
+ const char *cmd[3] = { "volume", "mount", id.c_str() };
+ return sendCommand(3, cmd);
+}
+
+// NB: can only force or detach, not both
+bool VoldClient::volumeUnmount(const std::string& id, bool detach /* = false */)
+{
+ // Special case for emulated storage
+ if (id == "emulated") {
+ if (ensure_path_unmounted("/data", detach) != 0) {
+ return false;
+ }
+ return true;
+ }
+ const char *cmd[4] = { "volume", "unmount", id.c_str(), NULL };
+ int cmdlen = 3;
+ if (detach) {
+ cmd[3] = "detach";
+ cmdlen = 4;
+ }
+ return sendCommand(cmdlen, cmd);
+}
+
+bool VoldClient::volumeFormat(const std::string& id)
+{
+ const char* cmd[3] = { "volume", "format", id.c_str() };
+ return sendCommand(3, cmd);
+}
+
+void VoldClient::resetVolumeState(void)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ mVolumes.clear();
+ mVolumeChanged = false;
+ mEmulatedStorage = true;
+ pthread_rwlock_unlock(&mVolumeLock);
+ if (mWatcher) {
+ mWatcher->onVolumeChanged();
+ }
+ const char *cmd[2] = { "volume", "reset" };
+ sendCommand(2, cmd, false);
+}
+
+VolumeInfo* VoldClient::getVolumeLocked(const std::string& id)
+{
+ for (auto iter = mVolumes.begin(); iter != mVolumes.end(); ++iter) {
+ if (iter->mId == id) {
+ return &(*iter);
+ }
+ }
+ return nullptr;
+}
+
+bool VoldClient::sendCommand(unsigned int len, const char** command, bool wait /* = true */)
+{
+ char line[4096];
+ char* p;
+ unsigned int i;
+ size_t sz;
+ bool ret = true;
+
+ p = line;
+ p += sprintf(p, "0 "); /* 0 is a (now required) sequence number */
+ for (i = 0; i < len; i++) {
+ const char* cmd = command[i];
+ if (!cmd[0] || !strchr(cmd, ' '))
+ p += sprintf(p, "%s", cmd);
+ else
+ p += sprintf(p, "\"%s\"", cmd);
+ if (i < len - 1)
+ *p++ = ' ';
+ if (p >= line + sizeof(line)) {
+ LOGE("vold command line too long\n");
+ exit(1);
+ }
+ }
+
+ // only one writer at a time
+ pthread_mutex_lock(&mSockMutex);
+ if (write(mSock, line, (p - line) + 1) < 0) {
+ LOGE("Unable to send command to vold!\n");
+ pthread_mutex_unlock(&mSockMutex);
+ return false;
+ }
+ ++mInFlight;
+
+ if (wait) {
+ while (mInFlight) {
+ // wait for completion
+ pthread_cond_wait(&mSockCond, &mSockMutex);
+ }
+ ret = (mResult >= 200 && mResult < 300);
+ }
+ pthread_mutex_unlock(&mSockMutex);
+
+ return ret;
+}
+
+void VoldClient::handleCommandOkay(void)
+{
+ bool changed = false;
+ pthread_rwlock_wrlock(&mVolumeLock);
+ if (mVolumeChanged) {
+ mVolumeChanged = false;
+ changed = true;
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+ if (changed) {
+ mWatcher->onVolumeChanged();
+ }
+}
+
+void VoldClient::handleVolumeCreated(const std::string& id, const std::string& type,
+ const std::string& disk, const std::string& guid)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ // Ignore emulated storage if primary storage is physical
+ if (id == "emulated") {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.vold.primary_physical", value, "0");
+ if (value[0] == '1' || value[0] == 'y' || !strcmp(value, "true")) {
+ mEmulatedStorage = false;
+ return;
+ }
+ mEmulatedStorage = true;
+ }
+ VolumeInfo info;
+ info.mId = id;
+ mVolumes.push_back(info);
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumeStateChanged(const std::string& id, const std::string& state)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ auto info = getVolumeLocked(id);
+ if (info) {
+ info->mState = atoi(state.c_str());
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumeFsLabelChanged(const std::string& id, const std::string& label)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ auto info = getVolumeLocked(id);
+ if (info) {
+ info->mLabel = label;
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumePathChanged(const std::string& id, const std::string& path)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ auto info = getVolumeLocked(id);
+ if (info) {
+ info->mPath = path;
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumeInternalPathChanged(const std::string& id, const std::string& path)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ auto info = getVolumeLocked(id);
+ if (info) {
+ info->mInternalPath = path;
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+void VoldClient::handleVolumeDestroyed(const std::string& id)
+{
+ pthread_rwlock_wrlock(&mVolumeLock);
+ for (auto iter = mVolumes.begin(); iter != mVolumes.end(); ++iter) {
+ if (iter->mId == id) {
+ mVolumes.erase(iter);
+ break;
+ }
+ }
+ pthread_rwlock_unlock(&mVolumeLock);
+}
+
+static std::vector<std::string> split(const std::string& line)
+{
+ std::vector<std::string> tokens;
+ const char* tok = line.c_str();
+
+ while (*tok) {
+ unsigned int toklen;
+ const char* next;
+ if (*tok == '"') {
+ ++tok;
+ const char* q = strchr(tok, '"');
+ if (!q) {
+ LOGE("vold line <%s> malformed\n", line.c_str());
+ exit(1);
+ }
+ toklen = q - tok;
+ next = q + 1;
+ if (*next) {
+ if (*next != ' ') {
+ LOGE("vold line <%s> malformed\n", line.c_str());
+ exit(0);
+ }
+ ++next;
+ }
+ }
+ else {
+ next = strchr(tok, ' ');
+ if (next) {
+ toklen = next - tok;
+ ++next;
+ }
+ else {
+ toklen = strlen(tok);
+ next = tok + toklen;
+ }
+ }
+ tokens.push_back(std::string(tok, toklen));
+ tok = next;
+ }
+
+ return tokens;
+}
+
+void VoldClient::dispatch(const std::string& line)
+{
+ std::vector<std::string> tokens = split(line);
+
+ switch (mResult) {
+ case ResponseCode::CommandOkay:
+ handleCommandOkay();
+ break;
+ case ResponseCode::VolumeCreated:
+ handleVolumeCreated(tokens[1], tokens[2], tokens[3], tokens[4]);
+ break;
+ case ResponseCode::VolumeStateChanged:
+ handleVolumeStateChanged(tokens[1], tokens[2]);
+ break;
+ case ResponseCode::VolumeFsLabelChanged:
+ handleVolumeFsLabelChanged(tokens[1], tokens[2]);
+ break;
+ case ResponseCode::VolumePathChanged:
+ handleVolumePathChanged(tokens[1], tokens[2]);
+ break;
+ case ResponseCode::VolumeInternalPathChanged:
+ handleVolumeInternalPathChanged(tokens[1], tokens[2]);
+ break;
+ case ResponseCode::VolumeDestroyed:
+ handleVolumeDestroyed(tokens[1]);
+ break;
+ }
+}
+
+void VoldClient::run(void)
+{
+ LOGI("VoldClient thread starting\n");
+ while (mRunning) {
+ if (mSock == -1) {
+ LOGI("Connecting to Vold...\n");
+ mSock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ if (mSock == -1) {
+ sleep(1);
+ continue;
+ }
+ resetVolumeState();
+ }
+
+ int rc;
+
+ struct timeval tv;
+ fd_set rfds;
+
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_usec = 100 * 1000;
+ FD_ZERO(&rfds);
+ FD_SET(mSock, &rfds);
+
+ rc = select(mSock + 1, &rfds, NULL, NULL, &tv);
+ if (rc <= 0) {
+ if (rc < 0 && errno != EINTR) {
+ LOGE("vdc: error in select (%s)\n", strerror(errno));
+ close(mSock);
+ mSock = -1;
+ }
+ continue;
+ }
+
+ char buf[4096];
+ memset(buf, 0, sizeof(buf));
+ rc = read(mSock, buf, sizeof(buf) - 1);
+ if (rc <= 0) {
+ LOGE("vdc: read failed: %s\n", (rc == 0 ? "EOF" : strerror(errno)));
+ close(mSock);
+ mSock = -1;
+ continue;
+ }
+
+ // dispatch each line of the response
+ int nread = rc;
+ int off = 0;
+ while (off < nread) {
+ char* eol = (char*)memchr(buf + off, 0, nread - off);
+ if (!eol) {
+ break;
+ }
+ mResult = atoi(buf + off);
+ dispatch(std::string(buf + off));
+ if (mResult >= 200 && mResult < 600) {
+ pthread_mutex_lock(&mSockMutex);
+ --mInFlight;
+ pthread_cond_signal(&mSockCond);
+ pthread_mutex_unlock(&mSockMutex);
+ }
+ off = (eol - buf) + 1;
+ }
+ }
+}
diff --git a/voldclient.h b/voldclient.h
new file mode 100644
index 0000000..c5f568e
--- /dev/null
+++ b/voldclient.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _VOLD_CLIENT_H
+#define _VOLD_CLIENT_H
+
+#include <sys/types.h>
+#include <pthread.h>
+
+#include <string>
+#include <vector>
+
+class VoldWatcher {
+public:
+ virtual ~VoldWatcher(void) {}
+ virtual void onVolumeChanged(void) = 0;
+};
+
+class VolumeInfo {
+public:
+ std::string mId;
+ std::string mLabel;
+ std::string mPath;
+ std::string mInternalPath;
+ int mState;
+};
+
+class VoldClient
+{
+public:
+ VoldClient(VoldWatcher* watcher = nullptr);
+
+ void start(void);
+ void stop(void);
+
+ std::vector<VolumeInfo> getVolumes(void) { return mVolumes; }
+ VolumeInfo getVolume(const std::string& id);
+
+ bool isEmulatedStorage(void) { return mEmulatedStorage; }
+
+ bool reset(void);
+ bool mountAll(void);
+ bool unmountAll(void);
+
+ bool volumeMount(const std::string& id);
+ bool volumeUnmount(const std::string& id, bool detach = false);
+ bool volumeFormat(const std::string& id);
+ bool volumeAvailable(const std::string& id);
+
+private:
+ void resetVolumeState(void);
+
+ VolumeInfo* getVolumeLocked(const std::string& id);
+ bool sendCommand(unsigned int len, const char** command, bool wait = true);
+
+ void handleCommandOkay(void);
+ void handleVolumeCreated(const std::string& id, const std::string& type,
+ const std::string& disk, const std::string& guid);
+ void handleVolumeStateChanged(const std::string& id, const std::string& state);
+ void handleVolumeFsLabelChanged(const std::string& id, const std::string& label);
+ void handleVolumePathChanged(const std::string& id, const std::string& path);
+ void handleVolumeInternalPathChanged(const std::string& id, const std::string& path);
+ void handleVolumeDestroyed(const std::string& id);
+
+ void dispatch(const std::string& line);
+
+public:
+ void run(void); // INTERNAL
+
+private:
+ bool mRunning;
+ int mSock;
+ pthread_t mThread;
+ pthread_mutex_t mSockMutex;
+ pthread_cond_t mSockCond;
+ unsigned int mInFlight;
+ unsigned int mResult;
+ VoldWatcher* mWatcher;
+ pthread_rwlock_t mVolumeLock;
+ bool mVolumeChanged;
+ bool mEmulatedStorage;
+ std::vector<VolumeInfo> mVolumes;
+};
+
+extern VoldClient* vdc;
+
+#endif
+
diff --git a/wear_ui.cpp b/wear_ui.cpp
index 55b7afc..9d4ec09 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -81,7 +81,7 @@ WearRecoveryUI::WearRecoveryUI() :
menu_items(0),
menu_sel(0) {
- for (size_t i = 0; i < 5; i++)
+ for (size_t i = 0; i < NR_ICONS; i++)
backgroundIcon[i] = NULL;
pthread_mutex_init(&updateMutex, NULL);
@@ -122,7 +122,7 @@ void WearRecoveryUI::draw_background_locked(Icon icon)
// Should only be called with updateMutex locked.
void WearRecoveryUI::draw_progress_locked()
{
- if (currentIcon == ERROR) return;
+ if (currentIcon == D_ERROR) return;
if (progressBarType != DETERMINATE) return;
int width = progress_bar_width;
@@ -348,8 +348,8 @@ void WearRecoveryUI::Init()
LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
- LoadBitmap("icon_error", &backgroundIcon[ERROR]);
- backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
+ LoadBitmap("icon_error", &backgroundIcon[D_ERROR]);
+ backgroundIcon[NO_COMMAND] = backgroundIcon[D_ERROR];
introFrames = (GRSurface**)malloc(intro_frames * sizeof(GRSurface*));
for (int i = 0; i < intro_frames; ++i) {
@@ -503,7 +503,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 839a264..2947d9c 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -53,7 +53,7 @@ class WearRecoveryUI : public RecoveryUI {
// 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();
@@ -91,7 +91,7 @@ class WearRecoveryUI : public RecoveryUI {
bool rtl_locale;
pthread_mutex_t updateMutex;
- GRSurface* backgroundIcon[5];
+ GRSurface* backgroundIcon[NR_ICONS];
GRSurface* *introFrames;
GRSurface* *loopFrames;