wip
26
Android.mk
@ -1,6 +1,3 @@
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
@ -13,15 +10,15 @@ LOCAL_SRC_FILES := \
|
||||
install.c \
|
||||
roots.c \
|
||||
ui.c \
|
||||
verifier.c \
|
||||
encryptedfs_provisioning.c \
|
||||
mounts.c \
|
||||
extendedcommands.c \
|
||||
nandroid.c \
|
||||
../../system/core/toolbox/reboot.c \
|
||||
firmware.c \
|
||||
edifyscripting.c \
|
||||
setprop.c
|
||||
setprop.c \
|
||||
default_recovery_ui.c \
|
||||
verifier.c
|
||||
|
||||
ADDITIONAL_RECOVERY_FILES := $(shell echo $$ADDITIONAL_RECOVERY_FILES)
|
||||
LOCAL_SRC_FILES += $(ADDITIONAL_RECOVERY_FILES)
|
||||
@ -51,6 +48,12 @@ $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
|
||||
) \
|
||||
)
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
|
||||
LOCAL_CFLAGS += -DUSE_EXT4
|
||||
LOCAL_C_INCLUDES += system/extras/ext4_utils
|
||||
LOCAL_STATIC_LIBRARIES += libext4_utils libz
|
||||
|
||||
# This binary is in the recovery ramdisk, which is otherwise a copy of root.
|
||||
# It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses
|
||||
# a (redundant) copy of the binary in /system/bin for user builds.
|
||||
@ -58,18 +61,16 @@ $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
|
||||
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
ifeq ($(BOARD_CUSTOM_RECOVERY_KEYMAPPING),)
|
||||
LOCAL_SRC_FILES += default_recovery_ui.c
|
||||
LOCAL_SRC_FILES += default_recovery_keys.c
|
||||
else
|
||||
LOCAL_SRC_FILES += $(BOARD_CUSTOM_RECOVERY_KEYMAPPING)
|
||||
endif
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += librebootrecovery
|
||||
LOCAL_STATIC_LIBRARIES += libext4_utils libz
|
||||
LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += libedify libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
|
||||
LOCAL_STATIC_LIBRARIES += libedify libbusybox libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += libcrecovery libflashutils libmtdutils libmmcutils libbmlutils
|
||||
|
||||
@ -151,6 +152,7 @@ include $(commands_recovery_local_path)/bmlutils/Android.mk
|
||||
include $(commands_recovery_local_path)/flashutils/Android.mk
|
||||
include $(commands_recovery_local_path)/libcrecovery/Android.mk
|
||||
include $(commands_recovery_local_path)/minui/Android.mk
|
||||
include $(commands_recovery_local_path)/minelf/Android.mk
|
||||
include $(commands_recovery_local_path)/minzip/Android.mk
|
||||
include $(commands_recovery_local_path)/mtdutils/Android.mk
|
||||
include $(commands_recovery_local_path)/mmcutils/Android.mk
|
||||
@ -160,7 +162,3 @@ include $(commands_recovery_local_path)/updater/Android.mk
|
||||
include $(commands_recovery_local_path)/applypatch/Android.mk
|
||||
include $(commands_recovery_local_path)/utilities/Android.mk
|
||||
commands_recovery_local_path :=
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
||||
|
||||
|
@ -12,9 +12,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
@ -31,7 +28,7 @@ include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := main.c
|
||||
LOCAL_MODULE := applypatch
|
||||
LOCAL_C_INCLUDES += bootable/recovery
|
||||
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
|
||||
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf
|
||||
LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
@ -43,7 +40,7 @@ LOCAL_MODULE := applypatch_static
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_C_INCLUDES += bootable/recovery
|
||||
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
|
||||
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf
|
||||
LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
@ -58,6 +55,3 @@ LOCAL_C_INCLUDES += external/zlib external/bzip2
|
||||
LOCAL_STATIC_LIBRARIES += libz libbz
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
||||
|
@ -30,16 +30,21 @@
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "edify/expr.h"
|
||||
|
||||
static int SaveFileContents(const char* filename, FileContents file);
|
||||
int SaveFileContents(const char* filename, FileContents file);
|
||||
static int LoadPartitionContents(const char* filename, FileContents* file);
|
||||
int ParseSha1(const char* str, uint8_t* digest);
|
||||
static ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
|
||||
|
||||
static int mtd_partitions_scanned = 0;
|
||||
|
||||
// Read a file into memory; store it and its associated metadata in
|
||||
// *file. Return 0 on success.
|
||||
int LoadFileContents(const char* filename, FileContents* file) {
|
||||
// Read a file into memory; optionally (retouch_flag == RETOUCH_DO_MASK) mask
|
||||
// the retouched entries back to their original value (such that SHA-1 checks
|
||||
// don't fail due to randomization); store the file contents and associated
|
||||
// metadata in *file.
|
||||
//
|
||||
// Return 0 on success.
|
||||
int LoadFileContents(const char* filename, FileContents* file,
|
||||
int retouch_flag) {
|
||||
file->data = NULL;
|
||||
|
||||
// A special 'filename' beginning with "MTD:" or "EMMC:" means to
|
||||
@ -75,6 +80,20 @@ int LoadFileContents(const char* filename, FileContents* file) {
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
// apply_patch[_check] functions are blind to randomization. Randomization
|
||||
// is taken care of in [Undo]RetouchBinariesFn. If there is a mismatch
|
||||
// within a file, this means the file is assumed "corrupt" for simplicity.
|
||||
if (retouch_flag) {
|
||||
int32_t desired_offset = 0;
|
||||
if (retouch_mask_data(file->data, file->size,
|
||||
&desired_offset, NULL) != RETOUCH_DATA_MATCHED) {
|
||||
printf("error trying to mask retouch entries\n");
|
||||
free(file->data);
|
||||
file->data = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
SHA(file->data, file->size, file->sha1);
|
||||
return 0;
|
||||
}
|
||||
@ -303,7 +322,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
|
||||
|
||||
// Save the contents of the given FileContents object under the given
|
||||
// filename. Return 0 on success.
|
||||
static int SaveFileContents(const char* filename, FileContents file) {
|
||||
int SaveFileContents(const char* filename, FileContents file) {
|
||||
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
if (fd < 0) {
|
||||
printf("failed to open \"%s\" for write: %s\n",
|
||||
@ -477,7 +496,7 @@ int applypatch_check(const char* filename,
|
||||
// LoadFileContents is successful. (Useful for reading
|
||||
// partitions, where the filename encodes the sha1s; no need to
|
||||
// check them twice.)
|
||||
if (LoadFileContents(filename, &file) != 0 ||
|
||||
if (LoadFileContents(filename, &file, RETOUCH_DO_MASK) != 0 ||
|
||||
(num_patches > 0 &&
|
||||
FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
|
||||
printf("file \"%s\" doesn't have any of expected "
|
||||
@ -491,7 +510,7 @@ int applypatch_check(const char* filename,
|
||||
// exists and matches the sha1 we're looking for, the check still
|
||||
// passes.
|
||||
|
||||
if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
|
||||
if (LoadFileContents(CACHE_TEMP_SOURCE, &file, RETOUCH_DO_MASK) != 0) {
|
||||
printf("failed to load cache file\n");
|
||||
return 1;
|
||||
}
|
||||
@ -617,7 +636,8 @@ int applypatch(const char* source_filename,
|
||||
int made_copy = 0;
|
||||
|
||||
// We try to load the target file into the source_file object.
|
||||
if (LoadFileContents(target_filename, &source_file) == 0) {
|
||||
if (LoadFileContents(target_filename, &source_file,
|
||||
RETOUCH_DO_MASK) == 0) {
|
||||
if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
|
||||
// The early-exit case: the patch was already applied, this file
|
||||
// has the desired hash, nothing for us to do.
|
||||
@ -633,7 +653,8 @@ int applypatch(const char* source_filename,
|
||||
// Need to load the source file: either we failed to load the
|
||||
// target file, or we did but it's different from the source file.
|
||||
free(source_file.data);
|
||||
LoadFileContents(source_filename, &source_file);
|
||||
LoadFileContents(source_filename, &source_file,
|
||||
RETOUCH_DO_MASK);
|
||||
}
|
||||
|
||||
if (source_file.data != NULL) {
|
||||
@ -648,7 +669,8 @@ int applypatch(const char* source_filename,
|
||||
free(source_file.data);
|
||||
printf("source file is bad; trying copy\n");
|
||||
|
||||
if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) {
|
||||
if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file,
|
||||
RETOUCH_DO_MASK) < 0) {
|
||||
// fail.
|
||||
printf("failed to read copy file\n");
|
||||
return 1;
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "mincrypt/sha.h"
|
||||
#include "minelf/Retouch.h"
|
||||
#include "edify/expr.h"
|
||||
|
||||
typedef struct _Patch {
|
||||
@ -59,10 +60,12 @@ int applypatch_check(const char* filename,
|
||||
int num_patches,
|
||||
char** const patch_sha1_str);
|
||||
|
||||
// Read a file into memory; store it and its associated metadata in
|
||||
// *file. Return 0 on success.
|
||||
int LoadFileContents(const char* filename, FileContents* file);
|
||||
int LoadFileContents(const char* filename, FileContents* file,
|
||||
int retouch_flag);
|
||||
int SaveFileContents(const char* filename, FileContents file);
|
||||
void FreeFileContents(FileContents* file);
|
||||
int FindMatchingPatch(uint8_t* sha1, char** const patch_sha1_str,
|
||||
int num_patches);
|
||||
|
||||
// bsdiff.c
|
||||
void ShowBSDiffLicense();
|
||||
|
@ -74,7 +74,7 @@ static int ParsePatchArgs(int argc, char** argv,
|
||||
(*patches)[i] = NULL;
|
||||
} else {
|
||||
FileContents fc;
|
||||
if (LoadFileContents(colon, &fc) != 0) {
|
||||
if (LoadFileContents(colon, &fc, RETOUCH_DONT_MASK) != 0) {
|
||||
goto abort;
|
||||
}
|
||||
(*patches)[i] = malloc(sizeof(Value));
|
||||
|
23
bootloader.c
@ -22,6 +22,8 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v);
|
||||
static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v);
|
||||
@ -140,8 +142,26 @@ static int set_bootloader_message_mtd(const struct bootloader_message *in,
|
||||
// for misc partitions on block devices
|
||||
// ------------------------------------
|
||||
|
||||
static void wait_for_device(const char* fn) {
|
||||
int tries = 0;
|
||||
int ret;
|
||||
struct stat buf;
|
||||
do {
|
||||
++tries;
|
||||
ret = stat(fn, &buf);
|
||||
if (ret) {
|
||||
printf("stat %s try %d: %s\n", fn, tries, strerror(errno));
|
||||
sleep(1);
|
||||
}
|
||||
} while (ret && tries < 10);
|
||||
if (ret) {
|
||||
printf("failed to stat %s\n", fn);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_bootloader_message_block(struct bootloader_message *out,
|
||||
const Volume* v) {
|
||||
wait_for_device(v->device);
|
||||
FILE* f = fopen(v->device, "rb");
|
||||
if (f == NULL) {
|
||||
LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
|
||||
@ -163,6 +183,7 @@ static int get_bootloader_message_block(struct bootloader_message *out,
|
||||
|
||||
static int set_bootloader_message_block(const struct bootloader_message *in,
|
||||
const Volume* v) {
|
||||
wait_for_device(v->device);
|
||||
FILE* f = fopen(v->device, "wb");
|
||||
if (f == NULL) {
|
||||
LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
|
||||
@ -328,4 +349,4 @@ int write_update_for_bootloader(
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
30
common.h
@ -26,6 +26,7 @@ void ui_init();
|
||||
int ui_wait_key(); // waits for a key/button press, returns the code
|
||||
int ui_key_pressed(int key); // returns >0 if the code is currently pressed
|
||||
int ui_text_visible(); // returns >0 if text log is currently visible
|
||||
int ui_text_ever_visible(); // returns >0 if text log was ever visible
|
||||
void ui_show_text(int visible);
|
||||
void ui_clear_key_queue();
|
||||
|
||||
@ -113,6 +114,13 @@ typedef struct {
|
||||
const char* device2; // alternative device to try if fs_type
|
||||
// == "ext4" or "vfat" and mounting
|
||||
// 'device' fails
|
||||
|
||||
long long length; // (ext4 partition only) when
|
||||
// formatting, size to use for the
|
||||
// partition. 0 or negative number
|
||||
// means to format all but the last
|
||||
// (that much).
|
||||
|
||||
const char* fs_type2;
|
||||
|
||||
const char* fs_options;
|
||||
@ -120,4 +128,26 @@ typedef struct {
|
||||
const char* fs_options2;
|
||||
} Volume;
|
||||
|
||||
typedef struct {
|
||||
// number of frames in indeterminate progress bar animation
|
||||
int indeterminate_frames;
|
||||
|
||||
// number of frames per second to try to maintain when animating
|
||||
int update_fps;
|
||||
|
||||
// number of frames in installing animation. may be zero for a
|
||||
// static installation icon.
|
||||
int installing_frames;
|
||||
|
||||
// the install icon is animated by drawing images containing the
|
||||
// changing part over the base icon. These specify the
|
||||
// coordinates of the upper-left corner.
|
||||
int install_overlay_offset_x;
|
||||
int install_overlay_offset_y;
|
||||
|
||||
} UIParameters;
|
||||
|
||||
// fopen a file, mounting volumes and making parent dirs as necessary.
|
||||
FILE* fopen_path(const char *path, const char *mode);
|
||||
|
||||
#endif // RECOVERY_COMMON_H
|
||||
|
64
default_recovery_keys.c
Normal file
@ -0,0 +1,64 @@
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "recovery_ui.h"
|
||||
#include "common.h"
|
||||
#include "extendedcommands.h"
|
||||
|
||||
|
||||
int device_toggle_display(volatile char* key_pressed, int key_code) {
|
||||
int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT];
|
||||
if (alt && key_code == KEY_L)
|
||||
return 1;
|
||||
// allow toggling of the display if the correct key is pressed, and the display toggle is allowed or the display is currently off
|
||||
if (ui_get_showing_back_button()) {
|
||||
return 0;
|
||||
//return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_END);
|
||||
}
|
||||
return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END);
|
||||
}
|
||||
|
||||
int device_handle_key(int key_code, int visible) {
|
||||
if (visible) {
|
||||
switch (key_code) {
|
||||
case KEY_CAPSLOCK:
|
||||
case KEY_DOWN:
|
||||
case KEY_VOLUMEDOWN:
|
||||
case KEY_MENU:
|
||||
return HIGHLIGHT_DOWN;
|
||||
|
||||
case KEY_LEFTSHIFT:
|
||||
case KEY_UP:
|
||||
case KEY_VOLUMEUP:
|
||||
case KEY_HOME:
|
||||
return HIGHLIGHT_UP;
|
||||
|
||||
case KEY_POWER:
|
||||
if (ui_get_showing_back_button()) {
|
||||
return SELECT_ITEM;
|
||||
}
|
||||
if (!get_allow_toggle_display())
|
||||
return GO_BACK;
|
||||
break;
|
||||
case KEY_LEFTBRACE:
|
||||
case KEY_ENTER:
|
||||
case BTN_MOUSE:
|
||||
case KEY_CAMERA:
|
||||
case KEY_F21:
|
||||
case KEY_SEND:
|
||||
return SELECT_ITEM;
|
||||
|
||||
case KEY_END:
|
||||
case KEY_BACKSPACE:
|
||||
case KEY_SEARCH:
|
||||
if (ui_get_showing_back_button()) {
|
||||
return SELECT_ITEM;
|
||||
}
|
||||
if (!get_allow_toggle_display())
|
||||
return GO_BACK;
|
||||
case KEY_BACK:
|
||||
return GO_BACK;
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ACTION;
|
||||
}
|
@ -23,83 +23,25 @@
|
||||
char* MENU_HEADERS[] = { NULL };
|
||||
|
||||
char* MENU_ITEMS[] = { "reboot system now",
|
||||
"apply update from sdcard",
|
||||
"install zip from sdcard",
|
||||
"wipe data/factory reset",
|
||||
"wipe cache partition",
|
||||
"install zip from sdcard",
|
||||
"backup and restore",
|
||||
"mounts and storage",
|
||||
"advanced",
|
||||
"power off",
|
||||
NULL };
|
||||
|
||||
void device_ui_init(UIParameters* ui_parameters) {
|
||||
}
|
||||
|
||||
int device_recovery_start() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_toggle_display(volatile char* key_pressed, int key_code) {
|
||||
int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT];
|
||||
if (alt && key_code == KEY_L)
|
||||
return 1;
|
||||
// allow toggling of the display if the correct key is pressed, and the display toggle is allowed or the display is currently off
|
||||
if (ui_get_showing_back_button()) {
|
||||
return 0;
|
||||
//return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_END);
|
||||
}
|
||||
return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END);
|
||||
}
|
||||
|
||||
int device_reboot_now(volatile char* key_pressed, int key_code) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_handle_key(int key_code, int visible) {
|
||||
if (visible) {
|
||||
switch (key_code) {
|
||||
case KEY_CAPSLOCK:
|
||||
case KEY_DOWN:
|
||||
case KEY_VOLUMEDOWN:
|
||||
case KEY_MENU:
|
||||
return HIGHLIGHT_DOWN;
|
||||
|
||||
case KEY_LEFTSHIFT:
|
||||
case KEY_UP:
|
||||
case KEY_VOLUMEUP:
|
||||
case KEY_HOME:
|
||||
return HIGHLIGHT_UP;
|
||||
|
||||
case KEY_POWER:
|
||||
if (ui_get_showing_back_button()) {
|
||||
return SELECT_ITEM;
|
||||
}
|
||||
if (!get_allow_toggle_display())
|
||||
return GO_BACK;
|
||||
break;
|
||||
case KEY_LEFTBRACE:
|
||||
case KEY_ENTER:
|
||||
case BTN_MOUSE:
|
||||
case KEY_CENTER:
|
||||
case KEY_CAMERA:
|
||||
case KEY_F21:
|
||||
case KEY_SEND:
|
||||
return SELECT_ITEM;
|
||||
|
||||
case KEY_END:
|
||||
case KEY_BACKSPACE:
|
||||
case KEY_SEARCH:
|
||||
if (ui_get_showing_back_button()) {
|
||||
return SELECT_ITEM;
|
||||
}
|
||||
if (!get_allow_toggle_display())
|
||||
return GO_BACK;
|
||||
case KEY_BACK:
|
||||
return GO_BACK;
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ACTION;
|
||||
}
|
||||
|
||||
int device_perform_action(int which) {
|
||||
return which;
|
||||
}
|
||||
|
@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "encryptedfs_provisioning.h"
|
||||
#include "cutils/misc.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "common.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "mounts.h"
|
||||
#include "roots.h"
|
||||
|
||||
const char* encrypted_fs_enabled_property = "persist.security.secfs.enabled";
|
||||
const char* encrypted_fs_property_dir = "/data/property/";
|
||||
const char* encrypted_fs_system_dir = "/data/system/";
|
||||
const char* encrypted_fs_key_file_name = "/data/fs_key.dat";
|
||||
const char* encrypted_fs_salt_file_name = "/data/hash_salt.dat";
|
||||
const char* encrypted_fs_hash_file_src_name = "/data/system/password.key";
|
||||
const char* encrypted_fs_hash_file_dst_name = "/data/hash.dat";
|
||||
const char* encrypted_fs_entropy_file_src_name = "/data/system/entropy.dat";
|
||||
const char* encrypted_fs_entropy_file_dst_name = "/data/ported_entropy.dat";
|
||||
|
||||
void get_property_file_name(char *buffer, const char *property_name) {
|
||||
sprintf(buffer, "%s%s", encrypted_fs_property_dir, property_name);
|
||||
}
|
||||
|
||||
int get_binary_file_contents(char *buffer, int buf_size, const char *file_name, int *out_size) {
|
||||
FILE *in_file;
|
||||
int read_bytes;
|
||||
|
||||
in_file = fopen(file_name, "r");
|
||||
if (in_file == NULL) {
|
||||
LOGE("Secure FS: error accessing key file.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
read_bytes = fread(buffer, 1, buf_size, in_file);
|
||||
if (out_size == NULL) {
|
||||
if (read_bytes != buf_size) {
|
||||
// Error or unexpected data
|
||||
fclose(in_file);
|
||||
LOGE("Secure FS: error reading conmplete key.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
} else {
|
||||
*out_size = read_bytes;
|
||||
}
|
||||
fclose(in_file);
|
||||
return ENCRYPTED_FS_OK;
|
||||
}
|
||||
|
||||
int set_binary_file_contents(char *buffer, int buf_size, const char *file_name) {
|
||||
FILE *out_file;
|
||||
int write_bytes;
|
||||
|
||||
out_file = fopen(file_name, "w");
|
||||
if (out_file == NULL) {
|
||||
LOGE("Secure FS: error setting up key file.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
write_bytes = fwrite(buffer, 1, buf_size, out_file);
|
||||
if (write_bytes != buf_size) {
|
||||
// Error or unexpected data
|
||||
fclose(out_file);
|
||||
LOGE("Secure FS: error reading conmplete key.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
fclose(out_file);
|
||||
return ENCRYPTED_FS_OK;
|
||||
}
|
||||
|
||||
int get_text_file_contents(char *buffer, int buf_size, char *file_name) {
|
||||
FILE *in_file;
|
||||
char *read_data;
|
||||
|
||||
in_file = fopen(file_name, "r");
|
||||
if (in_file == NULL) {
|
||||
LOGE("Secure FS: error accessing properties.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
read_data = fgets(buffer, buf_size, in_file);
|
||||
if (read_data == NULL) {
|
||||
// Error or unexpected data
|
||||
fclose(in_file);
|
||||
LOGE("Secure FS: error accessing properties.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
fclose(in_file);
|
||||
return ENCRYPTED_FS_OK;
|
||||
}
|
||||
|
||||
int set_text_file_contents(char *buffer, char *file_name) {
|
||||
FILE *out_file;
|
||||
int result;
|
||||
|
||||
out_file = fopen(file_name, "w");
|
||||
if (out_file == NULL) {
|
||||
LOGE("Secure FS: error setting up properties.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
result = fputs(buffer, out_file);
|
||||
if (result != 0) {
|
||||
// Error or unexpected data
|
||||
fclose(out_file);
|
||||
LOGE("Secure FS: error setting up properties.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
fflush(out_file);
|
||||
fclose(out_file);
|
||||
return ENCRYPTED_FS_OK;
|
||||
}
|
||||
|
||||
int read_encrypted_fs_boolean_property(const char *prop_name, int *value) {
|
||||
char prop_file_name[PROPERTY_KEY_MAX + 32];
|
||||
char prop_value[PROPERTY_VALUE_MAX];
|
||||
int result;
|
||||
|
||||
get_property_file_name(prop_file_name, prop_name);
|
||||
result = get_text_file_contents(prop_value, PROPERTY_VALUE_MAX, prop_file_name);
|
||||
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (strncmp(prop_value, "1", 1) == 0) {
|
||||
*value = 1;
|
||||
} else if (strncmp(prop_value, "0", 1) == 0) {
|
||||
*value = 0;
|
||||
} else {
|
||||
LOGE("Secure FS: error accessing properties.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
return ENCRYPTED_FS_OK;
|
||||
}
|
||||
|
||||
int write_encrypted_fs_boolean_property(const char *prop_name, int value) {
|
||||
char prop_file_name[PROPERTY_KEY_MAX + 32];
|
||||
char prop_value[PROPERTY_VALUE_MAX];
|
||||
int result;
|
||||
|
||||
get_property_file_name(prop_file_name, prop_name);
|
||||
|
||||
// Create the directory if needed
|
||||
mkdir(encrypted_fs_property_dir, 0755);
|
||||
if (value == 1) {
|
||||
result = set_text_file_contents("1", prop_file_name);
|
||||
} else if (value == 0) {
|
||||
result = set_text_file_contents("0", prop_file_name);
|
||||
} else {
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return ENCRYPTED_FS_OK;
|
||||
}
|
||||
|
||||
int read_encrypted_fs_info(encrypted_fs_info *encrypted_fs_data) {
|
||||
int result;
|
||||
int value;
|
||||
result = ensure_path_mounted("/data");
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error mounting userdata partition.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
// Read the pre-generated encrypted FS key, password hash and salt.
|
||||
result = get_binary_file_contents(encrypted_fs_data->key, ENCRYPTED_FS_KEY_SIZE,
|
||||
encrypted_fs_key_file_name, NULL);
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error reading generated file system key.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
result = get_binary_file_contents(encrypted_fs_data->salt, ENCRYPTED_FS_SALT_SIZE,
|
||||
encrypted_fs_salt_file_name, &(encrypted_fs_data->salt_length));
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error reading file system salt.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
result = get_binary_file_contents(encrypted_fs_data->hash, ENCRYPTED_FS_MAX_HASH_SIZE,
|
||||
encrypted_fs_hash_file_src_name, &(encrypted_fs_data->hash_length));
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error reading password hash.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
result = get_binary_file_contents(encrypted_fs_data->entropy, ENTROPY_MAX_SIZE,
|
||||
encrypted_fs_entropy_file_src_name, &(encrypted_fs_data->entropy_length));
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error reading ported entropy.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
result = ensure_path_unmounted("/data");
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error unmounting data partition.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
return ENCRYPTED_FS_OK;
|
||||
}
|
||||
|
||||
int restore_encrypted_fs_info(encrypted_fs_info *encrypted_fs_data) {
|
||||
int result;
|
||||
result = ensure_path_mounted("/data");
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error mounting userdata partition.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
// Write the pre-generated secure FS key, password hash and salt.
|
||||
result = set_binary_file_contents(encrypted_fs_data->key, ENCRYPTED_FS_KEY_SIZE,
|
||||
encrypted_fs_key_file_name);
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error writing generated file system key.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
result = set_binary_file_contents(encrypted_fs_data->salt, encrypted_fs_data->salt_length,
|
||||
encrypted_fs_salt_file_name);
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error writing file system salt.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
result = set_binary_file_contents(encrypted_fs_data->hash, encrypted_fs_data->hash_length,
|
||||
encrypted_fs_hash_file_dst_name);
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error writing password hash.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
result = set_binary_file_contents(encrypted_fs_data->entropy, encrypted_fs_data->entropy_length,
|
||||
encrypted_fs_entropy_file_dst_name);
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error writing ported entropy.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
// Set the secure FS properties to their respective values
|
||||
result = write_encrypted_fs_boolean_property(encrypted_fs_enabled_property, encrypted_fs_data->mode);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ensure_path_unmounted("/data");
|
||||
if (result != 0) {
|
||||
LOGE("Secure FS: error unmounting data partition.");
|
||||
return ENCRYPTED_FS_ERROR;
|
||||
}
|
||||
|
||||
return ENCRYPTED_FS_OK;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 <stdio.h>
|
||||
|
||||
#ifndef __ENCRYPTEDFS_PROVISIONING_H__
|
||||
#define __ENCRYPTEDFS_PROVISIONING_H__
|
||||
|
||||
#define MODE_ENCRYPTED_FS_DISABLED 0
|
||||
#define MODE_ENCRYPTED_FS_ENABLED 1
|
||||
|
||||
#define ENCRYPTED_FS_OK 0
|
||||
#define ENCRYPTED_FS_ERROR (-1)
|
||||
|
||||
#define ENCRYPTED_FS_KEY_SIZE 16
|
||||
#define ENCRYPTED_FS_SALT_SIZE 16
|
||||
#define ENCRYPTED_FS_MAX_HASH_SIZE 128
|
||||
#define ENTROPY_MAX_SIZE 4096
|
||||
|
||||
struct encrypted_fs_info {
|
||||
int mode;
|
||||
char key[ENCRYPTED_FS_KEY_SIZE];
|
||||
char salt[ENCRYPTED_FS_SALT_SIZE];
|
||||
int salt_length;
|
||||
char hash[ENCRYPTED_FS_MAX_HASH_SIZE];
|
||||
int hash_length;
|
||||
char entropy[ENTROPY_MAX_SIZE];
|
||||
int entropy_length;
|
||||
};
|
||||
|
||||
typedef struct encrypted_fs_info encrypted_fs_info;
|
||||
|
||||
int read_encrypted_fs_info(encrypted_fs_info *secure_fs_data);
|
||||
|
||||
int restore_encrypted_fs_info(encrypted_fs_info *secure_data);
|
||||
|
||||
#endif /* __ENCRYPTEDFS_PROVISIONING_H__ */
|
||||
|
18
etc/init.rc
@ -35,8 +35,20 @@ service recovery /sbin/recovery
|
||||
service adbd /sbin/adbd recovery
|
||||
disabled
|
||||
|
||||
on property:persist.service.adb.enable=1
|
||||
# Always start adbd on userdebug and eng builds
|
||||
on property:ro.debuggable=1
|
||||
write /sys/class/android_usb/android0/enable 0
|
||||
write /sys/class/android_usb/android0/idVendor 18D1
|
||||
write /sys/class/android_usb/android0/idProduct D001
|
||||
write /sys/class/android_usb/android0/functions adb
|
||||
write /sys/class/android_usb/android0/enable 1
|
||||
write /sys/class/android_usb/android0/iManufacturer $ro.product.manufacturer
|
||||
write /sys/class/android_usb/android0/iProduct $ro.product.model
|
||||
write /sys/class/android_usb/android0/iSerial $ro.serialno
|
||||
start adbd
|
||||
|
||||
on property:persist.service.adb.enable=0
|
||||
stop adbd
|
||||
# Restart adbd so it can run as root
|
||||
on property:service.adb.root=1
|
||||
write /sys/class/android_usb/android0/enable 0
|
||||
restart adbd
|
||||
write /sys/class/android_usb/android0/enable 1
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <reboot/reboot.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
@ -42,6 +41,7 @@
|
||||
#include <libgen.h>
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "bmlutils/bmlutils.h"
|
||||
#include "cutils/android_reboot.h"
|
||||
|
||||
|
||||
int signature_check_enabled = 1;
|
||||
@ -914,7 +914,7 @@ void show_advanced_menu()
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
reboot_wrapper("recovery");
|
||||
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
|
26
install.c
@ -103,6 +103,8 @@ handle_firmware_update(char* type, char* filename, ZipArchive* zip) {
|
||||
return INSTALL_SUCCESS;
|
||||
}
|
||||
|
||||
static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
|
||||
|
||||
// If the package contains an update binary, extract it and run it.
|
||||
static int
|
||||
try_update_binary(const char *path, ZipArchive *zip) {
|
||||
@ -332,8 +334,8 @@ exit:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
install_package(const char *path)
|
||||
static int
|
||||
really_install_package(const char *path)
|
||||
{
|
||||
ui_set_background(BACKGROUND_ICON_INSTALLING);
|
||||
ui_print("Finding update package...\n");
|
||||
@ -387,3 +389,23 @@ install_package(const char *path)
|
||||
ui_print("Installing update...\n");
|
||||
return try_update_binary(path, &zip);
|
||||
}
|
||||
|
||||
int
|
||||
install_package(const char* path)
|
||||
{
|
||||
FILE* install_log = fopen_path(LAST_INSTALL_FILE, "w");
|
||||
if (install_log) {
|
||||
fputs(path, install_log);
|
||||
fputc('\n', install_log);
|
||||
} else {
|
||||
LOGE("failed to open last_install: %s\n", strerror(errno));
|
||||
}
|
||||
int result = really_install_package(path);
|
||||
if (install_log) {
|
||||
fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);
|
||||
fputc('\n', install_log);
|
||||
fclose(install_log);
|
||||
chmod(LAST_INSTALL_FILE, 0644);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
102
make-overlay.py
Normal file
@ -0,0 +1,102 @@
|
||||
# Copyright (C) 2011 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.
|
||||
|
||||
"""Script to take a set of frames (PNG files) for a recovery
|
||||
"installing" icon animation and turn it into a base image plus a set
|
||||
of overlays, as needed by the recovery UI code. Run with the names of
|
||||
all the input frames on the command line, in order."""
|
||||
|
||||
import sys
|
||||
try:
|
||||
import Image
|
||||
except ImportError:
|
||||
print "This script requires the Python Imaging Library to be installed."
|
||||
sys.exit(1)
|
||||
|
||||
# Find the smallest box that contains all the pixels which change
|
||||
# between images.
|
||||
|
||||
print "reading", sys.argv[1]
|
||||
base = Image.open(sys.argv[1])
|
||||
|
||||
minmini = base.size[0]-1
|
||||
maxmaxi = 0
|
||||
minminj = base.size[1]-1
|
||||
maxmaxj = 0
|
||||
|
||||
for top_name in sys.argv[2:]:
|
||||
print "reading", top_name
|
||||
top = Image.open(top_name)
|
||||
|
||||
assert base.size == top.size
|
||||
|
||||
mini = base.size[0]-1
|
||||
maxi = 0
|
||||
minj = base.size[1]-1
|
||||
maxj = 0
|
||||
|
||||
h, w = base.size
|
||||
for j in range(w):
|
||||
for i in range(h):
|
||||
b = base.getpixel((i,j))
|
||||
t = top.getpixel((i,j))
|
||||
if b != t:
|
||||
if i < mini: mini = i
|
||||
if i > maxi: maxi = i
|
||||
if j < minj: minj = j
|
||||
if j > maxj: maxj = j
|
||||
|
||||
minmini = min(minmini, mini)
|
||||
maxmaxi = max(maxmaxi, maxi)
|
||||
minminj = min(minminj, minj)
|
||||
maxmaxj = max(maxmaxj, maxj)
|
||||
|
||||
w = maxmaxi - minmini + 1
|
||||
h = maxmaxj - minminj + 1
|
||||
|
||||
# Now write out an image containing just that box, for each frame.
|
||||
|
||||
for num, top_name in enumerate(sys.argv[1:]):
|
||||
top = Image.open(top_name)
|
||||
|
||||
out = Image.new("RGB", (w, h))
|
||||
for i in range(w):
|
||||
for j in range(h):
|
||||
t = top.getpixel((i+minmini, j+minminj))
|
||||
out.putpixel((i, j), t)
|
||||
|
||||
fn = "icon_installing_overlay%02d.png" % (num+1,)
|
||||
out.save(fn)
|
||||
print "saved", fn
|
||||
|
||||
# Write out the base icon, which is the first frame with that box
|
||||
# blacked out (just to make the file smaller, since it's always
|
||||
# displayed with one of the overlays on top of it).
|
||||
|
||||
for i in range(w):
|
||||
for j in range(h):
|
||||
base.putpixel((i+minmini, j+minminj), (0, 0, 0))
|
||||
fn = "icon_installing.png"
|
||||
base.save(fn)
|
||||
print "saved", fn
|
||||
|
||||
# The device_ui_init() function needs to tell the recovery UI the
|
||||
# position of the overlay box.
|
||||
|
||||
print
|
||||
print "add this to your device_ui_init() function:"
|
||||
print "-" * 40
|
||||
print " ui_parameters->install_overlay_offset_x = %d;" % (minmini,)
|
||||
print " ui_parameters->install_overlay_offset_y = %d;" % (minminj,)
|
||||
print "-" * 40
|
27
minelf/Android.mk
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2009 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 := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
Retouch.c
|
||||
|
||||
LOCAL_C_INCLUDES += bootable/recovery
|
||||
|
||||
LOCAL_MODULE := libminelf
|
||||
|
||||
LOCAL_CFLAGS += -Wall
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
406
minelf/Retouch.c
Normal file
@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include "Retouch.h"
|
||||
#include "applypatch/applypatch.h"
|
||||
|
||||
typedef struct {
|
||||
int32_t mmap_addr;
|
||||
char tag[4]; /* 'P', 'R', 'E', ' ' */
|
||||
} prelink_info_t __attribute__((packed));
|
||||
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
||||
static int32_t offs_prev;
|
||||
static uint32_t cont_prev;
|
||||
|
||||
static void init_compression_state(void) {
|
||||
offs_prev = 0;
|
||||
cont_prev = 0;
|
||||
}
|
||||
|
||||
// For details on the encoding used for relocation lists, please
|
||||
// refer to build/tools/retouch/retouch-prepare.c. The intent is to
|
||||
// save space by removing most of the inherent redundancy.
|
||||
|
||||
static void decode_bytes(uint8_t *encoded_bytes, int encoded_size,
|
||||
int32_t *dst_offset, uint32_t *dst_contents) {
|
||||
if (encoded_size == 2) {
|
||||
*dst_offset = offs_prev + (((encoded_bytes[0]&0x60)>>5)+1)*4;
|
||||
|
||||
// if the original was negative, we need to 1-pad before applying delta
|
||||
int32_t tmp = (((encoded_bytes[0] & 0x0000001f) << 8) |
|
||||
encoded_bytes[1]);
|
||||
if (tmp & 0x1000) tmp = 0xffffe000 | tmp;
|
||||
*dst_contents = cont_prev + tmp;
|
||||
} else if (encoded_size == 3) {
|
||||
*dst_offset = offs_prev + (((encoded_bytes[0]&0x30)>>4)+1)*4;
|
||||
|
||||
// if the original was negative, we need to 1-pad before applying delta
|
||||
int32_t tmp = (((encoded_bytes[0] & 0x0000000f) << 16) |
|
||||
(encoded_bytes[1] << 8) |
|
||||
encoded_bytes[2]);
|
||||
if (tmp & 0x80000) tmp = 0xfff00000 | tmp;
|
||||
*dst_contents = cont_prev + tmp;
|
||||
} else {
|
||||
*dst_offset =
|
||||
(encoded_bytes[0]<<24) |
|
||||
(encoded_bytes[1]<<16) |
|
||||
(encoded_bytes[2]<<8) |
|
||||
encoded_bytes[3];
|
||||
if (*dst_offset == 0x3fffffff) *dst_offset = -1;
|
||||
*dst_contents =
|
||||
(encoded_bytes[4]<<24) |
|
||||
(encoded_bytes[5]<<16) |
|
||||
(encoded_bytes[6]<<8) |
|
||||
encoded_bytes[7];
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t *decode_in_memory(uint8_t *encoded_bytes,
|
||||
int32_t *offset, uint32_t *contents) {
|
||||
int input_size, charIx;
|
||||
uint8_t input[8];
|
||||
|
||||
input[0] = *(encoded_bytes++);
|
||||
if (input[0] & 0x80)
|
||||
input_size = 2;
|
||||
else if (input[0] & 0x40)
|
||||
input_size = 3;
|
||||
else
|
||||
input_size = 8;
|
||||
|
||||
// we already read one byte..
|
||||
charIx = 1;
|
||||
while (charIx < input_size) {
|
||||
input[charIx++] = *(encoded_bytes++);
|
||||
}
|
||||
|
||||
// depends on the decoder state!
|
||||
decode_bytes(input, input_size, offset, contents);
|
||||
|
||||
offs_prev = *offset;
|
||||
cont_prev = *contents;
|
||||
|
||||
return encoded_bytes;
|
||||
}
|
||||
|
||||
int retouch_mask_data(uint8_t *binary_object,
|
||||
int32_t binary_size,
|
||||
int32_t *desired_offset,
|
||||
int32_t *retouch_offset) {
|
||||
retouch_info_t *r_info;
|
||||
prelink_info_t *p_info;
|
||||
|
||||
int32_t target_offset = 0;
|
||||
if (desired_offset) target_offset = *desired_offset;
|
||||
|
||||
int32_t p_offs = binary_size-sizeof(prelink_info_t); // prelink_info_t
|
||||
int32_t r_offs = p_offs-sizeof(retouch_info_t); // retouch_info_t
|
||||
int32_t b_offs; // retouch data blob
|
||||
|
||||
// If not retouched, we say it was a match. This might get invoked on
|
||||
// non-retouched binaries, so that's why we need to do this.
|
||||
if (retouch_offset != NULL) *retouch_offset = target_offset;
|
||||
if (r_offs < 0) return (desired_offset == NULL) ?
|
||||
RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
|
||||
p_info = (prelink_info_t *)(binary_object+p_offs);
|
||||
r_info = (retouch_info_t *)(binary_object+r_offs);
|
||||
if (strncmp(p_info->tag, "PRE ", 4) ||
|
||||
strncmp(r_info->tag, "RETOUCH ", 8))
|
||||
return (desired_offset == NULL) ?
|
||||
RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
|
||||
|
||||
b_offs = r_offs-r_info->blob_size;
|
||||
if (b_offs < 0) {
|
||||
printf("negative binary offset: %d = %d - %d\n",
|
||||
b_offs, r_offs, r_info->blob_size);
|
||||
return RETOUCH_DATA_ERROR;
|
||||
}
|
||||
uint8_t *b_ptr = binary_object+b_offs;
|
||||
|
||||
// Retouched: let's go through the work then.
|
||||
int32_t offset_candidate = target_offset;
|
||||
bool offset_set = false, offset_mismatch = false;
|
||||
init_compression_state();
|
||||
while (b_ptr < (uint8_t *)r_info) {
|
||||
int32_t retouch_entry_offset;
|
||||
uint32_t *retouch_entry;
|
||||
uint32_t retouch_original_value;
|
||||
|
||||
b_ptr = decode_in_memory(b_ptr,
|
||||
&retouch_entry_offset,
|
||||
&retouch_original_value);
|
||||
if (retouch_entry_offset < (-1) ||
|
||||
retouch_entry_offset >= b_offs) {
|
||||
printf("bad retouch_entry_offset: %d", retouch_entry_offset);
|
||||
return RETOUCH_DATA_ERROR;
|
||||
}
|
||||
|
||||
// "-1" means this is the value in prelink_info_t, which also gets
|
||||
// randomized.
|
||||
if (retouch_entry_offset == -1)
|
||||
retouch_entry = (uint32_t *)&(p_info->mmap_addr);
|
||||
else
|
||||
retouch_entry = (uint32_t *)(binary_object+retouch_entry_offset);
|
||||
|
||||
if (desired_offset)
|
||||
*retouch_entry = retouch_original_value + target_offset;
|
||||
|
||||
// Infer the randomization shift, compare to previously inferred.
|
||||
int32_t offset_of_this_entry = (int32_t)(*retouch_entry-
|
||||
retouch_original_value);
|
||||
if (!offset_set) {
|
||||
offset_candidate = offset_of_this_entry;
|
||||
offset_set = true;
|
||||
} else {
|
||||
if (offset_candidate != offset_of_this_entry) {
|
||||
offset_mismatch = true;
|
||||
printf("offset is mismatched: %d, this entry is %d,"
|
||||
" original 0x%x @ 0x%x",
|
||||
offset_candidate, offset_of_this_entry,
|
||||
retouch_original_value, retouch_entry_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (b_ptr > (uint8_t *)r_info) {
|
||||
printf("b_ptr went too far: %p, while r_info is %p",
|
||||
b_ptr, r_info);
|
||||
return RETOUCH_DATA_ERROR;
|
||||
}
|
||||
|
||||
if (offset_mismatch) return RETOUCH_DATA_MISMATCHED;
|
||||
if (retouch_offset != NULL) *retouch_offset = offset_candidate;
|
||||
return RETOUCH_DATA_MATCHED;
|
||||
}
|
||||
|
||||
// On success, _override is set to the offset that was actually applied.
|
||||
// This implies that once we randomize to an offset we stick with it.
|
||||
// This in turn is necessary in order to guarantee recovery after crash.
|
||||
bool retouch_one_library(const char *binary_name,
|
||||
const char *binary_sha1,
|
||||
int32_t retouch_offset,
|
||||
int32_t *retouch_offset_override) {
|
||||
bool success = true;
|
||||
int result;
|
||||
|
||||
FileContents file;
|
||||
file.data = NULL;
|
||||
|
||||
char binary_name_atomic[strlen(binary_name)+10];
|
||||
strcpy(binary_name_atomic, binary_name);
|
||||
strcat(binary_name_atomic, ".atomic");
|
||||
|
||||
// We need a path that exists for calling statfs() later.
|
||||
//
|
||||
// Assume that binary_name (eg "/system/app/Foo.apk") is located
|
||||
// on the same filesystem as its top-level directory ("/system").
|
||||
char target_fs[strlen(binary_name)+1];
|
||||
char* slash = strchr(binary_name+1, '/');
|
||||
if (slash != NULL) {
|
||||
int count = slash - binary_name;
|
||||
strncpy(target_fs, binary_name, count);
|
||||
target_fs[count] = '\0';
|
||||
} else {
|
||||
strcpy(target_fs, binary_name);
|
||||
}
|
||||
|
||||
result = LoadFileContents(binary_name, &file, RETOUCH_DONT_MASK);
|
||||
|
||||
if (result == 0) {
|
||||
// Figure out the *apparent* offset to which this file has been
|
||||
// retouched. If it looks good, we will skip processing (we might
|
||||
// have crashed and during this recovery pass we don't want to
|
||||
// overwrite a valuable saved file in /cache---which would happen
|
||||
// if we blindly retouch everything again). NOTE: This implies
|
||||
// that we might have to override the supplied retouch offset. We
|
||||
// can do the override only once though: everything should match
|
||||
// afterward.
|
||||
|
||||
int32_t inferred_offset;
|
||||
int retouch_probe_result = retouch_mask_data(file.data,
|
||||
file.size,
|
||||
NULL,
|
||||
&inferred_offset);
|
||||
|
||||
if (retouch_probe_result == RETOUCH_DATA_MATCHED) {
|
||||
if ((retouch_offset == inferred_offset) ||
|
||||
((retouch_offset != 0 && inferred_offset != 0) &&
|
||||
(retouch_offset_override != NULL))) {
|
||||
// This file is OK already and we are allowed to override.
|
||||
// Let's just return the offset override value. It is critical
|
||||
// to skip regardless of override: a broken file might need
|
||||
// recovery down the list and we should not mess up the saved
|
||||
// copy by doing unnecessary retouching.
|
||||
//
|
||||
// NOTE: If retouching was already started with a different
|
||||
// value, we will not be allowed to override. This happens
|
||||
// if on the retouch list there is a patched binary (which is
|
||||
// masked in apply_patch()) before there is a non-patched
|
||||
// binary.
|
||||
if (retouch_offset_override != NULL)
|
||||
*retouch_offset_override = inferred_offset;
|
||||
success = true;
|
||||
goto out;
|
||||
} else {
|
||||
// Retouch to zero (mask the retouching), to make sure that
|
||||
// the SHA-1 check will pass below.
|
||||
int32_t zero = 0;
|
||||
retouch_mask_data(file.data, file.size, &zero, NULL);
|
||||
SHA(file.data, file.size, file.sha1);
|
||||
}
|
||||
}
|
||||
|
||||
if (retouch_probe_result == RETOUCH_DATA_NOTAPPLICABLE) {
|
||||
// In the case of not retouchable, fake it. We do not want
|
||||
// to do the normal processing and overwrite the backup file:
|
||||
// we might be recovering!
|
||||
//
|
||||
// We return a zero override, which tells the caller that we
|
||||
// simply skipped the file.
|
||||
if (retouch_offset_override != NULL)
|
||||
*retouch_offset_override = 0;
|
||||
success = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// If we get here, either there was a mismatch in the offset, or
|
||||
// the file has not been processed yet. Continue with normal
|
||||
// processing.
|
||||
}
|
||||
|
||||
if (result != 0 || FindMatchingPatch(file.sha1, &binary_sha1, 1) < 0) {
|
||||
free(file.data);
|
||||
printf("Attempting to recover source from '%s' ...\n",
|
||||
CACHE_TEMP_SOURCE);
|
||||
result = LoadFileContents(CACHE_TEMP_SOURCE, &file, RETOUCH_DO_MASK);
|
||||
if (result != 0 || FindMatchingPatch(file.sha1, &binary_sha1, 1) < 0) {
|
||||
printf(" failed.\n");
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
printf(" succeeded.\n");
|
||||
}
|
||||
|
||||
// Retouch in-memory before worrying about backing up the original.
|
||||
//
|
||||
// Recovery steps will be oblivious to the actual retouch offset used,
|
||||
// so might as well write out the already-retouched copy. Then, in the
|
||||
// usual case, we will just swap the file locally, with no more writes
|
||||
// needed. In the no-free-space case, we will then write the same to the
|
||||
// original location.
|
||||
|
||||
result = retouch_mask_data(file.data, file.size, &retouch_offset, NULL);
|
||||
if (result != RETOUCH_DATA_MATCHED) {
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
if (retouch_offset_override != NULL)
|
||||
*retouch_offset_override = retouch_offset;
|
||||
|
||||
// How much free space do we need?
|
||||
bool enough_space = false;
|
||||
size_t free_space = FreeSpaceForFile(target_fs);
|
||||
// 50% margin when estimating the space needed.
|
||||
enough_space = (free_space > (file.size * 3 / 2));
|
||||
|
||||
// The experts say we have to allow for a retry of the
|
||||
// whole process to avoid filesystem weirdness.
|
||||
int retry = 1;
|
||||
bool made_copy = false;
|
||||
do {
|
||||
// First figure out where to store a copy of the original.
|
||||
// Ideally leave the original itself intact until the
|
||||
// atomic swap. If no room on the same partition, fall back
|
||||
// to the cache partition and remove the original.
|
||||
|
||||
if (!enough_space) {
|
||||
printf("Target is %ldB; free space is %ldB: not enough.\n",
|
||||
(long)file.size, (long)free_space);
|
||||
|
||||
retry = 0;
|
||||
if (MakeFreeSpaceOnCache(file.size) < 0) {
|
||||
printf("Not enough free space on '/cache'.\n");
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
if (SaveFileContents(CACHE_TEMP_SOURCE, file) < 0) {
|
||||
printf("Failed to back up source file.\n");
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
made_copy = true;
|
||||
unlink(binary_name);
|
||||
|
||||
size_t free_space = FreeSpaceForFile(target_fs);
|
||||
printf("(now %ld bytes free for target)\n", (long)free_space);
|
||||
}
|
||||
|
||||
result = SaveFileContents(binary_name_atomic, file);
|
||||
if (result != 0) {
|
||||
// Maybe the filesystem was optimistic: retry.
|
||||
enough_space = false;
|
||||
unlink(binary_name_atomic);
|
||||
printf("Saving the retouched contents failed; retrying.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Succeeded; no need to retry.
|
||||
break;
|
||||
} while (retry-- > 0);
|
||||
|
||||
// Give the .atomic file the same owner, group, and mode of the
|
||||
// original source file.
|
||||
if (chmod(binary_name_atomic, file.st.st_mode) != 0) {
|
||||
printf("chmod of \"%s\" failed: %s\n",
|
||||
binary_name_atomic, strerror(errno));
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
if (chown(binary_name_atomic, file.st.st_uid, file.st.st_gid) != 0) {
|
||||
printf("chown of \"%s\" failed: %s\n",
|
||||
binary_name_atomic,
|
||||
strerror(errno));
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Finally, rename the .atomic file to replace the target file.
|
||||
if (rename(binary_name_atomic, binary_name) != 0) {
|
||||
printf("rename of .atomic to \"%s\" failed: %s\n",
|
||||
binary_name, strerror(errno));
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// If this run created a copy, and we're here, we can delete it.
|
||||
if (made_copy) unlink(CACHE_TEMP_SOURCE);
|
||||
|
||||
out:
|
||||
// clean up
|
||||
free(file.data);
|
||||
unlink(binary_name_atomic);
|
||||
|
||||
return success;
|
||||
}
|
51
minelf/Retouch.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 _MINELF_RETOUCH
|
||||
#define _MINELF_RETOUCH
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct {
|
||||
char tag[8]; /* "RETOUCH ", not zero-terminated */
|
||||
uint32_t blob_size; /* in bytes, located right before this struct */
|
||||
} retouch_info_t __attribute__((packed));
|
||||
|
||||
// Retouch a file. Use CACHED_SOURCE_TEMP to store a copy.
|
||||
bool retouch_one_library(const char *binary_name,
|
||||
const char *binary_sha1,
|
||||
int32_t retouch_offset,
|
||||
int32_t *retouch_offset_override);
|
||||
|
||||
#define RETOUCH_DONT_MASK 0
|
||||
#define RETOUCH_DO_MASK 1
|
||||
|
||||
#define RETOUCH_DATA_ERROR 0 // This is bad. Should not happen.
|
||||
#define RETOUCH_DATA_MATCHED 1 // Up to an uniform random offset.
|
||||
#define RETOUCH_DATA_MISMATCHED 2 // Partially randomized, or total mess.
|
||||
#define RETOUCH_DATA_NOTAPPLICABLE 3 // Not retouched. Only when inferring.
|
||||
|
||||
// Mask retouching in-memory. Used before apply_patch[_check].
|
||||
// Also used to determine status of retouching after a crash.
|
||||
//
|
||||
// If desired_offset is not NULL, then apply retouching instead,
|
||||
// and return that in retouch_offset.
|
||||
int retouch_mask_data(uint8_t *binary_object,
|
||||
int32_t binary_size,
|
||||
int32_t *desired_offset,
|
||||
int32_t *retouch_offset);
|
||||
#endif
|
@ -1,29 +1,19 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := events.c resources.c
|
||||
ifneq ($(BOARD_CUSTOM_GRAPHICS),)
|
||||
LOCAL_SRC_FILES += $(BOARD_CUSTOM_GRAPHICS)
|
||||
else
|
||||
LOCAL_SRC_FILES += graphics.c
|
||||
endif
|
||||
LOCAL_SRC_FILES := graphics.c events.c resources.c
|
||||
|
||||
LOCAL_C_INCLUDES +=\
|
||||
external/libpng\
|
||||
external/zlib
|
||||
|
||||
ifneq ($(BOARD_LDPI_RECOVERY),)
|
||||
LOCAL_CFLAGS += -DBOARD_LDPI_RECOVERY='"$(BOARD_LDPI_RECOVERY)"'
|
||||
endif
|
||||
|
||||
ifneq ($(BOARD_HAS_JANKY_BACKBUFFER),)
|
||||
LOCAL_CFLAGS += -DBOARD_HAS_JANKY_BACKBUFFER
|
||||
endif
|
||||
|
||||
ifeq ($(BOARD_HAS_FLIPPED_SCREEN), true)
|
||||
LOCAL_CFLAGS += -DBOARD_HAS_FLIPPED_SCREEN
|
||||
endif
|
||||
|
||||
LOCAL_MODULE := libminui
|
||||
|
||||
ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"RGBX_8888")
|
||||
LOCAL_CFLAGS += -DRECOVERY_RGBX
|
||||
endif
|
||||
ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"BGRA_8888")
|
||||
LOCAL_CFLAGS += -DRECOVERY_BGRA
|
||||
endif
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
399
minui/events.c
@ -19,167 +19,33 @@
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/poll.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#include "minui.h"
|
||||
|
||||
#define MAX_DEVICES 16
|
||||
#define MAX_MISC_FDS 16
|
||||
|
||||
#define VIBRATOR_TIMEOUT_FILE "/sys/class/timed_output/vibrator/enable"
|
||||
#define VIBRATOR_TIME_MS 50
|
||||
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
|
||||
#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
|
||||
|
||||
#define PRESS_THRESHHOLD 10
|
||||
#define test_bit(bit, array) \
|
||||
((array)[(bit)/BITS_PER_LONG] & (1 << ((bit) % BITS_PER_LONG)))
|
||||
|
||||
#define ABS_MT_POSITION_X 0x35
|
||||
#define ABS_MT_POSITION_Y 0x36
|
||||
#define ABS_MT_TOUCH_MAJOR 0x30
|
||||
#define SYN_MT_REPORT 2
|
||||
|
||||
struct virtualkey {
|
||||
int scancode;
|
||||
int centerx, centery;
|
||||
int width, height;
|
||||
struct fd_info {
|
||||
ev_callback cb;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct position {
|
||||
int x, y;
|
||||
int pressed;
|
||||
struct input_absinfo xi, yi;
|
||||
};
|
||||
static struct pollfd ev_fds[MAX_DEVICES + MAX_MISC_FDS];
|
||||
static struct fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
|
||||
|
||||
struct ev {
|
||||
struct pollfd *fd;
|
||||
|
||||
struct virtualkey *vks;
|
||||
int vk_count;
|
||||
|
||||
struct position p, mt_p;
|
||||
int sent, mt_idx;
|
||||
};
|
||||
|
||||
static struct pollfd ev_fds[MAX_DEVICES];
|
||||
static struct ev evs[MAX_DEVICES];
|
||||
static unsigned ev_count = 0;
|
||||
static unsigned ev_dev_count = 0;
|
||||
static unsigned ev_misc_count = 0;
|
||||
|
||||
static inline int ABS(int x) {
|
||||
return x<0?-x:x;
|
||||
}
|
||||
|
||||
int vibrate(int timeout_ms)
|
||||
{
|
||||
char str[20];
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
ret = snprintf(str, sizeof(str), "%d", timeout_ms);
|
||||
ret = write(fd, str, ret);
|
||||
close(fd);
|
||||
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns empty tokens */
|
||||
static char *vk_strtok_r(char *str, const char *delim, char **save_str)
|
||||
{
|
||||
if(!str) {
|
||||
if(!*save_str) return NULL;
|
||||
str = (*save_str) + 1;
|
||||
}
|
||||
*save_str = strpbrk(str, delim);
|
||||
if(*save_str) **save_str = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
static int vk_init(struct ev *e)
|
||||
{
|
||||
char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys.";
|
||||
char vks[2048], *ts;
|
||||
ssize_t len;
|
||||
int vk_fd;
|
||||
int i;
|
||||
|
||||
e->vk_count = 0;
|
||||
|
||||
len = strlen(vk_path);
|
||||
len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(vk_path) - len), vk_path + len);
|
||||
if (len <= 0)
|
||||
return -1;
|
||||
|
||||
vk_fd = open(vk_path, O_RDONLY);
|
||||
if (vk_fd < 0)
|
||||
return -1;
|
||||
|
||||
len = read(vk_fd, vks, sizeof(vks)-1);
|
||||
close(vk_fd);
|
||||
if (len <= 0)
|
||||
return -1;
|
||||
|
||||
vks[len] = '\0';
|
||||
|
||||
/* Parse a line like:
|
||||
keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:...
|
||||
*/
|
||||
for (ts = vks, e->vk_count = 1; *ts; ++ts) {
|
||||
if (*ts == ':')
|
||||
++e->vk_count;
|
||||
}
|
||||
|
||||
if (e->vk_count % 6) {
|
||||
LOGW("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6);
|
||||
}
|
||||
e->vk_count /= 6;
|
||||
if (e->vk_count <= 0)
|
||||
return -1;
|
||||
|
||||
e->sent = 0;
|
||||
e->mt_idx = 0;
|
||||
|
||||
ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi);
|
||||
ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi);
|
||||
e->p.pressed = 0;
|
||||
|
||||
ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi);
|
||||
ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi);
|
||||
e->mt_p.pressed = 0;
|
||||
|
||||
e->vks = malloc(sizeof(*e->vks) * e->vk_count);
|
||||
|
||||
for (i = 0; i < e->vk_count; ++i) {
|
||||
char *token[6];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 6; ++j) {
|
||||
token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts);
|
||||
}
|
||||
|
||||
if (strcmp(token[0], "0x01") != 0) {
|
||||
/* Java does string compare, so we do too. */
|
||||
LOGW("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]);
|
||||
continue;
|
||||
}
|
||||
|
||||
e->vks[i].scancode = strtol(token[1], NULL, 0);
|
||||
e->vks[i].centerx = strtol(token[2], NULL, 0);
|
||||
e->vks[i].centery = strtol(token[3], NULL, 0);
|
||||
e->vks[i].width = strtol(token[4], NULL, 0);
|
||||
e->vks[i].height = strtol(token[5], NULL, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ev_init(void)
|
||||
int ev_init(ev_callback input_cb, void *data)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
@ -188,177 +54,122 @@ int ev_init(void)
|
||||
dir = opendir("/dev/input");
|
||||
if(dir != 0) {
|
||||
while((de = readdir(dir))) {
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
|
||||
|
||||
// fprintf(stderr,"/dev/input/%s\n", de->d_name);
|
||||
if(strncmp(de->d_name,"event",5)) continue;
|
||||
fd = openat(dirfd(dir), de->d_name, O_RDONLY);
|
||||
if(fd < 0) continue;
|
||||
|
||||
/* read the evbits of the input device */
|
||||
if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* TODO: add ability to specify event masks. For now, just assume
|
||||
* that only EV_KEY and EV_REL event types are ever needed. */
|
||||
if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits)) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
ev_fds[ev_count].fd = fd;
|
||||
ev_fds[ev_count].events = POLLIN;
|
||||
evs[ev_count].fd = &ev_fds[ev_count];
|
||||
|
||||
/* Load virtualkeys if there are any */
|
||||
vk_init(&evs[ev_count]);
|
||||
|
||||
ev_fdinfo[ev_count].cb = input_cb;
|
||||
ev_fdinfo[ev_count].data = data;
|
||||
ev_count++;
|
||||
if(ev_count == MAX_DEVICES) break;
|
||||
ev_dev_count++;
|
||||
if(ev_dev_count == MAX_DEVICES) break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ev_add_fd(int fd, ev_callback cb, void *data)
|
||||
{
|
||||
if (ev_misc_count == MAX_MISC_FDS || cb == NULL)
|
||||
return -1;
|
||||
|
||||
ev_fds[ev_count].fd = fd;
|
||||
ev_fds[ev_count].events = POLLIN;
|
||||
ev_fdinfo[ev_count].cb = cb;
|
||||
ev_fdinfo[ev_count].data = data;
|
||||
ev_count++;
|
||||
ev_misc_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ev_exit(void)
|
||||
{
|
||||
while (ev_count-- > 0) {
|
||||
if (evs[ev_count].vk_count) {
|
||||
free(evs[ev_count].vks);
|
||||
evs[ev_count].vk_count = 0;
|
||||
}
|
||||
close(ev_fds[ev_count].fd);
|
||||
while (ev_count > 0) {
|
||||
close(ev_fds[--ev_count].fd);
|
||||
}
|
||||
ev_misc_count = 0;
|
||||
ev_dev_count = 0;
|
||||
}
|
||||
|
||||
static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size)
|
||||
{
|
||||
int screen_pos;
|
||||
|
||||
if (info->minimum == info->maximum)
|
||||
return 0;
|
||||
|
||||
screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum);
|
||||
return (screen_pos >= 0 && screen_pos < screen_size);
|
||||
}
|
||||
|
||||
static int vk_tp_to_screen(struct position *p, int *x, int *y)
|
||||
{
|
||||
if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum)
|
||||
return 0;
|
||||
|
||||
*x = (p->x - p->xi.minimum) * (gr_fb_width() - 1) / (p->xi.maximum - p->xi.minimum);
|
||||
*y = (p->y - p->yi.minimum) * (gr_fb_height() - 1) / (p->yi.maximum - p->yi.minimum);
|
||||
|
||||
if (*x >= 0 && *x < gr_fb_width() &&
|
||||
*y >= 0 && *y < gr_fb_height()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Translate a virtual key in to a real key event, if needed */
|
||||
/* Returns non-zero when the event should be consumed */
|
||||
static int vk_modify(struct ev *e, struct input_event *ev)
|
||||
{
|
||||
int i;
|
||||
int x, y;
|
||||
|
||||
if (ev->type == EV_KEY) {
|
||||
if (ev->code == BTN_TOUCH)
|
||||
e->p.pressed = ev->value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ev->type == EV_ABS) {
|
||||
switch (ev->code) {
|
||||
case ABS_X:
|
||||
e->p.x = ev->value;
|
||||
return !vk_inside_display(e->p.x, &e->p.xi, gr_fb_width());
|
||||
case ABS_Y:
|
||||
e->p.y = ev->value;
|
||||
return !vk_inside_display(e->p.y, &e->p.yi, gr_fb_height());
|
||||
case ABS_MT_POSITION_X:
|
||||
if (e->mt_idx) return 1;
|
||||
e->mt_p.x = ev->value;
|
||||
return !vk_inside_display(e->mt_p.x, &e->mt_p.xi, gr_fb_width());
|
||||
case ABS_MT_POSITION_Y:
|
||||
if (e->mt_idx) return 1;
|
||||
e->mt_p.y = ev->value;
|
||||
return !vk_inside_display(e->mt_p.y, &e->mt_p.yi, gr_fb_height());
|
||||
case ABS_MT_TOUCH_MAJOR:
|
||||
if (e->mt_idx) return 1;
|
||||
if (e->sent)
|
||||
e->mt_p.pressed = (ev->value > 0);
|
||||
else
|
||||
e->mt_p.pressed = (ev->value > PRESS_THRESHHOLD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ev->type != EV_SYN)
|
||||
return 0;
|
||||
|
||||
if (ev->code == SYN_MT_REPORT) {
|
||||
/* Ignore the rest of the points */
|
||||
++e->mt_idx;
|
||||
return 1;
|
||||
}
|
||||
if (ev->code != SYN_REPORT)
|
||||
return 0;
|
||||
|
||||
/* Report complete */
|
||||
|
||||
e->mt_idx = 0;
|
||||
|
||||
if (!e->p.pressed && !e->mt_p.pressed) {
|
||||
/* No touch */
|
||||
e->sent = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(e->p.pressed && vk_tp_to_screen(&e->p, &x, &y)) &&
|
||||
!(e->mt_p.pressed && vk_tp_to_screen(&e->mt_p, &x, &y))) {
|
||||
/* No touch inside vk area */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (e->sent) {
|
||||
/* We've already sent a fake key for this touch */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The screen is being touched on the vk area */
|
||||
e->sent = 1;
|
||||
|
||||
for (i = 0; i < e->vk_count; ++i) {
|
||||
int xd = ABS(e->vks[i].centerx - x);
|
||||
int yd = ABS(e->vks[i].centery - y);
|
||||
if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2) {
|
||||
/* Fake a key event */
|
||||
ev->type = EV_KEY;
|
||||
ev->code = e->vks[i].scancode;
|
||||
ev->value = 1;
|
||||
|
||||
vibrate(VIBRATOR_TIME_MS);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ev_get(struct input_event *ev, unsigned dont_wait)
|
||||
int ev_wait(int timeout)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = poll(ev_fds, ev_count, timeout);
|
||||
if (r <= 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ev_dispatch(void)
|
||||
{
|
||||
unsigned n;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
r = poll(ev_fds, ev_count, dont_wait ? 0 : -1);
|
||||
for (n = 0; n < ev_count; n++) {
|
||||
ev_callback cb = ev_fdinfo[n].cb;
|
||||
if (cb && (ev_fds[n].revents & ev_fds[n].events))
|
||||
cb(ev_fds[n].fd, ev_fds[n].revents, ev_fdinfo[n].data);
|
||||
}
|
||||
}
|
||||
|
||||
if(r > 0) {
|
||||
for(n = 0; n < ev_count; n++) {
|
||||
if(ev_fds[n].revents & POLLIN) {
|
||||
r = read(ev_fds[n].fd, ev, sizeof(*ev));
|
||||
if(r == sizeof(*ev)) {
|
||||
if (!vk_modify(&evs[n], ev))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(dont_wait == 0);
|
||||
int ev_get_input(int fd, short revents, struct input_event *ev)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (revents & POLLIN) {
|
||||
r = read(fd, ev, sizeof(*ev));
|
||||
if (r == sizeof(*ev))
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data)
|
||||
{
|
||||
unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
|
||||
unsigned i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < ev_dev_count; i++) {
|
||||
int code;
|
||||
|
||||
memset(key_bits, 0, sizeof(key_bits));
|
||||
memset(ev_bits, 0, sizeof(ev_bits));
|
||||
|
||||
ret = ioctl(ev_fds[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
|
||||
if (ret < 0 || !test_bit(EV_KEY, ev_bits))
|
||||
continue;
|
||||
|
||||
ret = ioctl(ev_fds[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
for (code = 0; code <= KEY_MAX; code++) {
|
||||
if (test_bit(code, key_bits))
|
||||
set_key_cb(code, 1, data);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
120
minui/graphics.c
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -29,14 +30,20 @@
|
||||
|
||||
#include <pixelflinger/pixelflinger.h>
|
||||
|
||||
#ifndef BOARD_LDPI_RECOVERY
|
||||
#include "font_10x18.h"
|
||||
#else
|
||||
#include "font_7x16.h"
|
||||
#endif
|
||||
|
||||
#include "font_10x18.h"
|
||||
#include "minui.h"
|
||||
|
||||
#if defined(RECOVERY_BGRA)
|
||||
#define PIXEL_FORMAT GGL_PIXEL_FORMAT_BGRA_8888
|
||||
#define PIXEL_SIZE 4
|
||||
#elif defined(RECOVERY_RGBX)
|
||||
#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888
|
||||
#define PIXEL_SIZE 4
|
||||
#else
|
||||
#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565
|
||||
#define PIXEL_SIZE 2
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
GGLSurface texture;
|
||||
unsigned cwidth;
|
||||
@ -55,11 +62,11 @@ static int gr_fb_fd = -1;
|
||||
static int gr_vt_fd = -1;
|
||||
|
||||
static struct fb_var_screeninfo vi;
|
||||
static struct fb_fix_screeninfo fi;
|
||||
|
||||
static int get_framebuffer(GGLSurface *fb)
|
||||
{
|
||||
int fd;
|
||||
struct fb_fix_screeninfo fi;
|
||||
void *bits;
|
||||
|
||||
fd = open("/dev/graphics/fb0", O_RDWR);
|
||||
@ -68,13 +75,48 @@ static int get_framebuffer(GGLSurface *fb)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
|
||||
perror("failed to get fb0 info");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
|
||||
vi.bits_per_pixel = PIXEL_SIZE * 8;
|
||||
if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_BGRA_8888) {
|
||||
vi.red.offset = 8;
|
||||
vi.red.length = 8;
|
||||
vi.green.offset = 16;
|
||||
vi.green.length = 8;
|
||||
vi.blue.offset = 24;
|
||||
vi.blue.length = 8;
|
||||
vi.transp.offset = 0;
|
||||
vi.transp.length = 8;
|
||||
} else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGBX_8888) {
|
||||
vi.red.offset = 24;
|
||||
vi.red.length = 8;
|
||||
vi.green.offset = 16;
|
||||
vi.green.length = 8;
|
||||
vi.blue.offset = 8;
|
||||
vi.blue.length = 8;
|
||||
vi.transp.offset = 0;
|
||||
vi.transp.length = 8;
|
||||
} else { /* RGB565*/
|
||||
vi.red.offset = 11;
|
||||
vi.red.length = 5;
|
||||
vi.green.offset = 5;
|
||||
vi.green.length = 6;
|
||||
vi.blue.offset = 0;
|
||||
vi.blue.length = 5;
|
||||
vi.transp.offset = 0;
|
||||
vi.transp.length = 0;
|
||||
}
|
||||
if (ioctl(fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
|
||||
perror("failed to put fb0 info");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
|
||||
perror("failed to get fb0 info");
|
||||
close(fd);
|
||||
return -1;
|
||||
@ -90,29 +132,20 @@ static int get_framebuffer(GGLSurface *fb)
|
||||
fb->version = sizeof(*fb);
|
||||
fb->width = vi.xres;
|
||||
fb->height = vi.yres;
|
||||
#ifdef BOARD_HAS_JANKY_BACKBUFFER
|
||||
fb->stride = fi.line_length/2;
|
||||
#else
|
||||
fb->stride = vi.xres;
|
||||
#endif
|
||||
fb->stride = fi.line_length/PIXEL_SIZE;
|
||||
fb->data = bits;
|
||||
fb->format = GGL_PIXEL_FORMAT_RGB_565;
|
||||
memset(fb->data, 0, vi.yres * vi.xres * 2);
|
||||
fb->format = PIXEL_FORMAT;
|
||||
memset(fb->data, 0, vi.yres * fi.line_length);
|
||||
|
||||
fb++;
|
||||
|
||||
fb->version = sizeof(*fb);
|
||||
fb->width = vi.xres;
|
||||
fb->height = vi.yres;
|
||||
#ifdef BOARD_HAS_JANKY_BACKBUFFER
|
||||
fb->stride = fi.line_length/2;
|
||||
fb->stride = fi.line_length/PIXEL_SIZE;
|
||||
fb->data = (void*) (((unsigned) bits) + vi.yres * fi.line_length);
|
||||
#else
|
||||
fb->stride = vi.xres;
|
||||
fb->data = (void*) (((unsigned) bits) + vi.yres * vi.xres * 2);
|
||||
#endif
|
||||
fb->format = GGL_PIXEL_FORMAT_RGB_565;
|
||||
memset(fb->data, 0, vi.yres * vi.xres * 2);
|
||||
fb->format = PIXEL_FORMAT;
|
||||
memset(fb->data, 0, vi.yres * fi.line_length);
|
||||
|
||||
return fd;
|
||||
}
|
||||
@ -121,17 +154,17 @@ static void get_memory_surface(GGLSurface* ms) {
|
||||
ms->version = sizeof(*ms);
|
||||
ms->width = vi.xres;
|
||||
ms->height = vi.yres;
|
||||
ms->stride = vi.xres;
|
||||
ms->data = malloc(vi.xres * vi.yres * 2);
|
||||
ms->format = GGL_PIXEL_FORMAT_RGB_565;
|
||||
ms->stride = fi.line_length/PIXEL_SIZE;
|
||||
ms->data = malloc(fi.line_length * vi.yres);
|
||||
ms->format = PIXEL_FORMAT;
|
||||
}
|
||||
|
||||
static void set_active_framebuffer(unsigned n)
|
||||
{
|
||||
if (n > 1) return;
|
||||
vi.yres_virtual = vi.yres * 2;
|
||||
vi.yres_virtual = vi.yres * PIXEL_SIZE;
|
||||
vi.yoffset = n * vi.yres;
|
||||
vi.bits_per_pixel = 16;
|
||||
vi.bits_per_pixel = PIXEL_SIZE * 8;
|
||||
if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
|
||||
perror("active fb swap failed");
|
||||
}
|
||||
@ -144,20 +177,10 @@ void gr_flip(void)
|
||||
/* swap front and back buffers */
|
||||
gr_active_fb = (gr_active_fb + 1) & 1;
|
||||
|
||||
#ifdef BOARD_HAS_FLIPPED_SCREEN
|
||||
/* flip buffer 180 degrees for devices with physicaly inverted screens */
|
||||
unsigned int i;
|
||||
for (i = 1; i < (vi.xres * vi.yres); i++) {
|
||||
unsigned short tmp = gr_mem_surface.data[i];
|
||||
gr_mem_surface.data[i] = gr_mem_surface.data[(vi.xres * vi.yres * 2) - i];
|
||||
gr_mem_surface.data[(vi.xres * vi.yres * 2) - i] = tmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* copy data from the in-memory surface to the buffer we're about
|
||||
* to make active. */
|
||||
memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data,
|
||||
vi.xres * vi.yres * 2);
|
||||
fi.line_length * vi.yres);
|
||||
|
||||
/* inform the display driver */
|
||||
set_active_framebuffer(gr_active_fb);
|
||||
@ -179,6 +202,12 @@ int gr_measure(const char *s)
|
||||
return gr_font->cwidth * strlen(s);
|
||||
}
|
||||
|
||||
void gr_font_size(int *x, int *y)
|
||||
{
|
||||
*x = gr_font->cwidth;
|
||||
*y = gr_font->cheight;
|
||||
}
|
||||
|
||||
int gr_text(int x, int y, const char *s)
|
||||
{
|
||||
GGLContext *gl = gr_context;
|
||||
@ -307,6 +336,9 @@ int gr_init(void)
|
||||
gl->enable(gl, GGL_BLEND);
|
||||
gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
gr_fb_blank(true);
|
||||
gr_fb_blank(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -337,3 +369,11 @@ gr_pixel *gr_fb_data(void)
|
||||
return (unsigned short *) gr_mem_surface.data;
|
||||
}
|
||||
|
||||
void gr_fb_blank(bool blank)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
|
||||
if (ret < 0)
|
||||
perror("ioctl(): blank");
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
#ifndef _MINUI_H_
|
||||
#define _MINUI_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void* gr_surface;
|
||||
typedef unsigned short gr_pixel;
|
||||
|
||||
@ -27,11 +29,13 @@ int gr_fb_width(void);
|
||||
int gr_fb_height(void);
|
||||
gr_pixel *gr_fb_data(void);
|
||||
void gr_flip(void);
|
||||
void gr_fb_blank(bool blank);
|
||||
|
||||
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||
void gr_fill(int x, int y, int w, int h);
|
||||
int gr_text(int x, int y, const char *s);
|
||||
int gr_measure(const char *s);
|
||||
void gr_font_size(int *x, int *y);
|
||||
|
||||
void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);
|
||||
unsigned int gr_get_width(gr_surface surface);
|
||||
@ -41,9 +45,23 @@ unsigned int gr_get_height(gr_surface surface);
|
||||
// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
|
||||
struct input_event;
|
||||
|
||||
int ev_init(void);
|
||||
typedef int (*ev_callback)(int fd, short revents, void *data);
|
||||
typedef int (*ev_set_key_callback)(int code, int value, void *data);
|
||||
|
||||
int ev_init(ev_callback input_cb, void *data);
|
||||
void ev_exit(void);
|
||||
int ev_get(struct input_event *ev, unsigned dont_wait);
|
||||
int ev_add_fd(int fd, ev_callback cb, void *data);
|
||||
int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data);
|
||||
|
||||
/* timeout has the same semantics as for poll
|
||||
* 0 : don't block
|
||||
* < 0 : block forever
|
||||
* > 0 : block for 'timeout' milliseconds
|
||||
*/
|
||||
int ev_wait(int timeout);
|
||||
|
||||
int ev_get_input(int fd, short revents, struct input_event *ev);
|
||||
void ev_dispatch(void);
|
||||
|
||||
// Resources
|
||||
|
||||
|
@ -49,6 +49,8 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
|
||||
*pSurface = NULL;
|
||||
|
||||
snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
|
||||
resPath[sizeof(resPath)-1] = '\0';
|
||||
FILE* fp = fopen(resPath, "rb");
|
||||
@ -119,13 +121,18 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
|
||||
surface->format = (channels == 3) ?
|
||||
GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888;
|
||||
|
||||
int alpha = 0;
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
}
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
||||
png_set_tRNS_to_alpha(png_ptr);
|
||||
alpha = 1;
|
||||
}
|
||||
|
||||
int y;
|
||||
if (channels == 3) {
|
||||
for (y = 0; y < (int)height; ++y) {
|
||||
if (channels == 3 || (channels == 1 && !alpha)) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
unsigned char* pRow = pData + y * stride;
|
||||
png_read_row(png_ptr, pRow, NULL);
|
||||
|
||||
@ -144,7 +151,7 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < (int)height; ++y) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
unsigned char* pRow = pData + y * stride;
|
||||
png_read_row(png_ptr, pRow, NULL);
|
||||
}
|
||||
|
@ -1,6 +1,3 @@
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
@ -30,6 +27,3 @@ LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
include $(BUILD_EXECUTABLE)
|
||||
endif
|
||||
|
||||
endif # TARGET_ARCH == arm
|
||||
endif # !TARGET_SIMULATOR
|
||||
|
@ -262,8 +262,8 @@ MtdReadContext *mtd_read_partition(const MtdPartition *partition)
|
||||
sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
|
||||
ctx->fd = open(mtddevname, O_RDONLY);
|
||||
if (ctx->fd < 0) {
|
||||
free(ctx);
|
||||
free(ctx->buffer);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
138
recovery.c
@ -24,7 +24,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
@ -35,12 +34,12 @@
|
||||
#include "bootloader.h"
|
||||
#include "common.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "cutils/android_reboot.h"
|
||||
#include "install.h"
|
||||
#include "minui/minui.h"
|
||||
#include "minzip/DirUtil.h"
|
||||
#include "roots.h"
|
||||
#include "recovery_ui.h"
|
||||
#include "encryptedfs_provisioning.h"
|
||||
|
||||
#include "extendedcommands.h"
|
||||
#include "flashutils/flashutils.h"
|
||||
@ -50,7 +49,6 @@ static const struct option OPTIONS[] = {
|
||||
{ "update_package", required_argument, NULL, 'u' },
|
||||
{ "wipe_data", no_argument, NULL, 'w' },
|
||||
{ "wipe_cache", no_argument, NULL, 'c' },
|
||||
{ "set_encrypted_filesystems", required_argument, NULL, 'e' },
|
||||
{ "show_text", no_argument, NULL, 't' },
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
@ -59,6 +57,7 @@ static const char *COMMAND_FILE = "/cache/recovery/command";
|
||||
static const char *INTENT_FILE = "/cache/recovery/intent";
|
||||
static const char *LOG_FILE = "/cache/recovery/log";
|
||||
static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
|
||||
static const char *CACHE_ROOT = "/cache";
|
||||
static const char *SDCARD_ROOT = "/sdcard";
|
||||
static int allow_display_toggle = 1;
|
||||
static int poweroff = 0;
|
||||
@ -66,6 +65,8 @@ static const char *SDCARD_PACKAGE_FILE = "/sdcard/update.zip";
|
||||
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
|
||||
static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
|
||||
|
||||
extern UIParameters ui_parameters; // from ui.c
|
||||
|
||||
/*
|
||||
* The recovery tool communicates with the main system through /cache files.
|
||||
* /cache/recovery/command - INPUT - command line for tool, one arg per line
|
||||
@ -122,33 +123,13 @@ static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
|
||||
* 8g. finish_recovery() erases BCB
|
||||
* -- after this, rebooting will (try to) restart the main system --
|
||||
* 9. main() calls reboot() to boot main system
|
||||
*
|
||||
* SECURE FILE SYSTEMS ENABLE/DISABLE
|
||||
* 1. user selects "enable encrypted file systems"
|
||||
* 2. main system writes "--set_encrypted_filesystems=on|off" to
|
||||
* /cache/recovery/command
|
||||
* 3. main system reboots into recovery
|
||||
* 4. get_args() writes BCB with "boot-recovery" and
|
||||
* "--set_encrypted_filesystems=on|off"
|
||||
* -- after this, rebooting will restart the transition --
|
||||
* 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /data
|
||||
* Settings include: property to specify the Encrypted FS istatus and
|
||||
* FS encryption key if enabled (not yet implemented)
|
||||
* 6. erase_volume() reformats /data
|
||||
* 7. erase_volume() reformats /cache
|
||||
* 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /data
|
||||
* Settings include: property to specify the Encrypted FS status and
|
||||
* FS encryption key if enabled (not yet implemented)
|
||||
* 9. finish_recovery() erases BCB
|
||||
* -- after this, rebooting will restart the main system --
|
||||
* 10. main() calls reboot() to boot main system
|
||||
*/
|
||||
|
||||
static const int MAX_ARG_LENGTH = 4096;
|
||||
static const int MAX_ARGS = 100;
|
||||
|
||||
// open a given path, mounting partitions as necessary
|
||||
static FILE*
|
||||
FILE*
|
||||
fopen_path(const char *path, const char *mode) {
|
||||
if (ensure_path_mounted(path) != 0) {
|
||||
LOGE("Can't mount %s\n", path);
|
||||
@ -467,6 +448,16 @@ get_menu_selection(char** headers, char** items, int menu_only,
|
||||
int key = ui_wait_key();
|
||||
int visible = ui_text_visible();
|
||||
|
||||
if (key == -1) { // ui_wait_key() timed out
|
||||
if (ui_text_ever_visible()) {
|
||||
continue;
|
||||
} else {
|
||||
LOGI("timed out waiting for key input; rebooting.\n");
|
||||
ui_end_menu();
|
||||
return ITEM_REBOOT;
|
||||
}
|
||||
}
|
||||
|
||||
int action = device_handle_key(key, visible);
|
||||
|
||||
int old_selected = selected;
|
||||
@ -525,8 +516,8 @@ static int compare_string(const void* a, const void* b) {
|
||||
}
|
||||
|
||||
static int
|
||||
sdcard_directory(const char* path) {
|
||||
ensure_path_mounted(SDCARD_ROOT);
|
||||
update_directory(const char* path, const char* unmount_when_done) {
|
||||
ensure_path_mounted(path);
|
||||
|
||||
const char* MENU_HEADERS[] = { "Choose a package to install:",
|
||||
path,
|
||||
@ -537,7 +528,9 @@ sdcard_directory(const char* path) {
|
||||
d = opendir(path);
|
||||
if (d == NULL) {
|
||||
LOGE("error opening %s: %s\n", path, strerror(errno));
|
||||
ensure_path_unmounted(SDCARD_ROOT);
|
||||
if (unmount_when_done != NULL) {
|
||||
ensure_path_unmounted(unmount_when_done);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -602,7 +595,7 @@ sdcard_directory(const char* path) {
|
||||
char* item = zips[chosen_item];
|
||||
int item_len = strlen(item);
|
||||
if (chosen_item == 0) { // item 0 is always "../"
|
||||
// go up but continue browsing (if the caller is sdcard_directory)
|
||||
// go up but continue browsing (if the caller is update_directory)
|
||||
result = -1;
|
||||
break;
|
||||
} else if (item[item_len-1] == '/') {
|
||||
@ -612,7 +605,7 @@ sdcard_directory(const char* path) {
|
||||
strlcat(new_path, "/", PATH_MAX);
|
||||
strlcat(new_path, item, PATH_MAX);
|
||||
new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/'
|
||||
result = sdcard_directory(new_path);
|
||||
result = update_directory(new_path, unmount_when_done);
|
||||
if (result >= 0) break;
|
||||
} else {
|
||||
// selected a zip file: attempt to install it, and return
|
||||
@ -625,7 +618,9 @@ sdcard_directory(const char* path) {
|
||||
ui_print("\n-- Install %s ...\n", path);
|
||||
set_sdcard_update_bootloader_message();
|
||||
char* copy = copy_sideloaded_package(new_path);
|
||||
ensure_path_unmounted(SDCARD_ROOT);
|
||||
if (unmount_when_done != NULL) {
|
||||
ensure_path_unmounted(unmount_when_done);
|
||||
}
|
||||
if (copy) {
|
||||
result = install_package(copy);
|
||||
free(copy);
|
||||
@ -641,7 +636,9 @@ sdcard_directory(const char* path) {
|
||||
free(zips);
|
||||
free(headers);
|
||||
|
||||
ensure_path_unmounted(SDCARD_ROOT);
|
||||
if (unmount_when_done != NULL) {
|
||||
ensure_path_unmounted(unmount_when_done);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -706,6 +703,7 @@ prompt_and_wait() {
|
||||
// statement below.
|
||||
chosen_item = device_perform_action(chosen_item);
|
||||
|
||||
int status;
|
||||
switch (chosen_item) {
|
||||
case ITEM_REBOOT:
|
||||
poweroff=0;
|
||||
@ -727,34 +725,23 @@ prompt_and_wait() {
|
||||
break;
|
||||
|
||||
case ITEM_APPLY_SDCARD:
|
||||
if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
|
||||
{
|
||||
ui_print("\n-- Install from sdcard...\n");
|
||||
int status = install_package(SDCARD_PACKAGE_FILE);
|
||||
if (status != INSTALL_SUCCESS) {
|
||||
ui_set_background(BACKGROUND_ICON_ERROR);
|
||||
ui_print("Installation aborted.\n");
|
||||
} else if (!ui_text_visible()) {
|
||||
return; // reboot if logs aren't visible
|
||||
} else {
|
||||
ui_print("\nInstall from sdcard complete.\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ITEM_INSTALL_ZIP:
|
||||
show_install_update_menu();
|
||||
break;
|
||||
|
||||
case ITEM_NANDROID:
|
||||
show_nandroid_menu();
|
||||
break;
|
||||
|
||||
case ITEM_PARTITION:
|
||||
show_partition_menu();
|
||||
break;
|
||||
|
||||
case ITEM_ADVANCED:
|
||||
show_advanced_menu();
|
||||
break;
|
||||
|
||||
case ITEM_POWEROFF:
|
||||
poweroff=1;
|
||||
poweroff = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -811,6 +798,7 @@ main(int argc, char **argv) {
|
||||
freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
|
||||
printf("Starting recovery on %s", ctime(&start));
|
||||
|
||||
device_ui_init(&ui_parameters);
|
||||
ui_init();
|
||||
ui_print(EXPAND(RECOVERY_VERSION)"\n");
|
||||
load_volume_table();
|
||||
@ -821,10 +809,7 @@ main(int argc, char **argv) {
|
||||
int previous_runs = 0;
|
||||
const char *send_intent = NULL;
|
||||
const char *update_package = NULL;
|
||||
const char *encrypted_fs_mode = NULL;
|
||||
int wipe_data = 0, wipe_cache = 0;
|
||||
int toggle_secure_fs = 0;
|
||||
encrypted_fs_info encrypted_fs_data;
|
||||
|
||||
LOGI("Checking arguments.\n");
|
||||
int arg;
|
||||
@ -839,7 +824,6 @@ main(int argc, char **argv) {
|
||||
#endif
|
||||
break;
|
||||
case 'c': wipe_cache = 1; break;
|
||||
case 'e': encrypted_fs_mode = optarg; toggle_secure_fs = 1; break;
|
||||
case 't': ui_show_text(1); break;
|
||||
case '?':
|
||||
LOGE("Invalid command argument\n");
|
||||
@ -876,44 +860,8 @@ main(int argc, char **argv) {
|
||||
printf("\n");
|
||||
|
||||
int status = INSTALL_SUCCESS;
|
||||
|
||||
if (toggle_secure_fs) {
|
||||
if (strcmp(encrypted_fs_mode,"on") == 0) {
|
||||
encrypted_fs_data.mode = MODE_ENCRYPTED_FS_ENABLED;
|
||||
ui_print("Enabling Encrypted FS.\n");
|
||||
} else if (strcmp(encrypted_fs_mode,"off") == 0) {
|
||||
encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
|
||||
ui_print("Disabling Encrypted FS.\n");
|
||||
} else {
|
||||
ui_print("Error: invalid Encrypted FS setting.\n");
|
||||
status = INSTALL_ERROR;
|
||||
}
|
||||
|
||||
// Recovery strategy: if the data partition is damaged, disable encrypted file systems.
|
||||
// This preventsthe device recycling endlessly in recovery mode.
|
||||
if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
|
||||
(read_encrypted_fs_info(&encrypted_fs_data))) {
|
||||
ui_print("Encrypted FS change aborted, resetting to disabled state.\n");
|
||||
encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
|
||||
}
|
||||
|
||||
if (status != INSTALL_ERROR) {
|
||||
if (erase_volume("/data")) {
|
||||
ui_print("Data wipe failed.\n");
|
||||
status = INSTALL_ERROR;
|
||||
} else if (erase_volume("/cache")) {
|
||||
ui_print("Cache wipe failed.\n");
|
||||
status = INSTALL_ERROR;
|
||||
} else if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
|
||||
(restore_encrypted_fs_info(&encrypted_fs_data))) {
|
||||
ui_print("Encrypted FS change aborted.\n");
|
||||
status = INSTALL_ERROR;
|
||||
} else {
|
||||
ui_print("Successfully updated Encrypted FS.\n");
|
||||
status = INSTALL_SUCCESS;
|
||||
}
|
||||
}
|
||||
} else if (update_package != NULL) {
|
||||
if (update_package != NULL) {
|
||||
status = install_package(update_package);
|
||||
if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
|
||||
} else if (wipe_data) {
|
||||
@ -960,12 +908,16 @@ main(int argc, char **argv) {
|
||||
|
||||
// Otherwise, get ready to boot the main system...
|
||||
finish_recovery(send_intent);
|
||||
if(!poweroff)
|
||||
ui_print("Rebooting...\n");
|
||||
else
|
||||
ui_print("Shutting down...\n");
|
||||
|
||||
sync();
|
||||
reboot((!poweroff) ? RB_AUTOBOOT : RB_POWER_OFF);
|
||||
if(!poweroff) {
|
||||
ui_print("Rebooting...\n");
|
||||
android_reboot(ANDROID_RB_RESTART, 0, 0);
|
||||
}
|
||||
else {
|
||||
ui_print("Shutting down...\n");
|
||||
android_reboot(ANDROID_RB_POWEROFF, 0, 0);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,12 @@
|
||||
#ifndef _RECOVERY_UI_H
|
||||
#define _RECOVERY_UI_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
// Called before UI library is initialized. Can change things like
|
||||
// how many frames are included in various animations, etc.
|
||||
extern void device_ui_init(UIParameters* ui_parameters);
|
||||
|
||||
// Called when recovery starts up. Returns 0.
|
||||
extern int device_recovery_start();
|
||||
|
||||
@ -67,14 +73,16 @@ int device_wipe_data();
|
||||
#define GO_BACK -5
|
||||
|
||||
#define ITEM_REBOOT 0
|
||||
#define ITEM_APPLY_SDCARD 1
|
||||
#define ITEM_APPLY_EXT 1
|
||||
#define ITEM_APPLY_SDCARD 1 // historical synonym for ITEM_APPLY_EXT
|
||||
#define ITEM_WIPE_DATA 2
|
||||
#define ITEM_WIPE_CACHE 3
|
||||
#define ITEM_INSTALL_ZIP 4
|
||||
#define ITEM_NANDROID 5
|
||||
#define ITEM_PARTITION 6
|
||||
#define ITEM_ADVANCED 7
|
||||
#define ITEM_POWEROFF 8
|
||||
// unused in cwr
|
||||
#define ITEM_APPLY_CACHE 4
|
||||
#define ITEM_NANDROID 4
|
||||
#define ITEM_PARTITION 5
|
||||
#define ITEM_ADVANCED 6
|
||||
#define ITEM_POWEROFF 7
|
||||
|
||||
// Header text to display above the main menu.
|
||||
extern char* MENU_HEADERS[];
|
||||
|
BIN
res/images/icon_error.png
Executable file → Normal file
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 19 KiB |
BIN
res/images/icon_installing.png
Executable file → Normal file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 25 KiB |
BIN
res/images/icon_installing_overlay01.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
res/images/icon_installing_overlay02.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
res/images/icon_installing_overlay03.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
res/images/icon_installing_overlay04.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
res/images/icon_installing_overlay05.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
res/images/icon_installing_overlay06.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
res/images/icon_installing_overlay07.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
res/images/indeterminate01.png
Normal file
After Width: | Height: | Size: 673 B |
BIN
res/images/indeterminate02.png
Normal file
After Width: | Height: | Size: 687 B |
BIN
res/images/indeterminate03.png
Normal file
After Width: | Height: | Size: 661 B |
BIN
res/images/indeterminate04.png
Normal file
After Width: | Height: | Size: 665 B |
BIN
res/images/indeterminate05.png
Normal file
After Width: | Height: | Size: 683 B |
BIN
res/images/indeterminate06.png
Normal file
After Width: | Height: | Size: 676 B |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 361 B After Width: | Height: | Size: 118 B |
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 404 B |
61
roots.c
@ -56,6 +56,28 @@ static char* dupe_string(const char* sz) {
|
||||
return strdup(sz);
|
||||
}
|
||||
|
||||
static int parse_options(char* options, Volume* volume) {
|
||||
char* option;
|
||||
while (option = strtok(options, ",")) {
|
||||
options = NULL;
|
||||
|
||||
if (strncmp(option, "length=", 7) == 0) {
|
||||
volume->length = strtoll(option+7, NULL, 10);
|
||||
} else if (strncmp(option, "fstype2=", 8) == 0) {
|
||||
volume->fs_type2 = volume->fs_type;
|
||||
volume->fs_type = strdup(option);
|
||||
} else if (strncmp(option, "fs_options=", 11) == 0) {
|
||||
volume->fs_options = strdup(option);
|
||||
} else if (strncmp(option, "fs_options2=", 12) == 0) {
|
||||
volume->fs_options2 = strdup(option);
|
||||
} else {
|
||||
LOGE("bad option \"%s\"\n", option);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void load_volume_table() {
|
||||
int alloc = 2;
|
||||
device_volumes = malloc(alloc * sizeof(Volume));
|
||||
@ -65,8 +87,10 @@ void load_volume_table() {
|
||||
device_volumes[0].fs_type = "ramdisk";
|
||||
device_volumes[0].device = NULL;
|
||||
device_volumes[0].device2 = NULL;
|
||||
device_volumes[0].fs_type2 = NULL;
|
||||
device_volumes[0].fs_options = NULL;
|
||||
device_volumes[0].fs_options2 = NULL;
|
||||
device_volumes[0].length = 0;
|
||||
num_volumes = 1;
|
||||
|
||||
FILE* fstab = fopen("/etc/recovery.fstab", "r");
|
||||
@ -88,10 +112,16 @@ void load_volume_table() {
|
||||
char* device = strtok(NULL, " \t\n");
|
||||
// lines may optionally have a second device, to use if
|
||||
// mounting the first one fails.
|
||||
char* options = NULL;
|
||||
char* device2 = strtok(NULL, " \t\n");
|
||||
char* fs_type2 = strtok(NULL, " \t\n");
|
||||
char* fs_options = strtok(NULL, " \t\n");
|
||||
char* fs_options2 = strtok(NULL, " \t\n");
|
||||
if (device2) {
|
||||
if (device2[0] == '/') {
|
||||
options = strtok(NULL, " \t\n");
|
||||
} else {
|
||||
options = device2;
|
||||
device2 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (mount_point && fs_type && device) {
|
||||
while (num_volumes >= alloc) {
|
||||
@ -99,21 +129,17 @@ void load_volume_table() {
|
||||
device_volumes = realloc(device_volumes, alloc*sizeof(Volume));
|
||||
}
|
||||
device_volumes[num_volumes].mount_point = strdup(mount_point);
|
||||
device_volumes[num_volumes].fs_type = !is_null(fs_type2) ? strdup(fs_type2) : strdup(fs_type);
|
||||
device_volumes[num_volumes].fs_type = strdup(fs_type);
|
||||
device_volumes[num_volumes].device = strdup(device);
|
||||
device_volumes[num_volumes].device2 =
|
||||
!is_null(device2) ? strdup(device2) : NULL;
|
||||
device_volumes[num_volumes].fs_type2 = !is_null(fs_type2) ? strdup(fs_type) : NULL;
|
||||
device2 ? strdup(device2) : NULL;
|
||||
|
||||
if (!is_null(fs_type2)) {
|
||||
device_volumes[num_volumes].fs_options2 = dupe_string(fs_options);
|
||||
device_volumes[num_volumes].fs_options = dupe_string(fs_options2);
|
||||
device_volumes[num_volumes].length = 0;
|
||||
if (parse_options(options, device_volumes + num_volumes) != 0) {
|
||||
LOGE("skipping malformed recovery.fstab line: %s\n", original);
|
||||
} else {
|
||||
++num_volumes;
|
||||
}
|
||||
else {
|
||||
device_volumes[num_volumes].fs_options2 = NULL;
|
||||
device_volumes[num_volumes].fs_options = dupe_string(fs_options);
|
||||
}
|
||||
++num_volumes;
|
||||
} else {
|
||||
LOGE("skipping malformed recovery.fstab line: %s\n", original);
|
||||
}
|
||||
@ -126,8 +152,8 @@ void load_volume_table() {
|
||||
printf("=========================\n");
|
||||
for (i = 0; i < num_volumes; ++i) {
|
||||
Volume* v = &device_volumes[i];
|
||||
printf(" %d %s %s %s %s\n", i, v->mount_point, v->fs_type,
|
||||
v->device, v->device2);
|
||||
printf(" %d %s %s %s %s %lld\n", i, v->mount_point, v->fs_type,
|
||||
v->device, v->device2, v->length);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
@ -345,8 +371,7 @@ int format_volume(const char* volume) {
|
||||
}
|
||||
|
||||
if (strcmp(v->fs_type, "ext4") == 0) {
|
||||
reset_ext4fs_info();
|
||||
int result = make_ext4fs(v->device, NULL, NULL, 0, 0, 0);
|
||||
int result = make_ext4fs(v->device, v->length);
|
||||
if (result != 0) {
|
||||
LOGE("format_volume: make_extf4fs failed on %s\n", v->device);
|
||||
return -1;
|
||||
|
@ -14,8 +14,6 @@
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_MODULE := add-property-tag
|
||||
@ -33,5 +31,3 @@ LOCAL_MODULE_TAGS := debug
|
||||
LOCAL_SRC_FILES := check-lost+found.c
|
||||
LOCAL_STATIC_LIBRARIES := libcutils libc
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif # !TARGET_SIMULATOR
|
||||
|
393
ui.c
@ -14,18 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include <cutils/android_reboot.h>
|
||||
#include "minui/minui.h"
|
||||
#include "recovery_ui.h"
|
||||
|
||||
@ -51,12 +55,19 @@ static int gShowBackButton = 0;
|
||||
#define CHAR_HEIGHT 16
|
||||
#endif
|
||||
|
||||
#define PROGRESSBAR_INDETERMINATE_STATES 6
|
||||
#define PROGRESSBAR_INDETERMINATE_FPS 15
|
||||
#define UI_WAIT_KEY_TIMEOUT_SEC 120
|
||||
|
||||
UIParameters ui_parameters = {
|
||||
6, // indeterminate progress bar frames
|
||||
20, // fps
|
||||
7, // installation icon frames (0 == static image)
|
||||
13, 190, // installation icon overlay offset
|
||||
};
|
||||
|
||||
static pthread_mutex_t gUpdateMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS];
|
||||
static gr_surface gProgressBarIndeterminate[PROGRESSBAR_INDETERMINATE_STATES];
|
||||
static gr_surface *gInstallationOverlay;
|
||||
static gr_surface *gProgressBarIndeterminate;
|
||||
static gr_surface gProgressBarEmpty;
|
||||
static gr_surface gProgressBarFill;
|
||||
static int ui_has_initialized = 0;
|
||||
@ -68,18 +79,13 @@ static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
|
||||
{ &gBackgroundIcon[BACKGROUND_ICON_CLOCKWORK], "icon_clockwork" },
|
||||
{ &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_INSTALLING], "icon_firmware_install" },
|
||||
{ &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_ERROR], "icon_firmware_error" },
|
||||
{ &gProgressBarIndeterminate[0], "indeterminate1" },
|
||||
{ &gProgressBarIndeterminate[1], "indeterminate2" },
|
||||
{ &gProgressBarIndeterminate[2], "indeterminate3" },
|
||||
{ &gProgressBarIndeterminate[3], "indeterminate4" },
|
||||
{ &gProgressBarIndeterminate[4], "indeterminate5" },
|
||||
{ &gProgressBarIndeterminate[5], "indeterminate6" },
|
||||
{ &gProgressBarEmpty, "progress_empty" },
|
||||
{ &gProgressBarFill, "progress_fill" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static gr_surface gCurrentIcon = NULL;
|
||||
static int gCurrentIcon = 0;
|
||||
static int gInstallingFrame = 0;
|
||||
|
||||
static enum ProgressBarType {
|
||||
PROGRESSBAR_TYPE_NONE,
|
||||
@ -89,7 +95,7 @@ static enum ProgressBarType {
|
||||
|
||||
// Progress bar scope of current operation
|
||||
static float gProgressScopeStart = 0, gProgressScopeSize = 0, gProgress = 0;
|
||||
static time_t gProgressScopeTime, gProgressScopeDuration;
|
||||
static double gProgressScopeTime, gProgressScopeDuration;
|
||||
|
||||
// Set to 1 when both graphics pages are the same (except for the progress bar)
|
||||
static int gPagesIdentical = 0;
|
||||
@ -99,6 +105,7 @@ static char text[MAX_ROWS][MAX_COLS];
|
||||
static int text_cols = 0, text_rows = 0;
|
||||
static int text_col = 0, text_row = 0, text_top = 0;
|
||||
static int show_text = 0;
|
||||
static int show_text_ever = 0; // has show_text ever been 1?
|
||||
|
||||
static char menu[MENU_MAX_ROWS][MENU_MAX_COLS];
|
||||
static int show_menu = 0;
|
||||
@ -111,20 +118,46 @@ static pthread_cond_t key_queue_cond = PTHREAD_COND_INITIALIZER;
|
||||
static int key_queue[256], key_queue_len = 0;
|
||||
static volatile char key_pressed[KEY_MAX + 1];
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Draw the given frame over the installation overlay animation. The
|
||||
// background is not cleared or draw with the base icon first; we
|
||||
// assume that the frame already contains some other frame of the
|
||||
// animation. Does nothing if no overlay animation is defined.
|
||||
// Should only be called with gUpdateMutex locked.
|
||||
static void draw_install_overlay_locked(int frame) {
|
||||
if (gInstallationOverlay == NULL) return;
|
||||
gr_surface surface = gInstallationOverlay[frame];
|
||||
int iconWidth = gr_get_width(surface);
|
||||
int iconHeight = gr_get_height(surface);
|
||||
gr_blit(surface, 0, 0, iconWidth, iconHeight,
|
||||
ui_parameters.install_overlay_offset_x,
|
||||
ui_parameters.install_overlay_offset_y);
|
||||
}
|
||||
|
||||
// Clear the screen and draw the currently selected background icon (if any).
|
||||
// Should only be called with gUpdateMutex locked.
|
||||
static void draw_background_locked(gr_surface icon)
|
||||
static void draw_background_locked(int icon)
|
||||
{
|
||||
gPagesIdentical = 0;
|
||||
gr_color(0, 0, 0, 255);
|
||||
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
|
||||
|
||||
if (icon) {
|
||||
int iconWidth = gr_get_width(icon);
|
||||
int iconHeight = gr_get_height(icon);
|
||||
gr_surface surface = gBackgroundIcon[icon];
|
||||
int iconWidth = gr_get_width(surface);
|
||||
int iconHeight = gr_get_height(surface);
|
||||
int iconX = (gr_fb_width() - iconWidth) / 2;
|
||||
int iconY = (gr_fb_height() - iconHeight) / 2;
|
||||
gr_blit(icon, 0, 0, iconWidth, iconHeight, iconX, iconY);
|
||||
gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
|
||||
if (icon == BACKGROUND_ICON_INSTALLING) {
|
||||
draw_install_overlay_locked(gInstallingFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,35 +165,39 @@ static void draw_background_locked(gr_surface icon)
|
||||
// Should only be called with gUpdateMutex locked.
|
||||
static void draw_progress_locked()
|
||||
{
|
||||
if (gProgressBarType == PROGRESSBAR_TYPE_NONE) return;
|
||||
|
||||
int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]);
|
||||
int width = gr_get_width(gProgressBarEmpty);
|
||||
int height = gr_get_height(gProgressBarEmpty);
|
||||
|
||||
int dx = (gr_fb_width() - width)/2;
|
||||
int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
|
||||
|
||||
// Erase behind the progress bar (in case this was a progress-only update)
|
||||
gr_color(0, 0, 0, 255);
|
||||
gr_fill(dx, dy, width, height);
|
||||
|
||||
if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL) {
|
||||
float progress = gProgressScopeStart + gProgress * gProgressScopeSize;
|
||||
int pos = (int) (progress * width);
|
||||
|
||||
if (pos > 0) {
|
||||
gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy);
|
||||
}
|
||||
if (pos < width-1) {
|
||||
gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
|
||||
}
|
||||
if (gCurrentIcon == BACKGROUND_ICON_INSTALLING) {
|
||||
draw_install_overlay_locked(gInstallingFrame);
|
||||
}
|
||||
|
||||
if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
|
||||
static int frame = 0;
|
||||
gr_blit(gProgressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
|
||||
frame = (frame + 1) % PROGRESSBAR_INDETERMINATE_STATES;
|
||||
if (gProgressBarType != PROGRESSBAR_TYPE_NONE) {
|
||||
int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]);
|
||||
int width = gr_get_width(gProgressBarEmpty);
|
||||
int height = gr_get_height(gProgressBarEmpty);
|
||||
|
||||
int dx = (gr_fb_width() - width)/2;
|
||||
int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
|
||||
|
||||
// Erase behind the progress bar (in case this was a progress-only update)
|
||||
gr_color(0, 0, 0, 255);
|
||||
gr_fill(dx, dy, width, height);
|
||||
|
||||
if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL) {
|
||||
float progress = gProgressScopeStart + gProgress * gProgressScopeSize;
|
||||
int pos = (int) (progress * width);
|
||||
|
||||
if (pos > 0) {
|
||||
gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy);
|
||||
}
|
||||
if (pos < width-1) {
|
||||
gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
|
||||
}
|
||||
}
|
||||
|
||||
if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
|
||||
static int frame = 0;
|
||||
gr_blit(gProgressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
|
||||
frame = (frame + 1) % ui_parameters.indeterminate_frames;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,7 +284,7 @@ static void update_progress_locked(void)
|
||||
draw_screen_locked(); // Must redraw the whole screen
|
||||
gPagesIdentical = 1;
|
||||
} else {
|
||||
draw_progress_locked(); // Draw only the progress bar
|
||||
draw_progress_locked(); // Draw only the progress bar and overlays
|
||||
}
|
||||
gr_flip();
|
||||
}
|
||||
@ -255,97 +292,130 @@ static void update_progress_locked(void)
|
||||
// Keeps the progress bar updated, even when the process is otherwise busy.
|
||||
static void *progress_thread(void *cookie)
|
||||
{
|
||||
double interval = 1.0 / ui_parameters.update_fps;
|
||||
for (;;) {
|
||||
usleep(1000000 / PROGRESSBAR_INDETERMINATE_FPS);
|
||||
double start = now();
|
||||
pthread_mutex_lock(&gUpdateMutex);
|
||||
|
||||
int redraw = 0;
|
||||
|
||||
// update the installation animation, if active
|
||||
// skip this if we have a text overlay (too expensive to update)
|
||||
if (gCurrentIcon == BACKGROUND_ICON_INSTALLING &&
|
||||
ui_parameters.installing_frames > 0 &&
|
||||
!show_text) {
|
||||
gInstallingFrame =
|
||||
(gInstallingFrame + 1) % ui_parameters.installing_frames;
|
||||
redraw = 1;
|
||||
}
|
||||
|
||||
// update the progress bar animation, if active
|
||||
// skip this if we have a text overlay (too expensive to update)
|
||||
if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE && !show_text) {
|
||||
update_progress_locked();
|
||||
redraw = 1;
|
||||
}
|
||||
|
||||
// move the progress bar forward on timed intervals, if configured
|
||||
int duration = gProgressScopeDuration;
|
||||
if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL && duration > 0) {
|
||||
int elapsed = time(NULL) - gProgressScopeTime;
|
||||
double elapsed = now() - gProgressScopeTime;
|
||||
float progress = 1.0 * elapsed / duration;
|
||||
if (progress > 1.0) progress = 1.0;
|
||||
if (progress > gProgress) {
|
||||
gProgress = progress;
|
||||
update_progress_locked();
|
||||
redraw = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (redraw) update_progress_locked();
|
||||
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
double end = now();
|
||||
// minimum of 20ms delay between frames
|
||||
double delay = interval - (end-start);
|
||||
if (delay < 0.02) delay = 0.02;
|
||||
usleep((long)(delay * 1000000));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int rel_sum = 0;
|
||||
|
||||
static int input_callback(int fd, short revents, void *data)
|
||||
{
|
||||
struct input_event ev;
|
||||
int ret;
|
||||
int fake_key = 0;
|
||||
|
||||
ret = ev_get_input(fd, revents, &ev);
|
||||
if (ret)
|
||||
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) {
|
||||
fake_key = 1;
|
||||
ev.type = EV_KEY;
|
||||
ev.code = KEY_DOWN;
|
||||
ev.value = 1;
|
||||
rel_sum = 0;
|
||||
} else if (rel_sum < -3) {
|
||||
fake_key = 1;
|
||||
ev.type = EV_KEY;
|
||||
ev.code = KEY_UP;
|
||||
ev.value = 1;
|
||||
rel_sum = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rel_sum = 0;
|
||||
}
|
||||
|
||||
if (ev.type != EV_KEY || ev.code > KEY_MAX)
|
||||
return 0;
|
||||
|
||||
pthread_mutex_lock(&key_queue_mutex);
|
||||
if (!fake_key) {
|
||||
// our "fake" keys only report a key-down event (no
|
||||
// key-up), so don't record them in the key_pressed
|
||||
// table.
|
||||
key_pressed[ev.code] = ev.value;
|
||||
}
|
||||
const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
|
||||
if (ev.value > 0 && key_queue_len < queue_max) {
|
||||
key_queue[key_queue_len++] = ev.code;
|
||||
pthread_cond_signal(&key_queue_cond);
|
||||
}
|
||||
pthread_mutex_unlock(&key_queue_mutex);
|
||||
|
||||
if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) {
|
||||
pthread_mutex_lock(&gUpdateMutex);
|
||||
show_text = !show_text;
|
||||
if (show_text) show_text_ever = 1;
|
||||
update_screen_locked();
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
}
|
||||
|
||||
if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) {
|
||||
android_reboot(ANDROID_RB_RESTART, 0, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reads input events, handles special hot keys, and adds to the key queue.
|
||||
static void *input_thread(void *cookie)
|
||||
{
|
||||
int rel_sum = 0;
|
||||
int fake_key = 0;
|
||||
for (;;) {
|
||||
// wait for the next key event
|
||||
struct input_event ev;
|
||||
do {
|
||||
ev_get(&ev, 0);
|
||||
|
||||
if (ev.type == EV_SYN) {
|
||||
continue;
|
||||
} 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) {
|
||||
fake_key = 1;
|
||||
ev.type = EV_KEY;
|
||||
ev.code = KEY_DOWN;
|
||||
ev.value = 1;
|
||||
rel_sum = 0;
|
||||
} else if (rel_sum < -3) {
|
||||
fake_key = 1;
|
||||
ev.type = EV_KEY;
|
||||
ev.code = KEY_UP;
|
||||
ev.value = 1;
|
||||
rel_sum = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rel_sum = 0;
|
||||
}
|
||||
} while (ev.type != EV_KEY || ev.code > KEY_MAX);
|
||||
|
||||
pthread_mutex_lock(&key_queue_mutex);
|
||||
if (!fake_key) {
|
||||
// our "fake" keys only report a key-down event (no
|
||||
// key-up), so don't record them in the key_pressed
|
||||
// table.
|
||||
key_pressed[ev.code] = ev.value;
|
||||
}
|
||||
fake_key = 0;
|
||||
const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
|
||||
if (ev.value > 0 && key_queue_len < queue_max) {
|
||||
key_queue[key_queue_len++] = ev.code;
|
||||
pthread_cond_signal(&key_queue_cond);
|
||||
}
|
||||
pthread_mutex_unlock(&key_queue_mutex);
|
||||
|
||||
if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) {
|
||||
pthread_mutex_lock(&gUpdateMutex);
|
||||
show_text = !show_text;
|
||||
update_screen_locked();
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
}
|
||||
|
||||
if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) {
|
||||
reboot(RB_AUTOBOOT);
|
||||
}
|
||||
if (!ev_wait(-1))
|
||||
ev_dispatch();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -354,7 +424,7 @@ void ui_init(void)
|
||||
{
|
||||
ui_has_initialized = 1;
|
||||
gr_init();
|
||||
ev_init();
|
||||
ev_init(input_callback, NULL);
|
||||
|
||||
text_col = text_row = 0;
|
||||
text_rows = gr_fb_height() / CHAR_HEIGHT;
|
||||
@ -368,15 +438,49 @@ void ui_init(void)
|
||||
for (i = 0; BITMAPS[i].name != NULL; ++i) {
|
||||
int result = res_create_surface(BITMAPS[i].name, BITMAPS[i].surface);
|
||||
if (result < 0) {
|
||||
if (result == -2) {
|
||||
LOGI("Bitmap %s missing header\n", BITMAPS[i].name);
|
||||
} else {
|
||||
LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
|
||||
}
|
||||
*BITMAPS[i].surface = NULL;
|
||||
LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
|
||||
}
|
||||
}
|
||||
|
||||
gProgressBarIndeterminate = malloc(ui_parameters.indeterminate_frames *
|
||||
sizeof(gr_surface));
|
||||
for (i = 0; i < ui_parameters.indeterminate_frames; ++i) {
|
||||
char filename[40];
|
||||
// "indeterminate01.png", "indeterminate02.png", ...
|
||||
sprintf(filename, "indeterminate%02d", i+1);
|
||||
int result = res_create_surface(filename, gProgressBarIndeterminate+i);
|
||||
if (result < 0) {
|
||||
LOGE("Missing bitmap %s\n(Code %d)\n", filename, result);
|
||||
}
|
||||
}
|
||||
|
||||
if (ui_parameters.installing_frames > 0) {
|
||||
gInstallationOverlay = malloc(ui_parameters.installing_frames *
|
||||
sizeof(gr_surface));
|
||||
for (i = 0; i < ui_parameters.installing_frames; ++i) {
|
||||
char filename[40];
|
||||
// "icon_installing_overlay01.png",
|
||||
// "icon_installing_overlay02.png", ...
|
||||
sprintf(filename, "icon_installing_overlay%02d", i+1);
|
||||
int result = res_create_surface(filename, gInstallationOverlay+i);
|
||||
if (result < 0) {
|
||||
LOGE("Missing bitmap %s\n(Code %d)\n", filename, result);
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust the offset to account for the positioning of the
|
||||
// base image on the screen.
|
||||
if (gBackgroundIcon[BACKGROUND_ICON_INSTALLING] != NULL) {
|
||||
gr_surface bg = gBackgroundIcon[BACKGROUND_ICON_INSTALLING];
|
||||
ui_parameters.install_overlay_offset_x +=
|
||||
(gr_fb_width() - gr_get_width(bg)) / 2;
|
||||
ui_parameters.install_overlay_offset_y +=
|
||||
(gr_fb_height() - gr_get_height(bg)) / 2;
|
||||
}
|
||||
} else {
|
||||
gInstallationOverlay = NULL;
|
||||
}
|
||||
|
||||
pthread_t t;
|
||||
pthread_create(&t, NULL, progress_thread, NULL);
|
||||
pthread_create(&t, NULL, input_thread, NULL);
|
||||
@ -402,7 +506,7 @@ char *ui_copy_image(int icon, int *width, int *height, int *bpp) {
|
||||
void ui_set_background(int icon)
|
||||
{
|
||||
pthread_mutex_lock(&gUpdateMutex);
|
||||
gCurrentIcon = gBackgroundIcon[icon];
|
||||
gCurrentIcon = icon;
|
||||
update_screen_locked();
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
}
|
||||
@ -423,7 +527,7 @@ void ui_show_progress(float portion, int seconds)
|
||||
gProgressBarType = PROGRESSBAR_TYPE_NORMAL;
|
||||
gProgressScopeStart += gProgressScopeSize;
|
||||
gProgressScopeSize = portion;
|
||||
gProgressScopeTime = time(NULL);
|
||||
gProgressScopeTime = now();
|
||||
gProgressScopeDuration = seconds;
|
||||
gProgress = 0;
|
||||
update_progress_locked();
|
||||
@ -599,23 +703,68 @@ int ui_text_visible()
|
||||
return visible;
|
||||
}
|
||||
|
||||
int ui_text_ever_visible()
|
||||
{
|
||||
pthread_mutex_lock(&gUpdateMutex);
|
||||
int ever_visible = show_text_ever;
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
return ever_visible;
|
||||
}
|
||||
|
||||
void ui_show_text(int visible)
|
||||
{
|
||||
pthread_mutex_lock(&gUpdateMutex);
|
||||
show_text = visible;
|
||||
if (show_text) show_text_ever = 1;
|
||||
update_screen_locked();
|
||||
pthread_mutex_unlock(&gUpdateMutex);
|
||||
}
|
||||
|
||||
// Return true if USB is connected.
|
||||
static int usb_connected() {
|
||||
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 = (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;
|
||||
}
|
||||
|
||||
int ui_wait_key()
|
||||
{
|
||||
pthread_mutex_lock(&key_queue_mutex);
|
||||
while (key_queue_len == 0) {
|
||||
pthread_cond_wait(&key_queue_cond, &key_queue_mutex);
|
||||
}
|
||||
|
||||
int key = key_queue[0];
|
||||
memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
|
||||
// Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
|
||||
// plugged in.
|
||||
do {
|
||||
struct timeval now;
|
||||
struct timespec timeout;
|
||||
gettimeofday(&now, NULL);
|
||||
timeout.tv_sec = now.tv_sec;
|
||||
timeout.tv_nsec = now.tv_usec * 1000;
|
||||
timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
|
||||
|
||||
int rc = 0;
|
||||
while (key_queue_len == 0 && rc != ETIMEDOUT) {
|
||||
rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex,
|
||||
&timeout);
|
||||
}
|
||||
} while (usb_connected() && key_queue_len == 0);
|
||||
|
||||
int key = -1;
|
||||
if (key_queue_len > 0) {
|
||||
key = key_queue[0];
|
||||
memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
|
||||
}
|
||||
pthread_mutex_unlock(&key_queue_mutex);
|
||||
return key;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils
|
||||
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
|
||||
LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
|
||||
LOCAL_STATIC_LIBRARIES += libmincrypt libbz
|
||||
LOCAL_STATIC_LIBRARIES += libminelf
|
||||
LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
|
||||
|
||||
@ -60,9 +61,9 @@ $(inc) : libs := $(TARGET_RECOVERY_UPDATER_LIBS)
|
||||
$(inc) : $(inc).list
|
||||
$(hide) mkdir -p $(dir $@)
|
||||
$(hide) echo "" > $@
|
||||
$(hide) $(foreach lib,$(libs),echo "extern void Register_$(lib)(void);" >> $@)
|
||||
$(hide) $(foreach lib,$(libs),echo "extern void Register_$(lib)(void);" >> $@;)
|
||||
$(hide) echo "void RegisterDeviceExtensions() {" >> $@
|
||||
$(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@)
|
||||
$(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@;)
|
||||
$(hide) echo "}" >> $@
|
||||
|
||||
$(call intermediates-dir-for,EXECUTABLES,updater)/updater.o : $(inc)
|
||||
@ -72,10 +73,4 @@ LOCAL_MODULE := updater
|
||||
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
|
||||
file := $(PRODUCT_OUT)/utilities/update-binary
|
||||
ALL_PREBUILT += $(file)
|
||||
$(file) : $(TARGET_OUT)/bin/updater | $(ACP)
|
||||
$(transform-prebuilt-to-target)
|
||||
include $(BUILD_EXECUTABLE)
|
@ -25,12 +25,15 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "cutils/misc.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "edify/expr.h"
|
||||
#include "mincrypt/sha.h"
|
||||
#include "minzip/DirUtil.h"
|
||||
#include "minelf/Retouch.h"
|
||||
#include "mounts.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
#include "updater.h"
|
||||
@ -176,19 +179,23 @@ done:
|
||||
}
|
||||
|
||||
|
||||
// format(fs_type, partition_type, location)
|
||||
// format(fs_type, partition_type, location, fs_size)
|
||||
//
|
||||
// fs_type="yaffs2" partition_type="MTD" location=partition
|
||||
// fs_type="ext4" partition_type="EMMC" location=device
|
||||
// fs_type="yaffs2" partition_type="MTD" location=partition fs_size=<bytes>
|
||||
// fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes>
|
||||
// if fs_size == 0, then make_ext4fs uses the entire partition.
|
||||
// if fs_size > 0, that is the size to use
|
||||
// if fs_size < 0, then reserve that many bytes at the end of the partition
|
||||
Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
if (argc != 3) {
|
||||
return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
|
||||
if (argc != 4) {
|
||||
return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc);
|
||||
}
|
||||
char* fs_type;
|
||||
char* partition_type;
|
||||
char* location;
|
||||
if (ReadArgs(state, argv, 3, &fs_type, &partition_type, &location) < 0) {
|
||||
char* fs_size;
|
||||
if (ReadArgs(state, argv, 4, &fs_type, &partition_type, &location, &fs_size) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -235,8 +242,7 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
result = location;
|
||||
#ifdef USE_EXT4
|
||||
} else if (strcmp(fs_type, "ext4") == 0) {
|
||||
reset_ext4fs_info();
|
||||
int status = make_ext4fs(location, NULL, NULL, 0, 0, 0);
|
||||
int status = make_ext4fs(location, atoll(fs_size));
|
||||
if (status != 0) {
|
||||
fprintf(stderr, "%s: make_ext4fs failed (%d) on %s",
|
||||
name, status, location);
|
||||
@ -449,6 +455,121 @@ Value* PackageExtractFileFn(const char* name, State* state,
|
||||
}
|
||||
|
||||
|
||||
// retouch_binaries(lib1, lib2, ...)
|
||||
Value* RetouchBinariesFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
|
||||
|
||||
char **retouch_entries = ReadVarArgs(state, argc, argv);
|
||||
if (retouch_entries == NULL) {
|
||||
return StringValue(strdup("t"));
|
||||
}
|
||||
|
||||
// some randomness from the clock
|
||||
int32_t override_base;
|
||||
bool override_set = false;
|
||||
int32_t random_base = time(NULL) % 1024;
|
||||
// some more randomness from /dev/random
|
||||
FILE *f_random = fopen("/dev/random", "rb");
|
||||
uint16_t random_bits = 0;
|
||||
if (f_random != NULL) {
|
||||
fread(&random_bits, 2, 1, f_random);
|
||||
random_bits = random_bits % 1024;
|
||||
fclose(f_random);
|
||||
}
|
||||
random_base = (random_base + random_bits) % 1024;
|
||||
fprintf(ui->cmd_pipe, "ui_print Random offset: 0x%x\n", random_base);
|
||||
fprintf(ui->cmd_pipe, "ui_print\n");
|
||||
|
||||
// make sure we never randomize to zero; this let's us look at a file
|
||||
// and know for sure whether it has been processed; important in the
|
||||
// crash recovery process
|
||||
if (random_base == 0) random_base = 1;
|
||||
// make sure our randomization is page-aligned
|
||||
random_base *= -0x1000;
|
||||
override_base = random_base;
|
||||
|
||||
int i = 0;
|
||||
bool success = true;
|
||||
while (i < (argc - 1)) {
|
||||
success = success && retouch_one_library(retouch_entries[i],
|
||||
retouch_entries[i+1],
|
||||
random_base,
|
||||
override_set ?
|
||||
NULL :
|
||||
&override_base);
|
||||
if (!success)
|
||||
ErrorAbort(state, "Failed to retouch '%s'.", retouch_entries[i]);
|
||||
|
||||
free(retouch_entries[i]);
|
||||
free(retouch_entries[i+1]);
|
||||
i += 2;
|
||||
|
||||
if (success && override_base != 0) {
|
||||
random_base = override_base;
|
||||
override_set = true;
|
||||
}
|
||||
}
|
||||
if (i < argc) {
|
||||
free(retouch_entries[i]);
|
||||
success = false;
|
||||
}
|
||||
free(retouch_entries);
|
||||
|
||||
if (!success) {
|
||||
Value* v = malloc(sizeof(Value));
|
||||
v->type = VAL_STRING;
|
||||
v->data = NULL;
|
||||
v->size = -1;
|
||||
return v;
|
||||
}
|
||||
return StringValue(strdup("t"));
|
||||
}
|
||||
|
||||
|
||||
// undo_retouch_binaries(lib1, lib2, ...)
|
||||
Value* UndoRetouchBinariesFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
|
||||
|
||||
char **retouch_entries = ReadVarArgs(state, argc, argv);
|
||||
if (retouch_entries == NULL) {
|
||||
return StringValue(strdup("t"));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
bool success = true;
|
||||
int32_t override_base;
|
||||
while (i < (argc-1)) {
|
||||
success = success && retouch_one_library(retouch_entries[i],
|
||||
retouch_entries[i+1],
|
||||
0 /* undo => offset==0 */,
|
||||
NULL);
|
||||
if (!success)
|
||||
ErrorAbort(state, "Failed to unretouch '%s'.",
|
||||
retouch_entries[i]);
|
||||
|
||||
free(retouch_entries[i]);
|
||||
free(retouch_entries[i+1]);
|
||||
i += 2;
|
||||
}
|
||||
if (i < argc) {
|
||||
free(retouch_entries[i]);
|
||||
success = false;
|
||||
}
|
||||
free(retouch_entries);
|
||||
|
||||
if (!success) {
|
||||
Value* v = malloc(sizeof(Value));
|
||||
v->type = VAL_STRING;
|
||||
v->data = NULL;
|
||||
v->size = -1;
|
||||
return v;
|
||||
}
|
||||
return StringValue(strdup("t"));
|
||||
}
|
||||
|
||||
|
||||
// symlink target src1 src2 ...
|
||||
// unlinks any previously existing src1, src2, etc before creating symlinks.
|
||||
Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
@ -681,21 +802,26 @@ static bool write_raw_image_cb(const unsigned char* data,
|
||||
return false;
|
||||
}
|
||||
|
||||
// write_raw_image(file, partition)
|
||||
// write_raw_image(filename_or_blob, partition)
|
||||
Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* result = NULL;
|
||||
|
||||
char* partition;
|
||||
char* filename;
|
||||
if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
|
||||
Value* partition_value;
|
||||
Value* contents;
|
||||
if (ReadValueArgs(state, argv, 2, &contents, &partition_value) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (partition_value->type != VAL_STRING) {
|
||||
ErrorAbort(state, "partition argument to %s must be string", name);
|
||||
goto done;
|
||||
}
|
||||
char* partition = partition_value->data;
|
||||
if (strlen(partition) == 0) {
|
||||
ErrorAbort(state, "partition argument to %s can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
if (strlen(filename) == 0) {
|
||||
if (contents->type == VAL_STRING && strlen((char*) contents->data) == 0) {
|
||||
ErrorAbort(state, "file argument to %s can't be empty", name);
|
||||
goto done;
|
||||
}
|
||||
@ -704,10 +830,12 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
result = strdup(partition);
|
||||
else
|
||||
result = strdup("");
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (result != partition) free(partition);
|
||||
free(filename);
|
||||
if (result != partition) FreeValue(partition_value);
|
||||
FreeValue(contents);
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
@ -980,7 +1108,7 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
return args[i];
|
||||
}
|
||||
|
||||
// Read a local file and return its contents (the char* returned
|
||||
// Read a local file and return its contents (the Value* returned
|
||||
// is actually a FileContents*).
|
||||
Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 1) {
|
||||
@ -993,7 +1121,7 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
v->type = VAL_BLOB;
|
||||
|
||||
FileContents fc;
|
||||
if (LoadFileContents(filename, &fc) != 0) {
|
||||
if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) {
|
||||
ErrorAbort(state, "%s() loading \"%s\" failed: %s",
|
||||
name, filename, strerror(errno));
|
||||
free(filename);
|
||||
@ -1020,6 +1148,8 @@ void RegisterInstallFunctions() {
|
||||
RegisterFunction("delete_recursive", DeleteFn);
|
||||
RegisterFunction("package_extract_dir", PackageExtractDirFn);
|
||||
RegisterFunction("package_extract_file", PackageExtractFileFn);
|
||||
RegisterFunction("retouch_binaries", RetouchBinariesFn);
|
||||
RegisterFunction("undo_retouch_binaries", UndoRetouchBinariesFn);
|
||||
RegisterFunction("symlink", SymlinkFn);
|
||||
RegisterFunction("set_perm", SetPermFn);
|
||||
RegisterFunction("set_perm_recursive", SetPermFn);
|
||||
|
@ -173,7 +173,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey
|
||||
// the signing tool appends after the signature itself.
|
||||
if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES,
|
||||
RSANUMBYTES, sha1)) {
|
||||
LOGI("whole-file signature verified\n");
|
||||
LOGI("whole-file signature verified against key %d\n", i);
|
||||
free(eocd);
|
||||
return VERIFY_SUCCESS;
|
||||
}
|
||||
|