Merge up to android-8.1.0_r1 and fix conflicts

Change-Id: I2dc060134d15ec9f015a606cb24ef8276f6af1fc
This commit is contained in:
Ethan Yonker
2017-12-15 12:48:49 -06:00
96 changed files with 5341 additions and 4510 deletions
+4
View File
@@ -0,0 +1,4 @@
subdirs = [
// "bootloader_message",
// "otautil",
]
+58 -11
View File
@@ -590,7 +590,7 @@ endif
include $(CLEAR_VARS)
LOCAL_SRC_FILES := fuse_sideload.cpp
LOCAL_CLANG := true
LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
LOCAL_CFLAGS := -Wall -Werror
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
LOCAL_MODULE_TAGS := optional
@@ -605,13 +605,35 @@ else
endif
include $(BUILD_SHARED_LIBRARY)
# static libfusesideload
# =============================== (required to fix build errors in 8.1 due to use by tests)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := fuse_sideload.cpp
LOCAL_CLANG := true
LOCAL_CFLAGS := -Wall -Werror
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libfusesideload
LOCAL_SHARED_LIBRARIES := libcutils libc
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes
LOCAL_STATIC_LIBRARIES += libmincrypttwrp
LOCAL_CFLAGS += -DUSE_MINCRYPT
else
LOCAL_STATIC_LIBRARIES += libcrypto_static
endif
include $(BUILD_STATIC_LIBRARY)
# libmounts (static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := mounts.cpp
LOCAL_CLANG := true
LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
LOCAL_CFLAGS := \
-Wall \
-Werror
LOCAL_MODULE := libmounts
LOCAL_STATIC_LIBRARIES := libbase
include $(BUILD_STATIC_LIBRARY)
# librecovery (static library)
@@ -619,7 +641,7 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
install.cpp
LOCAL_CFLAGS := -Wno-unused-parameter -Werror
LOCAL_CFLAGS := -Wall -Werror
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
ifeq ($(AB_OTA_UPDATER),true)
@@ -632,7 +654,8 @@ LOCAL_STATIC_LIBRARIES := \
libvintf_recovery \
libcrypto_utils \
libcrypto \
libbase
libbase \
libziparchive \
include $(BUILD_STATIC_LIBRARY)
@@ -661,6 +684,7 @@ else
LOCAL_SHARED_LIBRARIES += libcrypto libbase
LOCAL_SRC_FILES += verifier.cpp asn1_decoder.cpp
endif
ifeq ($(AB_OTA_UPDATER),true)
LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
endif
@@ -679,7 +703,6 @@ include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_MODULE := libverifier
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
asn1_decoder.cpp \
verifier.cpp \
@@ -687,16 +710,40 @@ LOCAL_SRC_FILES := \
LOCAL_STATIC_LIBRARIES := libcrypto_static
include $(BUILD_STATIC_LIBRARY)
# Wear default device
# ===============================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := wear_device.cpp
# Should match TARGET_RECOVERY_UI_LIB in BoardConfig.mk.
LOCAL_MODULE := librecovery_ui_wear
include $(BUILD_STATIC_LIBRARY)
# vr headset default device
# ===============================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := vr_device.cpp
# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk
LOCAL_MODULE := librecovery_ui_vr
include $(BUILD_STATIC_LIBRARY)
commands_recovery_local_path := $(LOCAL_PATH)
include $(LOCAL_PATH)/tests/Android.mk \
$(LOCAL_PATH)/tools/Android.mk \
include \
$(LOCAL_PATH)/applypatch/Android.mk \
$(LOCAL_PATH)/boot_control/Android.mk \
$(LOCAL_PATH)/edify/Android.mk \
$(LOCAL_PATH)/otafault/Android.mk \
$(LOCAL_PATH)/bootloader_message/Android.mk \
$(LOCAL_PATH)/bootloader_message_twrp/Android.mk \
$(LOCAL_PATH)/tests/Android.mk \
$(LOCAL_PATH)/tools/Android.mk \
$(LOCAL_PATH)/updater/Android.mk \
$(LOCAL_PATH)/update_verifier/Android.mk \
$(LOCAL_PATH)/applypatch/Android.mk
$(LOCAL_PATH)/bootloader_message/Android.mk \
$(LOCAL_PATH)/bootloader_message_twrp/Android.mk
ifeq ($(wildcard system/core/uncrypt/Android.mk),)
include $(commands_recovery_local_path)/uncrypt/Android.mk
+3
View File
@@ -0,0 +1,3 @@
enh+aosp-gerrit@google.com
tbao@google.com
xunchang@google.com
+133 -125
View File
@@ -14,24 +14,27 @@
* limitations under the License.
*/
#include <unistd.h>
#include "adb_install.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include "ui.h"
#include "cutils/properties.h"
#include "install.h"
#include "common.h"
#include "adb_install.h"
#include "minadbd/fuse_adb_provider.h"
#include "fuse_sideload.h"
#ifdef USE_OLD_VERIFIER
#include "verifier24/verifier.h"
@@ -40,86 +43,91 @@
#endif
static void set_usb_driver(bool enabled) {
int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY);
if (fd < 0) {
/* These error messages show when built in older Android branches (e.g. Gingerbread)
It's not a critical error so we're disabling the error messages.
ui->Print("failed to open driver control: %s\n", strerror(errno));
*/
printf("failed to open driver control: %s\n", strerror(errno));
return;
}
char configfs[PROPERTY_VALUE_MAX];
property_get("sys.usb.configfs", configfs, "false");
if (strcmp(configfs, "false") == 0 || strcmp(configfs, "0") == 0)
return;
if (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) {
/*
ui->Print("failed to set driver control: %s\n", strerror(errno));
int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY);
if (fd < 0) {
/* These error messages show when built in older Android branches (e.g. Gingerbread)
It's not a critical error so we're disabling the error messages.
ui->Print("failed to open driver control: %s\n", strerror(errno));
*/
printf("failed to set driver control: %s\n", strerror(errno));
}
if (close(fd) < 0) {
printf("failed to open driver control: %s\n", strerror(errno));
return;
}
if (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) {
/*
ui->Print("failed to close driver control: %s\n", strerror(errno));
ui->Print("failed to set driver control: %s\n", strerror(errno));
*/
printf("failed to close driver control: %s\n", strerror(errno));
}
printf("failed to set driver control: %s\n", strerror(errno));
}
if (close(fd) < 0) {
/*
ui->Print("failed to close driver control: %s\n", strerror(errno));
*/
printf("failed to close driver control: %s\n", strerror(errno));
}
}
// On Android 8.0 for some reason init can't seem to completely stop adbd
// so we have to kill it too if it doesn't die on its own.
static void kill_adbd() {
DIR* dir = opendir("/proc");
if (dir) {
struct dirent* de = 0;
DIR* dir = opendir("/proc");
if (dir) {
struct dirent* de = 0;
while ((de = readdir(dir)) != 0) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
while ((de = readdir(dir)) != 0) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
int pid = -1;
int ret = sscanf(de->d_name, "%d", &pid);
int pid = -1;
int ret = sscanf(de->d_name, "%d", &pid);
if (ret == 1) {
char cmdpath[PATH_MAX];
sprintf(cmdpath, "/proc/%d/cmdline", pid);
if (ret == 1) {
char cmdpath[PATH_MAX];
sprintf(cmdpath, "/proc/%d/cmdline", pid);
FILE* file = fopen(cmdpath, "r");
size_t task_size = PATH_MAX;
char task[PATH_MAX];
char* p = task;
if (getline(&p, &task_size, file) > 0) {
if (strstr(task, "adbd") != 0) {
printf("adbd pid %d found, sending kill.\n", pid);
kill(pid, SIGINT);
usleep(5000);
kill(pid, SIGKILL);
}
}
fclose(file);
}
FILE* file = fopen(cmdpath, "r");
size_t task_size = PATH_MAX;
char task[PATH_MAX];
char* p = task;
if (getline(&p, &task_size, file) > 0) {
if (strstr(task, "adbd") != 0) {
printf("adbd pid %d found, sending kill.\n", pid);
kill(pid, SIGINT);
usleep(5000);
kill(pid, SIGKILL);
}
}
closedir(dir);
fclose(file);
}
}
closedir(dir);
}
}
static void stop_adbd() {
printf("Stopping adbd...\n");
property_set("ctl.stop", "adbd");
usleep(5000);
kill_adbd();
set_usb_driver(false);
printf("Stopping adbd...\n");
property_set("ctl.stop", "adbd");
usleep(5000);
kill_adbd();
set_usb_driver(false);
}
static bool is_ro_debuggable() {
char value[PROPERTY_VALUE_MAX+1];
return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1');
char value[PROPERTY_VALUE_MAX+1];
return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1');
}
static void maybe_restart_adbd() {
if (is_ro_debuggable()) {
printf("Restarting adbd...\n");
set_usb_driver(true);
property_set("ctl.start", "adbd");
}
if (is_ro_debuggable()) {
printf("Restarting adbd...\n");
set_usb_driver(true);
property_set("ctl.start", "adbd");
}
}
// How long (in seconds) we wait for the host to start sending us a
@@ -129,83 +137,83 @@ static void maybe_restart_adbd() {
int
apply_from_adb(const char* install_file, pid_t* child_pid) {
stop_adbd();
set_usb_driver(true);
stop_adbd();
set_usb_driver(true);
/*
int apply_from_adb(RecoveryUI* ui, bool* wipe_cache, const char* install_file) {
modified_flash = true;
modified_flash = true;
stop_adbd(ui);
set_usb_driver(ui, true);
stop_adbd(ui);
set_usb_driver(ui, true);
ui->Print("\n\nNow send the package you want to apply\n"
"to the device with \"adb sideload <filename>\"...\n");
ui->Print("\n\nNow send the package you want to apply\n"
"to the device with \"adb sideload <filename>\"...\n");
*/
pid_t child;
if ((child = fork()) == 0) {
execl("/sbin/recovery", "recovery", "--adbd", install_file, NULL);
_exit(-1);
pid_t child;
if ((child = fork()) == 0) {
execl("/sbin/recovery", "recovery", "--adbd", install_file, NULL);
_exit(-1);
}
*child_pid = child;
// caller can now kill the child thread from another thread
// FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host
// connects and starts serving a package. Poll for its
// appearance. (Note that inotify doesn't work with FUSE.)
int result = INSTALL_ERROR;
int status;
bool waited = false;
struct stat st;
for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
if (waitpid(child, &status, WNOHANG) != 0) {
result = -1;
waited = true;
break;
}
*child_pid = child;
// caller can now kill the child thread from another thread
// FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host
// connects and starts serving a package. Poll for its
// appearance. (Note that inotify doesn't work with FUSE.)
int result = INSTALL_ERROR;
int status;
bool waited = false;
struct stat st;
for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
if (waitpid(child, &status, WNOHANG) != 0) {
result = -1;
waited = true;
break;
}
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) {
sleep(1);
continue;
} else {
printf("\nTimed out waiting for package: %s\n\n", strerror(errno));
result = -1;
kill(child, SIGKILL);
break;
}
}
// Install is handled elsewhere in TWRP
//install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false);
return 0;
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) {
sleep(1);
continue;
} else {
printf("\nTimed out waiting for package: %s\n\n", strerror(errno));
result = -1;
kill(child, SIGKILL);
break;
}
}
// Install is handled elsewhere in TWRP
//install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false);
return 0;
}
// if we got here, something failed
*child_pid = 0;
// if we got here, something failed
*child_pid = 0;
if (!waited) {
// Calling stat() on this magic filename signals the minadbd
// subprocess to shut down.
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
if (!waited) {
// Calling stat() on this magic filename signals the minadbd
// subprocess to shut down.
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
// 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);
// 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);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
if (WEXITSTATUS(status) == 3) {
printf("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
result = -2;
} else if (!WIFSIGNALED(status)) {
printf("adbd status %d\n", WEXITSTATUS(status));
}
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
if (WEXITSTATUS(status) == 3) {
printf("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
result = -2;
} else if (!WIFSIGNALED(status)) {
printf("status %d\n", WEXITSTATUS(status));
}
}
set_usb_driver(false);
maybe_restart_adbd();
set_usb_driver(false);
maybe_restart_adbd();
return result;
return result;
}
+2
View File
@@ -17,6 +17,8 @@
#ifndef _ADB_INSTALL_H
#define _ADB_INSTALL_H
#include <sys/types.h>
//class RecoveryUI;
static void set_usb_driver(bool enabled);
+20 -25
View File
@@ -27,6 +27,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <functional>
#include <memory>
#include <string>
#include <utility>
@@ -45,7 +46,7 @@
#include "print_sha1.h"
static int LoadPartitionContents(const std::string& filename, FileContents* file);
static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token);
static size_t FileSink(const unsigned char* data, size_t len, int fd);
static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch,
const std::string& target_filename,
const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data);
@@ -224,8 +225,8 @@ int SaveFileContents(const char* filename, const FileContents* file) {
return -1;
}
ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd);
if (bytes_written != static_cast<ssize_t>(file->data.size())) {
size_t bytes_written = FileSink(file->data.data(), file->data.size(), fd);
if (bytes_written != file->data.size()) {
printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written,
file->data.size(), strerror(errno));
return -1;
@@ -531,25 +532,17 @@ int ShowLicenses() {
return 0;
}
ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) {
int fd = *static_cast<int*>(token);
ssize_t done = 0;
ssize_t wrote;
while (done < len) {
wrote = TEMP_FAILURE_RETRY(ota_write(fd, data+done, len-done));
if (wrote == -1) {
printf("error writing %zd bytes: %s\n", (len-done), strerror(errno));
return done;
}
done += wrote;
static size_t FileSink(const unsigned char* data, size_t len, int fd) {
size_t done = 0;
while (done < len) {
ssize_t wrote = TEMP_FAILURE_RETRY(ota_write(fd, data + done, len - done));
if (wrote == -1) {
printf("error writing %zd bytes: %s\n", (len - done), strerror(errno));
return done;
}
return done;
}
ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) {
std::string* s = static_cast<std::string*>(token);
s->append(reinterpret_cast<const char*>(data), len);
return len;
done += wrote;
}
return done;
}
// Return the amount of free space (in bytes) on the filesystem
@@ -745,9 +738,11 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr
}
// We store the decoded output in memory.
SinkFn sink = MemorySink;
std::string memory_sink_str; // Don't need to reserve space.
void* token = &memory_sink_str;
SinkFn sink = [&memory_sink_str](const unsigned char* data, size_t len) {
memory_sink_str.append(reinterpret_cast<const char*>(data), len);
return len;
};
SHA_CTX ctx;
SHA1_Init(&ctx);
@@ -755,10 +750,10 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr
int result;
if (use_bsdiff) {
result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch.get(), 0,
sink, token, &ctx);
sink, &ctx);
} else {
result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch.get(), sink,
token, &ctx, bonus_data);
&ctx, bonus_data);
}
if (result != 0) {
-350
View File
@@ -1,350 +0,0 @@
#!/bin/bash
#
# A test suite for applypatch. Run in a client where you have done
# envsetup, choosecombo, etc.
#
# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your
# system partition.
#
#
# TODO: find some way to get this run regularly along with the rest of
# the tests.
EMULATOR_PORT=5580
DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/applypatch/testdata
# This must be the filename that applypatch uses for its copies.
CACHE_TEMP_SOURCE=/cache/saved.file
# Put all binaries and files here. We use /cache because it's a
# temporary filesystem in the emulator; it's created fresh each time
# the emulator starts.
WORK_DIR=/system
# partition that WORK_DIR is located on, without the leading slash
WORK_FS=system
# set to 0 to use a device instead
USE_EMULATOR=1
# ------------------------
tmpdir=$(mktemp -d)
if [ "$USE_EMULATOR" == 1 ]; then
emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
pid_emulator=$!
ADB="adb -s emulator-$EMULATOR_PORT "
else
ADB="adb -d "
fi
echo "waiting to connect to device"
$ADB wait-for-device
echo "device is available"
$ADB remount
# free up enough space on the system partition for the test to run.
$ADB shell rm -r /system/media
# run a command on the device; exit with the exit status of the device
# command.
run_command() {
$ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
}
testname() {
echo
echo "$1"...
testname="$1"
}
fail() {
echo
echo FAIL: $testname
echo
[ "$open_pid" == "" ] || kill $open_pid
[ "$pid_emulator" == "" ] || kill $pid_emulator
exit 1
}
sha1() {
sha1sum $1 | awk '{print $1}'
}
free_space() {
run_command df | awk "/$1/ {print gensub(/K/, \"\", \"g\", \$6)}"
}
cleanup() {
# not necessary if we're about to kill the emulator, but nice for
# running on real devices or already-running emulators.
testname "removing test files"
run_command rm $WORK_DIR/bloat.dat
run_command rm $WORK_DIR/old.file
run_command rm $WORK_DIR/foo
run_command rm $WORK_DIR/patch.bsdiff
run_command rm $WORK_DIR/applypatch
run_command rm $CACHE_TEMP_SOURCE
run_command rm /cache/bloat*.dat
[ "$pid_emulator" == "" ] || kill $pid_emulator
if [ $# == 0 ]; then
rm -rf $tmpdir
fi
}
cleanup leave_tmp
$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
BAD1_SHA1=$(printf "%040x" $RANDOM)
BAD2_SHA1=$(printf "%040x" $RANDOM)
OLD_SHA1=$(sha1 $DATA_DIR/old.file)
NEW_SHA1=$(sha1 $DATA_DIR/new.file)
NEW_SIZE=$(stat -c %s $DATA_DIR/new.file)
# --------------- basic execution ----------------------
testname "usage message"
run_command $WORK_DIR/applypatch && fail
testname "display license"
run_command $WORK_DIR/applypatch -l | grep -q -i copyright || fail
# --------------- check mode ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
testname "check mode single"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
testname "check mode multiple"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
testname "check mode failure"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
# put some junk in the old file
run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
testname "check mode cache (corrupted) single"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
testname "check mode cache (corrupted) multiple"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
testname "check mode cache (corrupted) failure"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
# remove the old file entirely
run_command rm $WORK_DIR/old.file
testname "check mode cache (missing) single"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
testname "check mode cache (missing) multiple"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
testname "check mode cache (missing) failure"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
# --------------- apply patch ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
echo hello > $tmpdir/foo
$ADB push $tmpdir/foo $WORK_DIR
# Check that the partition has enough space to apply the patch without
# copying. If it doesn't, we'll be testing the low-space condition
# when we intend to test the not-low-space condition.
testname "apply patches (with enough space)"
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS."
if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
echo "Not enough space on /$WORK_FS to patch test file."
echo
echo "This doesn't mean that applypatch is necessarily broken;"
echo "just that /$WORK_FS doesn't have enough free space to"
echo "properly run this test."
exit 1
fi
testname "apply bsdiff patch"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
testname "reapply bsdiff patch"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
# --------------- apply patch in new location ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
# Check that the partition has enough space to apply the patch without
# copying. If it doesn't, we'll be testing the low-space condition
# when we intend to test the not-low-space condition.
testname "apply patch to new location (with enough space)"
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS."
if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
echo "Not enough space on /$WORK_FS to patch test file."
echo
echo "This doesn't mean that applypatch is necessarily broken;"
echo "just that /$WORK_FS doesn't have enough free space to"
echo "properly run this test."
exit 1
fi
run_command rm $WORK_DIR/new.file
run_command rm $CACHE_TEMP_SOURCE
testname "apply bsdiff patch to new location"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/new.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
testname "reapply bsdiff patch to new location"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/new.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
# put some junk in the old file
run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
testname "apply bsdiff patch to new location with corrupted source"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo || fail
$ADB pull $WORK_DIR/new.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
# put some junk in the cache copy, too
run_command dd if=/dev/urandom of=$CACHE_TEMP_SOURCE count=100 bs=1024 || fail
run_command rm $WORK_DIR/new.file
testname "apply bsdiff patch to new location with corrupted source and copy (no new file)"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
# put some junk in the new file
run_command dd if=/dev/urandom of=$WORK_DIR/new.file count=100 bs=1024 || fail
testname "apply bsdiff patch to new location with corrupted source and copy (bad new file)"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
# --------------- apply patch with low space on /system ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS; we'll soon fix that."
echo run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS now."
testname "apply bsdiff patch with low space"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
testname "reapply bsdiff patch with low space"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
# --------------- apply patch with low space on /system and /cache ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS"
run_command mkdir /cache/subdir
run_command 'echo > /cache/subdir/a.file'
run_command 'echo > /cache/a.file'
run_command mkdir /cache/recovery /cache/recovery/otatest
run_command 'echo > /cache/recovery/otatest/b.file'
run_command "echo > $CACHE_TEMP_SOURCE"
free_kb=$(free_space cache)
echo "${free_kb}kb free on /cache; we'll soon fix that."
run_command dd if=/dev/zero of=/cache/bloat_small.dat count=128 bs=1024 || fail
run_command dd if=/dev/zero of=/cache/bloat_large.dat count=$((free_kb-640)) bs=1024 || fail
free_kb=$(free_space cache)
echo "${free_kb}kb free on /cache now."
testname "apply bsdiff patch with low space, full cache, can't delete enough"
$ADB shell 'cat >> /cache/bloat_large.dat' & open_pid=$!
echo "open_pid is $open_pid"
# size check should fail even though it deletes some stuff
run_command $WORK_DIR/applypatch -s $NEW_SIZE && fail
run_command ls /cache/bloat_small.dat && fail # was deleted
run_command ls /cache/a.file && fail # was deleted
run_command ls /cache/recovery/otatest/b.file && fail # was deleted
run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open
run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir
run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
# should fail; not enough files can be deleted
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff && fail
run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open
run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir
run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
kill $open_pid # /cache/bloat_large.dat is no longer open
testname "apply bsdiff patch with low space, full cache, can delete enough"
# should succeed after deleting /cache/bloat_large.dat
run_command $WORK_DIR/applypatch -s $NEW_SIZE || fail
run_command ls /cache/bloat_large.dat && fail # was deleted
run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir
run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
# should succeed
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir
run_command ls $CACHE_TEMP_SOURCE && fail # was deleted because patching overwrote it, then deleted it
# --------------- apply patch from cache ----------------------
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
# put some junk in the old file
run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
testname "apply bsdiff patch from cache (corrupted source) with low space"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
# remove the old file entirely
run_command rm $WORK_DIR/old.file
testname "apply bsdiff patch from cache (missing source) with low space"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
# --------------- cleanup ----------------------
cleanup
echo
echo PASS
echo
+1 -17
View File
@@ -44,19 +44,6 @@ static int CheckMode(int argc, const char** argv) {
return applypatch_check(argv[2], sha1);
}
static int SpaceMode(int argc, const char** argv) {
if (argc != 3) {
return 2;
}
size_t bytes;
if (!android::base::ParseUint(argv[2], &bytes) || bytes == 0) {
printf("can't parse \"%s\" as byte count\n\n", argv[2]);
return 1;
}
return CacheSizeCheck(bytes);
}
// Parse arguments (which should be of the form "<sha1>:<filename>" into the
// new parallel arrays *sha1s and *files. Returns true on success.
static bool ParsePatchArgs(int argc, const char** argv, std::vector<std::string>* sha1s,
@@ -175,13 +162,12 @@ int applypatch_modes(int argc, const char** argv) {
"usage: %s [-b <bonus-file>] <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
"[<src-sha1>:<patch> ...]\n"
" or %s -c <file> [<sha1> ...]\n"
" or %s -s <bytes>\n"
" or %s -l\n"
"\n"
"Filenames may be of the form\n"
" EMMC:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
"to specify reading from or writing to an EMMC partition.\n\n",
argv[0], argv[0], argv[0], argv[0]);
argv[0], argv[0], argv[0]);
return 2;
}
@@ -191,8 +177,6 @@ int applypatch_modes(int argc, const char** argv) {
result = ShowLicenses();
} else if (strncmp(argv[1], "-c", 3) == 0) {
result = CheckMode(argc, argv);
} else if (strncmp(argv[1], "-s", 3) == 0) {
result = SpaceMode(argc, argv);
} else {
result = PatchMode(argc, argv);
}
+29 -19
View File
@@ -23,10 +23,14 @@
#include <stdio.h>
#include <sys/types.h>
#include <string>
#include <android-base/logging.h>
#include <bspatch.h>
#include <openssl/sha.h>
#include "applypatch/applypatch.h"
#include "openssl/sha.h"
#include "print_sha1.h"
void ShowBSDiffLicense() {
puts("The bsdiff library used herein is:\n"
@@ -60,25 +64,31 @@ void ShowBSDiffLicense() {
);
}
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Value* patch,
ssize_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx) {
auto sha_sink = [&](const uint8_t* data, size_t len) {
len = sink(data, len, token);
int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch,
size_t patch_offset, SinkFn sink, SHA_CTX* ctx) {
auto sha_sink = [&sink, &ctx](const uint8_t* data, size_t len) {
len = sink(data, len);
if (ctx) SHA1_Update(ctx, data, len);
return len;
};
return bsdiff::bspatch(old_data, old_size,
reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
patch->data.size(), sha_sink);
}
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch,
ssize_t patch_offset, std::vector<unsigned char>* new_data) {
auto vector_sink = [new_data](const uint8_t* data, size_t len) {
new_data->insert(new_data->end(), data, data + len);
return len;
};
return bsdiff::bspatch(old_data, old_size,
reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
patch->data.size(), vector_sink);
}
CHECK(patch != nullptr);
CHECK_LE(patch_offset, patch->data.size());
int result = bsdiff::bspatch(old_data, old_size,
reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
patch->data.size() - patch_offset, sha_sink);
if (result != 0) {
LOG(ERROR) << "bspatch failed, result: " << result;
// print SHA1 of the patch in the case of a data error.
if (result == 2) {
uint8_t digest[SHA_DIGEST_LENGTH];
SHA1(reinterpret_cast<const uint8_t*>(patch->data.data() + patch_offset),
patch->data.size() - patch_offset, digest);
std::string patch_sha1 = print_sha1(digest);
LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: "
<< patch_sha1;
}
}
return result;
}
+14 -11
View File
@@ -693,6 +693,20 @@ static bool ReadImage(const char* filename, std::vector<ImageChunk>* chunks,
continue;
}
// The footer contains the size of the uncompressed data. Double-check to make sure that it
// matches the size of the data we got when we actually did the decompression.
size_t footer_index = pos + raw_data_len + GZIP_FOOTER_LEN - 4;
if (sz - footer_index < 4) {
printf("Warning: invalid footer position; treating as a nomal chunk\n");
continue;
}
size_t footer_size = get_unaligned<uint32_t>(img->data() + footer_index);
if (footer_size != uncompressed_len) {
printf("Warning: footer size %zu != decompressed size %zu; treating as a nomal chunk\n",
footer_size, uncompressed_len);
continue;
}
ImageChunk body(CHUNK_DEFLATE, pos, img, raw_data_len);
uncompressed_data.resize(uncompressed_len);
body.SetUncompressedData(std::move(uncompressed_data));
@@ -704,17 +718,6 @@ static bool ReadImage(const char* filename, std::vector<ImageChunk>* chunks,
chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_FOOTER_LEN);
pos += GZIP_FOOTER_LEN;
// The footer (that we just skipped over) contains the size of
// the uncompressed data. Double-check to make sure that it
// matches the size of the data we got when we actually did
// the decompression.
size_t footer_size = get_unaligned<uint32_t>(img->data() + pos - 4);
if (footer_size != body.DataLengthForPatch()) {
printf("Error: footer size %zu != decompressed size %zu\n", footer_size,
body.GetRawDataLength());
return false;
}
} else {
// Use a normal chunk to take all the contents until the next gzip chunk (or EOF); we expect
// the number of chunks to be small (5 for typical boot and recovery images).
-118
View File
@@ -1,118 +0,0 @@
#!/bin/bash
#
# A script for testing imgdiff/applypatch. It takes two full OTA
# packages as arguments. It generates (on the host) patches for all
# the zip/jar/apk files they have in common, as well as boot and
# recovery images. It then applies the patches on the device (or
# emulator) and checks that the resulting file is correct.
EMULATOR_PORT=5580
# set to 0 to use a device instead
USE_EMULATOR=0
# where on the device to do all the patching.
WORK_DIR=/data/local/tmp
START_OTA_PACKAGE=$1
END_OTA_PACKAGE=$2
# ------------------------
tmpdir=$(mktemp -d)
if [ "$USE_EMULATOR" == 1 ]; then
emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
pid_emulator=$!
ADB="adb -s emulator-$EMULATOR_PORT "
else
ADB="adb -d "
fi
echo "waiting to connect to device"
$ADB wait-for-device
# run a command on the device; exit with the exit status of the device
# command.
run_command() {
$ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
}
testname() {
echo
echo "$1"...
testname="$1"
}
fail() {
echo
echo FAIL: $testname
echo
[ "$open_pid" == "" ] || kill $open_pid
[ "$pid_emulator" == "" ] || kill $pid_emulator
exit 1
}
sha1() {
sha1sum $1 | awk '{print $1}'
}
size() {
stat -c %s $1 | tr -d '\n'
}
cleanup() {
# not necessary if we're about to kill the emulator, but nice for
# running on real devices or already-running emulators.
testname "removing test files"
run_command rm $WORK_DIR/applypatch
run_command rm $WORK_DIR/source
run_command rm $WORK_DIR/target
run_command rm $WORK_DIR/patch
[ "$pid_emulator" == "" ] || kill $pid_emulator
rm -rf $tmpdir
}
$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
patch_and_apply() {
local fn=$1
shift
unzip -p $START_OTA_PACKAGE $fn > $tmpdir/source
unzip -p $END_OTA_PACKAGE $fn > $tmpdir/target
imgdiff "$@" $tmpdir/source $tmpdir/target $tmpdir/patch
bsdiff $tmpdir/source $tmpdir/target $tmpdir/patch.bs
echo "patch for $fn is $(size $tmpdir/patch) [of $(size $tmpdir/target)] ($(size $tmpdir/patch.bs) with bsdiff)"
echo "$fn $(size $tmpdir/patch) of $(size $tmpdir/target) bsdiff $(size $tmpdir/patch.bs)" >> /tmp/stats.txt
$ADB push $tmpdir/source $WORK_DIR/source || fail "source push failed"
run_command rm /data/local/tmp/target
$ADB push $tmpdir/patch $WORK_DIR/patch || fail "patch push failed"
run_command /data/local/tmp/applypatch /data/local/tmp/source \
/data/local/tmp/target $(sha1 $tmpdir/target) $(size $tmpdir/target) \
$(sha1 $tmpdir/source):/data/local/tmp/patch \
|| fail "applypatch of $fn failed"
$ADB pull /data/local/tmp/target $tmpdir/result
diff -q $tmpdir/target $tmpdir/result || fail "patch output not correct!"
}
# --------------- basic execution ----------------------
for i in $((zipinfo -1 $START_OTA_PACKAGE; zipinfo -1 $END_OTA_PACKAGE) | \
sort | uniq -d | egrep -e '[.](apk|jar|zip)$'); do
patch_and_apply $i -z
done
patch_and_apply boot.img
patch_and_apply system/recovery.img
# --------------- cleanup ----------------------
cleanup
echo
echo PASS
echo
+99 -68
View File
@@ -26,12 +26,14 @@
#include <sys/stat.h>
#include <unistd.h>
#include <memory>
#include <string>
#include <vector>
#include <android-base/logging.h>
#include <android-base/memory.h>
#include <applypatch/applypatch.h>
#include <applypatch/imgdiff.h>
#include <android-base/memory.h>
#include <openssl/sha.h>
#include <zlib.h>
@@ -43,12 +45,91 @@ static inline int32_t Read4(const void *address) {
return android::base::get_unaligned<int32_t>(address);
}
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const unsigned char* patch_data, ssize_t patch_size,
SinkFn sink, void* token) {
// This function is a wrapper of ApplyBSDiffPatch(). It has a custom sink function to deflate the
// patched data and stream the deflated data to output.
static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len,
const Value* patch, size_t patch_offset,
const char* deflate_header, SinkFn sink, SHA_CTX* ctx) {
size_t expected_target_length = static_cast<size_t>(Read8(deflate_header + 32));
int level = Read4(deflate_header + 40);
int method = Read4(deflate_header + 44);
int window_bits = Read4(deflate_header + 48);
int mem_level = Read4(deflate_header + 52);
int strategy = Read4(deflate_header + 56);
std::unique_ptr<z_stream, decltype(&deflateEnd)> strm(new z_stream(), deflateEnd);
strm->zalloc = Z_NULL;
strm->zfree = Z_NULL;
strm->opaque = Z_NULL;
strm->avail_in = 0;
strm->next_in = nullptr;
int ret = deflateInit2(strm.get(), level, method, window_bits, mem_level, strategy);
if (ret != Z_OK) {
LOG(ERROR) << "Failed to init uncompressed data deflation: " << ret;
return false;
}
// Define a custom sink wrapper that feeds to bspatch. It deflates the available patch data on
// the fly and outputs the compressed data to the given sink.
size_t actual_target_length = 0;
size_t total_written = 0;
static constexpr size_t buffer_size = 32768;
auto compression_sink = [&](const uint8_t* data, size_t len) -> size_t {
// The input patch length for an update never exceeds INT_MAX.
strm->avail_in = len;
strm->next_in = data;
do {
std::vector<uint8_t> buffer(buffer_size);
strm->avail_out = buffer_size;
strm->next_out = buffer.data();
if (actual_target_length + len < expected_target_length) {
ret = deflate(strm.get(), Z_NO_FLUSH);
} else {
ret = deflate(strm.get(), Z_FINISH);
}
if (ret != Z_OK && ret != Z_STREAM_END) {
LOG(ERROR) << "Failed to deflate stream: " << ret;
// zero length indicates an error in the sink function of bspatch().
return 0;
}
size_t have = buffer_size - strm->avail_out;
total_written += have;
if (sink(buffer.data(), have) != have) {
LOG(ERROR) << "Failed to write " << have << " compressed bytes to output.";
return 0;
}
if (ctx) SHA1_Update(ctx, buffer.data(), have);
} while ((strm->avail_in != 0 || strm->avail_out == 0) && ret != Z_STREAM_END);
actual_target_length += len;
return len;
};
if (ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink, nullptr) != 0) {
return false;
}
if (ret != Z_STREAM_END) {
LOG(ERROR) << "ret is expected to be Z_STREAM_END, but it's " << ret;
return false;
}
if (expected_target_length != actual_target_length) {
LOG(ERROR) << "target length is expected to be " << expected_target_length << ", but it's "
<< actual_target_length;
return false;
}
LOG(DEBUG) << "bspatch writes " << total_written << " bytes in total to streaming output.";
return true;
}
int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data,
size_t patch_size, SinkFn sink) {
Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size));
return ApplyImagePatch(old_data, old_size, &patch, sink, token, nullptr, nullptr);
return ApplyImagePatch(old_data, old_size, &patch, sink, nullptr, nullptr);
}
/*
@@ -57,8 +138,8 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
* file, and update the SHA context with the output data as well.
* Return 0 on success.
*/
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch,
SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data) {
int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink,
SHA_CTX* ctx, const Value* bonus_data) {
if (patch->data.size() < 12) {
printf("patch too short to contain header\n");
return -1;
@@ -97,11 +178,14 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value
size_t src_len = static_cast<size_t>(Read8(normal_header + 8));
size_t patch_offset = static_cast<size_t>(Read8(normal_header + 16));
if (src_start + src_len > static_cast<size_t>(old_size)) {
if (src_start + src_len > old_size) {
printf("source data too short\n");
return -1;
}
ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, token, ctx);
if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx) != 0) {
printf("Failed to apply bsdiff patch.\n");
return -1;
}
} else if (type == CHUNK_RAW) {
const char* raw_header = &patch->data[pos];
pos += 4;
@@ -110,15 +194,14 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value
return -1;
}
ssize_t data_len = Read4(raw_header);
size_t data_len = static_cast<size_t>(Read4(raw_header));
if (pos + data_len > patch->data.size()) {
printf("failed to read chunk %d raw data\n", i);
return -1;
}
if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len);
if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len, token) !=
data_len) {
if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len) != data_len) {
printf("failed to write chunk %d raw data\n", i);
return -1;
}
@@ -136,14 +219,8 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value
size_t src_len = static_cast<size_t>(Read8(deflate_header + 8));
size_t patch_offset = static_cast<size_t>(Read8(deflate_header + 16));
size_t expanded_len = static_cast<size_t>(Read8(deflate_header + 24));
size_t target_len = static_cast<size_t>(Read8(deflate_header + 32));
int level = Read4(deflate_header + 40);
int method = Read4(deflate_header + 44);
int windowBits = Read4(deflate_header + 48);
int memLevel = Read4(deflate_header + 52);
int strategy = Read4(deflate_header + 56);
if (src_start + src_len > static_cast<size_t>(old_size)) {
if (src_start + src_len > old_size) {
printf("source data too short\n");
return -1;
}
@@ -198,58 +275,12 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value
}
}
// Next, apply the bsdiff patch (in memory) to the uncompressed data.
std::vector<unsigned char> uncompressed_target_data;
// TODO(senj): Remove the only usage of ApplyBSDiffPatchMem here,
// replace it with ApplyBSDiffPatch with a custom sink function that
// wraps the given sink function to stream output to save memory.
if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset,
&uncompressed_target_data) != 0) {
return -1;
}
if (uncompressed_target_data.size() != target_len) {
printf("expected target len to be %zu, but it's %zu\n", target_len,
uncompressed_target_data.size());
if (!ApplyBSDiffPatchAndStreamOutput(expanded_source.data(), expanded_len, patch,
patch_offset, deflate_header, sink, ctx)) {
LOG(ERROR) << "Fail to apply streaming bspatch.";
return -1;
}
// Now compress the target data and append it to the output.
// we're done with the expanded_source data buffer, so we'll
// reuse that memory to receive the output of deflate.
if (expanded_source.size() < 32768U) {
expanded_source.resize(32768U);
}
{
std::vector<unsigned char>& temp_data = expanded_source;
// now the deflate stream
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = uncompressed_target_data.size();
strm.next_in = uncompressed_target_data.data();
int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
if (ret != Z_OK) {
printf("failed to init uncompressed data deflation: %d\n", ret);
return -1;
}
do {
strm.avail_out = temp_data.size();
strm.next_out = temp_data.data();
ret = deflate(&strm, Z_FINISH);
ssize_t have = temp_data.size() - strm.avail_out;
if (sink(temp_data.data(), have, token) != have) {
printf("failed to write %zd compressed bytes to output\n", have);
return -1;
}
if (ctx) SHA1_Update(ctx, temp_data.data(), have);
} while (ret != Z_STREAM_END);
deflateEnd(&strm);
}
} else {
printf("patch chunk %d is unknown type %d\n", i, type);
return -1;
+6 -11
View File
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <sys/stat.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
@@ -41,7 +42,7 @@ struct FileContents {
// and use it as the source instead.
#define CACHE_TEMP_SOURCE "/cache/saved.file"
typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
using SinkFn = std::function<size_t(const unsigned char*, size_t)>;
// applypatch.cpp
int ShowLicenses();
@@ -66,18 +67,12 @@ int SaveFileContents(const char* filename, const FileContents* file);
// bspatch.cpp
void ShowBSDiffLicense();
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
SinkFn sink, void* token, SHA_CTX* ctx);
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
std::vector<unsigned char>* new_data);
int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch,
size_t patch_offset, SinkFn sink, SHA_CTX* ctx);
// imgpatch.cpp
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch,
SinkFn sink, void* token, SHA_CTX* ctx,
const Value* bonus_data);
int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink,
SHA_CTX* ctx, const Value* bonus_data);
// freecache.cpp
int MakeFreeSpaceOnCache(size_t bytes_needed);
+5 -4
View File
@@ -19,10 +19,11 @@
#include <sys/types.h>
using SinkFn = ssize_t (*)(const unsigned char*, ssize_t, void*);
#include <functional>
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const unsigned char* patch_data, ssize_t patch_size,
SinkFn sink, void* token);
using SinkFn = std::function<size_t(const unsigned char*, size_t)>;
int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data,
size_t patch_size, SinkFn sink);
#endif // _APPLYPATCH_IMGPATCH_H
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+34
View File
@@ -0,0 +1,34 @@
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := bootctrl.bcb
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_SRC_FILES := boot_control.cpp
LOCAL_CFLAGS := \
-D_FILE_OFFSET_BITS=64 \
-Werror \
-Wall \
-Wextra \
-Wno-unused-parameter
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_STATIC_LIBRARIES := libbootloader_message libfs_mgr libbase
LOCAL_POST_INSTALL_CMD := \
$(hide) mkdir -p $(TARGET_OUT_SHARED_LIBRARIES)/hw && \
ln -sf bootctrl.bcb.so $(TARGET_OUT_SHARED_LIBRARIES)/hw/bootctrl.default.so
include $(BUILD_SHARED_LIBRARY)
+401
View File
@@ -0,0 +1,401 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <string>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <hardware/boot_control.h>
#include <hardware/hardware.h>
#include <bootloader_message/bootloader_message.h>
struct boot_control_private_t {
// The base struct needs to be first in the list.
boot_control_module_t base;
// Whether this struct was initialized with data from the bootloader message
// that doesn't change until next reboot.
bool initialized;
// The path to the misc_device as reported in the fstab.
const char* misc_device;
// The number of slots present on the device.
unsigned int num_slots;
// The slot where we are running from.
unsigned int current_slot;
};
namespace {
// The number of boot attempts that should be made from a new slot before
// rolling back to the previous slot.
constexpr unsigned int kDefaultBootAttempts = 7;
static_assert(kDefaultBootAttempts < 8, "tries_remaining field only has 3 bits");
constexpr unsigned int kMaxNumSlots =
sizeof(bootloader_control::slot_info) / sizeof(bootloader_control::slot_info[0]);
constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix);
static uint32_t CRC32(const uint8_t* buf, size_t size) {
static uint32_t crc_table[256];
// Compute the CRC-32 table only once.
if (!crc_table[1]) {
for (uint32_t i = 0; i < 256; ++i) {
uint32_t crc = i;
for (uint32_t j = 0; j < 8; ++j) {
uint32_t mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
crc_table[i] = crc;
}
}
uint32_t ret = -1;
for (size_t i = 0; i < size; ++i) {
ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF];
}
return ~ret;
}
// Return the little-endian representation of the CRC-32 of the first fields
// in |boot_ctrl| up to the crc32_le field.
uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) {
return htole32(
CRC32(reinterpret_cast<const uint8_t*>(boot_ctrl), offsetof(bootloader_control, crc32_le)));
}
bool LoadBootloaderControl(const char* misc_device, bootloader_control* buffer) {
android::base::unique_fd fd(open(misc_device, O_RDONLY));
if (fd.get() == -1) {
PLOG(ERROR) << "failed to open " << misc_device;
return false;
}
if (lseek(fd, kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
PLOG(ERROR) << "failed to lseek " << misc_device;
return false;
}
if (!android::base::ReadFully(fd.get(), buffer, sizeof(bootloader_control))) {
PLOG(ERROR) << "failed to read " << misc_device;
return false;
}
return true;
}
bool UpdateAndSaveBootloaderControl(const char* misc_device, bootloader_control* buffer) {
buffer->crc32_le = BootloaderControlLECRC(buffer);
android::base::unique_fd fd(open(misc_device, O_WRONLY | O_SYNC));
if (fd.get() == -1) {
PLOG(ERROR) << "failed to open " << misc_device;
return false;
}
if (lseek(fd.get(), kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
PLOG(ERROR) << "failed to lseek " << misc_device;
return false;
}
if (!android::base::WriteFully(fd.get(), buffer, sizeof(bootloader_control))) {
PLOG(ERROR) << "failed to write " << misc_device;
return false;
}
return true;
}
void InitDefaultBootloaderControl(const boot_control_private_t* module,
bootloader_control* boot_ctrl) {
memset(boot_ctrl, 0, sizeof(*boot_ctrl));
if (module->current_slot < kMaxNumSlots) {
strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[module->current_slot],
sizeof(boot_ctrl->slot_suffix));
}
boot_ctrl->magic = BOOT_CTRL_MAGIC;
boot_ctrl->version = BOOT_CTRL_VERSION;
// Figure out the number of slots by checking if the partitions exist,
// otherwise assume the maximum supported by the header.
boot_ctrl->nb_slot = kMaxNumSlots;
std::string base_path = module->misc_device;
size_t last_path_sep = base_path.rfind('/');
if (last_path_sep != std::string::npos) {
// We test the existence of the "boot" partition on each possible slot,
// which is a partition required by Android Bootloader Requirements.
base_path = base_path.substr(0, last_path_sep + 1) + "boot";
int last_existing_slot = -1;
int first_missing_slot = -1;
for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
std::string partition_path = base_path + kSlotSuffixes[slot];
struct stat part_stat;
int err = stat(partition_path.c_str(), &part_stat);
if (!err) {
last_existing_slot = slot;
LOG(INFO) << "Found slot: " << kSlotSuffixes[slot];
} else if (err < 0 && errno == ENOENT && first_missing_slot == -1) {
first_missing_slot = slot;
}
}
// We only declare that we found the actual number of slots if we found all
// the boot partitions up to the number of slots, and no boot partition
// after that. Not finding any of the boot partitions implies a problem so
// we just leave the number of slots in the maximum value.
if ((last_existing_slot != -1 && last_existing_slot + 1 == first_missing_slot) ||
(first_missing_slot == -1 && last_existing_slot + 1 == kMaxNumSlots)) {
boot_ctrl->nb_slot = last_existing_slot + 1;
LOG(INFO) << "Found a system with " << last_existing_slot + 1 << " slots.";
}
}
for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
slot_metadata entry = {};
if (slot < boot_ctrl->nb_slot) {
entry.priority = 7;
entry.tries_remaining = kDefaultBootAttempts;
entry.successful_boot = 0;
} else {
entry.priority = 0; // Unbootable
}
// When the boot_control stored on disk is invalid, we assume that the
// current slot is successful. The bootloader should repair this situation
// before booting and write a valid boot_control slot, so if we reach this
// stage it means that the misc partition was corrupted since boot.
if (module->current_slot == slot) {
entry.successful_boot = 1;
}
boot_ctrl->slot_info[slot] = entry;
}
boot_ctrl->recovery_tries_remaining = 0;
boot_ctrl->crc32_le = BootloaderControlLECRC(boot_ctrl);
}
// Return the index of the slot suffix passed or -1 if not a valid slot suffix.
int SlotSuffixToIndex(const char* suffix) {
for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;
}
return -1;
}
// Initialize the boot_control_private struct with the information from
// the bootloader_message buffer stored in |boot_ctrl|. Returns whether the
// initialization succeeded.
bool BootControl_lazyInitialization(boot_control_private_t* module) {
if (module->initialized) return true;
// Initialize the current_slot from the read-only property. If the property
// was not set (from either the command line or the device tree), we can later
// initialize it from the bootloader_control struct.
std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", "");
module->current_slot = SlotSuffixToIndex(suffix_prop.c_str());
std::string err;
std::string device = get_bootloader_message_blk_device(&err);
if (device.empty()) return false;
bootloader_control boot_ctrl;
if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) return false;
// Note that since there isn't a module unload function this memory is leaked.
module->misc_device = strdup(device.c_str());
module->initialized = true;
// Validate the loaded data, otherwise we will destroy it and re-initialize it
// with the current information.
uint32_t computed_crc32 = BootloaderControlLECRC(&boot_ctrl);
if (boot_ctrl.crc32_le != computed_crc32) {
LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32
<< " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing.";
InitDefaultBootloaderControl(module, &boot_ctrl);
UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl);
}
module->num_slots = boot_ctrl.nb_slot;
return true;
}
void BootControl_init(boot_control_module_t* module) {
BootControl_lazyInitialization(reinterpret_cast<boot_control_private_t*>(module));
}
unsigned int BootControl_getNumberSlots(boot_control_module_t* module) {
return reinterpret_cast<boot_control_private_t*>(module)->num_slots;
}
unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) {
return reinterpret_cast<boot_control_private_t*>(module)->current_slot;
}
int BootControl_markBootSuccessful(boot_control_module_t* module) {
boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
bootloader_control bootctrl;
if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
bootctrl.slot_info[bootctrl_module->current_slot].successful_boot = 1;
// tries_remaining == 0 means that the slot is not bootable anymore, make
// sure we mark the current slot as bootable if it succeeds in the last
// attempt.
bootctrl.slot_info[bootctrl_module->current_slot].tries_remaining = 1;
if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
return 0;
}
int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) {
boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
// Invalid slot number.
return -1;
}
bootloader_control bootctrl;
if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
// Set every other slot with a lower priority than the new "active" slot.
const unsigned int kActivePriority = 15;
const unsigned int kActiveTries = 6;
for (unsigned int i = 0; i < bootctrl_module->num_slots; ++i) {
if (i != slot) {
if (bootctrl.slot_info[i].priority >= kActivePriority)
bootctrl.slot_info[i].priority = kActivePriority - 1;
}
}
// Note that setting a slot as active doesn't change the successful bit.
// The successful bit will only be changed by setSlotAsUnbootable().
bootctrl.slot_info[slot].priority = kActivePriority;
bootctrl.slot_info[slot].tries_remaining = kActiveTries;
// Setting the current slot as active is a way to revert the operation that
// set *another* slot as active at the end of an updater. This is commonly
// used to cancel the pending update. We should only reset the verity_corrpted
// bit when attempting a new slot, otherwise the verity bit on the current
// slot would be flip.
if (slot != bootctrl_module->current_slot) bootctrl.slot_info[slot].verity_corrupted = 0;
if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
return 0;
}
int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) {
boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
// Invalid slot number.
return -1;
}
bootloader_control bootctrl;
if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
// The only way to mark a slot as unbootable, regardless of the priority is to
// set the tries_remaining to 0.
bootctrl.slot_info[slot].successful_boot = 0;
bootctrl.slot_info[slot].tries_remaining = 0;
if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
return 0;
}
int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) {
boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
// Invalid slot number.
return -1;
}
bootloader_control bootctrl;
if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
return bootctrl.slot_info[slot].tries_remaining;
}
int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) {
boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
// Invalid slot number.
return -1;
}
bootloader_control bootctrl;
if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining;
}
const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) {
if (slot >= kMaxNumSlots || slot >= reinterpret_cast<boot_control_private_t*>(module)->num_slots) {
return NULL;
}
return kSlotSuffixes[slot];
}
static int BootControl_open(const hw_module_t* module __unused, const char* id __unused,
hw_device_t** device __unused) {
/* Nothing to do currently. */
return 0;
}
struct hw_module_methods_t BootControl_methods = {
.open = BootControl_open,
};
} // namespace
boot_control_private_t HAL_MODULE_INFO_SYM = {
.base =
{
.common =
{
.tag = HARDWARE_MODULE_TAG,
.module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = BOOT_CONTROL_HARDWARE_MODULE_ID,
.name = "AOSP reference bootctrl HAL",
.author = "The Android Open Source Project",
.methods = &BootControl_methods,
},
.init = BootControl_init,
.getNumberSlots = BootControl_getNumberSlots,
.getCurrentSlot = BootControl_getCurrentSlot,
.markBootSuccessful = BootControl_markBootSuccessful,
.setActiveBootSlot = BootControl_setActiveBootSlot,
.setSlotAsUnbootable = BootControl_setSlotAsUnbootable,
.isSlotBootable = BootControl_isSlotBootable,
.getSuffix = BootControl_getSuffix,
.isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful,
},
.initialized = false,
.misc_device = nullptr,
.num_slots = 0,
.current_slot = 0,
};
+26
View File
@@ -0,0 +1,26 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
cc_library_static {
name: "libbootloader_message",
srcs: ["bootloader_message.cpp"],
cppflags: ["-Werror"],
static_libs: [
"libbase",
"libfs_mgr",
],
export_include_dirs: ["include"],
}
@@ -144,6 +144,13 @@ static bool write_misc_partition(const void* p, size_t size, const std::string&
return true;
}
std::string get_bootloader_message_blk_device(std::string* err) {
std::string misc_blk_device = get_misc_blk_device(err);
if (misc_blk_device.empty()) return "";
if (!wait_for_device(misc_blk_device, err)) return "";
return misc_blk_device;
}
bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
std::string* err) {
return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
@@ -191,6 +191,11 @@ static_assert(sizeof(struct bootloader_control) ==
#include <string>
#include <vector>
// Return the block device name for the bootloader message partition and waits
// for the device for up to 10 seconds. In case of error returns the empty
// string.
std::string get_bootloader_message_blk_device(std::string* err);
// Read bootloader message into boot. Error message will be set in err.
bool read_bootloader_message(bootloader_message* boot, std::string* err);
+3
View File
@@ -23,6 +23,8 @@
#include <string>
#include <vector>
#include <android-base/macros.h>
#include "expr.h"
#include "yydefs.h"
#include "parser.h"
@@ -121,6 +123,7 @@ arglist: /* empty */ {
$$->emplace_back($1);
}
| arglist ',' expr {
UNUSED($1);
$$->push_back(std::unique_ptr<Expr>($3));
}
;
+2
View File
@@ -24,6 +24,7 @@ enum ErrorCode {
kZipOpenFailure,
kBootreasonInBlacklist,
kPackageCompatibilityFailure,
kScriptExecutionFailure,
};
enum CauseCode {
@@ -43,6 +44,7 @@ enum CauseCode {
kTune2FsFailure,
kRebootFailure,
kPackageExtractFileFailure,
kPatchApplicationFailure,
kVendorFailure = 200
};
+143 -140
View File
@@ -235,11 +235,13 @@ static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse
return NO_STATUS;
}
static int handle_flush(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
static int handle_flush(void* /* data */, struct fuse_data* /* fd */,
const struct fuse_in_header* /* hdr */) {
return 0;
}
static int handle_release(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
static int handle_release(void* /* data */, struct fuse_data* /* fd */,
const struct fuse_in_header* /* hdr */) {
return 0;
}
@@ -375,166 +377,167 @@ static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_he
return NO_STATUS;
}
int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
uint64_t file_size, uint32_t block_size)
{
int result;
int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_size,
uint32_t block_size) {
// If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a
// previous abnormal exit.)
umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE);
// If something's already mounted on our mountpoint, try to remove
// it. (Mostly in case of a previous abnormal exit.)
umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE);
// fs/fuse/inode.c in kernel code uses the greater of 4096 and the passed-in max_read.
if (block_size < 4096) {
fprintf(stderr, "block size (%u) is too small\n", block_size);
return -1;
}
if (block_size > (1 << 22)) { // 4 MiB
fprintf(stderr, "block size (%u) is too large\n", block_size);
return -1;
}
if (block_size < 1024) {
fprintf(stderr, "block size (%u) is too small\n", block_size);
return -1;
}
if (block_size > (1<<22)) { // 4 MiB
fprintf(stderr, "block size (%u) is too large\n", block_size);
return -1;
}
struct fuse_data fd = {};
fd.vtab = vtab;
fd.cookie = cookie;
fd.file_size = file_size;
fd.block_size = block_size;
fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1);
struct fuse_data fd;
memset(&fd, 0, sizeof(fd));
fd.vtab = vtab;
fd.cookie = cookie;
fd.file_size = file_size;
fd.block_size = block_size;
fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1);
int result;
if (fd.file_blocks > (1 << 18)) {
fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks);
result = -1;
goto done;
}
if (fd.file_blocks > (1<<18)) {
fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks);
result = -1;
goto done;
}
fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH);
if (fd.hashes == NULL) {
fprintf(stderr, "failed to allocate %d bites for hashes\n",
fd.file_blocks * SHA256_DIGEST_LENGTH);
result = -1;
goto done;
}
fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH);
if (fd.hashes == NULL) {
fprintf(stderr, "failed to allocate %d bites for hashes\n",
fd.file_blocks * SHA256_DIGEST_LENGTH);
result = -1;
goto done;
}
fd.uid = getuid();
fd.gid = getgid();
fd.uid = getuid();
fd.gid = getgid();
fd.curr_block = -1;
fd.block_data = (uint8_t*)malloc(block_size);
if (fd.block_data == NULL) {
fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size);
result = -1;
goto done;
}
fd.extra_block = (uint8_t*)malloc(block_size);
if (fd.extra_block == NULL) {
fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size);
result = -1;
goto done;
}
fd.curr_block = -1;
fd.block_data = (uint8_t*)malloc(block_size);
if (fd.block_data == NULL) {
fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size);
result = -1;
goto done;
}
fd.extra_block = (uint8_t*)malloc(block_size);
if (fd.extra_block == NULL) {
fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size);
result = -1;
goto done;
}
fd.ffd = open("/dev/fuse", O_RDWR);
if (fd.ffd < 0) {
perror("open /dev/fuse");
result = -1;
goto done;
}
fd.ffd = open("/dev/fuse", O_RDWR);
if (fd.ffd < 0) {
perror("open /dev/fuse");
result = -1;
goto done;
}
{
char opts[256];
snprintf(opts, sizeof(opts),
("fd=%d,user_id=%d,group_id=%d,max_read=%u,"
"allow_other,rootmode=040000"),
fd.ffd, fd.uid, fd.gid, block_size);
result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT,
"fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts);
result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, "fuse",
MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts);
if (result < 0) {
perror("mount");
goto done;
perror("mount");
goto done;
}
uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8];
for (;;) {
ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
if (len == -1) {
perror("read request");
if (errno == ENODEV) {
result = -1;
break;
}
continue;
}
}
if ((size_t)len < sizeof(struct fuse_in_header)) {
fprintf(stderr, "request too short: len=%zu\n", (size_t)len);
continue;
}
struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer;
void* data = request_buffer + sizeof(struct fuse_in_header);
result = -ENOSYS;
switch (hdr->opcode) {
case FUSE_INIT:
result = handle_init(data, &fd, hdr);
break;
case FUSE_LOOKUP:
result = handle_lookup(data, &fd, hdr);
break;
case FUSE_GETATTR:
result = handle_getattr(data, &fd, hdr);
break;
case FUSE_OPEN:
result = handle_open(data, &fd, hdr);
break;
case FUSE_READ:
result = handle_read(data, &fd, hdr);
break;
case FUSE_FLUSH:
result = handle_flush(data, &fd, hdr);
break;
case FUSE_RELEASE:
result = handle_release(data, &fd, hdr);
break;
default:
fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode);
break;
}
if (result == NO_STATUS_EXIT) {
result = 0;
break;
}
if (result != NO_STATUS) {
struct fuse_out_header outhdr;
outhdr.len = sizeof(outhdr);
outhdr.error = result;
outhdr.unique = hdr->unique;
TEMP_FAILURE_RETRY(write(fd.ffd, &outhdr, sizeof(outhdr)));
}
uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX * 8];
for (;;) {
ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
if (len == -1) {
perror("read request");
if (errno == ENODEV) {
result = -1;
break;
}
continue;
}
done:
fd.vtab->close(fd.cookie);
result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH);
if (result < 0) {
printf("fuse_sideload umount failed: %s\n", strerror(errno));
if (static_cast<size_t>(len) < sizeof(struct fuse_in_header)) {
fprintf(stderr, "request too short: len=%zd\n", len);
continue;
}
if (fd.ffd) close(fd.ffd);
free(fd.hashes);
free(fd.block_data);
free(fd.extra_block);
struct fuse_in_header* hdr = reinterpret_cast<struct fuse_in_header*>(request_buffer);
void* data = request_buffer + sizeof(struct fuse_in_header);
return result;
result = -ENOSYS;
switch (hdr->opcode) {
case FUSE_INIT:
result = handle_init(data, &fd, hdr);
break;
case FUSE_LOOKUP:
result = handle_lookup(data, &fd, hdr);
break;
case FUSE_GETATTR:
result = handle_getattr(data, &fd, hdr);
break;
case FUSE_OPEN:
result = handle_open(data, &fd, hdr);
break;
case FUSE_READ:
result = handle_read(data, &fd, hdr);
break;
case FUSE_FLUSH:
result = handle_flush(data, &fd, hdr);
break;
case FUSE_RELEASE:
result = handle_release(data, &fd, hdr);
break;
default:
fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode);
break;
}
if (result == NO_STATUS_EXIT) {
result = 0;
break;
}
if (result != NO_STATUS) {
struct fuse_out_header outhdr;
outhdr.len = sizeof(outhdr);
outhdr.error = result;
outhdr.unique = hdr->unique;
TEMP_FAILURE_RETRY(write(fd.ffd, &outhdr, sizeof(outhdr)));
}
}
done:
fd.vtab->close(fd.cookie);
result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH);
if (result < 0) {
printf("fuse_sideload umount failed: %s\n", strerror(errno));
}
if (fd.ffd) close(fd.ffd);
free(fd.hashes);
free(fd.block_data);
free(fd.extra_block);
return result;
}
extern "C" int run_old_fuse_sideload(struct provider_vtab* vtab, void* cookie,
+10
View File
@@ -1370,13 +1370,19 @@ int PageManager::LoadPackage(std::string name, std::string package, std::string
tw_h_offset = 0;
if (!TWFunc::Path_Exists(package))
return -1;
#ifdef USE_MINZIP
if (sysMapFile(package.c_str(), &map) != 0) {
#else
if (!map.MapFile(package)) {
#endif
LOGERR("Failed to map '%s'\n", package.c_str());
goto error;
}
if (!zip.Open(package.c_str(), &map)) {
LOGERR("Unable to open zip archive '%s'\n", package.c_str());
#ifdef USE_MINZIP
sysReleaseMap(&map);
#endif
goto error;
}
ctx.zip = &zip;
@@ -1418,7 +1424,9 @@ int PageManager::LoadPackage(std::string name, std::string package, std::string
if (ctx.zip) {
ctx.zip->Close();
#ifdef USE_MINZIP
sysReleaseMap(&map);
#endif
}
return ret;
@@ -1426,7 +1434,9 @@ error:
// Sometimes we get here without a real error
if (ctx.zip) {
ctx.zip->Close();
#ifdef USE_MINZIP
sysReleaseMap(&map);
#endif
}
return -1;
}
+194 -202
View File
@@ -27,6 +27,7 @@
#include <unistd.h>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
@@ -49,97 +50,79 @@
#include "common.h"
#include "error_code.h"
#include "minui/minui.h"
#include "otautil/SysUtil.h"
#include "otautil/ThermalUtil.h"
#include "private/install.h"
#include "roots.h"
#include "ui.h"
#include "verifier.h"
using namespace std::chrono_literals;
#define PUBLIC_KEYS_FILE "/res/keys"
static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
// Default allocation of progress bar segments to operations
static constexpr int VERIFICATION_PROGRESS_TIME = 60;
static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
static constexpr float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
static constexpr float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
static std::condition_variable finish_log_temperature;
// This function parses and returns the build.version.incremental
static int parse_build_number(const std::string& str) {
static std::string parse_build_number(const std::string& str) {
size_t pos = str.find('=');
if (pos != std::string::npos) {
std::string num_string = android::base::Trim(str.substr(pos+1));
int build_number;
if (android::base::ParseInt(num_string.c_str(), &build_number, 0)) {
return build_number;
}
return android::base::Trim(str.substr(pos+1));
}
LOG(ERROR) << "Failed to parse build number in " << str;
return -1;
return "";
}
bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data) {
ZipString metadata_path(METADATA_PATH);
ZipEntry meta_entry;
if (meta_data == nullptr) {
LOG(ERROR) << "string* meta_data can't be nullptr";
return false;
}
if (FindEntry(zip, metadata_path, &meta_entry) != 0) {
LOG(ERROR) << "Failed to find " << METADATA_PATH << " in update package";
return false;
}
bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) {
CHECK(metadata != nullptr);
meta_data->resize(meta_entry.uncompressed_length, '\0');
if (ExtractToMemory(zip, &meta_entry, reinterpret_cast<uint8_t*>(&(*meta_data)[0]),
meta_entry.uncompressed_length) != 0) {
LOG(ERROR) << "Failed to read metadata in update package";
return false;
}
return true;
static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
ZipString path(METADATA_PATH);
ZipEntry entry;
if (FindEntry(zip, path, &entry) != 0) {
LOG(ERROR) << "Failed to find " << METADATA_PATH;
return false;
}
uint32_t length = entry.uncompressed_length;
metadata->resize(length, '\0');
int32_t err = ExtractToMemory(zip, &entry, reinterpret_cast<uint8_t*>(&(*metadata)[0]), length);
if (err != 0) {
LOG(ERROR) << "Failed to extract " << METADATA_PATH << ": " << ErrorCodeString(err);
return false;
}
return true;
}
// Read the build.version.incremental of src/tgt from the metadata and log it to last_install.
static void read_source_target_build(ZipArchiveHandle zip, std::vector<std::string>& log_buffer) {
std::string meta_data;
if (!read_metadata_from_package(zip, &meta_data)) {
return;
}
// Examples of the pre-build and post-build strings in metadata:
// pre-build-incremental=2943039
// post-build-incremental=2951741
std::vector<std::string> lines = android::base::Split(meta_data, "\n");
for (const std::string& line : lines) {
std::string str = android::base::Trim(line);
if (android::base::StartsWith(str, "pre-build-incremental")){
int source_build = parse_build_number(str);
if (source_build != -1) {
log_buffer.push_back(android::base::StringPrintf("source_build: %d",
source_build));
}
} else if (android::base::StartsWith(str, "post-build-incremental")) {
int target_build = parse_build_number(str);
if (target_build != -1) {
log_buffer.push_back(android::base::StringPrintf("target_build: %d",
target_build));
}
}
static void read_source_target_build(ZipArchiveHandle zip, std::vector<std::string>* log_buffer) {
std::string metadata;
if (!read_metadata_from_package(zip, &metadata)) {
return;
}
// Examples of the pre-build and post-build strings in metadata:
// pre-build-incremental=2943039
// post-build-incremental=2951741
std::vector<std::string> lines = android::base::Split(metadata, "\n");
for (const std::string& line : lines) {
std::string str = android::base::Trim(line);
if (android::base::StartsWith(str, "pre-build-incremental")) {
std::string source_build = parse_build_number(str);
if (!source_build.empty()) {
log_buffer->push_back("source_build: " + source_build);
}
} else if (android::base::StartsWith(str, "post-build-incremental")) {
std::string target_build = parse_build_number(str);
if (!target_build.empty()) {
log_buffer->push_back("target_build: " + target_build);
}
}
}
}
// Extract the update binary from the open zip archive |zip| located at |path| and store into |cmd|
// the command line that should be called. The |status_fd| is the file descriptor the child process
// should use to report back the progress of the update.
int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
int status_fd, std::vector<std::string>* cmd);
#ifdef AB_OTA_UPDATER
// Parses the metadata of the OTA package in |zip| and checks whether we are
@@ -220,8 +203,9 @@ static int check_newer_ab_build(ZipArchiveHandle zip) {
return 0;
}
int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
int status_fd, std::vector<std::string>* cmd) {
int update_binary_command(const std::string& package, ZipArchiveHandle zip,
const std::string& binary_path, int /* retry_count */, int status_fd,
std::vector<std::string>* cmd) {
CHECK(cmd != nullptr);
int ret = check_newer_ab_build(zip);
if (ret != 0) {
@@ -255,8 +239,8 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int ret
}
long payload_offset = payload_entry.offset;
*cmd = {
"/sbin/update_engine_sideload",
"--payload=file://" + path,
binary_path,
"--payload=file://" + package,
android::base::StringPrintf("--offset=%ld", payload_offset),
"--headers=" + std::string(payload_properties.begin(), payload_properties.end()),
android::base::StringPrintf("--status_fd=%d", status_fd),
@@ -266,8 +250,9 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int ret
#else // !AB_OTA_UPDATER
int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
int status_fd, std::vector<std::string>* cmd) {
int update_binary_command(const std::string& package, ZipArchiveHandle zip,
const std::string& binary_path, int retry_count, int status_fd,
std::vector<std::string>* cmd) {
CHECK(cmd != nullptr);
// On traditional updates we extract the update binary from the package.
@@ -279,11 +264,10 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int ret
return INSTALL_CORRUPT;
}
const char* binary = "/tmp/update_binary";
unlink(binary);
int fd = creat(binary, 0755);
unlink(binary_path.c_str());
int fd = open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755);
if (fd == -1) {
PLOG(ERROR) << "Failed to create " << binary;
PLOG(ERROR) << "Failed to create " << binary_path;
return INSTALL_ERROR;
}
@@ -295,10 +279,10 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int ret
}
*cmd = {
binary,
binary_path,
EXPAND(RECOVERY_API_VERSION), // defined in Android.mk
std::to_string(status_fd),
path,
package,
};
if (retry_count > 0) {
cmd->push_back("retry");
@@ -307,18 +291,19 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int ret
}
#endif // !AB_OTA_UPDATER
static void log_max_temperature(int* max_temperature) {
static void log_max_temperature(int* max_temperature, const std::atomic<bool>& logger_finished) {
CHECK(max_temperature != nullptr);
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
while (finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) {
while (!logger_finished.load() &&
finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) {
*max_temperature = std::max(*max_temperature, GetMaxValueFromThermalZone());
}
}
// If the package contains an update binary, extract it and run it.
static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_cache,
std::vector<std::string>& log_buffer, int retry_count,
static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
std::vector<std::string>* log_buffer, int retry_count,
int* max_temperature) {
read_source_target_build(zip, log_buffer);
@@ -326,7 +311,13 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_
pipe(pipefd);
std::vector<std::string> args;
int ret = update_binary_command(path, zip, retry_count, pipefd[1], &args);
#ifdef AB_OTA_UPDATER
int ret = update_binary_command(package, zip, "/sbin/update_engine_sideload", retry_count,
pipefd[1], &args);
#else
int ret = update_binary_command(package, zip, "/tmp/update-binary", retry_count, pipefd[1],
&args);
#endif
if (ret) {
close(pipefd[0]);
close(pipefd[1]);
@@ -410,7 +401,8 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_
}
close(pipefd[1]);
std::thread temperature_logger(log_max_temperature, max_temperature);
std::atomic<bool> logger_finished(false);
std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));
*wipe_cache = false;
bool retry_update = false;
@@ -461,7 +453,7 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_
} else if (command == "log") {
if (!args.empty()) {
// Save the logging request from updater and write to last_install later.
log_buffer.push_back(args);
log_buffer->push_back(args);
} else {
LOG(ERROR) << "invalid \"log\" parameters: " << line;
}
@@ -474,6 +466,7 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_
int status;
waitpid(pid, &status, 0);
logger_finished.store(true);
finish_log_temperature.notify_one();
temperature_logger.join();
@@ -481,7 +474,7 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_
return INSTALL_RETRY;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
LOG(ERROR) << "Error in " << path << " (Status " << WEXITSTATUS(status) << ")";
LOG(ERROR) << "Error in " << package << " (Status " << WEXITSTATUS(status) << ")";
return INSTALL_ERROR;
}
@@ -556,150 +549,149 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) {
return false;
}
static int
really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
std::vector<std::string>& log_buffer, int retry_count, int* max_temperature)
{
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);
LOG(INFO) << "Update location: " << path;
static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
std::vector<std::string>* log_buffer, int retry_count,
int* max_temperature) {
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);
LOG(INFO) << "Update location: " << path;
// Map the update package into memory.
ui->Print("Opening update package...\n");
// Map the update package into memory.
ui->Print("Opening update package...\n");
if (path && needs_mount) {
if (path[0] == '@') {
ensure_path_mounted(path+1);
} else {
ensure_path_mounted(path);
}
if (needs_mount) {
if (path[0] == '@') {
ensure_path_mounted(path.substr(1).c_str());
} else {
ensure_path_mounted(path.c_str());
}
}
MemMapping map;
if (sysMapFile(path, &map) != 0) {
LOG(ERROR) << "failed to map file";
return INSTALL_CORRUPT;
}
MemMapping map;
if (!map.MapFile(path)) {
LOG(ERROR) << "failed to map file";
return INSTALL_CORRUPT;
}
// Verify package.
if (!verify_package(map.addr, map.length)) {
log_buffer.push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
sysReleaseMap(&map);
return INSTALL_CORRUPT;
}
// Verify package.
if (!verify_package(map.addr, map.length)) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
return INSTALL_CORRUPT;
}
// Try to open the package.
ZipArchiveHandle zip;
int err = OpenArchiveFromMemory(map.addr, map.length, path, &zip);
if (err != 0) {
LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);
log_buffer.push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
// Try to open the package.
ZipArchiveHandle zip;
int err = OpenArchiveFromMemory(map.addr, map.length, path.c_str(), &zip);
if (err != 0) {
LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
sysReleaseMap(&map);
CloseArchive(zip);
return INSTALL_CORRUPT;
}
// Additionally verify the compatibility of the package.
if (!verify_package_compatibility(zip)) {
log_buffer.push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
sysReleaseMap(&map);
CloseArchive(zip);
return INSTALL_CORRUPT;
}
// Verify and install the contents of the package.
ui->Print("Installing update...\n");
if (retry_count > 0) {
ui->Print("Retry attempt: %d\n", retry_count);
}
ui->SetEnableReboot(false);
int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
ui->SetEnableReboot(true);
ui->Print("\n");
sysReleaseMap(&map);
CloseArchive(zip);
return result;
return INSTALL_CORRUPT;
}
// Additionally verify the compatibility of the package.
if (!verify_package_compatibility(zip)) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
CloseArchive(zip);
return INSTALL_CORRUPT;
}
// Verify and install the contents of the package.
ui->Print("Installing update...\n");
if (retry_count > 0) {
ui->Print("Retry attempt: %d\n", retry_count);
}
ui->SetEnableReboot(false);
int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
ui->SetEnableReboot(true);
ui->Print("\n");
CloseArchive(zip);
return result;
}
int
install_package(const char* path, bool* wipe_cache, const char* install_file,
bool needs_mount, int retry_count)
{
modified_flash = true;
auto start = std::chrono::system_clock::now();
int install_package(const std::string& path, bool* wipe_cache, const std::string& install_file,
bool needs_mount, int retry_count) {
CHECK(!path.empty());
CHECK(!install_file.empty());
CHECK(wipe_cache != nullptr);
int start_temperature = GetMaxValueFromThermalZone();
int max_temperature = start_temperature;
modified_flash = true;
auto start = std::chrono::system_clock::now();
int result;
std::vector<std::string> log_buffer;
if (setup_install_mounts() != 0) {
LOG(ERROR) << "failed to set up expected mounts for install; aborting";
result = INSTALL_ERROR;
int start_temperature = GetMaxValueFromThermalZone();
int max_temperature = start_temperature;
int result;
std::vector<std::string> log_buffer;
if (setup_install_mounts() != 0) {
LOG(ERROR) << "failed to set up expected mounts for install; aborting";
result = INSTALL_ERROR;
} else {
result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count,
&max_temperature);
}
// Measure the time spent to apply OTA update in seconds.
std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
int time_total = static_cast<int>(duration.count());
bool has_cache = volume_for_path("/cache") != nullptr;
// Skip logging the uncrypt_status on devices without /cache.
if (has_cache) {
static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS;
} else {
result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count,
&max_temperature);
}
// Measure the time spent to apply OTA update in seconds.
std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
int time_total = static_cast<int>(duration.count());
bool has_cache = volume_for_path("/cache") != nullptr;
// Skip logging the uncrypt_status on devices without /cache.
if (has_cache) {
if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS;
std::string uncrypt_status;
if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
PLOG(WARNING) << "failed to read uncrypt status";
} else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
} else {
std::string uncrypt_status;
if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
PLOG(WARNING) << "failed to read uncrypt status";
} else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
} else {
log_buffer.push_back(android::base::Trim(uncrypt_status));
}
log_buffer.push_back(android::base::Trim(uncrypt_status));
}
}
}
// The first two lines need to be the package name and install result.
std::vector<std::string> log_header = {
path,
result == INSTALL_SUCCESS ? "1" : "0",
"time_total: " + std::to_string(time_total),
"retry: " + std::to_string(retry_count),
};
// The first two lines need to be the package name and install result.
std::vector<std::string> log_header = {
path,
result == INSTALL_SUCCESS ? "1" : "0",
"time_total: " + std::to_string(time_total),
"retry: " + std::to_string(retry_count),
};
int end_temperature = GetMaxValueFromThermalZone();
max_temperature = std::max(end_temperature, max_temperature);
if (start_temperature > 0) {
log_buffer.push_back("temperature_start: " + std::to_string(start_temperature));
}
if (end_temperature > 0) {
log_buffer.push_back("temperature_end: " + std::to_string(end_temperature));
}
if (max_temperature > 0) {
log_buffer.push_back("temperature_max: " + std::to_string(max_temperature));
}
int end_temperature = GetMaxValueFromThermalZone();
max_temperature = std::max(end_temperature, max_temperature);
if (start_temperature > 0) {
log_buffer.push_back("temperature_start: " + std::to_string(start_temperature));
}
if (end_temperature > 0) {
log_buffer.push_back("temperature_end: " + std::to_string(end_temperature));
}
if (max_temperature > 0) {
log_buffer.push_back("temperature_max: " + std::to_string(max_temperature));
}
std::string log_content = android::base::Join(log_header, "\n") + "\n" +
android::base::Join(log_buffer, "\n") + "\n";
if (!android::base::WriteStringToFile(log_content, install_file)) {
PLOG(ERROR) << "failed to write " << install_file;
}
std::string log_content =
android::base::Join(log_header, "\n") + "\n" + android::base::Join(log_buffer, "\n") + "\n";
if (!android::base::WriteStringToFile(log_content, install_file)) {
PLOG(ERROR) << "failed to write " << install_file;
}
// Write a copy into last_log.
LOG(INFO) << log_content;
// Write a copy into last_log.
LOG(INFO) << log_content;
return result;
return result;
}
bool verify_package(const unsigned char* package_data, size_t package_size) {
static constexpr const char* PUBLIC_KEYS_FILE = "/res/keys";
std::vector<Certificate> loadedKeys;
if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) {
LOG(ERROR) << "Failed to load keys";
+8 -6
View File
@@ -20,10 +20,12 @@
#include <string>
#include <ziparchive/zip_archive.h>
// Install the package specified by root_path. If INSTALL_SUCCESS is
// returned and *wipe_cache is true on exit, caller should wipe the
// cache partition.
int install_package(const char* root_path, bool* wipe_cache, const char* install_file,
enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED,
INSTALL_RETRY };
// Installs the given update package. If INSTALL_SUCCESS is returned and *wipe_cache is true on
// exit, caller should wipe the cache partition.
int install_package(const std::string& package, bool* wipe_cache, const std::string& install_file,
bool needs_mount, int retry_count);
// Verify the package by ota keys. Return true if the package is verified successfully,
@@ -32,9 +34,9 @@ bool verify_package(const unsigned char* package_data, size_t package_size);
// Read meta data file of the package, write its content in the string pointed by meta_data.
// Return true if succeed, otherwise return false.
bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data);
bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata);
// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the
// entry doesn't exist.
bool verify_package_compatibility(ZipArchiveHandle package_zip);
+1
View File
@@ -67,6 +67,7 @@ include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_MODULE := minadbd_test
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_SRC_FILES := fuse_adb_provider_test.cpp
LOCAL_CFLAGS := $(minadbd_cflags)
LOCAL_C_INCLUDES := $(LOCAL_PATH) system/core/adb
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<configuration description="Config for minadbd_test">
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="minadbd_test->/data/local/tmp/minadbd_test" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="minadbd_test" />
</test>
</configuration>
+11 -9
View File
@@ -14,17 +14,17 @@
* limitations under the License.
*/
#include "fuse_adb_provider.h"
#include <gtest/gtest.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <string>
#include <gtest/gtest.h>
#include "adb_io.h"
#include "fuse_adb_provider.h"
TEST(fuse_adb_provider, read_block_adb) {
adb_data data = {};
@@ -46,9 +46,8 @@ TEST(fuse_adb_provider, read_block_adb) {
uint32_t block = 1234U;
const char expected_block[] = "00001234";
ASSERT_EQ(0, read_block_adb(reinterpret_cast<void*>(&data), block,
reinterpret_cast<uint8_t*>(block_data),
sizeof(expected_data) - 1));
ASSERT_EQ(0, read_block_adb(static_cast<void*>(&data), block,
reinterpret_cast<uint8_t*>(block_data), sizeof(expected_data) - 1));
// Check that read_block_adb requested the right block.
char block_req[sizeof(expected_block)] = {};
@@ -80,9 +79,12 @@ TEST(fuse_adb_provider, read_block_adb_fail_write) {
ASSERT_EQ(0, close(sockets[1]));
// write(2) raises SIGPIPE since the reading end has been closed. Ignore the signal to avoid
// failing the test.
signal(SIGPIPE, SIG_IGN);
char buf[1];
ASSERT_EQ(-EIO, read_block_adb(reinterpret_cast<void*>(&data), 0,
reinterpret_cast<uint8_t*>(buf), 1));
ASSERT_EQ(-EIO, read_block_adb(static_cast<void*>(&data), 0, reinterpret_cast<uint8_t*>(buf), 1));
close(sockets[0]);
}
-1
View File
@@ -69,7 +69,6 @@ static void sideload_host_service(int sfd, const std::string& args) {
int result = run_adb_fuse(sfd, file_size, block_size);
printf("sideload_host finished\n");
sleep(1);
exit(result == 0 ? 0 : 1);
}
+5 -2
View File
@@ -51,8 +51,11 @@ ifeq ($(TW_NEW_ION_HEAP), true)
endif
LOCAL_STATIC_LIBRARIES += libpng
LOCAL_WHOLE_STATIC_LIBRARIES += \
libdrm
ifneq ($(wildcard external/libdrm/Android.common.mk),)
LOCAL_WHOLE_STATIC_LIBRARIES += libdrm_platform
else
LOCAL_WHOLE_STATIC_LIBRARIES += libdrm
endif
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
LOCAL_CFLAGS += -DHAS_LIBSYNC
LOCAL_WHOLE_STATIC_LIBRARIES += libsync_recovery
+42 -9
View File
@@ -57,40 +57,47 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
}
#ifdef TW_USE_MINUI_WITH_OPTIONAL_TOUCH_EVENTS
int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
#else
#ifdef TW_USE_MINUI_WITH_DATA
int ev_init(ev_callback input_cb, void* data) {
#else
int ev_init(ev_callback input_cb) {
#endif
bool epollctlfail = false;
bool allow_touch_inputs = false;
#endif
g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
if (g_epoll_fd == -1) {
return -1;
}
bool epollctlfail = false;
DIR* dir = opendir("/dev/input");
if (dir != NULL) {
if (dir != nullptr) {
dirent* de;
while ((de = readdir(dir))) {
// Use unsigned long to match ioctl's parameter type.
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
// fprintf(stderr,"/dev/input/%s\n", de->d_name);
if (strncmp(de->d_name, "event", 5)) continue;
int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
if (fd == -1) continue;
// Use unsigned long to match ioctl's parameter type.
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
// Read the evbits of the input device.
if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
close(fd);
continue;
}
// We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
// We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also
// allowed if allow_touch_inputs is set.
if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
close(fd);
continue;
if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
close(fd);
continue;
}
}
epoll_event ev;
@@ -261,3 +268,29 @@ void ev_iterate_available_keys(const std::function<void(int)>& f) {
}
}
}
#ifdef TW_USE_MINUI_WITH_OPTIONAL_TOUCH_EVENTS
void ev_iterate_touch_inputs(const std::function<void(int)>& action) {
for (size_t i = 0; i < ev_dev_count; ++i) {
// Use unsigned long to match ioctl's parameter type.
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)] = {}; // NOLINT
if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
continue;
}
if (!test_bit(EV_ABS, ev_bits)) {
continue;
}
unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)] = {}; // NOLINT
if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_ABS, KEY_MAX), key_bits) == -1) {
continue;
}
for (int key_code = 0; key_code <= KEY_MAX; ++key_code) {
if (test_bit(key_code, key_bits)) {
action(key_code);
}
}
}
}
#endif
+2 -1
View File
@@ -30,7 +30,8 @@
#include "minui/minui.h"
MinuiBackendAdf::MinuiBackendAdf() : intf_fd(-1), dev(), n_surfaces(0), surfaces() {}
MinuiBackendAdf::MinuiBackendAdf()
: intf_fd(-1), dev(), current_surface(0), n_surfaces(0), surfaces() {}
int MinuiBackendAdf::SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf) {
*surf = {};
+5
View File
@@ -91,7 +91,12 @@ int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
using ev_callback = std::function<int(int fd, uint32_t epevents)>;
using ev_set_key_callback = std::function<int(int code, int value)>;
#ifdef TW_USE_MINUI_WITH_OPTIONAL_TOUCH_EVENTS
int ev_init(ev_callback input_cb, bool allow_touch_inputs = false);
void ev_iterate_touch_inputs(const std::function<void(int)>& action);
#else
int ev_init(ev_callback input_cb);
#endif
int ev_add_fd(int fd, ev_callback cb);
int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
#endif
+1 -1
View File
@@ -56,7 +56,7 @@ static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
resPath[sizeof(resPath)-1] = '\0';
FILE* fp = fopen(resPath, "rb");
FILE* fp = fopen(resPath, "rbe");
if (fp == NULL) {
result = -1;
goto exit;
+11 -18
View File
@@ -27,6 +27,8 @@
#include <string>
#include <vector>
#include <android-base/logging.h>
struct MountedVolume {
std::string device;
std::string mount_point;
@@ -60,13 +62,6 @@ bool scan_mounted_volumes() {
return true;
}
MountedVolume* find_mounted_volume_by_device(const char* device) {
for (size_t i = 0; i < g_mounts_state.size(); ++i) {
if (g_mounts_state[i]->device == device) return g_mounts_state[i];
}
return nullptr;
}
MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) {
for (size_t i = 0; i < g_mounts_state.size(); ++i) {
if (g_mounts_state[i]->mount_point == mount_point) return g_mounts_state[i];
@@ -75,15 +70,13 @@ MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) {
}
int unmount_mounted_volume(MountedVolume* volume) {
// Intentionally pass the empty string to umount if the caller tries
// to unmount a volume they already unmounted using this
// function.
std::string mount_point = volume->mount_point;
volume->mount_point.clear();
return umount(mount_point.c_str());
}
int remount_read_only(MountedVolume* volume) {
return mount(volume->device.c_str(), volume->mount_point.c_str(), volume->filesystem.c_str(),
MS_NOATIME | MS_NODEV | MS_NODIRATIME | MS_RDONLY | MS_REMOUNT, 0);
// Intentionally pass the empty string to umount if the caller tries to unmount a volume they
// already unmounted using this function.
std::string mount_point = volume->mount_point;
volume->mount_point.clear();
int result = umount(mount_point.c_str());
if (result == -1) {
PLOG(WARNING) << "Failed to umount " << mount_point;
}
return result;
}
-4
View File
@@ -21,12 +21,8 @@ struct MountedVolume;
bool scan_mounted_volumes();
MountedVolume* find_mounted_volume_by_device(const char* device);
MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point);
int unmount_mounted_volume(MountedVolume* volume);
int remount_read_only(MountedVolume* volume);
#endif
+6 -1
View File
@@ -29,7 +29,12 @@ otafault_static_libs := \
libbase \
liblog
LOCAL_CFLAGS := -Werror
LOCAL_CFLAGS := \
-Werror \
-Wthread-safety \
-Wthread-safety-negative \
-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS
LOCAL_SRC_FILES := config.cpp ota_io.cpp
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE := libotafault
+16 -2
View File
@@ -24,10 +24,13 @@
#include <map>
#include <memory>
#include <mutex>
#include <android-base/thread_annotations.h>
#include "config.h"
static std::map<intptr_t, const char*> filename_cache;
static std::mutex filename_mutex;
static std::map<intptr_t, const char*> filename_cache GUARDED_BY(filename_mutex);
static std::string read_fault_file_name = "";
static std::string write_fault_file_name = "";
static std::string fsync_fault_file_name = "";
@@ -55,23 +58,28 @@ bool have_eio_error = false;
int ota_open(const char* path, int oflags) {
// Let the caller handle errors; we do not care if open succeeds or fails
int fd = open(path, oflags);
std::lock_guard<std::mutex> lock(filename_mutex);
filename_cache[fd] = path;
return fd;
}
int ota_open(const char* path, int oflags, mode_t mode) {
int fd = open(path, oflags, mode);
std::lock_guard<std::mutex> lock(filename_mutex);
filename_cache[fd] = path;
return fd; }
return fd;
}
FILE* ota_fopen(const char* path, const char* mode) {
FILE* fh = fopen(path, mode);
std::lock_guard<std::mutex> lock(filename_mutex);
filename_cache[(intptr_t)fh] = path;
return fh;
}
static int __ota_close(int fd) {
// descriptors can be reused, so make sure not to leave them in the cache
std::lock_guard<std::mutex> lock(filename_mutex);
filename_cache.erase(fd);
return close(fd);
}
@@ -85,6 +93,7 @@ int ota_close(unique_fd& fd) {
}
static int __ota_fclose(FILE* fh) {
std::lock_guard<std::mutex> lock(filename_mutex);
filename_cache.erase(reinterpret_cast<intptr_t>(fh));
return fclose(fh);
}
@@ -99,6 +108,7 @@ int ota_fclose(unique_file& fh) {
size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) {
if (should_fault_inject(OTAIO_READ)) {
std::lock_guard<std::mutex> lock(filename_mutex);
auto cached = filename_cache.find((intptr_t)stream);
const char* cached_path = cached->second;
if (cached != filename_cache.end() &&
@@ -119,6 +129,7 @@ size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) {
ssize_t ota_read(int fd, void* buf, size_t nbyte) {
if (should_fault_inject(OTAIO_READ)) {
std::lock_guard<std::mutex> lock(filename_mutex);
auto cached = filename_cache.find(fd);
const char* cached_path = cached->second;
if (cached != filename_cache.end()
@@ -138,6 +149,7 @@ ssize_t ota_read(int fd, void* buf, size_t nbyte) {
size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) {
if (should_fault_inject(OTAIO_WRITE)) {
std::lock_guard<std::mutex> lock(filename_mutex);
auto cached = filename_cache.find((intptr_t)stream);
const char* cached_path = cached->second;
if (cached != filename_cache.end() &&
@@ -157,6 +169,7 @@ size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) {
ssize_t ota_write(int fd, const void* buf, size_t nbyte) {
if (should_fault_inject(OTAIO_WRITE)) {
std::lock_guard<std::mutex> lock(filename_mutex);
auto cached = filename_cache.find(fd);
const char* cached_path = cached->second;
if (cached != filename_cache.end() &&
@@ -176,6 +189,7 @@ ssize_t ota_write(int fd, const void* buf, size_t nbyte) {
int ota_fsync(int fd) {
if (should_fault_inject(OTAIO_FSYNC)) {
std::lock_guard<std::mutex> lock(filename_mutex);
auto cached = filename_cache.find(fd);
const char* cached_path = cached->second;
if (cached != filename_cache.end() &&
+33
View File
@@ -0,0 +1,33 @@
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
cc_library_static {
name: "libotautil",
srcs: [
"SysUtil.cpp",
"DirUtil.cpp",
"ThermalUtil.cpp",
],
static_libs: [
"libselinux",
"libbase",
],
cflags: [
"-Werror",
"-Wall",
],
}
+58 -68
View File
@@ -16,14 +16,12 @@
#include "SysUtil.h"
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdint.h> // SIZE_MAX
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <algorithm>
#include <string>
#include <vector>
@@ -32,9 +30,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
static bool sysMapFD(int fd, MemMapping* pMap) {
CHECK(pMap != nullptr);
bool MemMapping::MapFD(int fd) {
struct stat sb;
if (fstat(fd, &sb) == -1) {
PLOG(ERROR) << "fstat(" << fd << ") failed";
@@ -47,50 +43,49 @@ static bool sysMapFD(int fd, MemMapping* pMap) {
return false;
}
pMap->addr = static_cast<unsigned char*>(memPtr);
pMap->length = sb.st_size;
pMap->ranges.push_back({ memPtr, static_cast<size_t>(sb.st_size) });
addr = static_cast<unsigned char*>(memPtr);
length = sb.st_size;
ranges_.clear();
ranges_.emplace_back(MappedRange{ memPtr, static_cast<size_t>(sb.st_size) });
return true;
}
// A "block map" which looks like this (from uncrypt/uncrypt.cpp):
//
// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device
// 49652 4096 # file size in bytes, block size
// 3 # count of block ranges
// 1000 1008 # block range 0
// 2100 2102 # ... block range 1
// 30 33 # ... block range 2
// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device
// 49652 4096 # file size in bytes, block size
// 3 # count of block ranges
// 1000 1008 # block range 0
// 2100 2102 # ... block range 1
// 30 33 # ... block range 2
//
// Each block range represents a half-open interval; the line "30 33"
// reprents the blocks [30, 31, 32].
static int sysMapBlockFile(const char* filename, MemMapping* pMap) {
CHECK(pMap != nullptr);
// Each block range represents a half-open interval; the line "30 33" reprents the blocks
// [30, 31, 32].
bool MemMapping::MapBlockFile(const std::string& filename) {
std::string content;
if (!android::base::ReadFileToString(filename, &content)) {
PLOG(ERROR) << "Failed to read " << filename;
return -1;
return false;
}
std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
if (lines.size() < 4) {
LOG(ERROR) << "Block map file is too short: " << lines.size();
return -1;
return false;
}
size_t size;
unsigned int blksize;
if (sscanf(lines[1].c_str(), "%zu %u", &size, &blksize) != 2) {
size_t blksize;
if (sscanf(lines[1].c_str(), "%zu %zu", &size, &blksize) != 2) {
LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
return -1;
return false;
}
size_t range_count;
if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
LOG(ERROR) << "Failed to parse block map header: " << lines[2];
return -1;
return false;
}
size_t blocks;
@@ -101,14 +96,14 @@ static int sysMapBlockFile(const char* filename, MemMapping* pMap) {
lines.size() != 3 + range_count) {
LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize
<< ", range_count " << range_count << ", lines " << lines.size();
return -1;
return false;
}
// Reserve enough contiguous address space for the whole file.
void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (reserve == MAP_FAILED) {
PLOG(ERROR) << "failed to reserve address space";
return -1;
return false;
}
const std::string& block_dev = lines[0];
@@ -116,10 +111,10 @@ static int sysMapBlockFile(const char* filename, MemMapping* pMap) {
if (fd == -1) {
PLOG(ERROR) << "failed to open block device " << block_dev;
munmap(reserve, blocks * blksize);
return -1;
return false;
}
pMap->ranges.resize(range_count);
ranges_.clear();
unsigned char* next = static_cast<unsigned char*>(reserve);
size_t remaining_size = blocks * blksize;
@@ -129,84 +124,79 @@ static int sysMapBlockFile(const char* filename, MemMapping* pMap) {
size_t start, end;
if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) {
LOG(ERROR) << "failed to parse range " << i << " in block map: " << line;
LOG(ERROR) << "failed to parse range " << i << ": " << line;
success = false;
break;
}
size_t length = (end - start) * blksize;
if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) {
LOG(ERROR) << "unexpected range in block map: " << start << " " << end;
size_t range_size = (end - start) * blksize;
if (end <= start || (end - start) > SIZE_MAX / blksize || range_size > remaining_size) {
LOG(ERROR) << "Invalid range: " << start << " " << end;
success = false;
break;
}
void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd,
static_cast<off64_t>(start) * blksize);
if (addr == MAP_FAILED) {
PLOG(ERROR) << "failed to map block " << i;
void* range_start = mmap64(next, range_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd,
static_cast<off64_t>(start) * blksize);
if (range_start == MAP_FAILED) {
PLOG(ERROR) << "failed to map range " << i << ": " << line;
success = false;
break;
}
pMap->ranges[i].addr = addr;
pMap->ranges[i].length = length;
ranges_.emplace_back(MappedRange{ range_start, range_size });
next += length;
remaining_size -= length;
next += range_size;
remaining_size -= range_size;
}
if (success && remaining_size != 0) {
LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size;
LOG(ERROR) << "Invalid ranges: remaining_size " << remaining_size;
success = false;
}
if (!success) {
munmap(reserve, blocks * blksize);
return -1;
return false;
}
pMap->addr = static_cast<unsigned char*>(reserve);
pMap->length = size;
addr = static_cast<unsigned char*>(reserve);
length = size;
LOG(INFO) << "mmapped " << range_count << " ranges";
return 0;
return true;
}
int sysMapFile(const char* fn, MemMapping* pMap) {
if (fn == nullptr || pMap == nullptr) {
LOG(ERROR) << "Invalid argument(s)";
return -1;
bool MemMapping::MapFile(const std::string& fn) {
if (fn.empty()) {
LOG(ERROR) << "Empty filename";
return false;
}
*pMap = {};
if (fn[0] == '@') {
if (sysMapBlockFile(fn + 1, pMap) != 0) {
// Block map file "@/cache/recovery/block.map".
if (!MapBlockFile(fn.substr(1))) {
LOG(ERROR) << "Map of '" << fn << "' failed";
return -1;
return false;
}
} else {
// This is a regular file.
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn, O_RDONLY)));
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY)));
if (fd == -1) {
PLOG(ERROR) << "Unable to open '" << fn << "'";
return -1;
return false;
}
if (!sysMapFD(fd, pMap)) {
if (!MapFD(fd)) {
LOG(ERROR) << "Map of '" << fn << "' failed";
return -1;
return false;
}
}
return 0;
return true;
}
/*
* Release a memory mapping.
*/
void sysReleaseMap(MemMapping* pMap) {
std::for_each(pMap->ranges.cbegin(), pMap->ranges.cend(), [](const MappedRange& range) {
MemMapping::~MemMapping() {
for (const auto& range : ranges_) {
if (munmap(range.addr, range.length) == -1) {
PLOG(ERROR) << "munmap(" << range.addr << ", " << range.length << ") failed";
PLOG(ERROR) << "Failed to munmap(" << range.addr << ", " << range.length << ")";
}
});
pMap->ranges.clear();
};
ranges_.clear();
}
+23 -25
View File
@@ -19,37 +19,35 @@
#include <sys/types.h>
#include <string>
#include <vector>
struct MappedRange {
void* addr;
size_t length;
};
/*
* Use this to keep track of mapped segments.
*/
struct MemMapping {
unsigned char* addr; /* start of data */
size_t length; /* length of data */
class MemMapping {
public:
~MemMapping();
// Map a file into a private, read-only memory segment. If 'filename' begins with an '@'
// character, it is a map of blocks to be mapped, otherwise it is treated as an ordinary file.
bool MapFile(const std::string& filename);
size_t ranges() const {
return ranges_.size();
};
std::vector<MappedRange> ranges;
unsigned char* addr; // start of data
size_t length; // length of data
private:
struct MappedRange {
void* addr;
size_t length;
};
bool MapBlockFile(const std::string& filename);
bool MapFD(int fd);
std::vector<MappedRange> ranges_;
};
/*
* Map a file into a private, read-only memory segment. If 'fn'
* begins with an '@' character, it is a map of blocks to be mapped,
* otherwise it is treated as an ordinary file.
*
* On success, "pMap" is filled in, and zero is returned.
*/
int sysMapFile(const char* fn, MemMapping* pMap);
/*
* Release the pages associated with a shared memory segment.
*
* This does not free "pMap"; it just releases the memory.
*/
void sysReleaseMap(MemMapping* pMap);
#endif // _OTAUTIL_SYSUTIL
+6 -2
View File
@@ -23,5 +23,9 @@
#include <ziparchive/zip_archive.h>
int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
int status_fd, std::vector<std::string>* cmd);
// Extract the update binary from the open zip archive |zip| located at |package| to |binary_path|.
// Store the command line that should be called into |cmd|. The |status_fd| is the file descriptor
// the child process should use to report back the progress of the update.
int update_binary_command(const std::string& package, ZipArchiveHandle zip,
const std::string& binary_path, int retry_count, int status_fd,
std::vector<std::string>* cmd);
+15 -15
View File
@@ -59,21 +59,21 @@ static void check_and_fclose(FILE *fp, const char *name) {
}
static void copy_file(const char* source, const char* destination) {
FILE* dest_fp = fopen(destination, "w");
if (dest_fp == nullptr) {
PLOG(ERROR) << "Can't open " << destination;
} else {
FILE* source_fp = fopen(source, "r");
if (source_fp != nullptr) {
char buf[4096];
size_t bytes;
while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
fwrite(buf, 1, bytes, dest_fp);
}
check_and_fclose(source_fp, source);
}
check_and_fclose(dest_fp, destination);
FILE* dest_fp = fopen(destination, "we");
if (dest_fp == nullptr) {
PLOG(ERROR) << "Can't open " << destination;
} else {
FILE* source_fp = fopen(source, "re");
if (source_fp != nullptr) {
char buf[4096];
size_t bytes;
while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
fwrite(buf, 1, bytes, dest_fp);
}
check_and_fclose(source_fp, source);
}
check_and_fclose(dest_fp, destination);
}
}
static bool rotated = false;
@@ -120,7 +120,7 @@ int main(int argc, char **argv) {
*/
bool has_cache = false;
static const char mounts_file[] = "/proc/mounts";
FILE *fp = fopen(mounts_file, "r");
FILE* fp = fopen(mounts_file, "re");
if (!fp) {
PLOG(ERROR) << "failed to open " << mounts_file;
} else {
+80 -74
View File
@@ -40,7 +40,6 @@
#include <string>
#include <vector>
#include <adb.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -114,8 +113,9 @@ 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";
static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
// We will try to apply the update package 5 times at most in case of an I/O error.
static const int EIO_RETRY_COUNT = 4;
// We will try to apply the update package 5 times at most in case of an I/O error or
// bspatch | imgpatch error.
static const int RETRY_LIMIT = 4;
static const int BATTERY_READ_TIMEOUT_IN_SEC = 10;
// GmsCore enters recovery mode to install package when having enough battery
// percentage. Normally, the threshold is 40% without charger and 20% with charger.
@@ -250,7 +250,7 @@ static void redirect_stdio(const char* filename) {
auto start = std::chrono::steady_clock::now();
// Child logger to actually write to the log file.
FILE* log_fp = fopen(filename, "a");
FILE* log_fp = fopen(filename, "ae");
if (log_fp == nullptr) {
PLOG(ERROR) << "fopen \"" << filename << "\" failed";
close(pipefd[0]);
@@ -419,27 +419,27 @@ static void copy_log_file_to_pmsg(const char* source, const char* destination) {
static off_t tmplog_offset = 0;
static void copy_log_file(const char* source, const char* destination, bool append) {
FILE* dest_fp = fopen_path(destination, append ? "a" : "w");
if (dest_fp == nullptr) {
PLOG(ERROR) << "Can't open " << destination;
} else {
FILE* source_fp = fopen(source, "r");
if (source_fp != nullptr) {
if (append) {
fseeko(source_fp, tmplog_offset, SEEK_SET); // Since last write
}
char buf[4096];
size_t bytes;
while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
fwrite(buf, 1, bytes, dest_fp);
}
if (append) {
tmplog_offset = ftello(source_fp);
}
check_and_fclose(source_fp, source);
}
check_and_fclose(dest_fp, destination);
FILE* dest_fp = fopen_path(destination, append ? "ae" : "we");
if (dest_fp == nullptr) {
PLOG(ERROR) << "Can't open " << destination;
} else {
FILE* source_fp = fopen(source, "re");
if (source_fp != nullptr) {
if (append) {
fseeko(source_fp, tmplog_offset, SEEK_SET); // Since last write
}
char buf[4096];
size_t bytes;
while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
fwrite(buf, 1, bytes, dest_fp);
}
if (append) {
tmplog_offset = ftello(source_fp);
}
check_and_fclose(source_fp, source);
}
check_and_fclose(dest_fp, destination);
}
}
static void copy_logs() {
@@ -478,40 +478,38 @@ static void copy_logs() {
sync();
}
// clear the recovery command and prepare to boot a (hopefully working) system,
// Clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read). This function is
// idempotent: call it as many times as you like.
static void finish_recovery() {
// Save the locale to cache, so if recovery is next started up
// without a --locale argument (eg, directly from the bootloader)
// it will use the last-known locale.
if (!locale.empty() && has_cache) {
LOG(INFO) << "Saving locale \"" << locale << "\"";
FILE* fp = fopen_path(LOCALE_FILE, "w");
if (!android::base::WriteStringToFd(locale, fileno(fp))) {
PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
}
check_and_fclose(fp, LOCALE_FILE);
// Save the locale to cache, so if recovery is next started up without a '--locale' argument
// (e.g., directly from the bootloader) it will use the last-known locale.
if (!locale.empty() && has_cache) {
LOG(INFO) << "Saving locale \"" << locale << "\"";
if (ensure_path_mounted(LOCALE_FILE) != 0) {
LOG(ERROR) << "Failed to mount " << LOCALE_FILE;
} else if (!android::base::WriteStringToFile(locale, LOCALE_FILE)) {
PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
}
}
copy_logs();
copy_logs();
// Reset to normal system boot so recovery won't cycle indefinitely.
std::string err;
if (!clear_bootloader_message(&err)) {
LOG(ERROR) << "Failed to clear BCB message: " << err;
// Reset to normal system boot so recovery won't cycle indefinitely.
std::string err;
if (!clear_bootloader_message(&err)) {
LOG(ERROR) << "Failed to clear BCB message: " << err;
}
// Remove the command file, so recovery won't repeat indefinitely.
if (has_cache) {
if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
}
ensure_path_unmounted(CACHE_ROOT);
}
// Remove the command file, so recovery won't repeat indefinitely.
if (has_cache) {
if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
}
ensure_path_unmounted(CACHE_ROOT);
}
sync(); // For good measure.
sync(); // For good measure.
}
struct saved_log_file {
@@ -552,7 +550,7 @@ static bool erase_volume(const char* volume) {
}
std::string data(sb.st_size, '\0');
FILE* f = fopen(path.c_str(), "rb");
FILE* f = fopen(path.c_str(), "rbe");
fread(&data[0], 1, data.size(), f);
fclose(f);
@@ -580,7 +578,7 @@ static bool erase_volume(const char* volume) {
ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno));
return true;
}
FILE* f = fopen(CONVERT_FBE_FILE, "wb");
FILE* f = fopen(CONVERT_FBE_FILE, "wbe");
if (!f) {
ui->Print("Failed to convert to file encryption %s\n", strerror(errno));
return true;
@@ -760,12 +758,13 @@ static bool wipe_data(Device* device) {
}
static bool prompt_and_wipe_data(Device* device) {
// Use a single string and let ScreenRecoveryUI handles the wrapping.
const char* const headers[] = {
"Can't load Android system. Your data may be corrupt.",
"If you continue to get this message, you may need to",
"perform a factory data reset and erase all user data",
"Can't load Android system. Your data may be corrupt. "
"If you continue to get this message, you may need to "
"perform a factory data reset and erase all user data "
"stored on this device.",
NULL
nullptr
};
const char* const items[] = {
"Try again",
@@ -1157,7 +1156,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
{
bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
if (adb) {
status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE);
} else {
status = apply_from_sdcard(device, &should_wipe_cache);
}
@@ -1528,9 +1527,9 @@ int main(int argc, char **argv) {
}
if (status != INSTALL_SUCCESS) {
ui->Print("Installation aborted.\n");
// When I/O error happens, reboot and retry installation EIO_RETRY_COUNT
// When I/O error happens, reboot and retry installation RETRY_LIMIT
// times before we abandon this OTA update.
if (status == INSTALL_RETRY && retry_count < EIO_RETRY_COUNT) {
if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) {
copy_logs();
set_retry_bootloader_message(retry_count, args);
// Print retry count on screen.
@@ -1582,7 +1581,7 @@ int main(int argc, char **argv) {
if (!sideload_auto_reboot) {
ui->ShowText(true);
}
status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE);
if (status == INSTALL_SUCCESS && should_wipe_cache) {
if (!wipe_cache(false, device)) {
status = INSTALL_ERROR;
@@ -1593,25 +1592,32 @@ int main(int argc, char **argv) {
ui->Print("Rebooting automatically.\n");
}
} else if (!just_exit) {
status = INSTALL_NONE; // No command specified
ui->SetBackground(RecoveryUI::NO_COMMAND);
// If this is an eng or userdebug build, automatically turn on the text display if no command
// is specified. Note that this should be called before setting the background to avoid
// flickering the background image.
if (is_ro_debuggable()) {
ui->ShowText(true);
}
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);
if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
ui->SetBackground(RecoveryUI::ERROR);
if (!ui->IsTextVisible()) {
sleep(5);
}
}
if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) {
copy_logs();
ui->SetBackground(RecoveryUI::ERROR);
}
Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
if ((status != INSTALL_SUCCESS && status != INSTALL_SKIPPED && !sideload_auto_reboot) ||
ui->IsTextVisible()) {
// 1. If the recovery menu is visible, prompt and wait for commands.
// 2. If the state is INSTALL_NONE, wait for commands. (i.e. In user build, manually reboot into
// recovery to sideload a package.)
// 3. sideload_auto_reboot is an option only available in user-debug build, reboot the device
// without waiting.
// 4. In all other cases, reboot the device. Therefore, normal users will observe the device
// reboot after it shows the "error" screen for 5s.
if ((status == INSTALL_NONE && !sideload_auto_reboot) || ui->IsTextVisible()) {
Device::BuiltinAction temp = prompt_and_wait(device, status);
if (temp != Device::NO_ACTION) {
after = temp;
+109 -41
View File
@@ -27,7 +27,9 @@
#include <fcntl.h>
#include <android-base/logging.h>
#include <ext4_utils/make_ext4fs.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/wipe.h>
#include <fs_mgr.h>
@@ -172,6 +174,23 @@ static int exec_cmd(const char* path, char* const argv[]) {
return WEXITSTATUS(status);
}
static ssize_t get_file_size(int fd, uint64_t reserve_len) {
struct stat buf;
int ret = fstat(fd, &buf);
if (ret) return 0;
ssize_t computed_size;
if (S_ISREG(buf.st_mode)) {
computed_size = buf.st_size - reserve_len;
} else if (S_ISBLK(buf.st_mode)) {
computed_size = get_block_device_size(fd) - reserve_len;
} else {
computed_size = 0;
}
return computed_size;
}
int format_volume(const char* volume, const char* directory) {
Volume* v = volume_for_path(volume);
if (v == NULL) {
@@ -211,35 +230,81 @@ int format_volume(const char* volume, const char* directory) {
if (v->length != 0) {
length = v->length;
} else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) {
length = -CRYPT_FOOTER_OFFSET;
android::base::unique_fd fd(open(v->blk_device, O_RDONLY));
if (fd < 0) {
PLOG(ERROR) << "get_file_size: failed to open " << v->blk_device;
return -1;
}
length = get_file_size(fd.get(), CRYPT_FOOTER_OFFSET);
if (length <= 0) {
LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device;
return -1;
}
}
int result;
if (strcmp(v->fs_type, "ext4") == 0) {
if (v->erase_blk_size != 0 && v->logical_blk_size != 0) {
result = make_ext4fs_directory_align(v->blk_device, length, volume, sehandle,
directory, v->erase_blk_size, v->logical_blk_size);
} else {
result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory);
}
static constexpr int block_size = 4096;
int raid_stride = v->logical_blk_size / block_size;
int raid_stripe_width = v->erase_blk_size / block_size;
// stride should be the max of 8kb and logical block size
if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) {
raid_stride = 8192 / block_size;
}
const char* mke2fs_argv[] = { "/sbin/mke2fs_static",
"-F",
"-t",
"ext4",
"-b",
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
int i = 5;
std::string block_size_str = std::to_string(block_size);
mke2fs_argv[i++] = block_size_str.c_str();
std::string ext_args;
if (v->erase_blk_size != 0 && v->logical_blk_size != 0) {
ext_args = android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride,
raid_stripe_width);
mke2fs_argv[i++] = "-E";
mke2fs_argv[i++] = ext_args.c_str();
}
mke2fs_argv[i++] = v->blk_device;
std::string size_str = std::to_string(length / block_size);
if (length != 0) {
mke2fs_argv[i++] = size_str.c_str();
}
result = exec_cmd(mke2fs_argv[0], const_cast<char**>(mke2fs_argv));
if (result == 0 && directory != nullptr) {
const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static",
"-e",
"-f",
directory,
"-a",
volume,
v->blk_device,
nullptr };
result = exec_cmd(e2fsdroid_argv[0], const_cast<char**>(e2fsdroid_argv));
}
} else { /* Has to be f2fs because we checked earlier. */
if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) {
LOG(ERROR) << "format_volume: crypt footer + negative length (" << length
<< ") not supported on " << v->fs_type;
return -1;
}
if (length < 0) {
LOG(ERROR) << "format_volume: negative length (" << length
<< ") not supported on " << v->fs_type;
return -1;
}
char *num_sectors;
if (asprintf(&num_sectors, "%zd", length / 512) <= 0) {
char *num_sectors = nullptr;
if (length >= 512 && asprintf(&num_sectors, "%zd", length / 512) <= 0) {
LOG(ERROR) << "format_volume: failed to create " << v->fs_type
<< " command for " << v->blk_device;
return -1;
}
const char *f2fs_path = "/sbin/mkfs.f2fs";
const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, NULL};
const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, nullptr};
result = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
free(num_sectors);
@@ -260,26 +325,29 @@ int format_volume(const char* volume) {
}
int setup_install_mounts() {
if (fstab == NULL) {
LOG(ERROR) << "can't set up install mounts: no fstab loaded";
if (fstab == nullptr) {
LOG(ERROR) << "can't set up install mounts: no fstab loaded";
return -1;
}
for (int i = 0; i < fstab->num_entries; ++i) {
const Volume* v = fstab->recs + i;
// We don't want to do anything with "/".
if (strcmp(v->mount_point, "/") == 0) {
continue;
}
if (strcmp(v->mount_point, "/tmp") == 0 || strcmp(v->mount_point, "/cache") == 0) {
if (ensure_path_mounted(v->mount_point) != 0) {
LOG(ERROR) << "failed to mount " << v->mount_point;
return -1;
}
} else {
if (ensure_path_unmounted(v->mount_point) != 0) {
LOG(ERROR) << "failed to unmount " << v->mount_point;
return -1;
}
}
for (int i = 0; i < fstab->num_entries; ++i) {
Volume* v = fstab->recs + i;
if (strcmp(v->mount_point, "/tmp") == 0 ||
strcmp(v->mount_point, "/cache") == 0) {
if (ensure_path_mounted(v->mount_point) != 0) {
LOG(ERROR) << "failed to mount " << v->mount_point;
return -1;
}
} else {
if (ensure_path_unmounted(v->mount_point) != 0) {
LOG(ERROR) << "failed to unmount " << v->mount_point;
return -1;
}
}
}
return 0;
}
return 0;
}
+497 -462
View File
File diff suppressed because it is too large Load Diff
+123 -100
View File
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <string>
#include <vector>
#include "ui.h"
@@ -30,144 +31,166 @@ struct GRSurface;
// Implementation of RecoveryUI appropriate for devices with a screen
// (shows an icon + a progress bar, text logging, menu, etc.)
class ScreenRecoveryUI : public RecoveryUI {
public:
ScreenRecoveryUI();
public:
ScreenRecoveryUI();
bool Init(const std::string& locale) override;
bool Init(const std::string& locale) override;
// overall recovery state ("background image")
void SetBackground(Icon icon);
void SetSystemUpdateText(bool security_update);
// overall recovery state ("background image")
void SetBackground(Icon icon) override;
void SetSystemUpdateText(bool security_update) override;
// progress indicator
void SetProgressType(ProgressType type) override;
void ShowProgress(float portion, float seconds) override;
void SetProgress(float fraction) override;
// progress indicator
void SetProgressType(ProgressType type) override;
void ShowProgress(float portion, float seconds) override;
void SetProgress(float fraction) override;
void SetStage(int current, int max) override;
void SetStage(int current, int max) override;
// text log
void ShowText(bool visible) override;
bool IsTextVisible() override;
bool WasTextEverVisible() override;
// text log
void ShowText(bool visible) override;
bool IsTextVisible() override;
bool WasTextEverVisible() override;
// printing messages
void Print(const char* fmt, ...) __printflike(2, 3);
void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3);
void ShowFile(const char* filename);
// printing messages
void Print(const char* fmt, ...) override __printflike(2, 3);
void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3);
void ShowFile(const char* filename) override;
// menu display
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection);
int SelectMenu(int sel);
void EndMenu();
// menu display
void StartMenu(const char* const* headers, const char* const* items,
int initial_selection) override;
int SelectMenu(int sel) override;
void EndMenu() override;
void KeyLongPress(int);
void KeyLongPress(int) override;
void Redraw();
void Redraw();
enum UIElement {
HEADER, MENU, MENU_SEL_BG, MENU_SEL_BG_ACTIVE, MENU_SEL_FG, LOG, TEXT_FILL, INFO
};
void SetColor(UIElement e);
enum UIElement {
HEADER,
MENU,
MENU_SEL_BG,
MENU_SEL_BG_ACTIVE,
MENU_SEL_FG,
LOG,
TEXT_FILL,
INFO
};
void SetColor(UIElement e) const;
protected:
Icon currentIcon;
protected:
// The margin that we don't want to use for showing texts (e.g. round screen, or screen with
// rounded corners).
const int kMarginWidth;
const int kMarginHeight;
// The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi.
float density_;
// The layout to use.
int layout_;
// Number of frames per sec (default: 30) for both parts of the animation.
const int kAnimationFps;
GRSurface* error_icon;
// The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi.
const float density_;
GRSurface* erasing_text;
GRSurface* error_text;
GRSurface* installing_text;
GRSurface* no_command_text;
Icon currentIcon;
GRSurface** introFrames;
GRSurface** loopFrames;
// The layout to use.
int layout_;
GRSurface* progressBarEmpty;
GRSurface* progressBarFill;
GRSurface* stageMarkerEmpty;
GRSurface* stageMarkerFill;
GRSurface* error_icon;
ProgressType progressBarType;
GRSurface* erasing_text;
GRSurface* error_text;
GRSurface* installing_text;
GRSurface* no_command_text;
float progressScopeStart, progressScopeSize, progress;
double progressScopeTime, progressScopeDuration;
GRSurface** introFrames;
GRSurface** loopFrames;
// true when both graphics pages are the same (except for the progress bar).
bool pagesIdentical;
GRSurface* progressBarEmpty;
GRSurface* progressBarFill;
GRSurface* stageMarkerEmpty;
GRSurface* stageMarkerFill;
size_t text_cols_, text_rows_;
ProgressType progressBarType;
// Log text overlay, displayed when a magic key is pressed.
char** text_;
size_t text_col_, text_row_, text_top_;
float progressScopeStart, progressScopeSize, progress;
double progressScopeTime, progressScopeDuration;
bool show_text;
bool show_text_ever; // has show_text ever been true?
// true when both graphics pages are the same (except for the progress bar).
bool pagesIdentical;
char** menu_;
const char* const* menu_headers_;
bool show_menu;
int menu_items, menu_sel;
size_t text_cols_, text_rows_;
// An alternate text screen, swapped with 'text_' when we're viewing a log file.
char** file_viewer_text_;
// Log text overlay, displayed when a magic key is pressed.
char** text_;
size_t text_col_, text_row_, text_top_;
pthread_t progress_thread_;
bool show_text;
bool show_text_ever; // has show_text ever been true?
// Number of intro frames and loop frames in the animation.
size_t intro_frames;
size_t loop_frames;
std::vector<std::string> menu_;
const char* const* menu_headers_;
bool show_menu;
int menu_items, menu_sel;
size_t current_frame;
bool intro_done;
// An alternate text screen, swapped with 'text_' when we're viewing a log file.
char** file_viewer_text_;
// Number of frames per sec (default: 30) for both parts of the animation.
int animation_fps;
pthread_t progress_thread_;
int stage, max_stage;
// Number of intro frames and loop frames in the animation.
size_t intro_frames;
size_t loop_frames;
int char_width_;
int char_height_;
pthread_mutex_t updateMutex;
size_t current_frame;
bool intro_done;
virtual bool InitTextParams();
int stage, max_stage;
virtual void draw_background_locked();
virtual void draw_foreground_locked();
virtual void draw_screen_locked();
virtual void update_screen_locked();
virtual void update_progress_locked();
int char_width_;
int char_height_;
GRSurface* GetCurrentFrame();
GRSurface* GetCurrentText();
pthread_mutex_t updateMutex;
static void* ProgressThreadStartRoutine(void* data);
void ProgressThreadLoop();
virtual bool InitTextParams();
virtual void ShowFile(FILE*);
virtual void PrintV(const char*, bool, va_list);
void PutChar(char);
void ClearText();
virtual void draw_background_locked();
virtual void draw_foreground_locked();
virtual void draw_screen_locked();
virtual void update_screen_locked();
virtual void update_progress_locked();
void LoadAnimation();
void LoadBitmap(const char* filename, GRSurface** surface);
void LoadLocalizedBitmap(const char* filename, GRSurface** surface);
GRSurface* GetCurrentFrame() const;
GRSurface* GetCurrentText() const;
int PixelsFromDp(int dp) const;
virtual int GetAnimationBaseline();
virtual int GetProgressBaseline();
virtual int GetTextBaseline();
static void* ProgressThreadStartRoutine(void* data);
void ProgressThreadLoop();
void DrawHorizontalRule(int* y);
void DrawTextLine(int x, int* y, const char* line, bool bold) const;
void DrawTextLines(int x, int* y, const char* const* lines) const;
virtual void ShowFile(FILE*);
virtual void PrintV(const char*, bool, va_list);
void PutChar(char);
void ClearText();
void LoadAnimation();
void LoadBitmap(const char* filename, GRSurface** surface);
void LoadLocalizedBitmap(const char* filename, GRSurface** surface);
int PixelsFromDp(int dp) const;
virtual int GetAnimationBaseline() const;
virtual int GetProgressBaseline() const;
virtual int GetTextBaseline() const;
// Draws a highlight bar at (x, y) - (x + width, y + height).
virtual void DrawHighlightBar(int x, int y, int width, int height) const;
// Draws a horizontal rule at Y. Returns the offset it should be moving along Y-axis.
virtual int DrawHorizontalRule(int y) const;
// Draws a line of text. Returns the offset it should be moving along Y-axis.
virtual int DrawTextLine(int x, int y, const char* line, bool bold) const;
// Draws multiple text lines. Returns the offset it should be moving along Y-axis.
int DrawTextLines(int x, int y, const char* const* lines) const;
// Similar to DrawTextLines() to draw multiple text lines, but additionally wraps long lines.
// Returns the offset it should be moving along Y-axis.
int DrawWrappedTextLines(int x, int y, const char* const* lines) const;
};
#endif // RECOVERY_UI_H
+18 -7
View File
@@ -26,11 +26,12 @@ endif
include $(CLEAR_VARS)
LOCAL_CFLAGS := -Werror
LOCAL_MODULE := recovery_unit_test
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_STATIC_LIBRARIES := \
libverifier \
libminui \
libotautil \
libupdater \
libziparchive \
libutils \
libz \
@@ -41,9 +42,9 @@ LOCAL_SRC_FILES := \
unit/asn1_decoder_test.cpp \
unit/dirutil_test.cpp \
unit/locale_test.cpp \
unit/rangeset_test.cpp \
unit/sysutil_test.cpp \
unit/zip_test.cpp \
unit/ziputil_test.cpp
LOCAL_C_INCLUDES := $(RECOVERY_PATH)
LOCAL_SHARED_LIBRARIES := liblog
@@ -51,10 +52,8 @@ include $(BUILD_NATIVE_TEST)
# Manual tests
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CFLAGS := -Werror
LOCAL_MODULE := recovery_manual_test
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_STATIC_LIBRARIES := \
libminui \
libbase
@@ -91,13 +90,20 @@ LOCAL_CFLAGS := \
-Werror \
-D_FILE_OFFSET_BITS=64
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
ifeq ($(AB_OTA_UPDATER),true)
LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
endif
ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true)
LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1
endif
ifeq ($(BOARD_AVB_ENABLE),true)
LOCAL_CFLAGS += -DBOARD_AVB_ENABLE=1
endif
LOCAL_MODULE := recovery_component_test
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_C_INCLUDES := $(RECOVERY_PATH)
LOCAL_SRC_FILES := \
component/applypatch_test.cpp \
@@ -108,9 +114,11 @@ LOCAL_SRC_FILES := \
component/sideload_test.cpp \
component/uncrypt_test.cpp \
component/updater_test.cpp \
component/update_verifier_test.cpp \
component/verifier_test.cpp
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_SHARED_LIBRARIES := \
libhidlbase
tune2fs_static_libraries := \
libext2_com_err \
@@ -128,6 +136,7 @@ LOCAL_STATIC_LIBRARIES := \
libimgpatch \
libbsdiff \
libbspatch \
libfusesideload \
libotafault \
librecovery \
libupdater \
@@ -135,6 +144,7 @@ LOCAL_STATIC_LIBRARIES := \
libverifier \
libotautil \
libmounts \
libupdate_verifier \
libdivsufsort \
libdivsufsort64 \
libfs_mgr \
@@ -157,6 +167,7 @@ LOCAL_STATIC_LIBRARIES := \
libfec_rs \
libsquashfs_utils \
libcutils \
libbrotli \
$(tune2fs_static_libraries)
testdata_files := $(call find-subdir-files, testdata/*)
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<configuration description="Config for recovery_component_test and recovery_unit_test">
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="recovery_component_test->/data/local/tmp/recovery_component_test" />
<option name="push" value="recovery_unit_test->/data/local/tmp/recovery_unit_test" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="recovery_component_test" />
</test>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="recovery_unit_test" />
</test>
</configuration>
-43
View File
@@ -1,43 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agree 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 _COMPONENT_TEST_UTIL_H
#define _COMPONENT_TEST_UTIL_H
#include <string>
#include <android-base/properties.h>
#include <fs_mgr.h>
// Check if the /misc entry exists in the fstab.
static bool parse_misc() {
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
fs_mgr_free_fstab);
if (!fstab) {
GTEST_LOG_(INFO) << "Failed to read default fstab";
return false;
}
fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab.get(), "/misc");
if (record == nullptr) {
GTEST_LOG_(INFO) << "Failed to find /misc in fstab.";
return false;
}
return true;
}
#endif //_COMPONENT_TEST_UTIL_H
+2 -63
View File
@@ -105,9 +105,6 @@ class ApplyPatchTest : public ::testing::Test {
static size_t new_size;
};
std::string ApplyPatchTest::old_file;
std::string ApplyPatchTest::new_file;
static void cp(const std::string& src, const std::string& tgt) {
std::string cmd = "cp " + src + " " + tgt;
system(cmd.c_str());
@@ -132,48 +129,8 @@ class ApplyPatchCacheTest : public ApplyPatchTest {
}
};
class ApplyPatchFullTest : public ApplyPatchCacheTest {
public:
static void SetUpTestCase() {
ApplyPatchTest::SetUpTestCase();
output_f = new TemporaryFile();
output_loc = std::string(output_f->path);
struct FileContents fc;
ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc));
patches.push_back(
std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())));
ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc));
patches.push_back(
std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())));
}
static void TearDownTestCase() {
delete output_f;
patches.clear();
}
static std::vector<std::unique_ptr<Value>> patches;
static TemporaryFile* output_f;
static std::string output_loc;
};
class ApplyPatchDoubleCacheTest : public ApplyPatchFullTest {
public:
virtual void SetUp() {
ApplyPatchCacheTest::SetUp();
cp(cache_file, "/cache/reallysaved.file");
}
virtual void TearDown() {
cp("/cache/reallysaved.file", cache_file);
ApplyPatchCacheTest::TearDown();
}
};
std::string ApplyPatchTest::old_file;
std::string ApplyPatchTest::new_file;
std::string ApplyPatchTest::rand_file;
std::string ApplyPatchTest::patch_file;
std::string ApplyPatchTest::cache_file;
@@ -184,10 +141,6 @@ std::string ApplyPatchTest::bad_sha1_b;
size_t ApplyPatchTest::old_size;
size_t ApplyPatchTest::new_size;
std::vector<std::unique_ptr<Value>> ApplyPatchFullTest::patches;
TemporaryFile* ApplyPatchFullTest::output_f;
std::string ApplyPatchFullTest::output_loc;
TEST_F(ApplyPatchTest, CheckModeSkip) {
std::vector<std::string> sha1s;
ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
@@ -424,20 +377,6 @@ TEST(ApplyPatchModesTest, CheckModeInvalidArgs) {
ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
}
TEST(ApplyPatchModesTest, SpaceModeInvalidArgs) {
// Insufficient args.
ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-s" }));
// Invalid bytes arg.
ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "x" }));
// 0 is invalid.
ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0" }));
// 0x10 is fine.
ASSERT_EQ(0, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0x10" }));
}
TEST(ApplyPatchModesTest, ShowLicenses) {
ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
}
+2 -3
View File
@@ -21,14 +21,13 @@
#include <bootloader_message/bootloader_message.h>
#include <gtest/gtest.h>
#include "common/component_test_util.h"
class BootloaderMessageTest : public ::testing::Test {
protected:
BootloaderMessageTest() : has_misc(true) {}
virtual void SetUp() override {
has_misc = parse_misc();
std::string err;
has_misc = !get_bootloader_message_blk_device(&err).empty();
}
virtual void TearDown() override {
+96 -56
View File
@@ -14,6 +14,8 @@
* limitations under the License.
*/
#include <stdio.h>
#include <string>
#include <vector>
@@ -27,12 +29,6 @@
using android::base::get_unaligned;
static ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) {
std::string* s = static_cast<std::string*>(token);
s->append(reinterpret_cast<const char*>(data), len);
return len;
}
// Sanity check for the given imgdiff patch header.
static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw,
size_t* num_deflate) {
@@ -79,6 +75,18 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si
if (num_deflate != nullptr) *num_deflate = deflate;
}
static void verify_patched_image(const std::string& src, const std::string& patch,
const std::string& tgt) {
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
[&patched](const unsigned char* data, size_t len) {
patched.append(reinterpret_cast<const char*>(data), len);
return len;
}));
ASSERT_EQ(tgt, patched);
}
TEST(ImgdiffTest, invalid_args) {
// Insufficient inputs.
ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" }));
@@ -124,11 +132,7 @@ TEST(ImgdiffTest, image_mode_smoke) {
ASSERT_EQ(0U, num_deflate);
ASSERT_EQ(1U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, zip_mode_smoke_store) {
@@ -177,11 +181,7 @@ TEST(ImgdiffTest, zip_mode_smoke_store) {
ASSERT_EQ(0U, num_deflate);
ASSERT_EQ(1U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, zip_mode_smoke_compressed) {
@@ -230,11 +230,7 @@ TEST(ImgdiffTest, zip_mode_smoke_compressed) {
ASSERT_EQ(1U, num_deflate);
ASSERT_EQ(2U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) {
@@ -286,11 +282,7 @@ TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) {
ASSERT_EQ(1U, num_deflate);
ASSERT_EQ(2U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, image_mode_simple) {
@@ -333,11 +325,40 @@ TEST(ImgdiffTest, image_mode_simple) {
ASSERT_EQ(1U, num_deflate);
ASSERT_EQ(2U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, image_mode_bad_gzip) {
// Modify the uncompressed length in the gzip footer.
const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
'\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
'\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
'\xff', '\xff', '\xff' };
const std::string src(src_data.cbegin(), src_data.cend());
TemporaryFile src_file;
ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
// Modify the uncompressed length in the gzip footer.
const std::vector<char> tgt_data = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
'\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
'\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\xff', '\xff', '\xff'
};
const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
TemporaryFile tgt_file;
ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(0, imgdiff(args.size(), args.data()));
// Verify.
std::string patch;
ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, image_mode_different_num_chunks) {
@@ -413,11 +434,7 @@ TEST(ImgdiffTest, image_mode_merge_chunks) {
ASSERT_EQ(1U, num_deflate);
ASSERT_EQ(2U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, image_mode_spurious_magic) {
@@ -454,11 +471,7 @@ TEST(ImgdiffTest, image_mode_spurious_magic) {
ASSERT_EQ(0U, num_deflate);
ASSERT_EQ(1U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, image_mode_short_input1) {
@@ -494,11 +507,7 @@ TEST(ImgdiffTest, image_mode_short_input1) {
ASSERT_EQ(0U, num_deflate);
ASSERT_EQ(1U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, image_mode_short_input2) {
@@ -534,11 +543,7 @@ TEST(ImgdiffTest, image_mode_short_input2) {
ASSERT_EQ(0U, num_deflate);
ASSERT_EQ(1U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgdiffTest, image_mode_single_entry_long) {
@@ -577,9 +582,44 @@ TEST(ImgdiffTest, image_mode_single_entry_long) {
ASSERT_EQ(0U, num_deflate);
ASSERT_EQ(0U, num_raw);
std::string patched;
ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
MemorySink, &patched));
ASSERT_EQ(tgt, patched);
verify_patched_image(src, patch, tgt);
}
TEST(ImgpatchTest, image_mode_patch_corruption) {
// src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
'\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
'\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
'\x00', '\x00', '\x00' };
const std::string src(src_data.cbegin(), src_data.cend());
TemporaryFile src_file;
ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
// tgt: "abcdefgxyz" + gzipped "xxyyzz".
const std::vector<char> tgt_data = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
'\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
'\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
};
const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
TemporaryFile tgt_file;
ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(0, imgdiff(args.size(), args.data()));
// Verify.
std::string patch;
ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
verify_patched_image(src, patch, tgt);
// Corrupt the end of the patch and expect the ApplyImagePatch to fail.
patch.insert(patch.end() - 10, 10, '0');
ASSERT_EQ(-1, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
[](const unsigned char* /*data*/, size_t len) { return len; }));
}
+124 -10
View File
@@ -15,6 +15,8 @@
*/
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
@@ -65,6 +67,56 @@ TEST(InstallTest, verify_package_compatibility_invalid_entry) {
CloseArchive(zip);
}
TEST(InstallTest, read_metadata_from_package_smoke) {
TemporaryFile temp_file;
FILE* zip_file = fdopen(temp_file.fd, "w");
ZipWriter writer(zip_file);
ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored));
const std::string content("abcdefg");
ASSERT_EQ(0, writer.WriteBytes(content.data(), content.size()));
ASSERT_EQ(0, writer.FinishEntry());
ASSERT_EQ(0, writer.Finish());
ASSERT_EQ(0, fclose(zip_file));
ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
std::string metadata;
ASSERT_TRUE(read_metadata_from_package(zip, &metadata));
ASSERT_EQ(content, metadata);
CloseArchive(zip);
TemporaryFile temp_file2;
FILE* zip_file2 = fdopen(temp_file2.fd, "w");
ZipWriter writer2(zip_file2);
ASSERT_EQ(0, writer2.StartEntry("META-INF/com/android/metadata", kCompressDeflated));
ASSERT_EQ(0, writer2.WriteBytes(content.data(), content.size()));
ASSERT_EQ(0, writer2.FinishEntry());
ASSERT_EQ(0, writer2.Finish());
ASSERT_EQ(0, fclose(zip_file2));
ASSERT_EQ(0, OpenArchive(temp_file2.path, &zip));
metadata.clear();
ASSERT_TRUE(read_metadata_from_package(zip, &metadata));
ASSERT_EQ(content, metadata);
CloseArchive(zip);
}
TEST(InstallTest, read_metadata_from_package_no_entry) {
TemporaryFile temp_file;
FILE* zip_file = fdopen(temp_file.fd, "w");
ZipWriter writer(zip_file);
ASSERT_EQ(0, writer.StartEntry("dummy_entry", kCompressStored));
ASSERT_EQ(0, writer.FinishEntry());
ASSERT_EQ(0, writer.Finish());
ASSERT_EQ(0, fclose(zip_file));
ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
std::string metadata;
ASSERT_FALSE(read_metadata_from_package(zip, &metadata));
CloseArchive(zip);
}
TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) {
TemporaryFile compatibility_zip_file;
FILE* compatibility_zip = fdopen(compatibility_zip_file.fd, "w");
@@ -175,18 +227,62 @@ TEST(InstallTest, update_binary_command_smoke) {
ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
ZipString payload_name("payload.bin");
ZipEntry payload_entry;
ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry));
int status_fd = 10;
std::string path = "/path/to/update.zip";
std::string package = "/path/to/update.zip";
std::string binary_path = "/sbin/update_engine_sideload";
std::vector<std::string> cmd;
ASSERT_EQ(0, update_binary_command(path, zip, 0, status_fd, &cmd));
ASSERT_EQ("/sbin/update_engine_sideload", cmd[0]);
ASSERT_EQ("--payload=file://" + path, cmd[1]);
ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd));
ASSERT_EQ(5U, cmd.size());
ASSERT_EQ(binary_path, cmd[0]);
ASSERT_EQ("--payload=file://" + package, cmd[1]);
ASSERT_EQ("--offset=" + std::to_string(payload_entry.offset), cmd[2]);
ASSERT_EQ("--headers=" + properties, cmd[3]);
ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]);
CloseArchive(zip);
#else
// Cannot test update_binary_command() because it tries to extract update-binary to /tmp.
GTEST_LOG_(INFO) << "Test skipped on non-A/B device.";
TemporaryFile temp_file;
FILE* zip_file = fdopen(temp_file.fd, "w");
ZipWriter writer(zip_file);
static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
ASSERT_EQ(0, writer.StartEntry(UPDATE_BINARY_NAME, kCompressStored));
ASSERT_EQ(0, writer.FinishEntry());
ASSERT_EQ(0, writer.Finish());
ASSERT_EQ(0, fclose(zip_file));
ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
int status_fd = 10;
std::string package = "/path/to/update.zip";
TemporaryDir td;
std::string binary_path = std::string(td.path) + "/update_binary";
std::vector<std::string> cmd;
ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd));
ASSERT_EQ(4U, cmd.size());
ASSERT_EQ(binary_path, cmd[0]);
ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION
ASSERT_EQ(std::to_string(status_fd), cmd[2]);
ASSERT_EQ(package, cmd[3]);
struct stat sb;
ASSERT_EQ(0, stat(binary_path.c_str(), &sb));
ASSERT_EQ(static_cast<mode_t>(0755), sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
// With non-zero retry count. update_binary will be removed automatically.
cmd.clear();
ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 2, status_fd, &cmd));
ASSERT_EQ(5U, cmd.size());
ASSERT_EQ(binary_path, cmd[0]);
ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION
ASSERT_EQ(std::to_string(status_fd), cmd[2]);
ASSERT_EQ(package, cmd[3]);
ASSERT_EQ("retry", cmd[4]);
sb = {};
ASSERT_EQ(0, stat(binary_path.c_str(), &sb));
ASSERT_EQ(static_cast<mode_t>(0755), sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
CloseArchive(zip);
#endif // AB_OTA_UPDATER
}
@@ -217,12 +313,30 @@ TEST(InstallTest, update_binary_command_invalid) {
ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
int status_fd = 10;
std::string path = "/path/to/update.zip";
std::string package = "/path/to/update.zip";
std::string binary_path = "/sbin/update_engine_sideload";
std::vector<std::string> cmd;
ASSERT_EQ(INSTALL_CORRUPT, update_binary_command(path, zip, 0, status_fd, &cmd));
ASSERT_EQ(INSTALL_CORRUPT, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd));
CloseArchive(zip);
#else
// Cannot test update_binary_command() because it tries to extract update-binary to /tmp.
GTEST_LOG_(INFO) << "Test skipped on non-A/B device.";
TemporaryFile temp_file;
FILE* zip_file = fdopen(temp_file.fd, "w");
ZipWriter writer(zip_file);
// The archive must have something to be opened correctly.
ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0));
ASSERT_EQ(0, writer.FinishEntry());
ASSERT_EQ(0, writer.Finish());
ASSERT_EQ(0, fclose(zip_file));
// Missing update binary.
ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
int status_fd = 10;
std::string package = "/path/to/update.zip";
TemporaryDir td;
std::string binary_path = std::string(td.path) + "/update_binary";
std::vector<std::string> cmd;
ASSERT_EQ(INSTALL_CORRUPT, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd));
CloseArchive(zip);
#endif // AB_OTA_UPDATER
}
+17 -2
View File
@@ -13,9 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <unistd.h>
#include <gtest/gtest.h>
TEST(SideloadTest, fusedevice) {
ASSERT_NE(-1, access("/dev/fuse", R_OK | W_OK));
#include "fuse_sideload.h"
TEST(SideloadTest, fuse_device) {
ASSERT_EQ(0, access("/dev/fuse", R_OK | W_OK));
}
TEST(SideloadTest, run_fuse_sideload_wrong_parameters) {
provider_vtab vtab;
vtab.close = [](void*) {};
ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, 4095));
ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, (1 << 22) + 1));
// Too many blocks.
ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, ((1 << 18) + 1) * 4096, 4096));
}
+95 -117
View File
@@ -25,11 +25,12 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
#include <gtest/gtest.h>
#include "common/component_test_util.h"
using namespace std::string_literals;
static const std::string UNCRYPT_SOCKET = "/dev/socket/uncrypt";
static const std::string INIT_SVC_SETUP_BCB = "init.svc.setup-bcb";
@@ -62,131 +63,108 @@ class UncryptTest : public ::testing::Test {
ASSERT_TRUE(success) << "uncrypt service is not available.";
has_misc = parse_misc();
std::string err;
has_misc = !get_bootloader_message_blk_device(&err).empty();
}
void SetupOrClearBcb(bool isSetup, const std::string& message,
const std::string& message_in_bcb) const {
if (!has_misc) {
GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
return;
}
// Trigger the setup-bcb service.
ASSERT_TRUE(android::base::SetProperty("ctl.start", isSetup ? "setup-bcb" : "clear-bcb"));
// Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected").
sleep(1);
sockaddr_un un = {};
un.sun_family = AF_UNIX;
strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path));
int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
ASSERT_NE(-1, sockfd);
// Connect to the uncrypt socket.
bool success = false;
for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
if (connect(sockfd, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)) != 0) {
success = true;
break;
}
sleep(1);
}
ASSERT_TRUE(success);
if (isSetup) {
// Send out the BCB message.
int length = static_cast<int>(message.size());
int length_out = htonl(length);
ASSERT_TRUE(android::base::WriteFully(sockfd, &length_out, sizeof(int)))
<< "Failed to write length: " << strerror(errno);
ASSERT_TRUE(android::base::WriteFully(sockfd, message.data(), length))
<< "Failed to write message: " << strerror(errno);
}
// Check the status code from uncrypt.
int status;
ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int)));
ASSERT_EQ(100U, ntohl(status));
// Ack having received the status code.
int code = 0;
ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int)));
ASSERT_EQ(0, close(sockfd));
ASSERT_TRUE(android::base::SetProperty("ctl.stop", isSetup ? "setup-bcb" : "clear-bcb"));
// Verify the message by reading from BCB directly.
bootloader_message boot;
std::string err;
ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
if (isSetup) {
ASSERT_EQ("boot-recovery", std::string(boot.command));
ASSERT_EQ(message_in_bcb, std::string(boot.recovery));
// The rest of the boot.recovery message should be zero'd out.
ASSERT_LE(message_in_bcb.size(), sizeof(boot.recovery));
size_t left = sizeof(boot.recovery) - message_in_bcb.size();
ASSERT_EQ(std::string(left, '\0'), std::string(&boot.recovery[message_in_bcb.size()], left));
// Clear the BCB.
ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
} else {
// All the bytes should be cleared.
ASSERT_EQ(std::string(sizeof(boot), '\0'),
std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)));
}
}
bool has_misc;
};
TEST_F(UncryptTest, setup_bcb) {
if (!has_misc) {
GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
return;
}
// Trigger the setup-bcb service.
ASSERT_TRUE(android::base::SetProperty("ctl.start", "setup-bcb"));
// Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected").
sleep(1);
struct sockaddr_un un = {};
un.sun_family = AF_UNIX;
strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path));
int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
ASSERT_NE(-1, sockfd);
// Connect to the uncrypt socket.
bool success = false;
for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&un), sizeof(struct sockaddr_un)) != 0) {
success = true;
break;
}
sleep(1);
}
ASSERT_TRUE(success);
// Send out the BCB message.
std::string message = "--update_message=abc value";
std::string message_in_bcb = "recovery\n--update_message=abc value\n";
int length = static_cast<int>(message.size());
int length_out = htonl(length);
ASSERT_TRUE(android::base::WriteFully(sockfd, &length_out, sizeof(int)))
<< "Failed to write length: " << strerror(errno);
ASSERT_TRUE(android::base::WriteFully(sockfd, message.data(), length))
<< "Failed to write message: " << strerror(errno);
// Check the status code from uncrypt.
int status;
ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int)));
ASSERT_EQ(100U, ntohl(status));
// Ack having received the status code.
int code = 0;
ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int)));
ASSERT_EQ(0, close(sockfd));
ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb"));
// Verify the message by reading from BCB directly.
bootloader_message boot;
std::string err;
ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
ASSERT_EQ("boot-recovery", std::string(boot.command));
ASSERT_EQ(message_in_bcb, std::string(boot.recovery));
// The rest of the boot.recovery message should be zero'd out.
ASSERT_LE(message_in_bcb.size(), sizeof(boot.recovery));
size_t left = sizeof(boot.recovery) - message_in_bcb.size();
ASSERT_EQ(std::string(left, '\0'), std::string(&boot.recovery[message_in_bcb.size()], left));
// Clear the BCB.
ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
SetupOrClearBcb(true, message, message_in_bcb);
}
TEST_F(UncryptTest, clear_bcb) {
if (!has_misc) {
GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device.";
return;
}
// Trigger the clear-bcb service.
ASSERT_TRUE(android::base::SetProperty("ctl.start", "clear-bcb"));
// Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected").
sleep(1);
struct sockaddr_un un = {};
un.sun_family = AF_UNIX;
strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path));
int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
ASSERT_NE(-1, sockfd);
// Connect to the uncrypt socket.
bool success = false;
for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&un), sizeof(struct sockaddr_un)) != 0) {
success = true;
break;
}
sleep(1);
}
ASSERT_TRUE(success);
// Check the status code from uncrypt.
int status;
ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int)));
ASSERT_EQ(100U, ntohl(status));
// Ack having received the status code.
int code = 0;
ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int)));
ASSERT_EQ(0, close(sockfd));
ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb"));
// Verify the content by reading from BCB directly.
bootloader_message boot;
std::string err;
ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
// All the bytes should be cleared.
ASSERT_EQ(std::string(sizeof(boot), '\0'),
std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)));
SetupOrClearBcb(false, "", "");
}
TEST_F(UncryptTest, setup_bcb_wipe_ab) {
TemporaryFile wipe_package;
ASSERT_TRUE(android::base::WriteStringToFile(std::string(345, 'a'), wipe_package.path));
// It's expected to store a wipe package in /misc, with the package size passed to recovery.
std::string message =
"--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s;
std::string message_in_bcb =
"recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n";
SetupOrClearBcb(true, message, message_in_bcb);
}
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <update_verifier/update_verifier.h>
class UpdateVerifierTest : public ::testing::Test {
protected:
void SetUp() override {
#if defined(PRODUCT_SUPPORTS_VERITY) || defined(BOARD_AVB_ENABLE)
verity_supported = true;
#else
verity_supported = false;
#endif
}
bool verity_supported;
};
TEST_F(UpdateVerifierTest, verify_image_no_care_map) {
// Non-existing care_map is allowed.
ASSERT_TRUE(verify_image("/doesntexist"));
}
TEST_F(UpdateVerifierTest, verify_image_smoke) {
// This test relies on dm-verity support.
if (!verity_supported) {
GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
return;
}
// The care map file can have only two or four lines.
TemporaryFile temp_file;
std::string content = "system\n2,0,1";
ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
ASSERT_TRUE(verify_image(temp_file.path));
// Leading and trailing newlines should be accepted.
ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", temp_file.path));
ASSERT_TRUE(verify_image(temp_file.path));
}
TEST_F(UpdateVerifierTest, verify_image_wrong_lines) {
// The care map file can have only two or four lines.
TemporaryFile temp_file;
ASSERT_FALSE(verify_image(temp_file.path));
ASSERT_TRUE(android::base::WriteStringToFile("line1", temp_file.path));
ASSERT_FALSE(verify_image(temp_file.path));
ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", temp_file.path));
ASSERT_FALSE(verify_image(temp_file.path));
}
TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) {
// This test relies on dm-verity support.
if (!verity_supported) {
GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
return;
}
TemporaryFile temp_file;
std::string content = "system\n2,1,0";
ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
ASSERT_FALSE(verify_image(temp_file.path));
}
TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) {
// This test relies on dm-verity support.
if (!verity_supported) {
GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
return;
}
TemporaryFile temp_file;
std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0";
ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
ASSERT_TRUE(verify_image(temp_file.path));
}
+146 -98
View File
@@ -15,10 +15,12 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
@@ -29,6 +31,7 @@
#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <bootloader_message/bootloader_message.h>
#include <brotli/encode.h>
#include <bsdiff.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_archive.h>
@@ -224,102 +227,6 @@ TEST_F(UpdaterTest, file_getprop) {
expect("", script6.c_str(), kNoCause);
}
TEST_F(UpdaterTest, package_extract_dir) {
// package_extract_dir expects 2 arguments.
expect(nullptr, "package_extract_dir()", kArgsParsingFailure);
expect(nullptr, "package_extract_dir(\"arg1\")", kArgsParsingFailure);
expect(nullptr, "package_extract_dir(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
std::string zip_path = from_testdata_base("ziptest_valid.zip");
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
// Need to set up the ziphandle.
UpdaterInfo updater_info;
updater_info.package_zip = handle;
// Extract "b/c.txt" and "b/d.txt" with package_extract_dir("b", "<dir>").
TemporaryDir td;
std::string temp_dir(td.path);
std::string script("package_extract_dir(\"b\", \"" + temp_dir + "\")");
expect("t", script.c_str(), kNoCause, &updater_info);
// Verify.
std::string data;
std::string file_c = temp_dir + "/c.txt";
ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
ASSERT_EQ(kCTxtContents, data);
std::string file_d = temp_dir + "/d.txt";
ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
ASSERT_EQ(kDTxtContents, data);
// Modify the contents in order to retry. It's expected to be overwritten.
ASSERT_TRUE(android::base::WriteStringToFile("random", file_c));
ASSERT_TRUE(android::base::WriteStringToFile("random", file_d));
// Extract again and verify.
expect("t", script.c_str(), kNoCause, &updater_info);
ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
ASSERT_EQ(kCTxtContents, data);
ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
ASSERT_EQ(kDTxtContents, data);
// Clean up the temp files under td.
ASSERT_EQ(0, unlink(file_c.c_str()));
ASSERT_EQ(0, unlink(file_d.c_str()));
// Extracting "b/" (with slash) should give the same result.
script = "package_extract_dir(\"b/\", \"" + temp_dir + "\")";
expect("t", script.c_str(), kNoCause, &updater_info);
ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
ASSERT_EQ(kCTxtContents, data);
ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
ASSERT_EQ(kDTxtContents, data);
ASSERT_EQ(0, unlink(file_c.c_str()));
ASSERT_EQ(0, unlink(file_d.c_str()));
// Extracting "" is allowed. The entries will carry the path name.
script = "package_extract_dir(\"\", \"" + temp_dir + "\")";
expect("t", script.c_str(), kNoCause, &updater_info);
std::string file_a = temp_dir + "/a.txt";
ASSERT_TRUE(android::base::ReadFileToString(file_a, &data));
ASSERT_EQ(kATxtContents, data);
std::string file_b = temp_dir + "/b.txt";
ASSERT_TRUE(android::base::ReadFileToString(file_b, &data));
ASSERT_EQ(kBTxtContents, data);
std::string file_b_c = temp_dir + "/b/c.txt";
ASSERT_TRUE(android::base::ReadFileToString(file_b_c, &data));
ASSERT_EQ(kCTxtContents, data);
std::string file_b_d = temp_dir + "/b/d.txt";
ASSERT_TRUE(android::base::ReadFileToString(file_b_d, &data));
ASSERT_EQ(kDTxtContents, data);
ASSERT_EQ(0, unlink(file_a.c_str()));
ASSERT_EQ(0, unlink(file_b.c_str()));
ASSERT_EQ(0, unlink(file_b_c.c_str()));
ASSERT_EQ(0, unlink(file_b_d.c_str()));
ASSERT_EQ(0, rmdir((temp_dir + "/b").c_str()));
// Extracting non-existent entry should still give "t".
script = "package_extract_dir(\"doesntexist\", \"" + temp_dir + "\")";
expect("t", script.c_str(), kNoCause, &updater_info);
// Only relative zip_path is allowed.
script = "package_extract_dir(\"/b\", \"" + temp_dir + "\")";
expect("", script.c_str(), kNoCause, &updater_info);
// Only absolute dest_path is allowed.
script = "package_extract_dir(\"b\", \"path\")";
expect("", script.c_str(), kNoCause, &updater_info);
CloseArchive(handle);
}
// TODO: Test extracting to block device.
TEST_F(UpdaterTest, package_extract_file) {
// package_extract_file expects 1 or 2 arguments.
@@ -570,7 +477,7 @@ TEST_F(UpdaterTest, block_image_update) {
ASSERT_EQ(0, fclose(zip_file_ptr));
MemMapping map;
ASSERT_EQ(0, sysMapFile(zip_file.path, &map));
ASSERT_TRUE(map.MapFile(zip_file.path));
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle));
@@ -578,7 +485,7 @@ TEST_F(UpdaterTest, block_image_update) {
UpdaterInfo updater_info;
updater_info.package_zip = handle;
TemporaryFile temp_pipe;
updater_info.cmd_pipe = fopen(temp_pipe.path, "wb");
updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe");
updater_info.package_zip_addr = map.addr;
updater_info.package_zip_len = map.length;
@@ -607,3 +514,144 @@ TEST_F(UpdaterTest, block_image_update) {
ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
CloseArchive(handle);
}
TEST_F(UpdaterTest, new_data_short_write) {
// Create a zip file with new_data.
TemporaryFile zip_file;
FILE* zip_file_ptr = fdopen(zip_file.fd, "wb");
ZipWriter zip_writer(zip_file_ptr);
// Add the empty new data.
ASSERT_EQ(0, zip_writer.StartEntry("empty_new_data", 0));
ASSERT_EQ(0, zip_writer.FinishEntry());
// Add the short written new data.
ASSERT_EQ(0, zip_writer.StartEntry("short_new_data", 0));
std::string new_data_short = std::string(10, 'a');
ASSERT_EQ(0, zip_writer.WriteBytes(new_data_short.data(), new_data_short.size()));
ASSERT_EQ(0, zip_writer.FinishEntry());
// Add the data of exactly one block.
ASSERT_EQ(0, zip_writer.StartEntry("exact_new_data", 0));
std::string new_data_exact = std::string(4096, 'a');
ASSERT_EQ(0, zip_writer.WriteBytes(new_data_exact.data(), new_data_exact.size()));
ASSERT_EQ(0, zip_writer.FinishEntry());
// Add a dummy patch data.
ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0));
ASSERT_EQ(0, zip_writer.FinishEntry());
std::vector<std::string> transfer_list = {
"4",
"1",
"0",
"0",
"new 2,0,1",
};
ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0));
std::string commands = android::base::Join(transfer_list, '\n');
ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size()));
ASSERT_EQ(0, zip_writer.FinishEntry());
ASSERT_EQ(0, zip_writer.Finish());
ASSERT_EQ(0, fclose(zip_file_ptr));
MemMapping map;
ASSERT_TRUE(map.MapFile(zip_file.path));
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle));
// Set up the handler, command_pipe, patch offset & length.
UpdaterInfo updater_info;
updater_info.package_zip = handle;
TemporaryFile temp_pipe;
updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe");
updater_info.package_zip_addr = map.addr;
updater_info.package_zip_len = map.length;
// Updater should report the failure gracefully rather than stuck in deadlock.
TemporaryFile update_file;
std::string script_empty_data = "block_image_update(\"" + std::string(update_file.path) +
R"(", package_extract_file("transfer_list"), "empty_new_data", "patch_data"))";
expect("", script_empty_data.c_str(), kNoCause, &updater_info);
std::string script_short_data = "block_image_update(\"" + std::string(update_file.path) +
R"(", package_extract_file("transfer_list"), "short_new_data", "patch_data"))";
expect("", script_short_data.c_str(), kNoCause, &updater_info);
// Expect to write 1 block of new data successfully.
std::string script_exact_data = "block_image_update(\"" + std::string(update_file.path) +
R"(", package_extract_file("transfer_list"), "exact_new_data", "patch_data"))";
expect("t", script_exact_data.c_str(), kNoCause, &updater_info);
CloseArchive(handle);
}
TEST_F(UpdaterTest, brotli_new_data) {
// Create a zip file with new_data.
TemporaryFile zip_file;
FILE* zip_file_ptr = fdopen(zip_file.fd, "wb");
ZipWriter zip_writer(zip_file_ptr);
// Add a brotli compressed new data entry.
ASSERT_EQ(0, zip_writer.StartEntry("new.dat.br", 0));
auto generator = []() { return rand() % 128; };
// Generate 100 blocks of random data.
std::string brotli_new_data;
brotli_new_data.reserve(4096 * 100);
generate_n(back_inserter(brotli_new_data), 4096 * 100, generator);
size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size());
std::vector<uint8_t> encoded_data(encoded_size);
ASSERT_TRUE(BrotliEncoderCompress(
BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, brotli_new_data.size(),
reinterpret_cast<const uint8_t*>(brotli_new_data.data()), &encoded_size, encoded_data.data()));
ASSERT_EQ(0, zip_writer.WriteBytes(encoded_data.data(), encoded_size));
ASSERT_EQ(0, zip_writer.FinishEntry());
// Add a dummy patch data.
ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0));
ASSERT_EQ(0, zip_writer.FinishEntry());
// Write a few small chunks of new data, then a large chunk, and finally a few small chunks.
// This helps us to catch potential short writes.
std::vector<std::string> transfer_list = {
"4",
"100",
"0",
"0",
"new 2,0,1",
"new 2,1,2",
"new 4,2,50,50,97",
"new 2,97,98",
"new 2,98,99",
"new 2,99,100",
};
ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0));
std::string commands = android::base::Join(transfer_list, '\n');
ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size()));
ASSERT_EQ(0, zip_writer.FinishEntry());
ASSERT_EQ(0, zip_writer.Finish());
ASSERT_EQ(0, fclose(zip_file_ptr));
MemMapping map;
ASSERT_TRUE(map.MapFile(zip_file.path));
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle));
// Set up the handler, command_pipe, patch offset & length.
UpdaterInfo updater_info;
updater_info.package_zip = handle;
TemporaryFile temp_pipe;
updater_info.cmd_pipe = fopen(temp_pipe.path, "wb");
updater_info.package_zip_addr = map.addr;
updater_info.package_zip_len = map.length;
// Check if we can decompress the new data correctly.
TemporaryFile update_file;
std::string script_new_data =
"block_image_update(\"" + std::string(update_file.path) +
R"(", package_extract_file("transfer_list"), "new.dat.br", "patch_data"))";
expect("t", script_new_data.c_str(), kNoCause, &updater_info);
std::string updated_content;
ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content));
ASSERT_EQ(brotli_new_data, updated_content);
CloseArchive(handle);
}
+47 -4
View File
@@ -40,7 +40,7 @@ class VerifierTest : public testing::TestWithParam<std::vector<std::string>> {
void SetUp() override {
std::vector<std::string> args = GetParam();
std::string package = from_testdata_base(args[0]);
if (sysMapFile(package.c_str(), &memmap) != 0) {
if (!memmap.MapFile(package)) {
FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n";
}
@@ -117,6 +117,51 @@ TEST(VerifierTest, load_keys_invalid_keys) {
ASSERT_FALSE(load_keys(key_file5.path, certs));
}
TEST(VerifierTest, BadPackage_AlteredFooter) {
std::string testkey_v3;
ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3));
TemporaryFile key_file1;
ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path));
std::vector<Certificate> certs;
ASSERT_TRUE(load_keys(key_file1.path, certs));
std::string package;
ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package));
ASSERT_EQ(std::string("\xc0\x06\xff\xff\xd2\x06", 6), package.substr(package.size() - 6, 6));
// Alter the footer.
package[package.size() - 5] = '\x05';
ASSERT_EQ(VERIFY_FAILURE,
verify_file(reinterpret_cast<const unsigned char*>(package.data()), package.size(),
certs));
}
TEST(VerifierTest, BadPackage_AlteredContent) {
std::string testkey_v3;
ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3));
TemporaryFile key_file1;
ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path));
std::vector<Certificate> certs;
ASSERT_TRUE(load_keys(key_file1.path, certs));
std::string package;
ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package));
ASSERT_GT(package.size(), static_cast<size_t>(100));
// Alter the content.
std::string altered1(package);
altered1[50] += 1;
ASSERT_EQ(VERIFY_FAILURE,
verify_file(reinterpret_cast<const unsigned char*>(altered1.data()), altered1.size(),
certs));
std::string altered2(package);
altered2[10] += 1;
ASSERT_EQ(VERIFY_FAILURE,
verify_file(reinterpret_cast<const unsigned char*>(altered2.data()), altered2.size(),
certs));
}
TEST(VerifierTest, BadPackage_SignatureStartOutOfBounds) {
std::string testkey_v3;
ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3));
@@ -174,6 +219,4 @@ INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest,
INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest,
::testing::Values(
std::vector<std::string>({"random.zip", "v1"}),
std::vector<std::string>({"fake-eocd.zip", "v1"}),
std::vector<std::string>({"alter-metadata.zip", "v1"}),
std::vector<std::string>({"alter-footer.zip", "v1"})));
std::vector<std::string>({"fake-eocd.zip", "v1"})));
+1 -1
View File
@@ -141,7 +141,7 @@ class ResourceTest : public testing::TestWithParam<std::string> {
// under recovery.
void SetUp() override {
std::string file_path = GetParam();
fp = fopen(file_path.c_str(), "rb");
fp = fopen(file_path.c_str(), "rbe");
ASSERT_NE(nullptr, fp);
unsigned char header[8];
BIN
View File
Binary file not shown.
Binary file not shown.
+112
View File
@@ -0,0 +1,112 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <signal.h>
#include <sys/types.h>
#include <vector>
#include <gtest/gtest.h>
#include "updater/rangeset.h"
TEST(RangeSetTest, Parse_smoke) {
RangeSet rs = RangeSet::Parse("2,1,10");
ASSERT_EQ(static_cast<size_t>(1), rs.size());
ASSERT_EQ((Range{ 1, 10 }), rs[0]);
ASSERT_EQ(static_cast<size_t>(9), rs.blocks());
RangeSet rs2 = RangeSet::Parse("4,15,20,1,10");
ASSERT_EQ(static_cast<size_t>(2), rs2.size());
ASSERT_EQ((Range{ 15, 20 }), rs2[0]);
ASSERT_EQ((Range{ 1, 10 }), rs2[1]);
ASSERT_EQ(static_cast<size_t>(14), rs2.blocks());
// Leading zeros are fine. But android::base::ParseUint() doesn't like trailing zeros like "10 ".
ASSERT_EQ(rs, RangeSet::Parse(" 2, 1, 10"));
ASSERT_EXIT(RangeSet::Parse("2,1,10 "), ::testing::KilledBySignal(SIGABRT), "");
}
TEST(RangeSetTest, Parse_InvalidCases) {
// Insufficient number of tokens.
ASSERT_EXIT(RangeSet::Parse(""), ::testing::KilledBySignal(SIGABRT), "");
ASSERT_EXIT(RangeSet::Parse("2,1"), ::testing::KilledBySignal(SIGABRT), "");
// The first token (i.e. the number of following tokens) is invalid.
ASSERT_EXIT(RangeSet::Parse("a,1,1"), ::testing::KilledBySignal(SIGABRT), "");
ASSERT_EXIT(RangeSet::Parse("3,1,1"), ::testing::KilledBySignal(SIGABRT), "");
ASSERT_EXIT(RangeSet::Parse("-3,1,1"), ::testing::KilledBySignal(SIGABRT), "");
ASSERT_EXIT(RangeSet::Parse("2,1,2,3"), ::testing::KilledBySignal(SIGABRT), "");
// Invalid tokens.
ASSERT_EXIT(RangeSet::Parse("2,1,10a"), ::testing::KilledBySignal(SIGABRT), "");
ASSERT_EXIT(RangeSet::Parse("2,,10"), ::testing::KilledBySignal(SIGABRT), "");
// Empty or negative range.
ASSERT_EXIT(RangeSet::Parse("2,2,2"), ::testing::KilledBySignal(SIGABRT), "");
ASSERT_EXIT(RangeSet::Parse("2,2,1"), ::testing::KilledBySignal(SIGABRT), "");
}
TEST(RangeSetTest, Overlaps) {
RangeSet r1 = RangeSet::Parse("2,1,6");
RangeSet r2 = RangeSet::Parse("2,5,10");
ASSERT_TRUE(r1.Overlaps(r2));
ASSERT_TRUE(r2.Overlaps(r1));
r2 = RangeSet::Parse("2,6,10");
ASSERT_FALSE(r1.Overlaps(r2));
ASSERT_FALSE(r2.Overlaps(r1));
ASSERT_FALSE(RangeSet::Parse("2,3,5").Overlaps(RangeSet::Parse("2,5,7")));
ASSERT_FALSE(RangeSet::Parse("2,5,7").Overlaps(RangeSet::Parse("2,3,5")));
}
TEST(RangeSetTest, GetBlockNumber) {
RangeSet rs = RangeSet::Parse("2,1,10");
ASSERT_EQ(static_cast<size_t>(1), rs.GetBlockNumber(0));
ASSERT_EQ(static_cast<size_t>(6), rs.GetBlockNumber(5));
ASSERT_EQ(static_cast<size_t>(9), rs.GetBlockNumber(8));
// Out of bound.
ASSERT_EXIT(rs.GetBlockNumber(9), ::testing::KilledBySignal(SIGABRT), "");
}
TEST(RangeSetTest, equality) {
ASSERT_EQ(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,1,6"));
ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,1,7"));
ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,2,7"));
// The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5".
ASSERT_NE(RangeSet::Parse("4,1,5,8,10"), RangeSet::Parse("4,8,10,1,5"));
}
TEST(RangeSetTest, iterators) {
RangeSet rs = RangeSet::Parse("4,1,5,8,10");
std::vector<Range> ranges;
for (const auto& range : rs) {
ranges.push_back(range);
}
ASSERT_EQ((std::vector<Range>{ Range{ 1, 5 }, Range{ 8, 10 } }), ranges);
ranges.clear();
// Reverse iterators.
for (auto it = rs.crbegin(); it != rs.crend(); it++) {
ranges.push_back(*it);
}
ASSERT_EQ((std::vector<Range>{ Range{ 8, 10 }, Range{ 1, 5 } }), ranges);
}
+25 -35
View File
@@ -27,27 +27,23 @@ TEST(SysUtilTest, InvalidArgs) {
MemMapping mapping;
// Invalid argument.
ASSERT_EQ(-1, sysMapFile(nullptr, &mapping));
ASSERT_EQ(-1, sysMapFile("/somefile", nullptr));
ASSERT_FALSE(mapping.MapFile(""));
}
TEST(SysUtilTest, sysMapFileRegularFile) {
TEST(SysUtilTest, MapFileRegularFile) {
TemporaryFile temp_file1;
std::string content = "abc";
ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file1.path));
// sysMapFile() should map the file to one range.
// MemMapping::MapFile() should map the file to one range.
MemMapping mapping;
ASSERT_EQ(0, sysMapFile(temp_file1.path, &mapping));
ASSERT_TRUE(mapping.MapFile(temp_file1.path));
ASSERT_NE(nullptr, mapping.addr);
ASSERT_EQ(content.size(), mapping.length);
ASSERT_EQ(1U, mapping.ranges.size());
sysReleaseMap(&mapping);
ASSERT_EQ(0U, mapping.ranges.size());
ASSERT_EQ(1U, mapping.ranges());
}
TEST(SysUtilTest, sysMapFileBlockMap) {
TEST(SysUtilTest, MapFileBlockMap) {
// Create a file that has 10 blocks.
TemporaryFile package;
std::string content;
@@ -63,78 +59,72 @@ TEST(SysUtilTest, sysMapFileBlockMap) {
std::string block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n";
ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping));
ASSERT_TRUE(mapping.MapFile(filename));
ASSERT_EQ(file_size, mapping.length);
ASSERT_EQ(1U, mapping.ranges.size());
ASSERT_EQ(1U, mapping.ranges());
// It's okay to not have the trailing '\n'.
block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10";
ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping));
ASSERT_TRUE(mapping.MapFile(filename));
ASSERT_EQ(file_size, mapping.length);
ASSERT_EQ(1U, mapping.ranges.size());
ASSERT_EQ(1U, mapping.ranges());
// Or having multiple trailing '\n's.
block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n\n\n";
ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping));
ASSERT_TRUE(mapping.MapFile(filename));
ASSERT_EQ(file_size, mapping.length);
ASSERT_EQ(1U, mapping.ranges.size());
ASSERT_EQ(1U, mapping.ranges());
// Multiple ranges.
block_map_content = std::string(package.path) + "\n40960 4096\n3\n0 3\n3 5\n5 10\n";
ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping));
ASSERT_TRUE(mapping.MapFile(filename));
ASSERT_EQ(file_size, mapping.length);
ASSERT_EQ(3U, mapping.ranges.size());
sysReleaseMap(&mapping);
ASSERT_EQ(0U, mapping.ranges.size());
ASSERT_EQ(3U, mapping.ranges());
}
TEST(SysUtilTest, sysMapFileBlockMapInvalidBlockMap) {
TEST(SysUtilTest, MapFileBlockMapInvalidBlockMap) {
MemMapping mapping;
TemporaryFile temp_file;
std::string filename = std::string("@") + temp_file.path;
// Block map file is too short.
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
ASSERT_FALSE(mapping.MapFile(filename));
// Block map file has unexpected number of lines.
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n1\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n2\n0 1\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
ASSERT_FALSE(mapping.MapFile(filename));
// Invalid size/blksize/range_count.
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\nabc 4096\n1\n0 1\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n\n0 1\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
ASSERT_FALSE(mapping.MapFile(filename));
// size/blksize/range_count don't match.
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n0 4096\n1\n0 1\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 0\n1\n0 1\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n0 1\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
ASSERT_FALSE(mapping.MapFile(filename));
// Invalid block dev path.
ASSERT_TRUE(android::base::WriteStringToFile("/doesntexist\n4096 4096\n1\n0 1\n", temp_file.path));
ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
sysReleaseMap(&mapping);
ASSERT_EQ(0U, mapping.ranges.size());
ASSERT_FALSE(mapping.MapFile(filename));
}
+2 -40
View File
@@ -24,51 +24,14 @@
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <otautil/SysUtil.h>
#include <otautil/ZipUtil.h>
#include <ziparchive/zip_archive.h>
#include "common/test_constants.h"
TEST(ZipTest, ExtractPackageRecursive) {
std::string zip_path = from_testdata_base("ziptest_valid.zip");
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
// Extract the whole package into a temp directory.
TemporaryDir td;
ASSERT_NE(nullptr, td.path);
ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr);
// Make sure all the files are extracted correctly.
std::string path(td.path);
ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK));
ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK));
ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK));
ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK));
// The content of the file is the same as expected.
std::string content1;
ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1));
ASSERT_EQ(kATxtContents, content1);
std::string content2;
ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2));
ASSERT_EQ(kDTxtContents, content2);
CloseArchive(handle);
// Clean up.
ASSERT_EQ(0, unlink((path + "/a.txt").c_str()));
ASSERT_EQ(0, unlink((path + "/b.txt").c_str()));
ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str()));
ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str()));
ASSERT_EQ(0, rmdir((path + "/b").c_str()));
}
TEST(ZipTest, OpenFromMemory) {
MemMapping map;
std::string zip_path = from_testdata_base("ziptest_dummy-update.zip");
ASSERT_EQ(0, sysMapFile(zip_path.c_str(), &map));
MemMapping map;
ASSERT_TRUE(map.MapFile(zip_path));
// Map an update package into memory and open the archive from there.
ZipArchiveHandle handle;
@@ -85,6 +48,5 @@ TEST(ZipTest, OpenFromMemory) {
ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
CloseArchive(handle);
sysReleaseMap(&map);
}
-191
View File
@@ -1,191 +0,0 @@
/*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <otautil/ZipUtil.h>
#include <ziparchive/zip_archive.h>
#include "common/test_constants.h"
TEST(ZipUtilTest, invalid_args) {
std::string zip_path = from_testdata_base("ziptest_valid.zip");
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
// zip_path must be a relative path.
ASSERT_FALSE(ExtractPackageRecursive(handle, "/a/b", "/tmp", nullptr, nullptr));
// dest_path must be an absolute path.
ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "tmp", nullptr, nullptr));
ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "", nullptr, nullptr));
CloseArchive(handle);
}
TEST(ZipUtilTest, extract_all) {
std::string zip_path = from_testdata_base("ziptest_valid.zip");
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
// Extract the whole package into a temp directory.
TemporaryDir td;
ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr);
// Make sure all the files are extracted correctly.
std::string path(td.path);
ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK));
ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK));
ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK));
ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK));
// The content of the file is the same as expected.
std::string content1;
ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1));
ASSERT_EQ(kATxtContents, content1);
std::string content2;
ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2));
ASSERT_EQ(kDTxtContents, content2);
// Clean up the temp files under td.
ASSERT_EQ(0, unlink((path + "/a.txt").c_str()));
ASSERT_EQ(0, unlink((path + "/b.txt").c_str()));
ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str()));
ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str()));
ASSERT_EQ(0, rmdir((path + "/b").c_str()));
CloseArchive(handle);
}
TEST(ZipUtilTest, extract_prefix_with_slash) {
std::string zip_path = from_testdata_base("ziptest_valid.zip");
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
// Extract all the entries starting with "b/".
TemporaryDir td;
ExtractPackageRecursive(handle, "b/", td.path, nullptr, nullptr);
// Make sure all the files with "b/" prefix are extracted correctly.
std::string path(td.path);
ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK));
ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK));
// And the rest are not extracted.
ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK));
ASSERT_EQ(ENOENT, errno);
ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK));
ASSERT_EQ(ENOENT, errno);
// The content of the file is the same as expected.
std::string content1;
ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1));
ASSERT_EQ(kCTxtContents, content1);
std::string content2;
ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2));
ASSERT_EQ(kDTxtContents, content2);
// Clean up the temp files under td.
ASSERT_EQ(0, unlink((path + "/c.txt").c_str()));
ASSERT_EQ(0, unlink((path + "/d.txt").c_str()));
CloseArchive(handle);
}
TEST(ZipUtilTest, extract_prefix_without_slash) {
std::string zip_path = from_testdata_base("ziptest_valid.zip");
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
// Extract all the file entries starting with "b/".
TemporaryDir td;
ExtractPackageRecursive(handle, "b", td.path, nullptr, nullptr);
// Make sure all the files with "b/" prefix are extracted correctly.
std::string path(td.path);
ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK));
ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK));
// And the rest are not extracted.
ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK));
ASSERT_EQ(ENOENT, errno);
ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK));
ASSERT_EQ(ENOENT, errno);
// The content of the file is the same as expected.
std::string content1;
ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1));
ASSERT_EQ(kCTxtContents, content1);
std::string content2;
ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2));
ASSERT_EQ(kDTxtContents, content2);
// Clean up the temp files under td.
ASSERT_EQ(0, unlink((path + "/c.txt").c_str()));
ASSERT_EQ(0, unlink((path + "/d.txt").c_str()));
CloseArchive(handle);
}
TEST(ZipUtilTest, set_timestamp) {
std::string zip_path = from_testdata_base("ziptest_valid.zip");
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
// Set the timestamp to 8/1/2008.
constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };
// Extract all the entries starting with "b/".
TemporaryDir td;
ExtractPackageRecursive(handle, "b", td.path, &timestamp, nullptr);
// Make sure all the files with "b/" prefix are extracted correctly.
std::string path(td.path);
std::string file_c = path + "/c.txt";
std::string file_d = path + "/d.txt";
ASSERT_EQ(0, access(file_c.c_str(), F_OK));
ASSERT_EQ(0, access(file_d.c_str(), F_OK));
// Verify the timestamp.
timespec time;
time.tv_sec = 1217592000;
time.tv_nsec = 0;
struct stat sb;
ASSERT_EQ(0, stat(file_c.c_str(), &sb)) << strerror(errno);
ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
ASSERT_EQ(0, stat(file_d.c_str(), &sb)) << strerror(errno);
ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
// Clean up the temp files under td.
ASSERT_EQ(0, unlink(file_c.c_str()));
ASSERT_EQ(0, unlink(file_d.c_str()));
CloseArchive(handle);
}
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recovery_installing" msgid="2013591905463558223">"Installing system update"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"Erasing"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"No command"</string>
<string name="recovery_error" msgid="5748178989622716736">"Error!"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"Installing security update"</string>
</resources>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recovery_installing" msgid="2013591905463558223">"Installing system update"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"Erasing"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"No command"</string>
<string name="recovery_error" msgid="5748178989622716736">"Error!"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"Installing security update"</string>
</resources>
@@ -3,7 +3,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recovery_installing" msgid="2013591905463558223">"सिस्टम अपडेट इंस्टॉल किया जा रहा है"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"मिटाया जा रहा है"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"कोई देश नहीं"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"कोई निर्देश नहीं मिला"</string>
<string name="recovery_error" msgid="5748178989622716736">"गड़बड़ी!"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"सुरक्षा अपडेट इंस्टॉल किया जा रहा है"</string>
</resources>
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recovery_installing" msgid="2013591905463558223">"सिस्टम अद्यतन स्थापित करत आहे"</string>
<string name="recovery_installing" msgid="2013591905463558223">"सिस्टम अपडेट इंस्टॉल करत आहे"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"मिटवत आहे"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"कोणतही आदेश नाही"</string>
<string name="recovery_error" msgid="5748178989622716736">"त्रुटी!"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"सुरक्षा अद्यतन स्थापित करत आहे"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"कोणतही कमांड नाही"</string>
<string name="recovery_error" msgid="5748178989622716736">"एरर!"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"सुरक्षा अपडेट इंस्टॉल करत आहे"</string>
</resources>
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recovery_installing" msgid="2013591905463558223">"ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤ ਜਾ ਰਹੀ ਹੈ"</string>
<string name="recovery_installing" msgid="2013591905463558223">"ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"ਕੋਈ ਕਮਾਂਡ ਨਹੀਂ"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"ਕੋਈ ਆਦੇਸ਼ ਨਹੀਂ"</string>
<string name="recovery_error" msgid="5748178989622716736">"ਅਸ਼ੁੱਧੀ!"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤ ਜਾ ਰਹੀ ਹੈ"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤ ਜਾ ਰਿਹਾ ਹੈ"</string>
</resources>
@@ -4,6 +4,6 @@
<string name="recovery_installing" msgid="2013591905463558223">"సిస్టమ్ నవీకరణను ఇన్‍స్టాల్ చేస్తోంది"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"డేటాను తొలగిస్తోంది"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"ఆదేశం లేదు"</string>
<string name="recovery_error" msgid="5748178989622716736">"లోపం సంభవించింది!"</string>
<string name="recovery_error" msgid="5748178989622716736">"ఎర్రర్ సంభవించింది!"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"భద్రతా నవీకరణను ఇన్‌స్టాల్ చేస్తోంది"</string>
</resources>
+17 -2
View File
@@ -363,7 +363,11 @@ int TWinstall_zip(const char* path, int* wipe_cache) {
DataManager::SetProgress(0);
MemMapping map;
#ifdef USE_MINZIP
if (sysMapFile(path, &map) != 0) {
#else
if (!map.MapFile(path)) {
#endif
gui_msg(Msg(msg::kError, "fail_sysmap=Failed to map file '{1}'")(path));
return -1;
}
@@ -377,6 +381,9 @@ int TWinstall_zip(const char* path, int* wipe_cache) {
if (!load_keys("/res/keys", loadedKeys)) {
LOGINFO("Failed to load keys");
gui_err("verify_zip_fail=Zip signature verification failed!");
#ifdef USE_MINZIP
sysReleaseMap(&map);
#endif
return -1;
}
ret_val = verify_file(map.addr, map.length, loadedKeys, std::bind(&DataManager::SetProgress, std::placeholders::_1));
@@ -384,7 +391,9 @@ int TWinstall_zip(const char* path, int* wipe_cache) {
if (ret_val != VERIFY_SUCCESS) {
LOGINFO("Zip signature verification failed: %i\n", ret_val);
gui_err("verify_zip_fail=Zip signature verification failed!");
#ifdef USE_MINZIP
sysReleaseMap(&map);
#endif
return -1;
} else {
gui_msg("verify_zip_done=Zip signature verified successfully.");
@@ -393,7 +402,9 @@ int TWinstall_zip(const char* path, int* wipe_cache) {
ZipWrap Zip;
if (!Zip.Open(path, &map)) {
gui_err("zip_corrupt=Zip file is corrupt!");
sysReleaseMap(&map);
#ifdef USE_MINZIP
sysReleaseMap(&map);
#endif
return INSTALL_CORRUPT;
}
@@ -404,8 +415,10 @@ int TWinstall_zip(const char* path, int* wipe_cache) {
// Additionally verify the compatibility of the package.
if (!verify_package_compatibility(&Zip)) {
gui_err("zip_compatible_err=Zip Treble compatibility error!");
sysReleaseMap(&map);
Zip.Close();
#ifdef USE_MINZIP
sysReleaseMap(&map);
#endif
ret_val = INSTALL_CORRUPT;
} else {
ret_val = Prepare_Update_Binary(path, &Zip, wipe_cache);
@@ -433,6 +446,8 @@ int TWinstall_zip(const char* path, int* wipe_cache) {
} else {
LOGINFO("Install took %i second(s).\n", total_time);
}
#ifdef USE_MINZIP
sysReleaseMap(&map);
#endif
return ret_val;
}
+318 -167
View File
@@ -54,6 +54,9 @@ RecoveryUI::RecoveryUI()
rtl_locale_(false),
brightness_normal_(50),
brightness_dimmed_(25),
touch_screen_allowed_(false),
kTouchLowThreshold(RECOVERY_UI_TOUCH_LOW_THRESHOLD),
kTouchHighThreshold(RECOVERY_UI_TOUCH_HIGH_THRESHOLD),
key_queue_len(0),
key_last_down(-1),
key_long_press(false),
@@ -64,6 +67,9 @@ RecoveryUI::RecoveryUI()
has_power_key(false),
has_up_key(false),
has_down_key(false),
has_touch_screen(false),
touch_slot_(0),
is_bootreason_recovery_ui_(false),
screensaver_state_(ScreensaverState::DISABLED) {
pthread_mutex_init(&key_queue_mutex, nullptr);
pthread_cond_init(&key_queue_cond, nullptr);
@@ -71,23 +77,25 @@ RecoveryUI::RecoveryUI()
}
void RecoveryUI::OnKeyDetected(int key_code) {
if (key_code == KEY_POWER) {
has_power_key = true;
} else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) {
has_down_key = true;
} else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) {
has_up_key = true;
}
if (key_code == KEY_POWER) {
has_power_key = true;
} else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) {
has_down_key = true;
} else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) {
has_up_key = true;
} else if (key_code == ABS_MT_POSITION_X || key_code == ABS_MT_POSITION_Y) {
has_touch_screen = true;
}
}
// Reads input events, handles special hot keys, and adds to the key queue.
static void* InputThreadLoop(void*) {
while (true) {
if (!ev_wait(-1)) {
ev_dispatch();
}
while (true) {
if (!ev_wait(-1)) {
ev_dispatch();
}
return nullptr;
}
return nullptr;
}
bool RecoveryUI::InitScreensaver() {
@@ -128,10 +136,28 @@ bool RecoveryUI::Init(const std::string& locale) {
// Set up the locale info.
SetLocale(locale);
ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2));
ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2),
touch_screen_allowed_);
ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
if (touch_screen_allowed_) {
ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
// Parse /proc/cmdline to determine if it's booting into recovery with a bootreason of
// "recovery_ui". This specific reason is set by some (wear) bootloaders, to allow an easier way
// to turn on text mode. It will only be set if the recovery boot is triggered from fastboot, or
// with 'adb reboot recovery'. Note that this applies to all build variants. Otherwise the text
// mode will be turned on automatically on debuggable builds, even without a swipe.
std::string cmdline;
if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
is_bootreason_recovery_ui_ = cmdline.find("bootreason=recovery_ui") != std::string::npos;
} else {
// Non-fatal, and won't affect Init() result.
PLOG(WARNING) << "Failed to read /proc/cmdline";
}
}
if (!InitScreensaver()) {
LOG(INFO) << "Screensaver disabled";
}
@@ -140,40 +166,157 @@ bool RecoveryUI::Init(const std::string& locale) {
return true;
}
void RecoveryUI::OnTouchDetected(int dx, int dy) {
enum SwipeDirection { UP, DOWN, RIGHT, LEFT } direction;
// We only consider a valid swipe if:
// - the delta along one axis is below kTouchLowThreshold;
// - and the delta along the other axis is beyond kTouchHighThreshold.
if (abs(dy) < kTouchLowThreshold && abs(dx) > kTouchHighThreshold) {
direction = dx < 0 ? SwipeDirection::LEFT : SwipeDirection::RIGHT;
} else if (abs(dx) < kTouchLowThreshold && abs(dy) > kTouchHighThreshold) {
direction = dy < 0 ? SwipeDirection::UP : SwipeDirection::DOWN;
} else {
LOG(DEBUG) << "Ignored " << dx << " " << dy << " (low: " << kTouchLowThreshold
<< ", high: " << kTouchHighThreshold << ")";
return;
}
// Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui.
if (is_bootreason_recovery_ui_ && !IsTextVisible()) {
ShowText(true);
return;
}
LOG(DEBUG) << "Swipe direction=" << direction;
switch (direction) {
case SwipeDirection::UP:
ProcessKey(KEY_UP, 1); // press up key
ProcessKey(KEY_UP, 0); // and release it
break;
case SwipeDirection::DOWN:
ProcessKey(KEY_DOWN, 1); // press down key
ProcessKey(KEY_DOWN, 0); // and release it
break;
case SwipeDirection::LEFT:
case SwipeDirection::RIGHT:
ProcessKey(KEY_POWER, 1); // press power key
ProcessKey(KEY_POWER, 0); // and release it
break;
};
}
int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
struct input_event ev;
if (ev_get_input(fd, epevents, &ev) == -1) {
return -1;
}
struct input_event ev;
if (ev_get_input(fd, epevents, &ev) == -1) {
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;
}
}
} else {
rel_sum = 0;
}
// Touch inputs handling.
//
// We handle the touch inputs by tracking the position changes between initial contacting and
// upon lifting. touch_start_X/Y record the initial positions, with touch_finger_down set. Upon
// detecting the lift, we unset touch_finger_down and detect a swipe based on position changes.
//
// Per the doc Multi-touch Protocol at below, there are two protocols.
// https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
//
// The main difference between the stateless type A protocol and the stateful type B slot protocol
// lies in the usage of identifiable contacts to reduce the amount of data sent to userspace. The
// slot protocol (i.e. type B) sends ABS_MT_TRACKING_ID with a unique id on initial contact, and
// sends ABS_MT_TRACKING_ID -1 upon lifting the contact. Protocol A doesn't send
// ABS_MT_TRACKING_ID -1 on lifting, but the driver may additionally report BTN_TOUCH event.
//
// For protocol A, we rely on BTN_TOUCH to recognize lifting, while for protocol B we look for
// ABS_MT_TRACKING_ID being -1.
//
// Touch input events will only be available if touch_screen_allowed_ is set.
if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
ProcessKey(ev.code, ev.value);
if (ev.type == EV_SYN) {
if (touch_screen_allowed_ && ev.code == SYN_REPORT) {
// There might be multiple SYN_REPORT events. We should only detect a swipe after lifting the
// contact.
if (touch_finger_down_ && !touch_swiping_) {
touch_start_X_ = touch_X_;
touch_start_Y_ = touch_Y_;
touch_swiping_ = true;
} else if (!touch_finger_down_ && touch_swiping_) {
touch_swiping_ = false;
OnTouchDetected(touch_X_ - touch_start_X_, touch_Y_ - touch_start_Y_);
}
}
return 0;
}
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;
}
}
} else {
rel_sum = 0;
}
if (touch_screen_allowed_ && ev.type == EV_ABS) {
if (ev.code == ABS_MT_SLOT) {
touch_slot_ = ev.value;
}
// Ignore other fingers.
if (touch_slot_ > 0) return 0;
switch (ev.code) {
case ABS_MT_POSITION_X:
touch_X_ = ev.value;
touch_finger_down_ = true;
break;
case ABS_MT_POSITION_Y:
touch_Y_ = ev.value;
touch_finger_down_ = true;
break;
case ABS_MT_TRACKING_ID:
// Protocol B: -1 marks lifting the contact.
if (ev.value < 0) touch_finger_down_ = false;
break;
}
return 0;
}
if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
if (touch_screen_allowed_) {
if (ev.code == BTN_TOUCH) {
// A BTN_TOUCH with value 1 indicates the start of contact (protocol A), with 0 means
// lifting the contact.
touch_finger_down_ = (ev.value == 1);
}
// Intentionally ignore BTN_TOUCH and BTN_TOOL_FINGER, which would otherwise trigger
// additional scrolling (because in ScreenRecoveryUI::ShowFile(), we consider keys other than
// KEY_POWER and KEY_UP as KEY_DOWN).
if (ev.code == BTN_TOUCH || ev.code == BTN_TOOL_FINGER) {
return 0;
}
}
ProcessKey(ev.code, ev.value);
}
return 0;
}
// Process a key-up or -down event. A key is "registered" when it is
@@ -189,82 +332,84 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
//
// updown == 1 for key down events; 0 for key up events
void RecoveryUI::ProcessKey(int key_code, int updown) {
bool register_key = false;
bool long_press = false;
bool reboot_enabled;
bool register_key = false;
bool long_press = false;
bool reboot_enabled;
pthread_mutex_lock(&key_queue_mutex);
key_pressed[key_code] = updown;
if (updown) {
++key_down_count;
key_last_down = key_code;
key_long_press = false;
key_timer_t* info = new key_timer_t;
info->ui = this;
info->key_code = key_code;
info->count = key_down_count;
pthread_t thread;
pthread_create(&thread, nullptr, &RecoveryUI::time_key_helper, info);
pthread_detach(thread);
} else {
if (key_last_down == key_code) {
long_press = key_long_press;
register_key = true;
}
key_last_down = -1;
pthread_mutex_lock(&key_queue_mutex);
key_pressed[key_code] = updown;
if (updown) {
++key_down_count;
key_last_down = key_code;
key_long_press = false;
key_timer_t* info = new key_timer_t;
info->ui = this;
info->key_code = key_code;
info->count = key_down_count;
pthread_t thread;
pthread_create(&thread, nullptr, &RecoveryUI::time_key_helper, info);
pthread_detach(thread);
} else {
if (key_last_down == key_code) {
long_press = key_long_press;
register_key = true;
}
reboot_enabled = enable_reboot;
pthread_mutex_unlock(&key_queue_mutex);
key_last_down = -1;
}
reboot_enabled = enable_reboot;
pthread_mutex_unlock(&key_queue_mutex);
if (register_key) {
switch (CheckKey(key_code, long_press)) {
case RecoveryUI::IGNORE:
break;
if (register_key) {
switch (CheckKey(key_code, long_press)) {
case RecoveryUI::IGNORE:
break;
case RecoveryUI::TOGGLE:
ShowText(!IsTextVisible());
break;
case RecoveryUI::TOGGLE:
ShowText(!IsTextVisible());
break;
case RecoveryUI::REBOOT:
if (reboot_enabled) {
reboot("reboot,");
while (true) { pause(); }
}
break;
case RecoveryUI::ENQUEUE:
EnqueueKey(key_code);
break;
case RecoveryUI::REBOOT:
if (reboot_enabled) {
reboot("reboot,");
while (true) {
pause();
}
}
break;
case RecoveryUI::ENQUEUE:
EnqueueKey(key_code);
break;
}
}
}
void* RecoveryUI::time_key_helper(void* cookie) {
key_timer_t* info = static_cast<key_timer_t*>(cookie);
info->ui->time_key(info->key_code, info->count);
delete info;
return nullptr;
key_timer_t* info = static_cast<key_timer_t*>(cookie);
info->ui->time_key(info->key_code, info->count);
delete info;
return nullptr;
}
void RecoveryUI::time_key(int key_code, int count) {
usleep(750000); // 750 ms == "long"
bool long_press = false;
pthread_mutex_lock(&key_queue_mutex);
if (key_last_down == key_code && key_down_count == count) {
long_press = key_long_press = true;
}
pthread_mutex_unlock(&key_queue_mutex);
if (long_press) KeyLongPress(key_code);
usleep(750000); // 750 ms == "long"
bool long_press = false;
pthread_mutex_lock(&key_queue_mutex);
if (key_last_down == key_code && key_down_count == count) {
long_press = key_long_press = true;
}
pthread_mutex_unlock(&key_queue_mutex);
if (long_press) KeyLongPress(key_code);
}
void RecoveryUI::EnqueueKey(int key_code) {
pthread_mutex_lock(&key_queue_mutex);
const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
if (key_queue_len < queue_max) {
key_queue[key_queue_len++] = key_code;
pthread_cond_signal(&key_queue_cond);
}
pthread_mutex_unlock(&key_queue_mutex);
pthread_mutex_lock(&key_queue_mutex);
const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
if (key_queue_len < queue_max) {
key_queue[key_queue_len++] = key_code;
pthread_cond_signal(&key_queue_cond);
}
pthread_mutex_unlock(&key_queue_mutex);
}
int RecoveryUI::WaitKey() {
@@ -330,98 +475,104 @@ int RecoveryUI::WaitKey() {
}
bool RecoveryUI::IsUsbConnected() {
int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
if (fd < 0) {
printf("failed to open /sys/class/android_usb/android0/state: %s\n",
strerror(errno));
return 0;
}
int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
if (fd < 0) {
printf("failed to open /sys/class/android_usb/android0/state: %s\n", strerror(errno));
return 0;
}
char buf;
// USB is connected if android_usb state is CONNECTED or CONFIGURED.
int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C');
if (close(fd) < 0) {
printf("failed to close /sys/class/android_usb/android0/state: %s\n",
strerror(errno));
}
return connected;
char buf;
// USB is connected if android_usb state is CONNECTED or CONFIGURED.
int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C');
if (close(fd) < 0) {
printf("failed to close /sys/class/android_usb/android0/state: %s\n", strerror(errno));
}
return connected;
}
bool RecoveryUI::IsKeyPressed(int key) {
pthread_mutex_lock(&key_queue_mutex);
int pressed = key_pressed[key];
pthread_mutex_unlock(&key_queue_mutex);
return pressed;
pthread_mutex_lock(&key_queue_mutex);
int pressed = key_pressed[key];
pthread_mutex_unlock(&key_queue_mutex);
return pressed;
}
bool RecoveryUI::IsLongPress() {
pthread_mutex_lock(&key_queue_mutex);
bool result = key_long_press;
pthread_mutex_unlock(&key_queue_mutex);
return result;
pthread_mutex_lock(&key_queue_mutex);
bool result = key_long_press;
pthread_mutex_unlock(&key_queue_mutex);
return result;
}
bool RecoveryUI::HasThreeButtons() {
return has_power_key && has_up_key && has_down_key;
return has_power_key && has_up_key && has_down_key;
}
bool RecoveryUI::HasPowerKey() const {
return has_power_key;
}
bool RecoveryUI::HasTouchScreen() const {
return has_touch_screen;
}
void RecoveryUI::FlushKeys() {
pthread_mutex_lock(&key_queue_mutex);
key_queue_len = 0;
pthread_mutex_unlock(&key_queue_mutex);
pthread_mutex_lock(&key_queue_mutex);
key_queue_len = 0;
pthread_mutex_unlock(&key_queue_mutex);
}
RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) {
pthread_mutex_lock(&key_queue_mutex);
key_long_press = false;
pthread_mutex_unlock(&key_queue_mutex);
// If we have power and volume up keys, that chord is the signal to toggle the text display.
if (HasThreeButtons() || (HasPowerKey() && HasTouchScreen() && touch_screen_allowed_)) {
if ((key == KEY_VOLUMEUP || key == KEY_UP) && IsKeyPressed(KEY_POWER)) {
return TOGGLE;
}
} else {
// Otherwise long press of any button toggles to the text display,
// and there's no way to toggle back (but that's pretty useless anyway).
if (is_long_press && !IsTextVisible()) {
return TOGGLE;
}
// Also, for button-limited devices, a long press is translated to KEY_ENTER.
if (is_long_press && IsTextVisible()) {
EnqueueKey(KEY_ENTER);
return IGNORE;
}
}
// Press power seven times in a row to reboot.
if (key == KEY_POWER) {
pthread_mutex_lock(&key_queue_mutex);
key_long_press = false;
bool reboot_enabled = enable_reboot;
pthread_mutex_unlock(&key_queue_mutex);
// If we have power and volume up keys, that chord is the signal to toggle the text display.
if (HasThreeButtons()) {
if (key == KEY_VOLUMEUP && IsKeyPressed(KEY_POWER)) {
return TOGGLE;
}
} else {
// Otherwise long press of any button toggles to the text display,
// and there's no way to toggle back (but that's pretty useless anyway).
if (is_long_press && !IsTextVisible()) {
return TOGGLE;
}
// Also, for button-limited devices, a long press is translated to KEY_ENTER.
if (is_long_press && IsTextVisible()) {
EnqueueKey(KEY_ENTER);
return IGNORE;
}
if (reboot_enabled) {
++consecutive_power_keys;
if (consecutive_power_keys >= 7) {
return REBOOT;
}
}
} else {
consecutive_power_keys = 0;
}
// Press power seven times in a row to reboot.
if (key == KEY_POWER) {
pthread_mutex_lock(&key_queue_mutex);
bool reboot_enabled = enable_reboot;
pthread_mutex_unlock(&key_queue_mutex);
if (reboot_enabled) {
++consecutive_power_keys;
if (consecutive_power_keys >= 7) {
return REBOOT;
}
}
} else {
consecutive_power_keys = 0;
}
last_key = key;
return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
last_key = key;
return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
}
void RecoveryUI::KeyLongPress(int) {
}
void RecoveryUI::SetEnableReboot(bool enabled) {
pthread_mutex_lock(&key_queue_mutex);
enable_reboot = enabled;
pthread_mutex_unlock(&key_queue_mutex);
pthread_mutex_lock(&key_queue_mutex);
enable_reboot = enabled;
pthread_mutex_unlock(&key_queue_mutex);
}
void RecoveryUI::SetLocale(const std::string& new_locale) {
+136 -119
View File
@@ -25,163 +25,180 @@
// Abstract class for controlling the user interface during recovery.
class RecoveryUI {
public:
RecoveryUI();
public:
RecoveryUI();
virtual ~RecoveryUI() { }
virtual ~RecoveryUI() {}
// Initialize the object; called before anything else. UI texts will be
// initialized according to the given locale. Returns true on success.
virtual bool Init(const std::string& locale);
// Initializes the object; called before anything else. UI texts will be initialized according to
// the given locale. Returns true on success.
virtual bool Init(const std::string& locale);
// Show a stage indicator. Call immediately after Init().
virtual void SetStage(int current, int max) = 0;
// Shows a stage indicator. Called immediately after Init().
virtual void SetStage(int current, int max) = 0;
// Set the overall recovery state ("background image").
enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR };
virtual void SetBackground(Icon icon) = 0;
virtual void SetSystemUpdateText(bool security_update) = 0;
// Sets the overall recovery state ("background image").
enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR };
virtual void SetBackground(Icon icon) = 0;
virtual void SetSystemUpdateText(bool security_update) = 0;
// --- progress indicator ---
enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE };
virtual void SetProgressType(ProgressType determinate) = 0;
// --- progress indicator ---
enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE };
virtual void SetProgressType(ProgressType determinate) = 0;
// Show a progress bar and define the scope of the next operation:
// portion - fraction of the progress bar the next operation will use
// seconds - expected time interval (progress bar moves at this minimum rate)
virtual void ShowProgress(float portion, float seconds) = 0;
// Shows a progress bar and define the scope of the next operation:
// portion - fraction of the progress bar the next operation will use
// seconds - expected time interval (progress bar moves at this minimum rate)
virtual void ShowProgress(float portion, float seconds) = 0;
// Set progress bar position (0.0 - 1.0 within the scope defined
// by the last call to ShowProgress).
virtual void SetProgress(float fraction) = 0;
// Sets progress bar position (0.0 - 1.0 within the scope defined by the last call to
// ShowProgress).
virtual void SetProgress(float fraction) = 0;
// --- text log ---
// --- text log ---
virtual void ShowText(bool visible) = 0;
virtual void ShowText(bool visible) = 0;
virtual bool IsTextVisible() = 0;
virtual bool IsTextVisible() = 0;
virtual bool WasTextEverVisible() = 0;
virtual bool WasTextEverVisible() = 0;
// Write a message to the on-screen log (shown if the user has
// 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;
// Writes a message to the on-screen log (shown if the user has 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 ShowFile(const char* filename) = 0;
// --- key handling ---
// --- key handling ---
// Wait for a key and return it. May return -1 after timeout.
virtual int WaitKey();
// Waits for a key and return it. May return -1 after timeout.
virtual int WaitKey();
virtual bool IsKeyPressed(int key);
virtual bool IsLongPress();
virtual bool IsKeyPressed(int key);
virtual bool IsLongPress();
// Returns true if you have the volume up/down and power trio typical
// of phones and tablets, false otherwise.
virtual bool HasThreeButtons();
// Returns true if you have the volume up/down and power trio typical of phones and tablets, false
// otherwise.
virtual bool HasThreeButtons();
// Erase any queued-up keys.
virtual void FlushKeys();
// Returns true if it has a power key.
virtual bool HasPowerKey() const;
// Called on each key press, even while operations are in progress.
// Return value indicates whether an immediate operation should be
// triggered (toggling the display, rebooting the device), or if
// the key should be enqueued for use by the main thread.
enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE };
virtual KeyAction CheckKey(int key, bool is_long_press);
// Returns true if it supports touch inputs.
virtual bool HasTouchScreen() const;
// Called when a key is held down long enough to have been a
// long-press (but before the key is released). This means that
// if the key is eventually registered (released without any other
// keys being pressed in the meantime), CheckKey will be called with
// 'is_long_press' true.
virtual void KeyLongPress(int key);
// Erases any queued-up keys.
virtual void FlushKeys();
// Normally in recovery there's a key sequence that triggers
// immediate reboot of the device, regardless of what recovery is
// doing (with the default CheckKey implementation, it's pressing
// the power button 7 times in row). Call this to enable or
// disable that feature. It is enabled by default.
virtual void SetEnableReboot(bool enabled);
// Called on each key press, even while operations are in progress. Return value indicates whether
// an immediate operation should be triggered (toggling the display, rebooting the device), or if
// the key should be enqueued for use by the main thread.
enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE };
virtual KeyAction CheckKey(int key, bool is_long_press);
// --- menu display ---
// Called when a key is held down long enough to have been a long-press (but before the key is
// released). This means that if the key is eventually registered (released without any other keys
// being pressed in the meantime), CheckKey will be called with 'is_long_press' true.
virtual void KeyLongPress(int key);
// 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).
virtual void StartMenu(const char* const * headers, const char* const * items,
int initial_selection) = 0;
// Normally in recovery there's a key sequence that triggers immediate reboot of the device,
// regardless of what recovery is doing (with the default CheckKey implementation, it's pressing
// the power button 7 times in row). Call this to enable or disable that feature. It is enabled by
// default.
virtual void SetEnableReboot(bool enabled);
// Set the menu highlight to the given index, wrapping if necessary.
// Returns the actual item selected.
virtual int SelectMenu(int sel) = 0;
// --- menu display ---
// End menu mode, resetting the text overlay so that ui_print()
// statements will be displayed.
virtual void EndMenu() = 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).
virtual void StartMenu(const char* const* headers, const char* const* items,
int initial_selection) = 0;
protected:
void EnqueueKey(int key_code);
// Sets the menu highlight to the given index, wrapping if necessary. Returns the actual item
// selected.
virtual int SelectMenu(int sel) = 0;
// The locale that's used to show the rendered texts.
std::string locale_;
bool rtl_locale_;
// Ends menu mode, resetting the text overlay so that ui_print() statements will be displayed.
virtual void EndMenu() = 0;
// The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25%
// of the max_brightness). Because the absolute values may vary across devices. These two
// values can be configured via subclassing. Setting brightness_normal_ to 0 to disable
// screensaver.
unsigned int brightness_normal_;
unsigned int brightness_dimmed_;
protected:
void EnqueueKey(int key_code);
private:
// Key event input queue
pthread_mutex_t key_queue_mutex;
pthread_cond_t key_queue_cond;
int key_queue[256], key_queue_len;
char key_pressed[KEY_MAX + 1]; // under key_queue_mutex
int key_last_down; // under key_queue_mutex
bool key_long_press; // under key_queue_mutex
int key_down_count; // under key_queue_mutex
bool enable_reboot; // under key_queue_mutex
int rel_sum;
// The locale that's used to show the rendered texts.
std::string locale_;
bool rtl_locale_;
int consecutive_power_keys;
int last_key;
// The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25% of
// the max_brightness). Because the absolute values may vary across devices. These two values can
// be configured via subclassing. Setting brightness_normal_ to 0 to disable screensaver.
unsigned int brightness_normal_;
unsigned int brightness_dimmed_;
bool has_power_key;
bool has_up_key;
bool has_down_key;
// Whether we should listen for touch inputs (default: false).
bool touch_screen_allowed_;
struct key_timer_t {
RecoveryUI* ui;
int key_code;
int count;
};
private:
// The sensitivity when detecting a swipe.
const int kTouchLowThreshold;
const int kTouchHighThreshold;
pthread_t input_thread_;
// Key event input queue
pthread_mutex_t key_queue_mutex;
pthread_cond_t key_queue_cond;
int key_queue[256], key_queue_len;
char key_pressed[KEY_MAX + 1]; // under key_queue_mutex
int key_last_down; // under key_queue_mutex
bool key_long_press; // under key_queue_mutex
int key_down_count; // under key_queue_mutex
bool enable_reboot; // under key_queue_mutex
int rel_sum;
void OnKeyDetected(int key_code);
int OnInputEvent(int fd, uint32_t epevents);
void ProcessKey(int key_code, int updown);
int consecutive_power_keys;
int last_key;
bool IsUsbConnected();
bool has_power_key;
bool has_up_key;
bool has_down_key;
bool has_touch_screen;
static void* time_key_helper(void* cookie);
void time_key(int key_code, int count);
// Touch event related variables. See the comments in RecoveryUI::OnInputEvent().
int touch_slot_;
int touch_X_;
int touch_Y_;
int touch_start_X_;
int touch_start_Y_;
bool touch_finger_down_;
bool touch_swiping_;
bool is_bootreason_recovery_ui_;
void SetLocale(const std::string&);
struct key_timer_t {
RecoveryUI* ui;
int key_code;
int count;
};
enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF };
ScreensaverState screensaver_state_;
// The following two contain the absolute values computed from brightness_normal_ and
// brightness_dimmed_ respectively.
unsigned int brightness_normal_value_;
unsigned int brightness_dimmed_value_;
bool InitScreensaver();
pthread_t input_thread_;
void OnKeyDetected(int key_code);
void OnTouchDetected(int dx, int dy);
int OnInputEvent(int fd, uint32_t epevents);
void ProcessKey(int key_code, int updown);
bool IsUsbConnected();
static void* time_key_helper(void* cookie);
void time_key(int key_code, int count);
void SetLocale(const std::string&);
enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF };
ScreensaverState screensaver_state_;
// The following two contain the absolute values computed from brightness_normal_ and
// brightness_dimmed_ respectively.
unsigned int brightness_normal_value_;
unsigned int brightness_dimmed_value_;
bool InitScreensaver();
};
#endif // RECOVERY_UI_H
+38 -8
View File
@@ -14,12 +14,47 @@
LOCAL_PATH := $(call my-dir)
# libupdate_verifier (static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_SRC_FILES := update_verifier.cpp
LOCAL_SRC_FILES := \
update_verifier.cpp
LOCAL_MODULE := libupdate_verifier
LOCAL_SHARED_LIBRARIES := \
libbase \
libcutils \
android.hardware.boot@1.0
LOCAL_CFLAGS := -Wall -Werror
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH)/include
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include
ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true)
LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1
endif
ifeq ($(BOARD_AVB_ENABLE),true)
LOCAL_CFLAGS += -DBOARD_AVB_ENABLE=1
endif
include $(BUILD_STATIC_LIBRARY)
# update_verifier (executable)
# ===============================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
update_verifier_main.cpp
LOCAL_MODULE := update_verifier
LOCAL_STATIC_LIBRARIES := \
libupdate_verifier
LOCAL_SHARED_LIBRARIES := \
libbase \
libcutils \
@@ -29,13 +64,8 @@ LOCAL_SHARED_LIBRARIES := \
libhidlbase \
android.hardware.boot@1.0
LOCAL_CFLAGS := -Werror
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
LOCAL_CFLAGS := -Wall -Werror
LOCAL_INIT_RC := update_verifier.rc
ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true)
LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1
endif
include $(BUILD_EXECUTABLE)
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <string>
int update_verifier(int argc, char** argv);
// Exposed for testing purpose.
bool verify_image(const std::string& care_map_name);
+139 -76
View File
@@ -35,6 +35,8 @@
* verifier reaches the end after the verification.
*/
#include "update_verifier/update_verifier.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -42,6 +44,8 @@
#include <string.h>
#include <unistd.h>
#include <algorithm>
#include <future>
#include <string>
#include <vector>
@@ -59,12 +63,6 @@ using android::hardware::boot::V1_0::IBootControl;
using android::hardware::boot::V1_0::BoolResult;
using android::hardware::boot::V1_0::CommandResult;
constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt";
constexpr auto DM_PATH_PREFIX = "/sys/block/";
constexpr auto DM_PATH_SUFFIX = "/dm/name";
constexpr auto DEV_PATH = "/dev/block/";
constexpr int BLOCKSIZE = 4096;
// Find directories in format of "/sys/block/dm-X".
static int dm_name_filter(const dirent* de) {
if (android::base::StartsWith(de->d_name, "dm-")) {
@@ -82,6 +80,7 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
// (or "vendor"), then dm-X is a dm-wrapped system/vendor partition.
// Afterwards, update_verifier will read every block on the care_map_file of
// "/dev/block/dm-X" to ensure the partition's integrity.
static constexpr auto DM_PATH_PREFIX = "/sys/block/";
dirent** namelist;
int n = scandir(DM_PATH_PREFIX, &namelist, dm_name_filter, alphasort);
if (n == -1) {
@@ -93,18 +92,29 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
return false;
}
static constexpr auto DM_PATH_SUFFIX = "/dm/name";
static constexpr auto DEV_PATH = "/dev/block/";
std::string dm_block_device;
while (n--) {
std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX;
std::string content;
if (!android::base::ReadFileToString(path, &content)) {
PLOG(WARNING) << "Failed to read " << path;
} else if (android::base::Trim(content) == partition) {
dm_block_device = DEV_PATH + std::string(namelist[n]->d_name);
while (n--) {
free(namelist[n]);
} else {
std::string dm_block_name = android::base::Trim(content);
#ifdef BOARD_AVB_ENABLE
// AVB is using 'vroot' for the root block device but we're expecting 'system'.
if (dm_block_name == "vroot") {
dm_block_name = "system";
}
#endif
if (dm_block_name == partition) {
dm_block_device = DEV_PATH + std::string(namelist[n]->d_name);
while (n--) {
free(namelist[n]);
}
break;
}
break;
}
free(namelist[n]);
}
@@ -114,11 +124,6 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
LOG(ERROR) << "Failed to find dm block device for " << partition;
return false;
}
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
if (fd.get() == -1) {
PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition;
return false;
}
// For block range string, first integer 'count' equals 2 * total number of valid ranges,
// followed by 'count' number comma separated integers. Every two integers reprensent a
@@ -132,69 +137,113 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
LOG(ERROR) << "Error in parsing range string.";
return false;
}
range_count /= 2;
size_t blk_count = 0;
for (size_t i = 1; i < ranges.size(); i += 2) {
unsigned int range_start, range_end;
bool parse_status = android::base::ParseUint(ranges[i], &range_start);
parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end);
if (!parse_status || range_start >= range_end) {
LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1];
return false;
}
std::vector<std::future<bool>> threads;
size_t thread_num = std::thread::hardware_concurrency() ?: 4;
thread_num = std::min(thread_num, range_count);
size_t group_range_count = (range_count + thread_num - 1) / thread_num;
if (lseek64(fd.get(), static_cast<off64_t>(range_start) * BLOCKSIZE, SEEK_SET) == -1) {
PLOG(ERROR) << "lseek to " << range_start << " failed";
return false;
}
for (size_t t = 0; t < thread_num; t++) {
auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() {
size_t blk_count = 0;
static constexpr size_t kBlockSize = 4096;
std::vector<uint8_t> buf(1024 * kBlockSize);
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
if (fd.get() == -1) {
PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition;
return false;
}
size_t size = (range_end - range_start) * BLOCKSIZE;
std::vector<uint8_t> buf(size);
if (!android::base::ReadFully(fd.get(), buf.data(), size)) {
PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end;
return false;
}
blk_count += (range_end - range_start);
for (size_t i = group_range_count * 2 * t + 1;
i < std::min(group_range_count * 2 * (t + 1) + 1, ranges.size()); i += 2) {
unsigned int range_start, range_end;
bool parse_status = android::base::ParseUint(ranges[i], &range_start);
parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end);
if (!parse_status || range_start >= range_end) {
LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1];
return false;
}
if (lseek64(fd.get(), static_cast<off64_t>(range_start) * kBlockSize, SEEK_SET) == -1) {
PLOG(ERROR) << "lseek to " << range_start << " failed";
return false;
}
size_t remain = (range_end - range_start) * kBlockSize;
while (remain > 0) {
size_t to_read = std::min(remain, 1024 * kBlockSize);
if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) {
PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end;
return false;
}
remain -= to_read;
}
blk_count += (range_end - range_start);
}
LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device;
return true;
};
threads.emplace_back(std::async(std::launch::async, thread_func));
}
LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device;
return true;
bool ret = true;
for (auto& t : threads) {
ret = t.get() && ret;
}
LOG(INFO) << "Finished reading blocks on " << dm_block_device << " with " << thread_num
<< " threads.";
return ret;
}
static bool verify_image(const std::string& care_map_name) {
android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
// If the device is flashed before the current boot, it may not have care_map.txt
// in /data/ota_package. To allow the device to continue booting in this situation,
// we should print a warning and skip the block verification.
if (care_map_fd.get() == -1) {
PLOG(WARNING) << "Failed to open " << care_map_name;
return true;
}
// Care map file has four lines (two lines if vendor partition is not present):
// First line has the block partition name (system/vendor).
// Second line holds all ranges of blocks to verify.
// The next two lines have the same format but for vendor partition.
std::string file_content;
if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) {
LOG(ERROR) << "Error reading care map contents to string.";
return false;
}
std::vector<std::string> lines;
lines = android::base::Split(android::base::Trim(file_content), "\n");
if (lines.size() != 2 && lines.size() != 4) {
LOG(ERROR) << "Invalid lines in care_map: found " << lines.size()
<< " lines, expecting 2 or 4 lines.";
return false;
}
for (size_t i = 0; i < lines.size(); i += 2) {
if (!read_blocks(lines[i], lines[i+1])) {
return false;
}
}
// Returns true to indicate a passing verification (or the error should be ignored); Otherwise
// returns false on fatal errors, where we should reject the current boot and trigger a fallback.
// Note that update_verifier should be backward compatible to not reject care_map.txt from old
// releases, which could otherwise fail to boot into the new release. For example, we've changed
// the care_map format between N and O. An O update_verifier would fail to work with N
// care_map.txt. This could be a result of sideloading an O OTA while the device having a pending N
// update.
bool verify_image(const std::string& care_map_name) {
android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
// If the device is flashed before the current boot, it may not have care_map.txt
// in /data/ota_package. To allow the device to continue booting in this situation,
// we should print a warning and skip the block verification.
if (care_map_fd.get() == -1) {
PLOG(WARNING) << "Failed to open " << care_map_name;
return true;
}
// Care map file has four lines (two lines if vendor partition is not present):
// First line has the block partition name (system/vendor).
// Second line holds all ranges of blocks to verify.
// The next two lines have the same format but for vendor partition.
std::string file_content;
if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) {
LOG(ERROR) << "Error reading care map contents to string.";
return false;
}
std::vector<std::string> lines;
lines = android::base::Split(android::base::Trim(file_content), "\n");
if (lines.size() != 2 && lines.size() != 4) {
LOG(ERROR) << "Invalid lines in care_map: found " << lines.size()
<< " lines, expecting 2 or 4 lines.";
return false;
}
for (size_t i = 0; i < lines.size(); i += 2) {
// We're seeing an N care_map.txt. Skip the verification since it's not compatible with O
// update_verifier (the last few metadata blocks can't be read via device mapper).
if (android::base::StartsWith(lines[i], "/dev/block/")) {
LOG(WARNING) << "Found legacy care_map.txt; skipped.";
return true;
}
if (!read_blocks(lines[i], lines[i+1])) {
return false;
}
}
return true;
}
static int reboot_device() {
@@ -205,7 +254,7 @@ static int reboot_device() {
while (true) pause();
}
int main(int argc, char** argv) {
int update_verifier(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
LOG(INFO) << "Started with arg " << i << ": " << argv[i];
}
@@ -224,23 +273,37 @@ int main(int argc, char** argv) {
if (is_successful == BoolResult::FALSE) {
// The current slot has not booted successfully.
#ifdef PRODUCT_SUPPORTS_VERITY
#if defined(PRODUCT_SUPPORTS_VERITY) || defined(BOARD_AVB_ENABLE)
bool skip_verification = false;
std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", "");
if (verity_mode.empty()) {
// With AVB it's possible to disable verification entirely and
// in this case ro.boot.veritymode is empty.
#if defined(BOARD_AVB_ENABLE)
LOG(WARNING) << "verification has been disabled; marking without verification.";
skip_verification = true;
#else
LOG(ERROR) << "Failed to get dm-verity mode.";
return reboot_device();
#endif
} else if (android::base::EqualsIgnoreCase(verity_mode, "eio")) {
// We shouldn't see verity in EIO mode if the current slot hasn't booted successfully before.
// Continue the verification until we fail to read some blocks.
LOG(WARNING) << "Found dm-verity in EIO mode.";
} else if (android::base::EqualsIgnoreCase(verity_mode, "disabled")) {
LOG(WARNING) << "dm-verity in disabled mode; marking without verification.";
skip_verification = true;
} else if (verity_mode != "enforcing") {
LOG(ERROR) << "Unexpected dm-verity mode : " << verity_mode << ", expecting enforcing.";
return reboot_device();
}
if (!verify_image(CARE_MAP_FILE)) {
LOG(ERROR) << "Failed to verify all blocks in care map file.";
return reboot_device();
if (!skip_verification) {
static constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt";
if (!verify_image(CARE_MAP_FILE)) {
LOG(ERROR) << "Failed to verify all blocks in care map file.";
return reboot_device();
}
}
#else
LOG(WARNING) << "dm-verity not enabled; marking without verification.";
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// See the comments in update_verifier.cpp.
#include "update_verifier/update_verifier.h"
int main(int argc, char** argv) {
return update_verifier(argc, argv);
}
+1
View File
@@ -52,6 +52,7 @@ updater_common_static_libraries := \
libcrypto_utils \
libcutils \
libtune2fs \
libbrotli \
$(tune2fs_static_libraries)
# libupdater (static library)
+848 -859
View File
File diff suppressed because it is too large Load Diff
+164
View File
@@ -0,0 +1,164 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <stddef.h>
#include <string>
#include <utility>
#include <vector>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
using Range = std::pair<size_t, size_t>;
class RangeSet {
public:
RangeSet() : blocks_(0) {}
explicit RangeSet(std::vector<Range>&& pairs) {
CHECK_NE(pairs.size(), static_cast<size_t>(0)) << "Invalid number of tokens";
// Sanity check the input.
size_t result = 0;
for (const auto& range : pairs) {
CHECK_LT(range.first, range.second)
<< "Empty or negative range: " << range.first << ", " << range.second;
size_t sz = range.second - range.first;
CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow";
result += sz;
}
ranges_ = pairs;
blocks_ = result;
}
static RangeSet Parse(const std::string& range_text) {
std::vector<std::string> pieces = android::base::Split(range_text, ",");
CHECK_GE(pieces.size(), static_cast<size_t>(3)) << "Invalid range text: " << range_text;
size_t num;
CHECK(android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX)))
<< "Failed to parse the number of tokens: " << range_text;
CHECK_NE(num, static_cast<size_t>(0)) << "Invalid number of tokens: " << range_text;
CHECK_EQ(num % 2, static_cast<size_t>(0)) << "Number of tokens must be even: " << range_text;
CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text;
std::vector<Range> pairs;
for (size_t i = 0; i < num; i += 2) {
size_t first;
CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast<size_t>(INT_MAX)));
size_t second;
CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast<size_t>(INT_MAX)));
pairs.emplace_back(first, second);
}
return RangeSet(std::move(pairs));
}
// Get the block number for the i-th (starting from 0) block in the RangeSet.
size_t GetBlockNumber(size_t idx) const {
CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")";
for (const auto& range : ranges_) {
if (idx < range.second - range.first) {
return range.first + idx;
}
idx -= (range.second - range.first);
}
CHECK(false) << "Failed to find block number for index " << idx;
return 0; // Unreachable, but to make compiler happy.
}
// RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5"
// and "5,7" are not overlapped.
bool Overlaps(const RangeSet& other) const {
for (const auto& range : ranges_) {
size_t start = range.first;
size_t end = range.second;
for (const auto& other_range : other.ranges_) {
size_t other_start = other_range.first;
size_t other_end = other_range.second;
// [start, end) vs [other_start, other_end)
if (!(other_start >= end || start >= other_end)) {
return true;
}
}
}
return false;
}
// size() gives the number of Range's in this RangeSet.
size_t size() const {
return ranges_.size();
}
// blocks() gives the number of all blocks in this RangeSet.
size_t blocks() const {
return blocks_;
}
// We provide const iterators only.
std::vector<Range>::const_iterator cbegin() const {
return ranges_.cbegin();
}
std::vector<Range>::const_iterator cend() const {
return ranges_.cend();
}
// Need to provide begin()/end() since range-based loop expects begin()/end().
std::vector<Range>::const_iterator begin() const {
return ranges_.cbegin();
}
std::vector<Range>::const_iterator end() const {
return ranges_.cend();
}
// Reverse const iterators for MoveRange().
std::vector<Range>::const_reverse_iterator crbegin() const {
return ranges_.crbegin();
}
std::vector<Range>::const_reverse_iterator crend() const {
return ranges_.crend();
}
const Range& operator[](size_t i) const {
return ranges_[i];
}
bool operator==(const RangeSet& other) const {
// The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5".
return (ranges_ == other.ranges_);
}
bool operator!=(const RangeSet& other) const {
return ranges_ != other.ranges_;
}
private:
// Actual limit for each value and the total number are both INT_MAX.
std::vector<Range> ranges_;
size_t blocks_;
};
+37 -45
View File
@@ -74,7 +74,6 @@
#endif
#include "otautil/DirUtil.h"
#include "otautil/ZipUtil.h"
#include "print_sha1.h"
#include "tune2fs.h"
#include "updater/updater.h"
@@ -194,8 +193,8 @@ Value* MountFn(const char* name, State* state, const std::vector<std::unique_ptr
if (mount(location.c_str(), mount_point.c_str(), fs_type.c_str(),
MS_NOATIME | MS_NODEV | MS_NODIRATIME, mount_options.c_str()) < 0) {
uiPrintf(state, "%s: failed to mount %s at %s: %s\n", name, location.c_str(),
mount_point.c_str(), strerror(errno));
uiPrintf(state, "%s: Failed to mount %s at %s: %s", name, location.c_str(), mount_point.c_str(),
strerror(errno));
return StringValue("");
}
@@ -244,12 +243,12 @@ Value* UnmountFn(const char* name, State* state, const std::vector<std::unique_p
scan_mounted_volumes();
MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str());
if (vol == nullptr) {
uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point.c_str());
uiPrintf(state, "Failed to unmount %s: No such volume", mount_point.c_str());
return nullptr;
} else {
int ret = unmount_mounted_volume(vol);
if (ret != 0) {
uiPrintf(state, "unmount of %s failed (%d): %s\n", mount_point.c_str(), ret, strerror(errno));
uiPrintf(state, "Failed to unmount %s: %s", mount_point.c_str(), strerror(errno));
}
}
@@ -316,9 +315,31 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
}
if (fs_type == "ext4") {
int status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle);
const char* mke2fs_argv[] = { "/sbin/mke2fs_static", "-t", "ext4", "-b", "4096",
location.c_str(), nullptr, nullptr };
std::string size_str;
if (size != 0) {
size_str = std::to_string(size / 4096LL);
mke2fs_argv[6] = size_str.c_str();
}
int status = exec_cmd(mke2fs_argv[0], const_cast<char**>(mke2fs_argv));
if (status != 0) {
LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location;
LOG(WARNING) << name << ": mke2fs failed (" << status << ") on " << location
<< ", falling back to make_ext4fs";
status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle);
if (status != 0) {
LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location;
return StringValue("");
}
return StringValue(location);
}
const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-a", mount_point.c_str(),
location.c_str(), nullptr };
status = exec_cmd(e2fsdroid_argv[0], const_cast<char**>(e2fsdroid_argv));
if (status != 0) {
LOG(ERROR) << name << ": e2fsdroid failed (" << status << ") on " << location;
return StringValue("");
}
return StringValue(location);
@@ -330,9 +351,11 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
std::string num_sectors = std::to_string(size / 512);
const char* f2fs_path = "/sbin/mkfs.f2fs";
const char* const f2fs_argv[] = { "mkfs.f2fs", "-t", "-d1", location.c_str(),
num_sectors.c_str(), nullptr };
int status = exec_cmd(f2fs_path, const_cast<char* const*>(f2fs_argv));
const char* f2fs_argv[] = {
"mkfs.f2fs", "-t", "-d1", location.c_str(), (size < 512) ? nullptr : num_sectors.c_str(),
nullptr
};
int status = exec_cmd(f2fs_path, const_cast<char**>(f2fs_argv));
if (status != 0) {
LOG(ERROR) << name << ": mkfs.f2fs failed (" << status << ") on " << location;
return StringValue("");
@@ -400,36 +423,6 @@ Value* SetProgressFn(const char* name, State* state, const std::vector<std::uniq
return StringValue(frac_str);
}
// package_extract_dir(package_dir, dest_dir)
// Extracts all files from the package underneath package_dir and writes them to the
// corresponding tree beneath dest_dir. Any existing files are overwritten.
// Example: package_extract_dir("system", "/system")
//
// Note: package_dir needs to be a relative path; dest_dir needs to be an absolute path.
Value* PackageExtractDirFn(const char* name, State* state,
const std::vector<std::unique_ptr<Expr>>&argv) {
if (argv.size() != 2) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
argv.size());
}
std::vector<std::string> args;
if (!ReadArgs(state, argv, &args)) {
return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
const std::string& zip_path = args[0];
const std::string& dest_path = args[1];
ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
// To create a consistent system image, never use the clock for timestamps.
constexpr struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
bool success = ExtractPackageRecursive(za, zip_path, dest_path, &timestamp, sehandle);
return StringValue(success ? "t" : "");
}
// package_extract_file(package_file[, dest_file])
// Extracts a single package_file from the update package and writes it to dest_file,
// overwriting existing files if necessary. Without the dest_file argument, returns the
@@ -712,15 +705,15 @@ Value* ApplyPatchCheckFn(const char* name, State* state, const std::vector<std::
return StringValue(result == 0 ? "t" : "");
}
// This is the updater side handler for ui_print() in edify script. Contents
// will be sent over to the recovery side for on-screen display.
// This is the updater side handler for ui_print() in edify script. Contents will be sent over to
// the recovery side for on-screen display.
Value* UIPrintFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
std::vector<std::string> args;
if (!ReadArgs(state, argv, &args)) {
return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
}
std::string buffer = android::base::Join(args, "") + "\n";
std::string buffer = android::base::Join(args, "");
uiPrint(state, buffer);
return StringValue(buffer);
}
@@ -1052,7 +1045,6 @@ void RegisterInstallFunctions() {
RegisterFunction("format", FormatFn);
RegisterFunction("show_progress", ShowProgressFn);
RegisterFunction("set_progress", SetProgressFn);
RegisterFunction("package_extract_dir", PackageExtractDirFn);
RegisterFunction("package_extract_file", PackageExtractFileFn);
RegisterFunction("getprop", GetPropFn);
+13 -8
View File
@@ -94,7 +94,7 @@ int main(int argc, char** argv) {
const char* package_filename = argv[3];
MemMapping map;
if (sysMapFile(package_filename, &map) != 0) {
if (!map.MapFile(package_filename)) {
LOG(ERROR) << "failed to map package " << argv[3];
return 3;
}
@@ -211,12 +211,18 @@ int main(int argc, char** argv) {
}
}
if (state.error_code != kNoError) {
fprintf(cmd_pipe, "log error: %d\n", state.error_code);
// Cause code should provide additional information about the abort;
// report only when an error exists.
if (state.cause_code != kNoCause) {
fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
// Installation has been aborted. Set the error code to kScriptExecutionFailure unless
// a more specific code has been set in errmsg.
if (state.error_code == kNoError) {
state.error_code = kScriptExecutionFailure;
}
fprintf(cmd_pipe, "log error: %d\n", state.error_code);
// Cause code should provide additional information about the abort.
if (state.cause_code != kNoCause) {
fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
if (state.cause_code == kPatchApplicationFailure) {
LOG(INFO) << "Patch application failed, retry update.";
fprintf(cmd_pipe, "retry_update\n");
}
}
@@ -231,7 +237,6 @@ int main(int argc, char** argv) {
if (updater_info.package_zip) {
CloseArchive(updater_info.package_zip);
}
sysReleaseMap(&map);
return 0;
}
+72 -73
View File
@@ -474,81 +474,80 @@ std::unique_ptr<EC_KEY, ECKEYDeleter> parse_ec_key(FILE* file) {
// Otherwise returns false if the file failed to parse, or if it contains zero
// keys. The contents in certs would be unspecified on failure.
bool load_keys(const char* filename, std::vector<Certificate>& certs) {
std::unique_ptr<FILE, decltype(&fclose)> f(fopen(filename, "r"), fclose);
if (!f) {
PLOG(ERROR) << "error opening " << filename;
std::unique_ptr<FILE, decltype(&fclose)> f(fopen(filename, "re"), fclose);
if (!f) {
PLOG(ERROR) << "error opening " << filename;
return false;
}
while (true) {
certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
Certificate& cert = certs.back();
uint32_t exponent = 0;
char start_char;
if (fscanf(f.get(), " %c", &start_char) != 1) return false;
if (start_char == '{') {
// a version 1 key has no version specifier.
cert.key_type = Certificate::KEY_TYPE_RSA;
exponent = 3;
cert.hash_len = SHA_DIGEST_LENGTH;
} else if (start_char == 'v') {
int version;
if (fscanf(f.get(), "%d {", &version) != 1) return false;
switch (version) {
case 2:
cert.key_type = Certificate::KEY_TYPE_RSA;
exponent = 65537;
cert.hash_len = SHA_DIGEST_LENGTH;
break;
case 3:
cert.key_type = Certificate::KEY_TYPE_RSA;
exponent = 3;
cert.hash_len = SHA256_DIGEST_LENGTH;
break;
case 4:
cert.key_type = Certificate::KEY_TYPE_RSA;
exponent = 65537;
cert.hash_len = SHA256_DIGEST_LENGTH;
break;
case 5:
cert.key_type = Certificate::KEY_TYPE_EC;
cert.hash_len = SHA256_DIGEST_LENGTH;
break;
default:
return false;
}
}
if (cert.key_type == Certificate::KEY_TYPE_RSA) {
cert.rsa = parse_rsa_key(f.get(), exponent);
if (!cert.rsa) {
return false;
}
LOG(INFO) << "read key e=" << exponent << " hash=" << cert.hash_len;
} else if (cert.key_type == Certificate::KEY_TYPE_EC) {
cert.ec = parse_ec_key(f.get());
if (!cert.ec) {
return false;
}
} else {
LOG(ERROR) << "Unknown key type " << cert.key_type;
return false;
}
while (true) {
certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
Certificate& cert = certs.back();
uint32_t exponent = 0;
char start_char;
if (fscanf(f.get(), " %c", &start_char) != 1) return false;
if (start_char == '{') {
// a version 1 key has no version specifier.
cert.key_type = Certificate::KEY_TYPE_RSA;
exponent = 3;
cert.hash_len = SHA_DIGEST_LENGTH;
} else if (start_char == 'v') {
int version;
if (fscanf(f.get(), "%d {", &version) != 1) return false;
switch (version) {
case 2:
cert.key_type = Certificate::KEY_TYPE_RSA;
exponent = 65537;
cert.hash_len = SHA_DIGEST_LENGTH;
break;
case 3:
cert.key_type = Certificate::KEY_TYPE_RSA;
exponent = 3;
cert.hash_len = SHA256_DIGEST_LENGTH;
break;
case 4:
cert.key_type = Certificate::KEY_TYPE_RSA;
exponent = 65537;
cert.hash_len = SHA256_DIGEST_LENGTH;
break;
case 5:
cert.key_type = Certificate::KEY_TYPE_EC;
cert.hash_len = SHA256_DIGEST_LENGTH;
break;
default:
return false;
}
}
if (cert.key_type == Certificate::KEY_TYPE_RSA) {
cert.rsa = parse_rsa_key(f.get(), exponent);
if (!cert.rsa) {
return false;
}
LOG(INFO) << "read key e=" << exponent << " hash=" << cert.hash_len;
} else if (cert.key_type == Certificate::KEY_TYPE_EC) {
cert.ec = parse_ec_key(f.get());
if (!cert.ec) {
return false;
}
} else {
LOG(ERROR) << "Unknown key type " << cert.key_type;
return false;
}
// if the line ends in a comma, this file has more keys.
int ch = fgetc(f.get());
if (ch == ',') {
// more keys to come.
continue;
} else if (ch == EOF) {
break;
} else {
LOG(ERROR) << "unexpected character between keys";
return false;
}
// if the line ends in a comma, this file has more keys.
int ch = fgetc(f.get());
if (ch == ',') {
// more keys to come.
continue;
} else if (ch == EOF) {
break;
} else {
LOG(ERROR) << "unexpected character between keys";
return false;
}
return true;
}
return true;
}
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "device.h"
#include "vr_ui.h"
Device* make_device() {
return new Device(new VrRecoveryUI);
}
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "vr_ui.h"
#include <minui/minui.h>
VrRecoveryUI::VrRecoveryUI() : kStereoOffset(RECOVERY_UI_VR_STEREO_OFFSET) {}
bool VrRecoveryUI::InitTextParams() {
if (!ScreenRecoveryUI::InitTextParams()) return false;
int mid_divide = gr_fb_width() / 2;
text_cols_ = (mid_divide - kMarginWidth - kStereoOffset) / char_width_;
return true;
}
int VrRecoveryUI::DrawTextLine(int x, int y, const char* line, bool bold) const {
int mid_divide = gr_fb_width() / 2;
gr_text(gr_sys_font(), x + kStereoOffset, y, line, bold);
gr_text(gr_sys_font(), x - kStereoOffset + mid_divide, y, line, bold);
return char_height_ + 4;
}
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RECOVERY_VR_UI_H
#define RECOVERY_VR_UI_H
#include "screen_ui.h"
class VrRecoveryUI : public ScreenRecoveryUI {
public:
VrRecoveryUI();
protected:
// Pixel offsets to move drawing functions to visible range.
// Can vary per device depending on screen size and lens distortion.
const int kStereoOffset;
bool InitTextParams() override;
int DrawTextLine(int x, int y, const char* line, bool bold) const override;
};
#endif // RECOVERY_VR_UI_H
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "device.h"
#include "wear_ui.h"
Device* make_device() {
return new Device(new WearRecoveryUI);
}
-177
View File
@@ -1,177 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <android-base/logging.h>
#include <linux/input.h>
#include "wear_touch.h"
#define DEVICE_PATH "/dev/input"
WearSwipeDetector::WearSwipeDetector(int low, int high, OnSwipeCallback callback, void* cookie):
mLowThreshold(low),
mHighThreshold(high),
mCallback(callback),
mCookie(cookie),
mCurrentSlot(-1) {
pthread_create(&mThread, NULL, touch_thread, this);
}
WearSwipeDetector::~WearSwipeDetector() {
}
void WearSwipeDetector::detect(int dx, int dy) {
enum SwipeDirection direction;
if (abs(dy) < mLowThreshold && abs(dx) > mHighThreshold) {
direction = dx < 0 ? LEFT : RIGHT;
} else if (abs(dx) < mLowThreshold && abs(dy) > mHighThreshold) {
direction = dy < 0 ? UP : DOWN;
} else {
LOG(DEBUG) << "Ignore " << dx << " " << dy;
return;
}
LOG(DEBUG) << "Swipe direction=" << direction;
mCallback(mCookie, direction);
}
void WearSwipeDetector::process(struct input_event *event) {
if (mCurrentSlot < 0) {
mCallback(mCookie, UP);
mCurrentSlot = 0;
}
if (event->type == EV_ABS) {
if (event->code == ABS_MT_SLOT)
mCurrentSlot = event->value;
// Ignore other fingers
if (mCurrentSlot > 0) {
return;
}
switch (event->code) {
case ABS_MT_POSITION_X:
mX = event->value;
mFingerDown = true;
break;
case ABS_MT_POSITION_Y:
mY = event->value;
mFingerDown = true;
break;
case ABS_MT_TRACKING_ID:
if (event->value < 0)
mFingerDown = false;
break;
}
} else if (event->type == EV_SYN) {
if (event->code == SYN_REPORT) {
if (mFingerDown && !mSwiping) {
mStartX = mX;
mStartY = mY;
mSwiping = true;
} else if (!mFingerDown && mSwiping) {
mSwiping = false;
detect(mX - mStartX, mY - mStartY);
}
}
}
}
void WearSwipeDetector::run() {
int fd = findDevice(DEVICE_PATH);
if (fd < 0) {
LOG(ERROR) << "no input devices found";
return;
}
struct input_event event;
while (read(fd, &event, sizeof(event)) == sizeof(event)) {
process(&event);
}
close(fd);
}
void* WearSwipeDetector::touch_thread(void* cookie) {
(static_cast<WearSwipeDetector*>(cookie))->run();
return NULL;
}
#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
int WearSwipeDetector::openDevice(const char *device) {
int fd = open(device, O_RDONLY);
if (fd < 0) {
PLOG(ERROR) << "could not open " << device;
return false;
}
char name[80];
name[sizeof(name) - 1] = '\0';
if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
PLOG(ERROR) << "could not get device name for " << device;
name[0] = '\0';
}
uint8_t bits[512];
memset(bits, 0, sizeof(bits));
int ret = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits);
if (ret > 0) {
if (test_bit(ABS_MT_POSITION_X, bits) && test_bit(ABS_MT_POSITION_Y, bits)) {
LOG(DEBUG) << "Found " << device << " " << name;
return fd;
}
}
close(fd);
return -1;
}
int WearSwipeDetector::findDevice(const char* path) {
DIR* dir = opendir(path);
if (dir == NULL) {
PLOG(ERROR) << "Could not open directory " << path;
return false;
}
struct dirent* entry;
int ret = -1;
while (ret < 0 && (entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') continue;
char device[PATH_MAX];
device[PATH_MAX-1] = '\0';
snprintf(device, PATH_MAX-1, "%s/%s", path, entry->d_name);
ret = openDevice(device);
}
closedir(dir);
return ret;
}
-58
View File
@@ -1,58 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __WEAR_TOUCH_H
#define __WEAR_TOUCH_H
#include <pthread.h>
class WearSwipeDetector {
public:
enum SwipeDirection { UP, DOWN, RIGHT, LEFT };
typedef void (*OnSwipeCallback)(void* cookie, enum SwipeDirection direction);
WearSwipeDetector(int low, int high, OnSwipeCallback cb, void* cookie);
~WearSwipeDetector();
private:
void run();
void process(struct input_event *event);
void detect(int dx, int dy);
pthread_t mThread;
static void* touch_thread(void* cookie);
int findDevice(const char* path);
int openDevice(const char* device);
int mLowThreshold;
int mHighThreshold;
OnSwipeCallback mCallback;
void *mCookie;
int mX;
int mY;
int mStartX;
int mStartY;
int mCurrentSlot;
bool mFingerDown;
bool mSwiping;
};
#endif // __WEAR_TOUCH_H
+241 -256
View File
@@ -45,167 +45,164 @@ static WearRecoveryUI* self = NULL;
// Return the current time as a double (including fractions of a second).
static double now() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
WearRecoveryUI::WearRecoveryUI() :
progress_bar_y(259),
outer_height(0),
outer_width(0),
menu_unusable_rows(0) {
intro_frames = 22;
loop_frames = 60;
animation_fps = 30;
WearRecoveryUI::WearRecoveryUI()
: kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE),
kMenuUnusableRows(RECOVERY_UI_MENU_UNUSABLE_ROWS) {
// TODO: kMenuUnusableRows should be computed based on the lines in draw_screen_locked().
for (size_t i = 0; i < 5; i++)
backgroundIcon[i] = NULL;
// TODO: The following three variables are likely not needed. The first two are detected
// automatically in ScreenRecoveryUI::LoadAnimation(), based on the actual files seen on device.
intro_frames = 22;
loop_frames = 60;
self = this;
touch_screen_allowed_ = true;
for (size_t i = 0; i < 5; i++) backgroundIcon[i] = NULL;
self = this;
}
int WearRecoveryUI::GetProgressBaseline() {
return progress_bar_y;
int WearRecoveryUI::GetProgressBaseline() const {
return kProgressBarBaseline;
}
// Draw background frame on the screen. Does not flip pages.
// Should only be called with updateMutex locked.
// TODO merge drawing routines with screen_ui
void WearRecoveryUI::draw_background_locked()
{
pagesIdentical = false;
gr_color(0, 0, 0, 255);
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
void WearRecoveryUI::draw_background_locked() {
pagesIdentical = false;
gr_color(0, 0, 0, 255);
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
if (currentIcon != NONE) {
GRSurface* surface;
if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
if (!intro_done) {
surface = introFrames[current_frame];
} else {
surface = loopFrames[current_frame];
}
}
else {
surface = backgroundIcon[currentIcon];
}
int width = gr_get_width(surface);
int height = gr_get_height(surface);
int x = (gr_fb_width() - width) / 2;
int y = (gr_fb_height() - height) / 2;
gr_blit(surface, 0, 0, width, height, x, y);
if (currentIcon != NONE) {
GRSurface* surface;
if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
if (!intro_done) {
surface = introFrames[current_frame];
} else {
surface = loopFrames[current_frame];
}
} else {
surface = backgroundIcon[currentIcon];
}
int width = gr_get_width(surface);
int height = gr_get_height(surface);
int x = (gr_fb_width() - width) / 2;
int y = (gr_fb_height() - height) / 2;
gr_blit(surface, 0, 0, width, height, x, y);
}
}
static const char* HEADERS[] = {
"Swipe up/down to move.",
"Swipe left/right to select.",
"",
NULL
static const char* SWIPE_HELP[] = {
"Swipe up/down to move.",
"Swipe left/right to select.",
"",
NULL
};
// TODO merge drawing routines with screen_ui
void WearRecoveryUI::draw_screen_locked()
{
char cur_selection_str[50];
void WearRecoveryUI::draw_screen_locked() {
char cur_selection_str[50];
draw_background_locked();
if (!show_text) {
draw_foreground_locked();
} else {
SetColor(TEXT_FILL);
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
draw_background_locked();
if (!show_text) {
draw_foreground_locked();
} else {
SetColor(TEXT_FILL);
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
int y = outer_height;
int x = outer_width;
if (show_menu) {
std::string recovery_fingerprint =
android::base::GetProperty("ro.bootimage.build.fingerprint", "");
SetColor(HEADER);
DrawTextLine(x + 4, &y, "Android Recovery", true);
for (auto& chunk: android::base::Split(recovery_fingerprint, ":")) {
DrawTextLine(x +4, &y, chunk.c_str(), false);
}
int y = kMarginHeight;
int x = kMarginWidth;
if (show_menu) {
std::string recovery_fingerprint =
android::base::GetProperty("ro.bootimage.build.fingerprint", "");
SetColor(HEADER);
y += DrawTextLine(x + 4, y, "Android Recovery", true);
for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
y += DrawTextLine(x + 4, y, chunk.c_str(), false);
}
// This is actually the help strings.
DrawTextLines(x + 4, &y, HEADERS);
SetColor(HEADER);
DrawTextLines(x + 4, &y, menu_headers_);
// This is actually the help strings.
y += DrawTextLines(x + 4, y, SWIPE_HELP);
SetColor(HEADER);
y += DrawTextLines(x + 4, y, menu_headers_);
// Show the current menu item number in relation to total number if
// items don't fit on the screen.
if (menu_items > menu_end - menu_start) {
sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items);
gr_text(gr_sys_font(), x+4, y, cur_selection_str, 1);
y += char_height_+4;
}
// Show the current menu item number in relation to total number if
// items don't fit on the screen.
if (menu_items > menu_end - menu_start) {
sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items);
gr_text(gr_sys_font(), x + 4, y, cur_selection_str, 1);
y += char_height_ + 4;
}
// Menu begins here
SetColor(MENU);
// Menu begins here
SetColor(MENU);
for (int i = menu_start; i < menu_end; ++i) {
if (i == menu_sel) {
// draw the highlight bar
SetColor(MENU_SEL_BG);
gr_fill(x, y-2, gr_fb_width()-x, y+char_height_+2);
// white text of selected item
SetColor(MENU_SEL_FG);
if (menu_[i][0]) {
gr_text(gr_sys_font(), x + 4, y, menu_[i], 1);
}
SetColor(MENU);
} else if (menu_[i][0]) {
gr_text(gr_sys_font(), x + 4, y, menu_[i], 0);
}
y += char_height_+4;
}
SetColor(MENU);
y += 4;
gr_fill(0, y, gr_fb_width(), y+2);
y += 4;
}
SetColor(LOG);
// 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.
int ty;
int row = (text_top_ + text_rows_ - 1) % text_rows_;
size_t count = 0;
for (int ty = gr_fb_height() - char_height_ - outer_height;
ty > y + 2 && count < text_rows_;
ty -= char_height_, ++count) {
gr_text(gr_sys_font(), x+4, ty, text_[row], 0);
--row;
if (row < 0) row = text_rows_ - 1;
for (int i = menu_start; i < menu_end; ++i) {
if (i == menu_sel) {
// draw the highlight bar
SetColor(MENU_SEL_BG);
gr_fill(x, y - 2, gr_fb_width() - x, y + char_height_ + 2);
// white text of selected item
SetColor(MENU_SEL_FG);
if (menu_[i][0]) {
gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 1);
}
SetColor(MENU);
} else if (menu_[i][0]) {
gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 0);
}
y += char_height_ + 4;
}
SetColor(MENU);
y += 4;
gr_fill(0, y, gr_fb_width(), y + 2);
y += 4;
}
SetColor(LOG);
// 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.
int ty;
int row = (text_top_ + text_rows_ - 1) % text_rows_;
size_t count = 0;
for (int ty = gr_fb_height() - char_height_ - kMarginHeight; ty > y + 2 && count < text_rows_;
ty -= char_height_, ++count) {
gr_text(gr_sys_font(), x + 4, ty, text_[row], 0);
--row;
if (row < 0) row = text_rows_ - 1;
}
}
}
// TODO merge drawing routines with screen_ui
void WearRecoveryUI::update_progress_locked() {
draw_screen_locked();
gr_flip();
draw_screen_locked();
gr_flip();
}
bool WearRecoveryUI::InitTextParams() {
if (!ScreenRecoveryUI::InitTextParams()) {
return false;
}
if (!ScreenRecoveryUI::InitTextParams()) {
return false;
}
text_cols_ = (gr_fb_width() - (outer_width * 2)) / char_width_;
text_cols_ = (gr_fb_width() - (kMarginWidth * 2)) / char_width_;
if (text_rows_ > kMaxRows) text_rows_ = kMaxRows;
if (text_cols_ > kMaxCols) text_cols_ = kMaxCols;
if (text_rows_ > kMaxRows) text_rows_ = kMaxRows;
if (text_cols_ > kMaxCols) text_cols_ = kMaxCols;
visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_;
return true;
visible_text_rows = (gr_fb_height() - (kMarginHeight * 2)) / char_height_;
return true;
}
bool WearRecoveryUI::Init(const std::string& locale) {
@@ -222,7 +219,8 @@ bool WearRecoveryUI::Init(const std::string& locale) {
return true;
}
void WearRecoveryUI::SetStage(int current, int max) {}
void WearRecoveryUI::SetStage(int current, int max) {
}
void WearRecoveryUI::Print(const char* fmt, ...) {
char buf[256];
@@ -252,165 +250,152 @@ void WearRecoveryUI::Print(const char* fmt, ...) {
pthread_mutex_unlock(&updateMutex);
}
void WearRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* items,
int initial_selection) {
pthread_mutex_lock(&updateMutex);
if (text_rows_ > 0 && text_cols_ > 0) {
menu_headers_ = headers;
size_t i = 0;
// "i < text_rows_" is removed from the loop termination condition,
// which is different from the one in ScreenRecoveryUI::StartMenu().
// Because WearRecoveryUI supports scrollable menu, it's fine to have
// more entries than text_rows_. The menu may be truncated otherwise.
// Bug: 23752519
for (; items[i] != nullptr; i++) {
strncpy(menu_[i], items[i], text_cols_ - 1);
menu_[i][text_cols_ - 1] = '\0';
}
menu_items = i;
show_menu = true;
menu_sel = initial_selection;
menu_start = 0;
menu_end = visible_text_rows - 1 - menu_unusable_rows;
if (menu_items <= menu_end)
menu_end = menu_items;
update_screen_locked();
pthread_mutex_lock(&updateMutex);
if (text_rows_ > 0 && text_cols_ > 0) {
menu_headers_ = headers;
menu_.clear();
// "i < text_rows_" is removed from the loop termination condition,
// which is different from the one in ScreenRecoveryUI::StartMenu().
// Because WearRecoveryUI supports scrollable menu, it's fine to have
// more entries than text_rows_. The menu may be truncated otherwise.
// Bug: 23752519
for (size_t i = 0; items[i] != nullptr; i++) {
menu_.emplace_back(std::string(items[i], strnlen(items[i], text_cols_ - 1)));
}
pthread_mutex_unlock(&updateMutex);
menu_items = static_cast<int>(menu_.size());
show_menu = true;
menu_sel = initial_selection;
menu_start = 0;
menu_end = visible_text_rows - 1 - kMenuUnusableRows;
if (menu_items <= menu_end) menu_end = menu_items;
update_screen_locked();
}
pthread_mutex_unlock(&updateMutex);
}
int WearRecoveryUI::SelectMenu(int sel) {
int old_sel;
pthread_mutex_lock(&updateMutex);
if (show_menu) {
old_sel = menu_sel;
menu_sel = sel;
if (menu_sel < 0) menu_sel = 0;
if (menu_sel >= menu_items) menu_sel = menu_items-1;
if (menu_sel < menu_start) {
menu_start--;
menu_end--;
} else if (menu_sel >= menu_end && menu_sel < menu_items) {
menu_end++;
menu_start++;
}
sel = menu_sel;
if (menu_sel != old_sel) update_screen_locked();
int old_sel;
pthread_mutex_lock(&updateMutex);
if (show_menu) {
old_sel = menu_sel;
menu_sel = sel;
if (menu_sel < 0) menu_sel = 0;
if (menu_sel >= menu_items) menu_sel = menu_items - 1;
if (menu_sel < menu_start) {
menu_start--;
menu_end--;
} else if (menu_sel >= menu_end && menu_sel < menu_items) {
menu_end++;
menu_start++;
}
pthread_mutex_unlock(&updateMutex);
return sel;
sel = menu_sel;
if (menu_sel != old_sel) update_screen_locked();
}
pthread_mutex_unlock(&updateMutex);
return sel;
}
void WearRecoveryUI::ShowFile(FILE* fp) {
std::vector<off_t> offsets;
offsets.push_back(ftello(fp));
ClearText();
std::vector<off_t> offsets;
offsets.push_back(ftello(fp));
ClearText();
struct stat sb;
fstat(fileno(fp), &sb);
struct stat sb;
fstat(fileno(fp), &sb);
bool show_prompt = false;
while (true) {
if (show_prompt) {
Print("--(%d%% of %d bytes)--",
static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
static_cast<int>(sb.st_size));
Redraw();
while (show_prompt) {
show_prompt = false;
int key = WaitKey();
if (key == KEY_POWER || key == KEY_ENTER) {
return;
} else if (key == KEY_UP || key == KEY_VOLUMEUP) {
if (offsets.size() <= 1) {
show_prompt = true;
} else {
offsets.pop_back();
fseek(fp, offsets.back(), SEEK_SET);
}
} else {
if (feof(fp)) {
return;
}
offsets.push_back(ftello(fp));
}
}
ClearText();
}
int ch = getc(fp);
if (ch == EOF) {
text_row_ = text_top_ = text_rows_ - 2;
bool show_prompt = false;
while (true) {
if (show_prompt) {
Print("--(%d%% of %d bytes)--",
static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
static_cast<int>(sb.st_size));
Redraw();
while (show_prompt) {
show_prompt = false;
int key = WaitKey();
if (key == KEY_POWER || key == KEY_ENTER) {
return;
} else if (key == KEY_UP || key == KEY_VOLUMEUP) {
if (offsets.size() <= 1) {
show_prompt = true;
} else {
offsets.pop_back();
fseek(fp, offsets.back(), SEEK_SET);
}
} else {
PutChar(ch);
if (text_col_ == 0 && text_row_ >= text_rows_ - 2) {
text_top_ = text_row_;
show_prompt = true;
}
if (feof(fp)) {
return;
}
offsets.push_back(ftello(fp));
}
}
ClearText();
}
int ch = getc(fp);
if (ch == EOF) {
text_row_ = text_top_ = text_rows_ - 2;
show_prompt = true;
} else {
PutChar(ch);
if (text_col_ == 0 && text_row_ >= text_rows_ - 2) {
text_top_ = text_row_;
show_prompt = true;
}
}
}
}
void WearRecoveryUI::PutChar(char ch) {
pthread_mutex_lock(&updateMutex);
if (ch != '\n') text_[text_row_][text_col_++] = ch;
if (ch == '\n' || text_col_ >= text_cols_) {
text_col_ = 0;
++text_row_;
}
pthread_mutex_unlock(&updateMutex);
pthread_mutex_lock(&updateMutex);
if (ch != '\n') text_[text_row_][text_col_++] = ch;
if (ch == '\n' || text_col_ >= text_cols_) {
text_col_ = 0;
++text_row_;
}
pthread_mutex_unlock(&updateMutex);
}
void WearRecoveryUI::ShowFile(const char* filename) {
FILE* fp = fopen_path(filename, "re");
if (fp == nullptr) {
Print(" Unable to open %s: %s\n", filename, strerror(errno));
return;
}
ShowFile(fp);
fclose(fp);
}
void WearRecoveryUI::ClearText() {
pthread_mutex_lock(&updateMutex);
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);
}
pthread_mutex_unlock(&updateMutex);
FILE* fp = fopen_path(filename, "re");
if (fp == nullptr) {
Print(" Unable to open %s: %s\n", filename, strerror(errno));
return;
}
ShowFile(fp);
fclose(fp);
}
void WearRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
PrintV(fmt, false, ap);
va_end(ap);
va_list ap;
va_start(ap, fmt);
PrintV(fmt, false, ap);
va_end(ap);
}
void WearRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
std::string str;
android::base::StringAppendV(&str, fmt, ap);
std::string str;
android::base::StringAppendV(&str, fmt, ap);
if (copy_to_stdout) {
fputs(str.c_str(), 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 = str.c_str(); *ptr != '\0'; ++ptr) {
if (*ptr == '\n' || text_col_ >= 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_;
}
if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
}
pthread_mutex_lock(&updateMutex);
if (text_rows_ > 0 && text_cols_ > 0) {
for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
if (*ptr == '\n' || text_col_ >= text_cols_) {
text_[text_row_][text_col_] = '\0';
update_screen_locked();
text_col_ = 0;
text_row_ = (text_row_ + 1) % text_rows_;
if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
}
if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
}
pthread_mutex_unlock(&updateMutex);
text_[text_row_][text_col_] = '\0';
update_screen_locked();
}
pthread_mutex_unlock(&updateMutex);
}
+35 -41
View File
@@ -22,64 +22,58 @@
#include <string>
class WearRecoveryUI : public ScreenRecoveryUI {
public:
WearRecoveryUI();
public:
WearRecoveryUI();
bool Init(const std::string& locale) override;
bool Init(const std::string& locale) override;
void SetStage(int current, int max) override;
void SetStage(int current, int max) override;
// printing messages
void Print(const char* fmt, ...) override;
void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3);
void ShowFile(const char* filename) override;
void ShowFile(FILE* fp) override;
// printing messages
void Print(const char* fmt, ...) override;
void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3);
void ShowFile(const char* filename) override;
void ShowFile(FILE* fp) override;
// menu display
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection) override;
int SelectMenu(int sel) override;
// menu display
void StartMenu(const char* const* headers, const char* const* items,
int initial_selection) override;
int SelectMenu(int sel) override;
protected:
// progress bar vertical position, it's centered horizontally
int progress_bar_y;
protected:
// progress bar vertical position, it's centered horizontally
const int kProgressBarBaseline;
// outer of window
int outer_height, outer_width;
// Unusable rows when displaying the recovery menu, including the lines for headers (Android
// Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen.
const int kMenuUnusableRows;
// Unusable rows when displaying the recovery menu, including the lines
// for headers (Android Recovery, build id and etc) and the bottom lines
// that may otherwise go out of the screen.
int menu_unusable_rows;
int GetProgressBaseline() const override;
int GetProgressBaseline() override;
bool InitTextParams() override;
bool InitTextParams() override;
void update_progress_locked() override;
void update_progress_locked() override;
void PrintV(const char*, bool, va_list) override;
void PrintV(const char*, bool, va_list) override;
private:
GRSurface* backgroundIcon[5];
private:
GRSurface* backgroundIcon[5];
static const int kMaxCols = 96;
static const int kMaxRows = 96;
static const int kMaxCols = 96;
static const int kMaxRows = 96;
// Number of text rows seen on screen
int visible_text_rows;
// Number of text rows seen on screen
int visible_text_rows;
const char* const* menu_headers_;
int menu_start, menu_end;
const char* const* menu_headers_;
int menu_start, menu_end;
pthread_t progress_t;
pthread_t progress_t;
void draw_background_locked() override;
void draw_screen_locked() override;
void draw_background_locked() override;
void draw_screen_locked() override;
void draw_progress_locked();
void PutChar(char);
void ClearText();
void PutChar(char);
};
#endif // RECOVERY_WEAR_UI_H