diff options
544 files changed, 2060 insertions, 633 deletions
diff --git a/Android.mk b/Android.mk index 384eefe3f..dcc11c596 100644 --- a/Android.mk +++ b/Android.mk @@ -51,6 +51,7 @@ LOCAL_SRC_FILES := \ ui.cpp \ verifier.cpp \ wear_ui.cpp \ + wear_touch.cpp \ LOCAL_MODULE := recovery diff --git a/adb_install.cpp b/adb_install.cpp index 60616ca33..b05fda13a 100644 --- a/adb_install.cpp +++ b/adb_install.cpp @@ -105,7 +105,7 @@ int apply_from_adb(RecoveryUI* ui, bool* wipe_cache, const char* install_file) { break; } } - result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false); + result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false, 0); break; } diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index f5aed763d..0c06d6b1e 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -152,40 +152,45 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0; std::vector<unsigned char> expanded_source(expanded_len); - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = src_len; - strm.next_in = (unsigned char*)(old_data + src_start); - strm.avail_out = expanded_len; - strm.next_out = expanded_source.data(); - - int ret; - ret = inflateInit2(&strm, -15); - if (ret != Z_OK) { - printf("failed to init source inflation: %d\n", ret); - return -1; - } - // Because we've provided enough room to accommodate the output - // data, we expect one call to inflate() to suffice. - ret = inflate(&strm, Z_SYNC_FLUSH); - if (ret != Z_STREAM_END) { - printf("source inflation returned %d\n", ret); - return -1; - } - // We should have filled the output buffer exactly, except - // for the bonus_size. - if (strm.avail_out != bonus_size) { - printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size); - return -1; - } - inflateEnd(&strm); + // inflate() doesn't like strm.next_out being a nullptr even with + // avail_out being zero (Z_STREAM_ERROR). + if (expanded_len != 0) { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = src_len; + strm.next_in = (unsigned char*)(old_data + src_start); + strm.avail_out = expanded_len; + strm.next_out = expanded_source.data(); + + int ret; + ret = inflateInit2(&strm, -15); + if (ret != Z_OK) { + printf("failed to init source inflation: %d\n", ret); + return -1; + } + + // Because we've provided enough room to accommodate the output + // data, we expect one call to inflate() to suffice. + ret = inflate(&strm, Z_SYNC_FLUSH); + if (ret != Z_STREAM_END) { + printf("source inflation returned %d\n", ret); + return -1; + } + // We should have filled the output buffer exactly, except + // for the bonus_size. + if (strm.avail_out != bonus_size) { + printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size); + return -1; + } + inflateEnd(&strm); - if (bonus_size) { - memcpy(expanded_source.data() + (expanded_len - bonus_size), - bonus_data->data, bonus_size); + if (bonus_size) { + memcpy(expanded_source.data() + (expanded_len - bonus_size), + bonus_data->data, bonus_size); + } } // Next, apply the bsdiff patch (in memory) to the uncompressed @@ -209,33 +214,37 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, if (expanded_source.size() < 32768U) { expanded_source.resize(32768U); } - std::vector<unsigned char>& temp_data = expanded_source; - - // now the deflate stream - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = uncompressed_target_data.size(); - strm.next_in = uncompressed_target_data.data(); - ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy); - if (ret != Z_OK) { - printf("failed to init uncompressed data deflation: %d\n", ret); - return -1; - } - do { - strm.avail_out = temp_data.size(); - strm.next_out = temp_data.data(); - ret = deflate(&strm, Z_FINISH); - ssize_t have = temp_data.size() - strm.avail_out; - - if (sink(temp_data.data(), have, token) != have) { - printf("failed to write %zd compressed bytes to output\n", - have); + + { + std::vector<unsigned char>& temp_data = expanded_source; + + // now the deflate stream + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = uncompressed_target_data.size(); + strm.next_in = uncompressed_target_data.data(); + int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy); + if (ret != Z_OK) { + printf("failed to init uncompressed data deflation: %d\n", ret); return -1; } - if (ctx) SHA1_Update(ctx, temp_data.data(), have); - } while (ret != Z_STREAM_END); - deflateEnd(&strm); + do { + strm.avail_out = temp_data.size(); + strm.next_out = temp_data.data(); + ret = deflate(&strm, Z_FINISH); + ssize_t have = temp_data.size() - strm.avail_out; + + if (sink(temp_data.data(), have, token) != have) { + printf("failed to write %zd compressed bytes to output\n", + have); + return -1; + } + if (ctx) SHA1_Update(ctx, temp_data.data(), have); + } while (ret != Z_STREAM_END); + deflateEnd(&strm); + } } else { printf("patch chunk %d is unknown type %d\n", i, type); return -1; diff --git a/device.cpp b/device.cpp index fd1a9875b..2465b0778 100644 --- a/device.cpp +++ b/device.cpp @@ -25,6 +25,7 @@ static const char* MENU_ITEMS[] = { "Wipe cache partition", "Mount /system", "View recovery logs", + "Run graphics test", "Power off", NULL }; @@ -43,7 +44,8 @@ Device::BuiltinAction Device::InvokeMenuItem(int menu_position) { case 5: return WIPE_CACHE; case 6: return MOUNT_SYSTEM; case 7: return VIEW_RECOVERY_LOGS; - case 8: return SHUTDOWN; + case 8: return RUN_GRAPHICS_TEST; + case 9: return SHUTDOWN; default: return NO_ACTION; } } @@ -68,6 +68,7 @@ class Device { SHUTDOWN = 8, VIEW_RECOVERY_LOGS = 9, MOUNT_SYSTEM = 10, + RUN_GRAPHICS_TEST = 11, }; // Return the list of menu items (an array of strings, diff --git a/edify/Android.mk b/edify/Android.mk index 038dec088..71cf7652a 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -22,6 +22,8 @@ LOCAL_YACCFLAGS := -v LOCAL_CPPFLAGS += -Wno-unused-parameter LOCAL_CPPFLAGS += -Wno-deprecated-register LOCAL_CLANG := true +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_STATIC_LIBRARIES += libbase include $(BUILD_HOST_EXECUTABLE) @@ -36,5 +38,7 @@ LOCAL_CPPFLAGS := -Wno-unused-parameter LOCAL_CPPFLAGS += -Wno-deprecated-register LOCAL_MODULE := libedify LOCAL_CLANG := true +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_STATIC_LIBRARIES += libbase include $(BUILD_STATIC_LIBRARY) diff --git a/edify/expr.cpp b/edify/expr.cpp index c34342f76..ecb1bea1a 100644 --- a/edify/expr.cpp +++ b/edify/expr.cpp @@ -21,6 +21,11 @@ #include <stdarg.h> #include <unistd.h> +#include <string> + +#include <android-base/stringprintf.h> +#include <android-base/strings.h> + #include "expr.h" // Functions should: @@ -36,7 +41,7 @@ char* Evaluate(State* state, Expr* expr) { Value* v = expr->fn(expr->name, state, expr->argc, expr->argv); if (v == NULL) return NULL; if (v->type != VAL_STRING) { - ErrorAbort(state, "expecting string, got value type %d", v->type); + ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type); FreeValue(v); return NULL; } @@ -494,15 +499,29 @@ Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]) { return args; } +static void ErrorAbortV(State* state, const char* format, va_list ap) { + std::string buffer; + android::base::StringAppendV(&buffer, format, ap); + free(state->errmsg); + state->errmsg = strdup(buffer.c_str()); + return; +} + // Use printf-style arguments to compose an error message to put into -// *state. Returns NULL. +// *state. Returns nullptr. Value* ErrorAbort(State* state, const char* format, ...) { - char* buffer = reinterpret_cast<char*>(malloc(4096)); - va_list v; - va_start(v, format); - vsnprintf(buffer, 4096, format, v); - va_end(v); - free(state->errmsg); - state->errmsg = buffer; - return NULL; + va_list ap; + va_start(ap, format); + ErrorAbortV(state, format, ap); + va_end(ap); + return nullptr; +} + +Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) { + va_list ap; + va_start(ap, format); + ErrorAbortV(state, format, ap); + va_end(ap); + state->cause_code = cause_code; + return nullptr; } diff --git a/edify/expr.h b/edify/expr.h index 36f8e9612..886347991 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -19,6 +19,7 @@ #include <unistd.h> +#include "error_code.h" #include "yydefs.h" #define MAX_STRING_LEN 1024 @@ -39,6 +40,17 @@ typedef struct { // Should be NULL initially, will be either NULL or a malloc'd // pointer after Evaluate() returns. char* errmsg; + + // error code indicates the type of failure (e.g. failure to update system image) + // during the OTA process. + ErrorCode error_code = kNoError; + + // cause code provides more detailed reason of an OTA failure (e.g. fsync error) + // in addition to the error code. + CauseCode cause_code = kNoCause; + + bool is_retry = false; + } State; #define VAL_STRING 1 // data will be NULL-terminated; size doesn't count null @@ -152,7 +164,13 @@ Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]); // Use printf-style arguments to compose an error message to put into // *state. Returns NULL. -Value* ErrorAbort(State* state, const char* format, ...) __attribute__((format(printf, 2, 3))); +Value* ErrorAbort(State* state, const char* format, ...) + __attribute__((format(printf, 2, 3), deprecated)); + +// ErrorAbort has an optional (but recommended) argument 'cause_code'. If the cause code +// is set, it will be logged into last_install and provides reason of OTA failures. +Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) + __attribute__((format(printf, 3, 4))); // Wrap a string into a Value, taking ownership of the string. Value* StringValue(char* str); diff --git a/error_code.h b/error_code.h new file mode 100644 index 000000000..259319ab4 --- /dev/null +++ b/error_code.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ERROR_CODE_H_ +#define _ERROR_CODE_H_ + +enum ErrorCode { + kNoError = -1, + kLowBattery = 20, + kZipVerificationFailure, + kZipOpenFailure +}; + +enum CauseCode { + kNoCause = -1, + kArgsParsingFailure = 100, + kStashCreationFailure, + kFileOpenFailure, + kLseekFailure, + kFreadFailure, + kFwriteFailure, + kFsyncFailure, + kLibfecFailure, + kFileGetPropFailure, + kFileRenameFailure, + kSymlinkFailure, + kSetMetadataFailure, + kTune2FsFailure, + kRebootFailure, + kVendorFailure = 200 +}; + +#endif diff --git a/install.cpp b/install.cpp index 9106db595..3d0729064 100644 --- a/install.cpp +++ b/install.cpp @@ -23,9 +23,16 @@ #include <sys/wait.h> #include <unistd.h> +#include <chrono> +#include <string> #include <vector> +#include <android-base/parseint.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> + #include "common.h" +#include "error_code.h" #include "install.h" #include "minui/minui.h" #include "minzip/SysUtil.h" @@ -38,6 +45,7 @@ extern RecoveryUI* ui; #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" +static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; // Default allocation of progress bar segments to operations static const int VERIFICATION_PROGRESS_TIME = 60; @@ -45,9 +53,64 @@ static const float VERIFICATION_PROGRESS_FRACTION = 0.25; static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; +// This function parses and returns the build.version.incremental +static int parse_build_number(std::string str) { + size_t pos = str.find("="); + if (pos != std::string::npos) { + std::string num_string = android::base::Trim(str.substr(pos+1)); + int build_number; + if (android::base::ParseInt(num_string.c_str(), &build_number, 0)) { + return build_number; + } + } + + LOGE("Failed to parse build number in %s.\n", str.c_str()); + return -1; +} + +// Read the build.version.incremental of src/tgt from the metadata and log it to last_install. +static void read_source_target_build(ZipArchive* zip, std::vector<std::string>& log_buffer) { + const ZipEntry* meta_entry = mzFindZipEntry(zip, METADATA_PATH); + if (meta_entry == nullptr) { + LOGE("Failed to find %s in update package.\n", METADATA_PATH); + return; + } + + std::string meta_data(meta_entry->uncompLen, '\0'); + if (!mzReadZipEntry(zip, meta_entry, &meta_data[0], meta_entry->uncompLen)) { + LOGE("Failed to read metadata in update package.\n"); + return; + } + + // Examples of the pre-build and post-build strings in metadata: + // pre-build-incremental=2943039 + // post-build-incremental=2951741 + std::vector<std::string> lines = android::base::Split(meta_data, "\n"); + for (const std::string& line : lines) { + std::string str = android::base::Trim(line); + if (android::base::StartsWith(str, "pre-build-incremental")){ + int source_build = parse_build_number(str); + if (source_build != -1) { + log_buffer.push_back(android::base::StringPrintf("source_build: %d", + source_build)); + } + } else if (android::base::StartsWith(str, "post-build-incremental")) { + int target_build = parse_build_number(str); + if (target_build != -1) { + log_buffer.push_back(android::base::StringPrintf("target_build: %d", + target_build)); + } + } + } +} + // If the package contains an update binary, extract it and run it. static int -try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { +try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache, + std::vector<std::string>& log_buffer, int retry_count) +{ + read_source_target_build(zip, log_buffer); + const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { @@ -120,15 +183,19 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { // // - the name of the package zip file. // + // - an optional argument "retry" if this update is a retry of a failed + // update attempt. + // - const char* args[5]; + const char* args[6]; args[0] = binary; args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk char temp[16]; snprintf(temp, sizeof(temp), "%d", pipefd[1]); args[2] = temp; args[3] = path; - args[4] = NULL; + args[4] = retry_count > 0 ? "retry" : NULL; + args[5] = NULL; pid_t pid = fork(); if (pid == 0) { @@ -180,6 +247,10 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { ui->SetEnableReboot(true); } else if (strcmp(command, "retry_update") == 0) { retry_update = true; + } else if (strcmp(command, "log") == 0) { + // Save the logging request from updater and write to + // last_install later. + log_buffer.push_back(std::string(strtok(NULL, "\n"))); } else { LOGE("unknown command [%s]\n", command); } @@ -200,7 +271,8 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { } static int -really_install_package(const char *path, bool* wipe_cache, bool needs_mount) +really_install_package(const char *path, bool* wipe_cache, bool needs_mount, + std::vector<std::string>& log_buffer, int retry_count) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); @@ -226,6 +298,7 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount) return INSTALL_CORRUPT; } + // Load keys. std::vector<Certificate> loadedKeys; if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) { LOGE("Failed to load keys\n"); @@ -233,31 +306,38 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount) } LOGI("%zu key(s) loaded from %s\n", loadedKeys.size(), PUBLIC_KEYS_FILE); + // Verify package. ui->Print("Verifying update package...\n"); - + auto t0 = std::chrono::system_clock::now(); int err = verify_file(map.addr, map.length, loadedKeys); - LOGI("verify_file returned %d\n", err); + std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0; + ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err); if (err != VERIFY_SUCCESS) { LOGE("signature verification failed\n"); + log_buffer.push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure)); + sysReleaseMap(&map); return INSTALL_CORRUPT; } - /* Try to open the package. - */ + // Try to open the package. ZipArchive zip; err = mzOpenZipArchive(map.addr, map.length, &zip); if (err != 0) { LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad"); + log_buffer.push_back(android::base::StringPrintf("error: %d", kZipOpenFailure)); + sysReleaseMap(&map); return INSTALL_CORRUPT; } - /* Verify and install the contents of the package. - */ + // Verify and install the contents of the package. ui->Print("Installing update...\n"); + if (retry_count > 0) { + ui->Print("Retry attempt: %d\n", retry_count); + } ui->SetEnableReboot(false); - int result = try_update_binary(path, &zip, wipe_cache); + int result = try_update_binary(path, &zip, wipe_cache, log_buffer, retry_count); ui->SetEnableReboot(true); ui->Print("\n"); @@ -268,9 +348,10 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount) int install_package(const char* path, bool* wipe_cache, const char* install_file, - bool needs_mount) + bool needs_mount, int retry_count) { modified_flash = true; + auto start = std::chrono::system_clock::now(); FILE* install_log = fopen_path(install_file, "w"); if (install_log) { @@ -280,15 +361,26 @@ install_package(const char* path, bool* wipe_cache, const char* install_file, LOGE("failed to open last_install: %s\n", strerror(errno)); } int result; + std::vector<std::string> log_buffer; if (setup_install_mounts() != 0) { LOGE("failed to set up expected mounts for install; aborting\n"); result = INSTALL_ERROR; } else { - result = really_install_package(path, wipe_cache, needs_mount); + result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count); } - if (install_log) { + if (install_log != nullptr) { fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log); fputc('\n', install_log); + std::chrono::duration<double> duration = std::chrono::system_clock::now() - start; + int count = static_cast<int>(duration.count()); + // Report the time spent to apply OTA update in seconds. + fprintf(install_log, "time_total: %d\n", count); + fprintf(install_log, "retry: %d\n", retry_count); + + for (const auto& s : log_buffer) { + fprintf(install_log, "%s\n", s.c_str()); + } + fclose(install_log); } return result; @@ -28,8 +28,8 @@ enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SK // Install the package specified by root_path. If INSTALL_SUCCESS is // returned and *wipe_cache is true on exit, caller should wipe the // cache partition. -int install_package(const char* root_path, bool* wipe_cache, - const char* install_file, bool needs_mount); +int install_package(const char* root_path, bool* wipe_cache, const char* install_file, + bool needs_mount, int retry_count); #ifdef __cplusplus } diff --git a/interlace-frames.py b/interlace-frames.py index 3e777b470..6b435aa90 100644..100755 --- a/interlace-frames.py +++ b/interlace-frames.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # Copyright (C) 2014 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,19 +14,16 @@ # limitations under the License. """ -Script to take a set of frames (PNG files) for a recovery animation and turn -it into a single output image which contains the input frames interlaced by -row. Run with the names of all the input frames on the command line. Specify -the name of the output file with -o (or --output), and optionally specify the -number of frames per second (FPS) with --fps (default: 20). - -e.g. -interlace-frames.py --fps 20 --output output.png frame0.png frame1.png frame3.png +Script to take a set of frames (PNG files) for a recovery animation +and turn it into a single output image which contains the input frames +interlaced by row. Run with the names of all the input frames on the +command line, in order, followed by the name of the output file. """ from __future__ import print_function import argparse +import os.path import sys try: import Image @@ -35,7 +33,7 @@ except ImportError: sys.exit(1) -def interlace(output, fps, inputs): +def interlace(output, inputs): frames = [Image.open(fn).convert("RGB") for fn in inputs] assert len(frames) > 0, "Must have at least one input frame." sizes = set() @@ -60,21 +58,57 @@ def interlace(output, fps, inputs): meta = PngImagePlugin.PngInfo() meta.add_text("Frames", str(N)) - meta.add_text("FPS", str(fps)) out.save(output, pnginfo=meta) +def deinterlace(output, input): + # Truncate the output filename extension if it's '.png'. + if os.path.splitext(output)[1].lower() == '.png': + output = output[:-4] + + img2 = Image.open(input) + print(img2.mode) + palette = img2.getpalette() + img = img2.convert("RGB") + num_frames = int(img.info.get('Frames', 1)) + print('Found %d frames in %s.' % (num_frames, input)) + assert num_frames > 0, 'Invalid Frames meta.' + + # palette = img.getpalette() + print(palette) + + width, height = img.size + height /= num_frames + for k in range(num_frames): + out = Image.new('RGB', (width, height)) + out.info = img.info + for i in range(width): + for j in range(height): + out.putpixel((i, j), img.getpixel((i, j * num_frames + k))) + # out.putpalette(img.getpalette(), rawmode='RGB') + out2 = out.convert(mode='P', palette=palette) + #out2 = out + print(out2.mode) + # out2.putpalette(palette) + filename = '%s%02d.png' % (output, k) + out2.save(filename) + print('Frame %d written to %s.' % (k, filename)) + + def main(argv): - parser = argparse.ArgumentParser() - parser.add_argument('--fps', default=20) + parser = argparse.ArgumentParser(description='Parse') + parser.add_argument('--deinterlace', '-d', action='store_true') parser.add_argument('--output', '-o', required=True) parser.add_argument('input', nargs='+') args = parser.parse_args(argv) - interlace(args.output, args.fps, args.input) + if args.deinterlace: + # args.input is a list, and we only process the first when deinterlacing. + deinterlace(args.output, args.input[0]) + else: + interlace(args.output, args.input) if __name__ == '__main__': main(sys.argv[1:]) - diff --git a/minui/minui.h b/minui/minui.h index e3bc00548..fb0bbe10c 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -84,6 +84,8 @@ int ev_get_epollfd(); // Resources // +bool matches_locale(const char* prefix, const char* locale); + // res_create_*_surface() functions return 0 if no error, else // negative. // diff --git a/minui/resources.cpp b/minui/resources.cpp index 8489d60ef..730b05f13 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -33,8 +33,6 @@ #include "minui.h" -extern char* locale; - #define SURFACE_DATA_ALIGNMENT 8 static GRSurface* malloc_surface(size_t data_size) { @@ -373,21 +371,16 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) { return result; } -static int matches_locale(const char* loc, const char* locale) { - if (locale == NULL) return 0; - - if (strcmp(loc, locale) == 0) return 1; - - // if loc does *not* have an underscore, and it matches the start - // of locale, and the next character in locale *is* an underscore, - // that's a match. For instance, loc == "en" matches locale == - // "en_US". +// This function tests if a locale string stored in PNG (prefix) matches +// the locale string provided by the system (locale). +bool matches_locale(const char* prefix, const char* locale) { + if (locale == NULL) return false; - int i; - for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i); - if (loc[i] == '_') return 0; + // Return true if the whole string of prefix matches the top part of + // locale. For instance, prefix == "en" matches locale == "en_US"; + // and prefix == "zh_CN" matches locale == "zh_CN_#Hans". - return (strncmp(locale, loc, i) == 0 && locale[i] == '_'); + return (strncmp(prefix, locale, strlen(prefix)) == 0); } int res_create_localized_alpha_surface(const char* name, diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c index 09ec8768f..e7dd17b51 100644 --- a/minzip/SysUtil.c +++ b/minzip/SysUtil.c @@ -39,6 +39,11 @@ static bool sysMapFD(int fd, MemMapping* pMap) { pMap->length = sb.st_size; pMap->range_count = 1; pMap->ranges = malloc(sizeof(MappedRange)); + if (pMap->ranges == NULL) { + LOGE("malloc failed: %s\n", strerror(errno)); + munmap(memPtr, sb.st_size); + return false; + } pMap->ranges[0].addr = memPtr; pMap->ranges[0].length = sb.st_size; @@ -50,7 +55,7 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) char block_dev[PATH_MAX+1]; size_t size; unsigned int blksize; - unsigned int blocks; + size_t blocks; unsigned int range_count; unsigned int i; @@ -69,49 +74,80 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) LOGE("failed to parse block map header\n"); return -1; } - - blocks = ((size-1) / blksize) + 1; + if (blksize != 0) { + blocks = ((size-1) / blksize) + 1; + } + if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0) { + LOGE("invalid data in block map file: size %zu, blksize %u, range_count %u\n", + size, blksize, range_count); + return -1; + } pMap->range_count = range_count; - pMap->ranges = malloc(range_count * sizeof(MappedRange)); - memset(pMap->ranges, 0, range_count * sizeof(MappedRange)); + pMap->ranges = calloc(range_count, sizeof(MappedRange)); + if (pMap->ranges == NULL) { + LOGE("calloc(%u, %zu) failed: %s\n", range_count, sizeof(MappedRange), strerror(errno)); + return -1; + } // Reserve enough contiguous address space for the whole file. unsigned char* reserve; reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); if (reserve == MAP_FAILED) { LOGE("failed to reserve address space: %s\n", strerror(errno)); + free(pMap->ranges); return -1; } - pMap->ranges[range_count-1].addr = reserve; - pMap->ranges[range_count-1].length = blocks * blksize; - int fd = open(block_dev, O_RDONLY); if (fd < 0) { LOGE("failed to open block device %s: %s\n", block_dev, strerror(errno)); + munmap(reserve, blocks * blksize); + free(pMap->ranges); return -1; } unsigned char* next = reserve; + size_t remaining_size = blocks * blksize; + bool success = true; for (i = 0; i < range_count; ++i) { - int start, end; - if (fscanf(mapf, "%d %d\n", &start, &end) != 2) { + size_t start, end; + if (fscanf(mapf, "%zu %zu\n", &start, &end) != 2) { LOGE("failed to parse range %d in block map\n", i); - return -1; + success = false; + break; + } + size_t length = (end - start) * blksize; + if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) { + LOGE("unexpected range in block map: %zu %zu\n", start, end); + success = false; + break; } - void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize); + void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize); if (addr == MAP_FAILED) { LOGE("failed to map block %d: %s\n", i, strerror(errno)); - return -1; + success = false; + break; } pMap->ranges[i].addr = addr; - pMap->ranges[i].length = (end-start)*blksize; + pMap->ranges[i].length = length; - next += pMap->ranges[i].length; + next += length; + remaining_size -= length; + } + if (success && remaining_size != 0) { + LOGE("ranges in block map are invalid: remaining_size = %zu\n", remaining_size); + success = false; + } + if (!success) { + close(fd); + munmap(reserve, blocks * blksize); + free(pMap->ranges); + return -1; } + close(fd); pMap->addr = reserve; pMap->length = size; @@ -134,6 +170,7 @@ int sysMapFile(const char* fn, MemMapping* pMap) if (sysMapBlockFile(mapf, pMap) != 0) { LOGE("Map of '%s' failed\n", fn); + fclose(mapf); return -1; } diff --git a/otafault/Android.mk b/otafault/Android.mk index 50e385efb..d0b1174a4 100644 --- a/otafault/Android.mk +++ b/otafault/Android.mk @@ -14,8 +14,6 @@ LOCAL_PATH := $(call my-dir) -# otafault (static library) -# =============================== include $(CLEAR_VARS) otafault_static_libs := \ @@ -25,11 +23,12 @@ otafault_static_libs := \ libselinux LOCAL_SRC_FILES := config.cpp ota_io.cpp +LOCAL_MODULE_TAGS := eng LOCAL_MODULE := libotafault LOCAL_CLANG := true LOCAL_C_INCLUDES := bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) -LOCAL_STATIC_LIBRARIES := $(otafault_static_libs) +LOCAL_WHOLE_STATIC_LIBRARIES := $(otafault_static_libs) include $(BUILD_STATIC_LIBRARY) @@ -40,9 +39,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := config.cpp ota_io.cpp test.cpp LOCAL_MODULE_TAGS := tests LOCAL_MODULE := otafault_test -LOCAL_STATIC_LIBRARIES := \ - libotafault \ - $(otafault_static_libs) +LOCAL_STATIC_LIBRARIES := $(otafault_static_libs) LOCAL_C_INCLUDES := bootable/recovery LOCAL_FORCE_STATIC_EXECUTABLE := true diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp index dd805e56e..94be8155a 100644 --- a/otafault/ota_io.cpp +++ b/otafault/ota_io.cpp @@ -29,7 +29,6 @@ static std::map<intptr_t, const char*> filename_cache; static std::string read_fault_file_name = ""; static std::string write_fault_file_name = ""; static std::string fsync_fault_file_name = ""; -bool have_eio_error = false; static bool get_hit_file(const char* cached_path, std::string ffn) { return should_hit_cache() @@ -49,6 +48,8 @@ void ota_set_fault_files() { } } +bool have_eio_error = false; + int ota_open(const char* path, int oflags) { // Let the caller handle errors; we do not care if open succeeds or fails int fd = open(path, oflags); diff --git a/print_sha1.h b/print_sha1.h index fa3d7e009..c7c1f3651 100644 --- a/print_sha1.h +++ b/print_sha1.h @@ -22,7 +22,7 @@ #include "openssl/sha.h" -static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH], size_t len) { +static std::string print_sha1(const uint8_t* sha1, size_t len) { const char* hex = "0123456789abcdef"; std::string result = ""; for (size_t i = 0; i < len; ++i) { @@ -40,4 +40,8 @@ static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { return print_sha1(sha1, 4); } +static std::string print_hex(const uint8_t* bytes, size_t len) { + return print_sha1(bytes, len); +} + #endif // RECOVERY_PRINT_SHA1_H diff --git a/recovery.cpp b/recovery.cpp index b4dc41bfa..467b335ba 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -57,6 +57,7 @@ #include "bootloader.h" #include "common.h" #include "device.h" +#include "error_code.h" #include "fuse_sdcard_provider.h" #include "fuse_sideload.h" #include "install.h" @@ -81,6 +82,7 @@ static const struct option OPTIONS[] = { { "stages", required_argument, NULL, 'g' }, { "shutdown_after", no_argument, NULL, 'p' }, { "reason", required_argument, NULL, 'r' }, + { "security", no_argument, NULL, 'e'}, { "brick", no_argument, NULL, 0 }, { NULL, 0, NULL, 0 }, }; @@ -90,14 +92,18 @@ static const char *COMMAND_FILE = "/cache/recovery/command"; static const char *LOG_FILE = "/cache/recovery/log"; static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install"; static const char *LOCALE_FILE = "/cache/recovery/last_locale"; +static const char *CONVERT_FBE_DIR = "/tmp/convert_fbe"; +static const char *CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; static const char *CACHE_ROOT = "/cache"; +static const char *DATA_ROOT = "/data"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; static const char *LAST_LOG_FILE = "/cache/recovery/last_log"; static const int KEEP_LOG_COUNT = 10; -static const int EIO_RETRY_COUNT = 2; +// We will try to apply the update package 5 times at most in case of an I/O error. +static const int EIO_RETRY_COUNT = 4; static const int BATTERY_READ_TIMEOUT_IN_SEC = 10; // GmsCore enters recovery mode to install package when having enough battery // percentage. Normally, the threshold is 40% without charger and 20% with charger. @@ -107,7 +113,7 @@ static const int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15; constexpr const char* RECOVERY_BRICK = "/etc/recovery.brick"; RecoveryUI* ui = NULL; -char* locale = NULL; +static const char* locale = "en_US"; char* stage = NULL; char* reason = NULL; bool modified_flash = false; @@ -538,6 +544,7 @@ typedef struct _saved_log_file { static bool erase_volume(const char* volume) { bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); + bool is_data = (strcmp(volume, DATA_ROOT) == 0); ui->SetBackground(RecoveryUI::ERASING); ui->SetProgressType(RecoveryUI::INDETERMINATE); @@ -592,7 +599,28 @@ static bool erase_volume(const char* volume) { ui->Print("Formatting %s...\n", volume); ensure_path_unmounted(volume); - int result = format_volume(volume); + + int result; + + if (is_data && reason && strcmp(reason, "convert_fbe") == 0) { + // Create convert_fbe breadcrumb file to signal to init + // to convert to file based encryption, not full disk encryption + if (mkdir(CONVERT_FBE_DIR, 0700) != 0) { + ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno)); + return true; + } + FILE* f = fopen(CONVERT_FBE_FILE, "wb"); + if (!f) { + ui->Print("Failed to convert to file encryption %s\n", strerror(errno)); + return true; + } + fclose(f); + result = format_volume(volume, CONVERT_FBE_DIR); + remove(CONVERT_FBE_FILE); + rmdir(CONVERT_FBE_DIR); + } else { + result = format_volume(volume); + } if (is_cache) { while (head) { @@ -947,6 +975,37 @@ static void choose_recovery_file(Device* device) { } } +static void run_graphics_test(Device* device) { + // Switch to graphics screen. + ui->ShowText(false); + + ui->SetProgressType(RecoveryUI::INDETERMINATE); + ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); + sleep(1); + + ui->SetBackground(RecoveryUI::ERROR); + sleep(1); + + ui->SetBackground(RecoveryUI::NO_COMMAND); + sleep(1); + + ui->SetBackground(RecoveryUI::ERASING); + sleep(1); + + ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); + + ui->SetProgressType(RecoveryUI::DETERMINATE); + ui->ShowProgress(1.0, 10.0); + float fraction = 0.0; + for (size_t i = 0; i < 100; ++i) { + fraction += .01; + ui->SetProgress(fraction); + usleep(100000); + } + + ui->ShowText(true); +} + // How long (in seconds) we wait for the fuse-provided package file to // appear, before timing out. #define SDCARD_INSTALL_TIMEOUT 10 @@ -1005,7 +1064,7 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) { } result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, - TEMPORARY_INSTALL_FILE, false); + TEMPORARY_INSTALL_FILE, false, 0/*retry_count*/); break; } @@ -1105,6 +1164,10 @@ prompt_and_wait(Device* device, int status) { choose_recovery_file(device); break; + case Device::RUN_GRAPHICS_TEST: + run_graphics_test(device); + break; + case Device::MOUNT_SYSTEM: char system_root_image[PROPERTY_VALUE_MAX]; property_get("ro.build.system_root_image", system_root_image, ""); @@ -1346,6 +1409,7 @@ int main(int argc, char **argv) { bool just_exit = false; bool shutdown_after = false; int retry_count = 0; + bool security_update = false; int arg; int option_index; @@ -1370,6 +1434,7 @@ int main(int argc, char **argv) { } case 'p': shutdown_after = true; break; case 'r': reason = optarg; break; + case 'e': security_update = true; break; case 0: { if (strcmp(OPTIONS[option_index].name, "brick") == 0) { should_brick = true; @@ -1396,6 +1461,9 @@ int main(int argc, char **argv) { ui->SetLocale(locale); ui->Init(); + // Set background string to "installing security update" for security update, + // otherwise set it to "installing system update". + ui->SetSystemUpdateText(security_update); int st_cur, st_max; if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) { @@ -1458,10 +1526,21 @@ int main(int argc, char **argv) { if (!is_battery_ok()) { ui->Print("battery capacity is not enough for installing package, needed is %d%%\n", BATTERY_OK_PERCENTAGE); + // Log the error code to last_install when installation skips due to + // low battery. + FILE* install_log = fopen_path(LAST_INSTALL_FILE, "w"); + if (install_log != nullptr) { + fprintf(install_log, "%s\n", update_package); + fprintf(install_log, "0\n"); + fprintf(install_log, "error: %d\n", kLowBattery); + fclose(install_log); + } else { + LOGE("failed to open last_install: %s\n", strerror(errno)); + } status = INSTALL_SKIPPED; } else { status = install_package(update_package, &should_wipe_cache, - TEMPORARY_INSTALL_FILE, true); + TEMPORARY_INSTALL_FILE, true, retry_count); if (status == INSTALL_SUCCESS && should_wipe_cache) { wipe_cache(false, device); } diff --git a/res-560dpi b/res-560dpi index 8576a9b95..1db3a2e23 120000 --- a/res-560dpi +++ b/res-560dpi @@ -1 +1 @@ -res-xxhdpi
\ No newline at end of file +res-xxxhdpi
\ No newline at end of file diff --git a/res-hdpi/images/erasing_text.png b/res-hdpi/images/erasing_text.png Binary files differindex 774244c84..e500d0268 100644 --- a/res-hdpi/images/erasing_text.png +++ b/res-hdpi/images/erasing_text.png diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png Binary files differindex 64a57ec4e..9a3597b82 100644 --- a/res-hdpi/images/error_text.png +++ b/res-hdpi/images/error_text.png diff --git a/res-hdpi/images/icon_installing.png b/res-hdpi/images/icon_installing.png Binary files differdeleted file mode 100644 index 0fcfbc231..000000000 --- a/res-hdpi/images/icon_installing.png +++ /dev/null diff --git a/res-hdpi/images/installing_security_text.png b/res-hdpi/images/installing_security_text.png Binary files differnew file mode 100644 index 000000000..76e747410 --- /dev/null +++ b/res-hdpi/images/installing_security_text.png diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png Binary files differindex 33b54f1bf..5d2a5fa0c 100644 --- a/res-hdpi/images/installing_text.png +++ b/res-hdpi/images/installing_text.png diff --git a/res-hdpi/images/loop00000.png b/res-hdpi/images/loop00000.png Binary files differnew file mode 100644 index 000000000..030fa2b36 --- /dev/null +++ b/res-hdpi/images/loop00000.png diff --git a/res-hdpi/images/loop00001.png b/res-hdpi/images/loop00001.png Binary files differnew file mode 100644 index 000000000..546a10247 --- /dev/null +++ b/res-hdpi/images/loop00001.png diff --git a/res-hdpi/images/loop00002.png b/res-hdpi/images/loop00002.png Binary files differnew file mode 100644 index 000000000..262be3f24 --- /dev/null +++ b/res-hdpi/images/loop00002.png diff --git a/res-hdpi/images/loop00003.png b/res-hdpi/images/loop00003.png Binary files differnew file mode 100644 index 000000000..1282fb308 --- /dev/null +++ b/res-hdpi/images/loop00003.png diff --git a/res-hdpi/images/loop00004.png b/res-hdpi/images/loop00004.png Binary files differnew file mode 100644 index 000000000..2ff7678ff --- /dev/null +++ b/res-hdpi/images/loop00004.png diff --git a/res-hdpi/images/loop00005.png b/res-hdpi/images/loop00005.png Binary files differnew file mode 100644 index 000000000..20b4d815b --- /dev/null +++ b/res-hdpi/images/loop00005.png diff --git a/res-hdpi/images/loop00006.png b/res-hdpi/images/loop00006.png Binary files differnew file mode 100644 index 000000000..0f5b28d49 --- /dev/null +++ b/res-hdpi/images/loop00006.png diff --git a/res-hdpi/images/loop00007.png b/res-hdpi/images/loop00007.png Binary files differnew file mode 100644 index 000000000..008acc85b --- /dev/null +++ b/res-hdpi/images/loop00007.png diff --git a/res-hdpi/images/loop00008.png b/res-hdpi/images/loop00008.png Binary files differnew file mode 100644 index 000000000..ca1309dc2 --- /dev/null +++ b/res-hdpi/images/loop00008.png diff --git a/res-hdpi/images/loop00009.png b/res-hdpi/images/loop00009.png Binary files differnew file mode 100644 index 000000000..b2730f198 --- /dev/null +++ b/res-hdpi/images/loop00009.png diff --git a/res-hdpi/images/loop00010.png b/res-hdpi/images/loop00010.png Binary files differnew file mode 100644 index 000000000..3867e9c96 --- /dev/null +++ b/res-hdpi/images/loop00010.png diff --git a/res-hdpi/images/loop00011.png b/res-hdpi/images/loop00011.png Binary files differnew file mode 100644 index 000000000..2761d8fcb --- /dev/null +++ b/res-hdpi/images/loop00011.png diff --git a/res-hdpi/images/loop00012.png b/res-hdpi/images/loop00012.png Binary files differnew file mode 100644 index 000000000..2d976ef62 --- /dev/null +++ b/res-hdpi/images/loop00012.png diff --git a/res-hdpi/images/loop00013.png b/res-hdpi/images/loop00013.png Binary files differnew file mode 100644 index 000000000..5c96bb52c --- /dev/null +++ b/res-hdpi/images/loop00013.png diff --git a/res-hdpi/images/loop00014.png b/res-hdpi/images/loop00014.png Binary files differnew file mode 100644 index 000000000..d481ec57a --- /dev/null +++ b/res-hdpi/images/loop00014.png diff --git a/res-hdpi/images/loop00015.png b/res-hdpi/images/loop00015.png Binary files differnew file mode 100644 index 000000000..47716ede3 --- /dev/null +++ b/res-hdpi/images/loop00015.png diff --git a/res-hdpi/images/loop00016.png b/res-hdpi/images/loop00016.png Binary files differnew file mode 100644 index 000000000..c0cffe85c --- /dev/null +++ b/res-hdpi/images/loop00016.png diff --git a/res-hdpi/images/loop00017.png b/res-hdpi/images/loop00017.png Binary files differnew file mode 100644 index 000000000..a0dc2e51f --- /dev/null +++ b/res-hdpi/images/loop00017.png diff --git a/res-hdpi/images/loop00018.png b/res-hdpi/images/loop00018.png Binary files differnew file mode 100644 index 000000000..c8eefc5f8 --- /dev/null +++ b/res-hdpi/images/loop00018.png diff --git a/res-hdpi/images/loop00019.png b/res-hdpi/images/loop00019.png Binary files differnew file mode 100644 index 000000000..0d9d8e0fe --- /dev/null +++ b/res-hdpi/images/loop00019.png diff --git a/res-hdpi/images/loop00020.png b/res-hdpi/images/loop00020.png Binary files differnew file mode 100644 index 000000000..b4909a80d --- /dev/null +++ b/res-hdpi/images/loop00020.png diff --git a/res-hdpi/images/loop00021.png b/res-hdpi/images/loop00021.png Binary files differnew file mode 100644 index 000000000..b3c5274c8 --- /dev/null +++ b/res-hdpi/images/loop00021.png diff --git a/res-hdpi/images/loop00022.png b/res-hdpi/images/loop00022.png Binary files differnew file mode 100644 index 000000000..827c93707 --- /dev/null +++ b/res-hdpi/images/loop00022.png diff --git a/res-hdpi/images/loop00023.png b/res-hdpi/images/loop00023.png Binary files differnew file mode 100644 index 000000000..84440fec6 --- /dev/null +++ b/res-hdpi/images/loop00023.png diff --git a/res-hdpi/images/loop00024.png b/res-hdpi/images/loop00024.png Binary files differnew file mode 100644 index 000000000..cfc4c5bab --- /dev/null +++ b/res-hdpi/images/loop00024.png diff --git a/res-hdpi/images/loop00025.png b/res-hdpi/images/loop00025.png Binary files differnew file mode 100644 index 000000000..fd048fdcf --- /dev/null +++ b/res-hdpi/images/loop00025.png diff --git a/res-hdpi/images/loop00026.png b/res-hdpi/images/loop00026.png Binary files differnew file mode 100644 index 000000000..68251873a --- /dev/null +++ b/res-hdpi/images/loop00026.png diff --git a/res-hdpi/images/loop00027.png b/res-hdpi/images/loop00027.png Binary files differnew file mode 100644 index 000000000..238dad60a --- /dev/null +++ b/res-hdpi/images/loop00027.png diff --git a/res-hdpi/images/loop00028.png b/res-hdpi/images/loop00028.png Binary files differnew file mode 100644 index 000000000..55e058d2e --- /dev/null +++ b/res-hdpi/images/loop00028.png diff --git a/res-hdpi/images/loop00029.png b/res-hdpi/images/loop00029.png Binary files differnew file mode 100644 index 000000000..fc761370e --- /dev/null +++ b/res-hdpi/images/loop00029.png diff --git a/res-hdpi/images/loop00030.png b/res-hdpi/images/loop00030.png Binary files differnew file mode 100644 index 000000000..920634f89 --- /dev/null +++ b/res-hdpi/images/loop00030.png diff --git a/res-hdpi/images/loop00031.png b/res-hdpi/images/loop00031.png Binary files differnew file mode 100644 index 000000000..f54846461 --- /dev/null +++ b/res-hdpi/images/loop00031.png diff --git a/res-hdpi/images/loop00032.png b/res-hdpi/images/loop00032.png Binary files differnew file mode 100644 index 000000000..4cff5c44c --- /dev/null +++ b/res-hdpi/images/loop00032.png diff --git a/res-hdpi/images/loop00033.png b/res-hdpi/images/loop00033.png Binary files differnew file mode 100644 index 000000000..5d2d27222 --- /dev/null +++ b/res-hdpi/images/loop00033.png diff --git a/res-hdpi/images/loop00034.png b/res-hdpi/images/loop00034.png Binary files differnew file mode 100644 index 000000000..b4d73416e --- /dev/null +++ b/res-hdpi/images/loop00034.png diff --git a/res-hdpi/images/loop00035.png b/res-hdpi/images/loop00035.png Binary files differnew file mode 100644 index 000000000..49025b82d --- /dev/null +++ b/res-hdpi/images/loop00035.png diff --git a/res-hdpi/images/loop00036.png b/res-hdpi/images/loop00036.png Binary files differnew file mode 100644 index 000000000..b3aa58da5 --- /dev/null +++ b/res-hdpi/images/loop00036.png diff --git a/res-hdpi/images/loop00037.png b/res-hdpi/images/loop00037.png Binary files differnew file mode 100644 index 000000000..ff47e85d6 --- /dev/null +++ b/res-hdpi/images/loop00037.png diff --git a/res-hdpi/images/loop00038.png b/res-hdpi/images/loop00038.png Binary files differnew file mode 100644 index 000000000..8039b925f --- /dev/null +++ b/res-hdpi/images/loop00038.png diff --git a/res-hdpi/images/loop00039.png b/res-hdpi/images/loop00039.png Binary files differnew file mode 100644 index 000000000..e76d4bc96 --- /dev/null +++ b/res-hdpi/images/loop00039.png diff --git a/res-hdpi/images/loop00040.png b/res-hdpi/images/loop00040.png Binary files differnew file mode 100644 index 000000000..963cce7b6 --- /dev/null +++ b/res-hdpi/images/loop00040.png diff --git a/res-hdpi/images/loop00041.png b/res-hdpi/images/loop00041.png Binary files differnew file mode 100644 index 000000000..dcd5f1117 --- /dev/null +++ b/res-hdpi/images/loop00041.png diff --git a/res-hdpi/images/loop00042.png b/res-hdpi/images/loop00042.png Binary files differnew file mode 100644 index 000000000..72fe63ab4 --- /dev/null +++ b/res-hdpi/images/loop00042.png diff --git a/res-hdpi/images/loop00043.png b/res-hdpi/images/loop00043.png Binary files differnew file mode 100644 index 000000000..c109af88f --- /dev/null +++ b/res-hdpi/images/loop00043.png diff --git a/res-hdpi/images/loop00044.png b/res-hdpi/images/loop00044.png Binary files differnew file mode 100644 index 000000000..6648ec226 --- /dev/null +++ b/res-hdpi/images/loop00044.png diff --git a/res-hdpi/images/loop00045.png b/res-hdpi/images/loop00045.png Binary files differnew file mode 100644 index 000000000..90bf4313d --- /dev/null +++ b/res-hdpi/images/loop00045.png diff --git a/res-hdpi/images/loop00046.png b/res-hdpi/images/loop00046.png Binary files differnew file mode 100644 index 000000000..50473f0eb --- /dev/null +++ b/res-hdpi/images/loop00046.png diff --git a/res-hdpi/images/loop00047.png b/res-hdpi/images/loop00047.png Binary files differnew file mode 100644 index 000000000..db4702369 --- /dev/null +++ b/res-hdpi/images/loop00047.png diff --git a/res-hdpi/images/loop00048.png b/res-hdpi/images/loop00048.png Binary files differnew file mode 100644 index 000000000..462a42131 --- /dev/null +++ b/res-hdpi/images/loop00048.png diff --git a/res-hdpi/images/loop00049.png b/res-hdpi/images/loop00049.png Binary files differnew file mode 100644 index 000000000..f86af40f2 --- /dev/null +++ b/res-hdpi/images/loop00049.png diff --git a/res-hdpi/images/loop00050.png b/res-hdpi/images/loop00050.png Binary files differnew file mode 100644 index 000000000..8c0af52ff --- /dev/null +++ b/res-hdpi/images/loop00050.png diff --git a/res-hdpi/images/loop00051.png b/res-hdpi/images/loop00051.png Binary files differnew file mode 100644 index 000000000..2360fc0db --- /dev/null +++ b/res-hdpi/images/loop00051.png diff --git a/res-hdpi/images/loop00052.png b/res-hdpi/images/loop00052.png Binary files differnew file mode 100644 index 000000000..dd5220018 --- /dev/null +++ b/res-hdpi/images/loop00052.png diff --git a/res-hdpi/images/loop00053.png b/res-hdpi/images/loop00053.png Binary files differnew file mode 100644 index 000000000..c7f0c1886 --- /dev/null +++ b/res-hdpi/images/loop00053.png diff --git a/res-hdpi/images/loop00054.png b/res-hdpi/images/loop00054.png Binary files differnew file mode 100644 index 000000000..7f16eff59 --- /dev/null +++ b/res-hdpi/images/loop00054.png diff --git a/res-hdpi/images/loop00055.png b/res-hdpi/images/loop00055.png Binary files differnew file mode 100644 index 000000000..b9af0ce54 --- /dev/null +++ b/res-hdpi/images/loop00055.png diff --git a/res-hdpi/images/loop00056.png b/res-hdpi/images/loop00056.png Binary files differnew file mode 100644 index 000000000..40b9e9b32 --- /dev/null +++ b/res-hdpi/images/loop00056.png diff --git a/res-hdpi/images/loop00057.png b/res-hdpi/images/loop00057.png Binary files differnew file mode 100644 index 000000000..51068cb20 --- /dev/null +++ b/res-hdpi/images/loop00057.png diff --git a/res-hdpi/images/loop00058.png b/res-hdpi/images/loop00058.png Binary files differnew file mode 100644 index 000000000..eba4486ac --- /dev/null +++ b/res-hdpi/images/loop00058.png diff --git a/res-hdpi/images/loop00059.png b/res-hdpi/images/loop00059.png Binary files differnew file mode 100644 index 000000000..28761ac84 --- /dev/null +++ b/res-hdpi/images/loop00059.png diff --git a/res-hdpi/images/loop00060.png b/res-hdpi/images/loop00060.png Binary files differnew file mode 100644 index 000000000..6532eb93a --- /dev/null +++ b/res-hdpi/images/loop00060.png diff --git a/res-hdpi/images/loop00061.png b/res-hdpi/images/loop00061.png Binary files differnew file mode 100644 index 000000000..fbe2e2e9d --- /dev/null +++ b/res-hdpi/images/loop00061.png diff --git a/res-hdpi/images/loop00062.png b/res-hdpi/images/loop00062.png Binary files differnew file mode 100644 index 000000000..54341e34a --- /dev/null +++ b/res-hdpi/images/loop00062.png diff --git a/res-hdpi/images/loop00063.png b/res-hdpi/images/loop00063.png Binary files differnew file mode 100644 index 000000000..cfe9c802c --- /dev/null +++ b/res-hdpi/images/loop00063.png diff --git a/res-hdpi/images/loop00064.png b/res-hdpi/images/loop00064.png Binary files differnew file mode 100644 index 000000000..e1fe674ea --- /dev/null +++ b/res-hdpi/images/loop00064.png diff --git a/res-hdpi/images/loop00065.png b/res-hdpi/images/loop00065.png Binary files differnew file mode 100644 index 000000000..efa35b64a --- /dev/null +++ b/res-hdpi/images/loop00065.png diff --git a/res-hdpi/images/loop00066.png b/res-hdpi/images/loop00066.png Binary files differnew file mode 100644 index 000000000..d8c20fef1 --- /dev/null +++ b/res-hdpi/images/loop00066.png diff --git a/res-hdpi/images/loop00067.png b/res-hdpi/images/loop00067.png Binary files differnew file mode 100644 index 000000000..ddf1ea4cc --- /dev/null +++ b/res-hdpi/images/loop00067.png diff --git a/res-hdpi/images/loop00068.png b/res-hdpi/images/loop00068.png Binary files differnew file mode 100644 index 000000000..827cfc6af --- /dev/null +++ b/res-hdpi/images/loop00068.png diff --git a/res-hdpi/images/loop00069.png b/res-hdpi/images/loop00069.png Binary files differnew file mode 100644 index 000000000..6ab833f4b --- /dev/null +++ b/res-hdpi/images/loop00069.png diff --git a/res-hdpi/images/loop00070.png b/res-hdpi/images/loop00070.png Binary files differnew file mode 100644 index 000000000..a4cc06f12 --- /dev/null +++ b/res-hdpi/images/loop00070.png diff --git a/res-hdpi/images/loop00071.png b/res-hdpi/images/loop00071.png Binary files differnew file mode 100644 index 000000000..96653c188 --- /dev/null +++ b/res-hdpi/images/loop00071.png diff --git a/res-hdpi/images/loop00072.png b/res-hdpi/images/loop00072.png Binary files differnew file mode 100644 index 000000000..44a15f874 --- /dev/null +++ b/res-hdpi/images/loop00072.png diff --git a/res-hdpi/images/loop00073.png b/res-hdpi/images/loop00073.png Binary files differnew file mode 100644 index 000000000..8352c7ce0 --- /dev/null +++ b/res-hdpi/images/loop00073.png diff --git a/res-hdpi/images/loop00074.png b/res-hdpi/images/loop00074.png Binary files differnew file mode 100644 index 000000000..914f1b7fb --- /dev/null +++ b/res-hdpi/images/loop00074.png diff --git a/res-hdpi/images/loop00075.png b/res-hdpi/images/loop00075.png Binary files differnew file mode 100644 index 000000000..372b87139 --- /dev/null +++ b/res-hdpi/images/loop00075.png diff --git a/res-hdpi/images/loop00076.png b/res-hdpi/images/loop00076.png Binary files differnew file mode 100644 index 000000000..ffbf28570 --- /dev/null +++ b/res-hdpi/images/loop00076.png diff --git a/res-hdpi/images/loop00077.png b/res-hdpi/images/loop00077.png Binary files differnew file mode 100644 index 000000000..8dc6a4002 --- /dev/null +++ b/res-hdpi/images/loop00077.png diff --git a/res-hdpi/images/loop00078.png b/res-hdpi/images/loop00078.png Binary files differnew file mode 100644 index 000000000..cf1ea6120 --- /dev/null +++ b/res-hdpi/images/loop00078.png diff --git a/res-hdpi/images/loop00079.png b/res-hdpi/images/loop00079.png Binary files differnew file mode 100644 index 000000000..8674c822a --- /dev/null +++ b/res-hdpi/images/loop00079.png diff --git a/res-hdpi/images/loop00080.png b/res-hdpi/images/loop00080.png Binary files differnew file mode 100644 index 000000000..3d84259bb --- /dev/null +++ b/res-hdpi/images/loop00080.png diff --git a/res-hdpi/images/loop00081.png b/res-hdpi/images/loop00081.png Binary files differnew file mode 100644 index 000000000..aed44c53b --- /dev/null +++ b/res-hdpi/images/loop00081.png diff --git a/res-hdpi/images/loop00082.png b/res-hdpi/images/loop00082.png Binary files differnew file mode 100644 index 000000000..a39769bdb --- /dev/null +++ b/res-hdpi/images/loop00082.png diff --git a/res-hdpi/images/loop00083.png b/res-hdpi/images/loop00083.png Binary files differnew file mode 100644 index 000000000..905355d9e --- /dev/null +++ b/res-hdpi/images/loop00083.png diff --git a/res-hdpi/images/loop00084.png b/res-hdpi/images/loop00084.png Binary files differnew file mode 100644 index 000000000..c86deea16 --- /dev/null +++ b/res-hdpi/images/loop00084.png diff --git a/res-hdpi/images/loop00085.png b/res-hdpi/images/loop00085.png Binary files differnew file mode 100644 index 000000000..3744ab708 --- /dev/null +++ b/res-hdpi/images/loop00085.png diff --git a/res-hdpi/images/loop00086.png b/res-hdpi/images/loop00086.png Binary files differnew file mode 100644 index 000000000..0bb9b0963 --- /dev/null +++ b/res-hdpi/images/loop00086.png diff --git a/res-hdpi/images/loop00087.png b/res-hdpi/images/loop00087.png Binary files differnew file mode 100644 index 000000000..83f97bdd0 --- /dev/null +++ b/res-hdpi/images/loop00087.png diff --git a/res-hdpi/images/loop00088.png b/res-hdpi/images/loop00088.png Binary files differnew file mode 100644 index 000000000..6fd37909a --- /dev/null +++ b/res-hdpi/images/loop00088.png diff --git a/res-hdpi/images/loop00089.png b/res-hdpi/images/loop00089.png Binary files differnew file mode 100644 index 000000000..09500f87f --- /dev/null +++ b/res-hdpi/images/loop00089.png diff --git a/res-hdpi/images/loop00090.png b/res-hdpi/images/loop00090.png Binary files differnew file mode 100644 index 000000000..030fa2b36 --- /dev/null +++ b/res-hdpi/images/loop00090.png diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png Binary files differindex 9927ecb6d..a567ad141 100644 --- a/res-hdpi/images/no_command_text.png +++ b/res-hdpi/images/no_command_text.png diff --git a/res-hdpi/images/progress_empty.png b/res-hdpi/images/progress_empty.png Binary files differindex 72581832c..96c4bf6b3 100644 --- a/res-hdpi/images/progress_empty.png +++ b/res-hdpi/images/progress_empty.png diff --git a/res-hdpi/images/progress_fill.png b/res-hdpi/images/progress_fill.png Binary files differindex becf87bdf..1717be820 100644 --- a/res-hdpi/images/progress_fill.png +++ b/res-hdpi/images/progress_fill.png diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png Binary files differindex fd86c3f6e..ad68a1941 100644 --- a/res-mdpi/images/erasing_text.png +++ b/res-mdpi/images/erasing_text.png diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png Binary files differindex f1b44c9b3..8ea5acb20 100644 --- a/res-mdpi/images/error_text.png +++ b/res-mdpi/images/error_text.png diff --git a/res-mdpi/images/icon_installing.png b/res-mdpi/images/icon_installing.png Binary files differdeleted file mode 100644 index 0fcfbc231..000000000 --- a/res-mdpi/images/icon_installing.png +++ /dev/null diff --git a/res-mdpi/images/installing_security_text.png b/res-mdpi/images/installing_security_text.png Binary files differnew file mode 100644 index 000000000..615b9b73f --- /dev/null +++ b/res-mdpi/images/installing_security_text.png diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png Binary files differindex 064b2a317..6cf8716b5 100644 --- a/res-mdpi/images/installing_text.png +++ b/res-mdpi/images/installing_text.png diff --git a/res-mdpi/images/loop00000.png b/res-mdpi/images/loop00000.png Binary files differnew file mode 100644 index 000000000..d7092b68f --- /dev/null +++ b/res-mdpi/images/loop00000.png diff --git a/res-mdpi/images/loop00001.png b/res-mdpi/images/loop00001.png Binary files differnew file mode 100644 index 000000000..e04a5255a --- /dev/null +++ b/res-mdpi/images/loop00001.png diff --git a/res-mdpi/images/loop00002.png b/res-mdpi/images/loop00002.png Binary files differnew file mode 100644 index 000000000..e2a783102 --- /dev/null +++ b/res-mdpi/images/loop00002.png diff --git a/res-mdpi/images/loop00003.png b/res-mdpi/images/loop00003.png Binary files differnew file mode 100644 index 000000000..28f79bf4b --- /dev/null +++ b/res-mdpi/images/loop00003.png diff --git a/res-mdpi/images/loop00004.png b/res-mdpi/images/loop00004.png Binary files differnew file mode 100644 index 000000000..e4bec8084 --- /dev/null +++ b/res-mdpi/images/loop00004.png diff --git a/res-mdpi/images/loop00005.png b/res-mdpi/images/loop00005.png Binary files differnew file mode 100644 index 000000000..de673e03a --- /dev/null +++ b/res-mdpi/images/loop00005.png diff --git a/res-mdpi/images/loop00006.png b/res-mdpi/images/loop00006.png Binary files differnew file mode 100644 index 000000000..71d420311 --- /dev/null +++ b/res-mdpi/images/loop00006.png diff --git a/res-mdpi/images/loop00007.png b/res-mdpi/images/loop00007.png Binary files differnew file mode 100644 index 000000000..dee70b3b4 --- /dev/null +++ b/res-mdpi/images/loop00007.png diff --git a/res-mdpi/images/loop00008.png b/res-mdpi/images/loop00008.png Binary files differnew file mode 100644 index 000000000..9eccc7fe4 --- /dev/null +++ b/res-mdpi/images/loop00008.png diff --git a/res-mdpi/images/loop00009.png b/res-mdpi/images/loop00009.png Binary files differnew file mode 100644 index 000000000..d6672ac0f --- /dev/null +++ b/res-mdpi/images/loop00009.png diff --git a/res-mdpi/images/loop00010.png b/res-mdpi/images/loop00010.png Binary files differnew file mode 100644 index 000000000..1bb8f5c38 --- /dev/null +++ b/res-mdpi/images/loop00010.png diff --git a/res-mdpi/images/loop00011.png b/res-mdpi/images/loop00011.png Binary files differnew file mode 100644 index 000000000..849ce3dd0 --- /dev/null +++ b/res-mdpi/images/loop00011.png diff --git a/res-mdpi/images/loop00012.png b/res-mdpi/images/loop00012.png Binary files differnew file mode 100644 index 000000000..cee9dcf04 --- /dev/null +++ b/res-mdpi/images/loop00012.png diff --git a/res-mdpi/images/loop00013.png b/res-mdpi/images/loop00013.png Binary files differnew file mode 100644 index 000000000..1ef61d734 --- /dev/null +++ b/res-mdpi/images/loop00013.png diff --git a/res-mdpi/images/loop00014.png b/res-mdpi/images/loop00014.png Binary files differnew file mode 100644 index 000000000..bc84637bd --- /dev/null +++ b/res-mdpi/images/loop00014.png diff --git a/res-mdpi/images/loop00015.png b/res-mdpi/images/loop00015.png Binary files differnew file mode 100644 index 000000000..f5607f237 --- /dev/null +++ b/res-mdpi/images/loop00015.png diff --git a/res-mdpi/images/loop00016.png b/res-mdpi/images/loop00016.png Binary files differnew file mode 100644 index 000000000..235527c33 --- /dev/null +++ b/res-mdpi/images/loop00016.png diff --git a/res-mdpi/images/loop00017.png b/res-mdpi/images/loop00017.png Binary files differnew file mode 100644 index 000000000..88307a6b6 --- /dev/null +++ b/res-mdpi/images/loop00017.png diff --git a/res-mdpi/images/loop00018.png b/res-mdpi/images/loop00018.png Binary files differnew file mode 100644 index 000000000..02472d753 --- /dev/null +++ b/res-mdpi/images/loop00018.png diff --git a/res-mdpi/images/loop00019.png b/res-mdpi/images/loop00019.png Binary files differnew file mode 100644 index 000000000..f06bdaae5 --- /dev/null +++ b/res-mdpi/images/loop00019.png diff --git a/res-mdpi/images/loop00020.png b/res-mdpi/images/loop00020.png Binary files differnew file mode 100644 index 000000000..dc522c071 --- /dev/null +++ b/res-mdpi/images/loop00020.png diff --git a/res-mdpi/images/loop00021.png b/res-mdpi/images/loop00021.png Binary files differnew file mode 100644 index 000000000..3a53ee53d --- /dev/null +++ b/res-mdpi/images/loop00021.png diff --git a/res-mdpi/images/loop00022.png b/res-mdpi/images/loop00022.png Binary files differnew file mode 100644 index 000000000..09b8eea14 --- /dev/null +++ b/res-mdpi/images/loop00022.png diff --git a/res-mdpi/images/loop00023.png b/res-mdpi/images/loop00023.png Binary files differnew file mode 100644 index 000000000..ebc677d41 --- /dev/null +++ b/res-mdpi/images/loop00023.png diff --git a/res-mdpi/images/loop00024.png b/res-mdpi/images/loop00024.png Binary files differnew file mode 100644 index 000000000..a4fd8e508 --- /dev/null +++ b/res-mdpi/images/loop00024.png diff --git a/res-mdpi/images/loop00025.png b/res-mdpi/images/loop00025.png Binary files differnew file mode 100644 index 000000000..9435624ee --- /dev/null +++ b/res-mdpi/images/loop00025.png diff --git a/res-mdpi/images/loop00026.png b/res-mdpi/images/loop00026.png Binary files differnew file mode 100644 index 000000000..b7e808154 --- /dev/null +++ b/res-mdpi/images/loop00026.png diff --git a/res-mdpi/images/loop00027.png b/res-mdpi/images/loop00027.png Binary files differnew file mode 100644 index 000000000..757d8ed90 --- /dev/null +++ b/res-mdpi/images/loop00027.png diff --git a/res-mdpi/images/loop00028.png b/res-mdpi/images/loop00028.png Binary files differnew file mode 100644 index 000000000..8eefa3a57 --- /dev/null +++ b/res-mdpi/images/loop00028.png diff --git a/res-mdpi/images/loop00029.png b/res-mdpi/images/loop00029.png Binary files differnew file mode 100644 index 000000000..8d890dee6 --- /dev/null +++ b/res-mdpi/images/loop00029.png diff --git a/res-mdpi/images/loop00030.png b/res-mdpi/images/loop00030.png Binary files differnew file mode 100644 index 000000000..8e0eeb6f8 --- /dev/null +++ b/res-mdpi/images/loop00030.png diff --git a/res-mdpi/images/loop00031.png b/res-mdpi/images/loop00031.png Binary files differnew file mode 100644 index 000000000..178b29d6e --- /dev/null +++ b/res-mdpi/images/loop00031.png diff --git a/res-mdpi/images/loop00032.png b/res-mdpi/images/loop00032.png Binary files differnew file mode 100644 index 000000000..39192c75a --- /dev/null +++ b/res-mdpi/images/loop00032.png diff --git a/res-mdpi/images/loop00033.png b/res-mdpi/images/loop00033.png Binary files differnew file mode 100644 index 000000000..0647e5001 --- /dev/null +++ b/res-mdpi/images/loop00033.png diff --git a/res-mdpi/images/loop00034.png b/res-mdpi/images/loop00034.png Binary files differnew file mode 100644 index 000000000..d6bc079a6 --- /dev/null +++ b/res-mdpi/images/loop00034.png diff --git a/res-mdpi/images/loop00035.png b/res-mdpi/images/loop00035.png Binary files differnew file mode 100644 index 000000000..68352e85c --- /dev/null +++ b/res-mdpi/images/loop00035.png diff --git a/res-mdpi/images/loop00036.png b/res-mdpi/images/loop00036.png Binary files differnew file mode 100644 index 000000000..92d9da27b --- /dev/null +++ b/res-mdpi/images/loop00036.png diff --git a/res-mdpi/images/loop00037.png b/res-mdpi/images/loop00037.png Binary files differnew file mode 100644 index 000000000..a0e4d337f --- /dev/null +++ b/res-mdpi/images/loop00037.png diff --git a/res-mdpi/images/loop00038.png b/res-mdpi/images/loop00038.png Binary files differnew file mode 100644 index 000000000..c52317363 --- /dev/null +++ b/res-mdpi/images/loop00038.png diff --git a/res-mdpi/images/loop00039.png b/res-mdpi/images/loop00039.png Binary files differnew file mode 100644 index 000000000..aae776583 --- /dev/null +++ b/res-mdpi/images/loop00039.png diff --git a/res-mdpi/images/loop00040.png b/res-mdpi/images/loop00040.png Binary files differnew file mode 100644 index 000000000..af9e0188b --- /dev/null +++ b/res-mdpi/images/loop00040.png diff --git a/res-mdpi/images/loop00041.png b/res-mdpi/images/loop00041.png Binary files differnew file mode 100644 index 000000000..8e089c200 --- /dev/null +++ b/res-mdpi/images/loop00041.png diff --git a/res-mdpi/images/loop00042.png b/res-mdpi/images/loop00042.png Binary files differnew file mode 100644 index 000000000..e3e3b8a49 --- /dev/null +++ b/res-mdpi/images/loop00042.png diff --git a/res-mdpi/images/loop00043.png b/res-mdpi/images/loop00043.png Binary files differnew file mode 100644 index 000000000..cc8acbaeb --- /dev/null +++ b/res-mdpi/images/loop00043.png diff --git a/res-mdpi/images/loop00044.png b/res-mdpi/images/loop00044.png Binary files differnew file mode 100644 index 000000000..9a3a9b998 --- /dev/null +++ b/res-mdpi/images/loop00044.png diff --git a/res-mdpi/images/loop00045.png b/res-mdpi/images/loop00045.png Binary files differnew file mode 100644 index 000000000..ec5e3c4d1 --- /dev/null +++ b/res-mdpi/images/loop00045.png diff --git a/res-mdpi/images/loop00046.png b/res-mdpi/images/loop00046.png Binary files differnew file mode 100644 index 000000000..925e2b788 --- /dev/null +++ b/res-mdpi/images/loop00046.png diff --git a/res-mdpi/images/loop00047.png b/res-mdpi/images/loop00047.png Binary files differnew file mode 100644 index 000000000..62fff8864 --- /dev/null +++ b/res-mdpi/images/loop00047.png diff --git a/res-mdpi/images/loop00048.png b/res-mdpi/images/loop00048.png Binary files differnew file mode 100644 index 000000000..46efe7032 --- /dev/null +++ b/res-mdpi/images/loop00048.png diff --git a/res-mdpi/images/loop00049.png b/res-mdpi/images/loop00049.png Binary files differnew file mode 100644 index 000000000..678dce473 --- /dev/null +++ b/res-mdpi/images/loop00049.png diff --git a/res-mdpi/images/loop00050.png b/res-mdpi/images/loop00050.png Binary files differnew file mode 100644 index 000000000..cbc6fdbf0 --- /dev/null +++ b/res-mdpi/images/loop00050.png diff --git a/res-mdpi/images/loop00051.png b/res-mdpi/images/loop00051.png Binary files differnew file mode 100644 index 000000000..afa906677 --- /dev/null +++ b/res-mdpi/images/loop00051.png diff --git a/res-mdpi/images/loop00052.png b/res-mdpi/images/loop00052.png Binary files differnew file mode 100644 index 000000000..4d2d98c53 --- /dev/null +++ b/res-mdpi/images/loop00052.png diff --git a/res-mdpi/images/loop00053.png b/res-mdpi/images/loop00053.png Binary files differnew file mode 100644 index 000000000..48136a5da --- /dev/null +++ b/res-mdpi/images/loop00053.png diff --git a/res-mdpi/images/loop00054.png b/res-mdpi/images/loop00054.png Binary files differnew file mode 100644 index 000000000..09f706af6 --- /dev/null +++ b/res-mdpi/images/loop00054.png diff --git a/res-mdpi/images/loop00055.png b/res-mdpi/images/loop00055.png Binary files differnew file mode 100644 index 000000000..7565a1c21 --- /dev/null +++ b/res-mdpi/images/loop00055.png diff --git a/res-mdpi/images/loop00056.png b/res-mdpi/images/loop00056.png Binary files differnew file mode 100644 index 000000000..2765831a9 --- /dev/null +++ b/res-mdpi/images/loop00056.png diff --git a/res-mdpi/images/loop00057.png b/res-mdpi/images/loop00057.png Binary files differnew file mode 100644 index 000000000..de440e0dd --- /dev/null +++ b/res-mdpi/images/loop00057.png diff --git a/res-mdpi/images/loop00058.png b/res-mdpi/images/loop00058.png Binary files differnew file mode 100644 index 000000000..67d49c78f --- /dev/null +++ b/res-mdpi/images/loop00058.png diff --git a/res-mdpi/images/loop00059.png b/res-mdpi/images/loop00059.png Binary files differnew file mode 100644 index 000000000..a622f4587 --- /dev/null +++ b/res-mdpi/images/loop00059.png diff --git a/res-mdpi/images/loop00060.png b/res-mdpi/images/loop00060.png Binary files differnew file mode 100644 index 000000000..06d6eec8b --- /dev/null +++ b/res-mdpi/images/loop00060.png diff --git a/res-mdpi/images/loop00061.png b/res-mdpi/images/loop00061.png Binary files differnew file mode 100644 index 000000000..7f11945a0 --- /dev/null +++ b/res-mdpi/images/loop00061.png diff --git a/res-mdpi/images/loop00062.png b/res-mdpi/images/loop00062.png Binary files differnew file mode 100644 index 000000000..8197c94cf --- /dev/null +++ b/res-mdpi/images/loop00062.png diff --git a/res-mdpi/images/loop00063.png b/res-mdpi/images/loop00063.png Binary files differnew file mode 100644 index 000000000..4093c9b06 --- /dev/null +++ b/res-mdpi/images/loop00063.png diff --git a/res-mdpi/images/loop00064.png b/res-mdpi/images/loop00064.png Binary files differnew file mode 100644 index 000000000..d09bd1ebd --- /dev/null +++ b/res-mdpi/images/loop00064.png diff --git a/res-mdpi/images/loop00065.png b/res-mdpi/images/loop00065.png Binary files differnew file mode 100644 index 000000000..cbb6c1b80 --- /dev/null +++ b/res-mdpi/images/loop00065.png diff --git a/res-mdpi/images/loop00066.png b/res-mdpi/images/loop00066.png Binary files differnew file mode 100644 index 000000000..aed0a700a --- /dev/null +++ b/res-mdpi/images/loop00066.png diff --git a/res-mdpi/images/loop00067.png b/res-mdpi/images/loop00067.png Binary files differnew file mode 100644 index 000000000..dd0da799f --- /dev/null +++ b/res-mdpi/images/loop00067.png diff --git a/res-mdpi/images/loop00068.png b/res-mdpi/images/loop00068.png Binary files differnew file mode 100644 index 000000000..161802c8e --- /dev/null +++ b/res-mdpi/images/loop00068.png diff --git a/res-mdpi/images/loop00069.png b/res-mdpi/images/loop00069.png Binary files differnew file mode 100644 index 000000000..4ee0372a5 --- /dev/null +++ b/res-mdpi/images/loop00069.png diff --git a/res-mdpi/images/loop00070.png b/res-mdpi/images/loop00070.png Binary files differnew file mode 100644 index 000000000..41a64fff6 --- /dev/null +++ b/res-mdpi/images/loop00070.png diff --git a/res-mdpi/images/loop00071.png b/res-mdpi/images/loop00071.png Binary files differnew file mode 100644 index 000000000..c4793d79c --- /dev/null +++ b/res-mdpi/images/loop00071.png diff --git a/res-mdpi/images/loop00072.png b/res-mdpi/images/loop00072.png Binary files differnew file mode 100644 index 000000000..9399d193c --- /dev/null +++ b/res-mdpi/images/loop00072.png diff --git a/res-mdpi/images/loop00073.png b/res-mdpi/images/loop00073.png Binary files differnew file mode 100644 index 000000000..d4e55adc9 --- /dev/null +++ b/res-mdpi/images/loop00073.png diff --git a/res-mdpi/images/loop00074.png b/res-mdpi/images/loop00074.png Binary files differnew file mode 100644 index 000000000..f29a0af5f --- /dev/null +++ b/res-mdpi/images/loop00074.png diff --git a/res-mdpi/images/loop00075.png b/res-mdpi/images/loop00075.png Binary files differnew file mode 100644 index 000000000..020568e4a --- /dev/null +++ b/res-mdpi/images/loop00075.png diff --git a/res-mdpi/images/loop00076.png b/res-mdpi/images/loop00076.png Binary files differnew file mode 100644 index 000000000..51a54cc98 --- /dev/null +++ b/res-mdpi/images/loop00076.png diff --git a/res-mdpi/images/loop00077.png b/res-mdpi/images/loop00077.png Binary files differnew file mode 100644 index 000000000..f6e80a918 --- /dev/null +++ b/res-mdpi/images/loop00077.png diff --git a/res-mdpi/images/loop00078.png b/res-mdpi/images/loop00078.png Binary files differnew file mode 100644 index 000000000..944452120 --- /dev/null +++ b/res-mdpi/images/loop00078.png diff --git a/res-mdpi/images/loop00079.png b/res-mdpi/images/loop00079.png Binary files differnew file mode 100644 index 000000000..b1ef2c308 --- /dev/null +++ b/res-mdpi/images/loop00079.png diff --git a/res-mdpi/images/loop00080.png b/res-mdpi/images/loop00080.png Binary files differnew file mode 100644 index 000000000..8a911fba8 --- /dev/null +++ b/res-mdpi/images/loop00080.png diff --git a/res-mdpi/images/loop00081.png b/res-mdpi/images/loop00081.png Binary files differnew file mode 100644 index 000000000..f848df4c8 --- /dev/null +++ b/res-mdpi/images/loop00081.png diff --git a/res-mdpi/images/loop00082.png b/res-mdpi/images/loop00082.png Binary files differnew file mode 100644 index 000000000..35b1325aa --- /dev/null +++ b/res-mdpi/images/loop00082.png diff --git a/res-mdpi/images/loop00083.png b/res-mdpi/images/loop00083.png Binary files differnew file mode 100644 index 000000000..1571fb5a4 --- /dev/null +++ b/res-mdpi/images/loop00083.png diff --git a/res-mdpi/images/loop00084.png b/res-mdpi/images/loop00084.png Binary files differnew file mode 100644 index 000000000..92b529509 --- /dev/null +++ b/res-mdpi/images/loop00084.png diff --git a/res-mdpi/images/loop00085.png b/res-mdpi/images/loop00085.png Binary files differnew file mode 100644 index 000000000..cde8880c7 --- /dev/null +++ b/res-mdpi/images/loop00085.png diff --git a/res-mdpi/images/loop00086.png b/res-mdpi/images/loop00086.png Binary files differnew file mode 100644 index 000000000..45889e551 --- /dev/null +++ b/res-mdpi/images/loop00086.png diff --git a/res-mdpi/images/loop00087.png b/res-mdpi/images/loop00087.png Binary files differnew file mode 100644 index 000000000..9cad9aa39 --- /dev/null +++ b/res-mdpi/images/loop00087.png diff --git a/res-mdpi/images/loop00088.png b/res-mdpi/images/loop00088.png Binary files differnew file mode 100644 index 000000000..dcf98c8b6 --- /dev/null +++ b/res-mdpi/images/loop00088.png diff --git a/res-mdpi/images/loop00089.png b/res-mdpi/images/loop00089.png Binary files differnew file mode 100644 index 000000000..584cb8994 --- /dev/null +++ b/res-mdpi/images/loop00089.png diff --git a/res-mdpi/images/loop00090.png b/res-mdpi/images/loop00090.png Binary files differnew file mode 100644 index 000000000..d7092b68f --- /dev/null +++ b/res-mdpi/images/loop00090.png diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png Binary files differindex 1f29b8951..5cc6b3715 100644 --- a/res-mdpi/images/no_command_text.png +++ b/res-mdpi/images/no_command_text.png diff --git a/res-mdpi/images/progress_empty.png b/res-mdpi/images/progress_empty.png Binary files differindex 72581832c..96c4bf6b3 100644 --- a/res-mdpi/images/progress_empty.png +++ b/res-mdpi/images/progress_empty.png diff --git a/res-mdpi/images/progress_fill.png b/res-mdpi/images/progress_fill.png Binary files differindex becf87bdf..1717be820 100644 --- a/res-mdpi/images/progress_fill.png +++ b/res-mdpi/images/progress_fill.png diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png Binary files differindex f88e0e6a8..01176e8cc 100644 --- a/res-xhdpi/images/erasing_text.png +++ b/res-xhdpi/images/erasing_text.png diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png Binary files differindex c3a4cc6f8..4f890fa59 100644 --- a/res-xhdpi/images/error_text.png +++ b/res-xhdpi/images/error_text.png diff --git a/res-xhdpi/images/icon_installing.png b/res-xhdpi/images/icon_installing.png Binary files differdeleted file mode 100644 index 0fcfbc231..000000000 --- a/res-xhdpi/images/icon_installing.png +++ /dev/null diff --git a/res-xhdpi/images/installing_security_text.png b/res-xhdpi/images/installing_security_text.png Binary files differnew file mode 100644 index 000000000..6192832e8 --- /dev/null +++ b/res-xhdpi/images/installing_security_text.png diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png Binary files differindex a4dacd0f6..441352949 100644 --- a/res-xhdpi/images/installing_text.png +++ b/res-xhdpi/images/installing_text.png diff --git a/res-xhdpi/images/loop00000.png b/res-xhdpi/images/loop00000.png Binary files differnew file mode 100644 index 000000000..f5bf7a73b --- /dev/null +++ b/res-xhdpi/images/loop00000.png diff --git a/res-xhdpi/images/loop00001.png b/res-xhdpi/images/loop00001.png Binary files differnew file mode 100644 index 000000000..95c14ebf3 --- /dev/null +++ b/res-xhdpi/images/loop00001.png diff --git a/res-xhdpi/images/loop00002.png b/res-xhdpi/images/loop00002.png Binary files differnew file mode 100644 index 000000000..5910fd106 --- /dev/null +++ b/res-xhdpi/images/loop00002.png diff --git a/res-xhdpi/images/loop00003.png b/res-xhdpi/images/loop00003.png Binary files differnew file mode 100644 index 000000000..e6861d2a2 --- /dev/null +++ b/res-xhdpi/images/loop00003.png diff --git a/res-xhdpi/images/loop00004.png b/res-xhdpi/images/loop00004.png Binary files differnew file mode 100644 index 000000000..453cdc615 --- /dev/null +++ b/res-xhdpi/images/loop00004.png diff --git a/res-xhdpi/images/loop00005.png b/res-xhdpi/images/loop00005.png Binary files differnew file mode 100644 index 000000000..12157c961 --- /dev/null +++ b/res-xhdpi/images/loop00005.png diff --git a/res-xhdpi/images/loop00006.png b/res-xhdpi/images/loop00006.png Binary files differnew file mode 100644 index 000000000..5e7838535 --- /dev/null +++ b/res-xhdpi/images/loop00006.png diff --git a/res-xhdpi/images/loop00007.png b/res-xhdpi/images/loop00007.png Binary files differnew file mode 100644 index 000000000..c69abf4f2 --- /dev/null +++ b/res-xhdpi/images/loop00007.png diff --git a/res-xhdpi/images/loop00008.png b/res-xhdpi/images/loop00008.png Binary files differnew file mode 100644 index 000000000..78c3b993c --- /dev/null +++ b/res-xhdpi/images/loop00008.png diff --git a/res-xhdpi/images/loop00009.png b/res-xhdpi/images/loop00009.png Binary files differnew file mode 100644 index 000000000..e510b6b80 --- /dev/null +++ b/res-xhdpi/images/loop00009.png diff --git a/res-xhdpi/images/loop00010.png b/res-xhdpi/images/loop00010.png Binary files differnew file mode 100644 index 000000000..9d775faa4 --- /dev/null +++ b/res-xhdpi/images/loop00010.png diff --git a/res-xhdpi/images/loop00011.png b/res-xhdpi/images/loop00011.png Binary files differnew file mode 100644 index 000000000..36c01957e --- /dev/null +++ b/res-xhdpi/images/loop00011.png diff --git a/res-xhdpi/images/loop00012.png b/res-xhdpi/images/loop00012.png Binary files differnew file mode 100644 index 000000000..ac65096d9 --- /dev/null +++ b/res-xhdpi/images/loop00012.png diff --git a/res-xhdpi/images/loop00013.png b/res-xhdpi/images/loop00013.png Binary files differnew file mode 100644 index 000000000..e3fdaafa7 --- /dev/null +++ b/res-xhdpi/images/loop00013.png diff --git a/res-xhdpi/images/loop00014.png b/res-xhdpi/images/loop00014.png Binary files differnew file mode 100644 index 000000000..6e85108df --- /dev/null +++ b/res-xhdpi/images/loop00014.png diff --git a/res-xhdpi/images/loop00015.png b/res-xhdpi/images/loop00015.png Binary files differnew file mode 100644 index 000000000..9e6032951 --- /dev/null +++ b/res-xhdpi/images/loop00015.png diff --git a/res-xhdpi/images/loop00016.png b/res-xhdpi/images/loop00016.png Binary files differnew file mode 100644 index 000000000..68417aa54 --- /dev/null +++ b/res-xhdpi/images/loop00016.png diff --git a/res-xhdpi/images/loop00017.png b/res-xhdpi/images/loop00017.png Binary files differnew file mode 100644 index 000000000..4ac5dded3 --- /dev/null +++ b/res-xhdpi/images/loop00017.png diff --git a/res-xhdpi/images/loop00018.png b/res-xhdpi/images/loop00018.png Binary files differnew file mode 100644 index 000000000..d6511287e --- /dev/null +++ b/res-xhdpi/images/loop00018.png diff --git a/res-xhdpi/images/loop00019.png b/res-xhdpi/images/loop00019.png Binary files differnew file mode 100644 index 000000000..374273574 --- /dev/null +++ b/res-xhdpi/images/loop00019.png diff --git a/res-xhdpi/images/loop00020.png b/res-xhdpi/images/loop00020.png Binary files differnew file mode 100644 index 000000000..04489a10f --- /dev/null +++ b/res-xhdpi/images/loop00020.png diff --git a/res-xhdpi/images/loop00021.png b/res-xhdpi/images/loop00021.png Binary files differnew file mode 100644 index 000000000..59c70166c --- /dev/null +++ b/res-xhdpi/images/loop00021.png diff --git a/res-xhdpi/images/loop00022.png b/res-xhdpi/images/loop00022.png Binary files differnew file mode 100644 index 000000000..0b9a59f38 --- /dev/null +++ b/res-xhdpi/images/loop00022.png diff --git a/res-xhdpi/images/loop00023.png b/res-xhdpi/images/loop00023.png Binary files differnew file mode 100644 index 000000000..31abae79f --- /dev/null +++ b/res-xhdpi/images/loop00023.png diff --git a/res-xhdpi/images/loop00024.png b/res-xhdpi/images/loop00024.png Binary files differnew file mode 100644 index 000000000..98d8ee39c --- /dev/null +++ b/res-xhdpi/images/loop00024.png diff --git a/res-xhdpi/images/loop00025.png b/res-xhdpi/images/loop00025.png Binary files differnew file mode 100644 index 000000000..9f074d266 --- /dev/null +++ b/res-xhdpi/images/loop00025.png diff --git a/res-xhdpi/images/loop00026.png b/res-xhdpi/images/loop00026.png Binary files differnew file mode 100644 index 000000000..063fca2de --- /dev/null +++ b/res-xhdpi/images/loop00026.png diff --git a/res-xhdpi/images/loop00027.png b/res-xhdpi/images/loop00027.png Binary files differnew file mode 100644 index 000000000..67e503a70 --- /dev/null +++ b/res-xhdpi/images/loop00027.png diff --git a/res-xhdpi/images/loop00028.png b/res-xhdpi/images/loop00028.png Binary files differnew file mode 100644 index 000000000..7e76be845 --- /dev/null +++ b/res-xhdpi/images/loop00028.png diff --git a/res-xhdpi/images/loop00029.png b/res-xhdpi/images/loop00029.png Binary files differnew file mode 100644 index 000000000..4902f6b5e --- /dev/null +++ b/res-xhdpi/images/loop00029.png diff --git a/res-xhdpi/images/loop00030.png b/res-xhdpi/images/loop00030.png Binary files differnew file mode 100644 index 000000000..387b893b0 --- /dev/null +++ b/res-xhdpi/images/loop00030.png diff --git a/res-xhdpi/images/loop00031.png b/res-xhdpi/images/loop00031.png Binary files differnew file mode 100644 index 000000000..ad116280f --- /dev/null +++ b/res-xhdpi/images/loop00031.png diff --git a/res-xhdpi/images/loop00032.png b/res-xhdpi/images/loop00032.png Binary files differnew file mode 100644 index 000000000..7d809e6b2 --- /dev/null +++ b/res-xhdpi/images/loop00032.png diff --git a/res-xhdpi/images/loop00033.png b/res-xhdpi/images/loop00033.png Binary files differnew file mode 100644 index 000000000..59fcdc108 --- /dev/null +++ b/res-xhdpi/images/loop00033.png diff --git a/res-xhdpi/images/loop00034.png b/res-xhdpi/images/loop00034.png Binary files differnew file mode 100644 index 000000000..cb4301c31 --- /dev/null +++ b/res-xhdpi/images/loop00034.png diff --git a/res-xhdpi/images/loop00035.png b/res-xhdpi/images/loop00035.png Binary files differnew file mode 100644 index 000000000..6b1687807 --- /dev/null +++ b/res-xhdpi/images/loop00035.png diff --git a/res-xhdpi/images/loop00036.png b/res-xhdpi/images/loop00036.png Binary files differnew file mode 100644 index 000000000..3aa78502f --- /dev/null +++ b/res-xhdpi/images/loop00036.png diff --git a/res-xhdpi/images/loop00037.png b/res-xhdpi/images/loop00037.png Binary files differnew file mode 100644 index 000000000..a60e8512c --- /dev/null +++ b/res-xhdpi/images/loop00037.png diff --git a/res-xhdpi/images/loop00038.png b/res-xhdpi/images/loop00038.png Binary files differnew file mode 100644 index 000000000..50107f3aa --- /dev/null +++ b/res-xhdpi/images/loop00038.png diff --git a/res-xhdpi/images/loop00039.png b/res-xhdpi/images/loop00039.png Binary files differnew file mode 100644 index 000000000..c85201e25 --- /dev/null +++ b/res-xhdpi/images/loop00039.png diff --git a/res-xhdpi/images/loop00040.png b/res-xhdpi/images/loop00040.png Binary files differnew file mode 100644 index 000000000..6ae161218 --- /dev/null +++ b/res-xhdpi/images/loop00040.png diff --git a/res-xhdpi/images/loop00041.png b/res-xhdpi/images/loop00041.png Binary files differnew file mode 100644 index 000000000..7602b04a7 --- /dev/null +++ b/res-xhdpi/images/loop00041.png diff --git a/res-xhdpi/images/loop00042.png b/res-xhdpi/images/loop00042.png Binary files differnew file mode 100644 index 000000000..054da6d75 --- /dev/null +++ b/res-xhdpi/images/loop00042.png diff --git a/res-xhdpi/images/loop00043.png b/res-xhdpi/images/loop00043.png Binary files differnew file mode 100644 index 000000000..d28be8b8e --- /dev/null +++ b/res-xhdpi/images/loop00043.png diff --git a/res-xhdpi/images/loop00044.png b/res-xhdpi/images/loop00044.png Binary files differnew file mode 100644 index 000000000..83271268f --- /dev/null +++ b/res-xhdpi/images/loop00044.png diff --git a/res-xhdpi/images/loop00045.png b/res-xhdpi/images/loop00045.png Binary files differnew file mode 100644 index 000000000..d749e22d7 --- /dev/null +++ b/res-xhdpi/images/loop00045.png diff --git a/res-xhdpi/images/loop00046.png b/res-xhdpi/images/loop00046.png Binary files differnew file mode 100644 index 000000000..60025d1be --- /dev/null +++ b/res-xhdpi/images/loop00046.png diff --git a/res-xhdpi/images/loop00047.png b/res-xhdpi/images/loop00047.png Binary files differnew file mode 100644 index 000000000..b0be5c69c --- /dev/null +++ b/res-xhdpi/images/loop00047.png diff --git a/res-xhdpi/images/loop00048.png b/res-xhdpi/images/loop00048.png Binary files differnew file mode 100644 index 000000000..be926d9f2 --- /dev/null +++ b/res-xhdpi/images/loop00048.png diff --git a/res-xhdpi/images/loop00049.png b/res-xhdpi/images/loop00049.png Binary files differnew file mode 100644 index 000000000..456085442 --- /dev/null +++ b/res-xhdpi/images/loop00049.png diff --git a/res-xhdpi/images/loop00050.png b/res-xhdpi/images/loop00050.png Binary files differnew file mode 100644 index 000000000..967dd87ff --- /dev/null +++ b/res-xhdpi/images/loop00050.png diff --git a/res-xhdpi/images/loop00051.png b/res-xhdpi/images/loop00051.png Binary files differnew file mode 100644 index 000000000..c1698597a --- /dev/null +++ b/res-xhdpi/images/loop00051.png diff --git a/res-xhdpi/images/loop00052.png b/res-xhdpi/images/loop00052.png Binary files differnew file mode 100644 index 000000000..27c23830a --- /dev/null +++ b/res-xhdpi/images/loop00052.png diff --git a/res-xhdpi/images/loop00053.png b/res-xhdpi/images/loop00053.png Binary files differnew file mode 100644 index 000000000..cd2ca21c8 --- /dev/null +++ b/res-xhdpi/images/loop00053.png diff --git a/res-xhdpi/images/loop00054.png b/res-xhdpi/images/loop00054.png Binary files differnew file mode 100644 index 000000000..588586b3a --- /dev/null +++ b/res-xhdpi/images/loop00054.png diff --git a/res-xhdpi/images/loop00055.png b/res-xhdpi/images/loop00055.png Binary files differnew file mode 100644 index 000000000..0984d01fe --- /dev/null +++ b/res-xhdpi/images/loop00055.png diff --git a/res-xhdpi/images/loop00056.png b/res-xhdpi/images/loop00056.png Binary files differnew file mode 100644 index 000000000..bab299846 --- /dev/null +++ b/res-xhdpi/images/loop00056.png diff --git a/res-xhdpi/images/loop00057.png b/res-xhdpi/images/loop00057.png Binary files differnew file mode 100644 index 000000000..4acfce540 --- /dev/null +++ b/res-xhdpi/images/loop00057.png diff --git a/res-xhdpi/images/loop00058.png b/res-xhdpi/images/loop00058.png Binary files differnew file mode 100644 index 000000000..d49fea46f --- /dev/null +++ b/res-xhdpi/images/loop00058.png diff --git a/res-xhdpi/images/loop00059.png b/res-xhdpi/images/loop00059.png Binary files differnew file mode 100644 index 000000000..fdd75c6b9 --- /dev/null +++ b/res-xhdpi/images/loop00059.png diff --git a/res-xhdpi/images/loop00060.png b/res-xhdpi/images/loop00060.png Binary files differnew file mode 100644 index 000000000..06ac591ef --- /dev/null +++ b/res-xhdpi/images/loop00060.png diff --git a/res-xhdpi/images/loop00061.png b/res-xhdpi/images/loop00061.png Binary files differnew file mode 100644 index 000000000..63be53676 --- /dev/null +++ b/res-xhdpi/images/loop00061.png diff --git a/res-xhdpi/images/loop00062.png b/res-xhdpi/images/loop00062.png Binary files differnew file mode 100644 index 000000000..e25c906cb --- /dev/null +++ b/res-xhdpi/images/loop00062.png diff --git a/res-xhdpi/images/loop00063.png b/res-xhdpi/images/loop00063.png Binary files differnew file mode 100644 index 000000000..1fcaefe25 --- /dev/null +++ b/res-xhdpi/images/loop00063.png diff --git a/res-xhdpi/images/loop00064.png b/res-xhdpi/images/loop00064.png Binary files differnew file mode 100644 index 000000000..fe373d042 --- /dev/null +++ b/res-xhdpi/images/loop00064.png diff --git a/res-xhdpi/images/loop00065.png b/res-xhdpi/images/loop00065.png Binary files differnew file mode 100644 index 000000000..c5feed6ed --- /dev/null +++ b/res-xhdpi/images/loop00065.png diff --git a/res-xhdpi/images/loop00066.png b/res-xhdpi/images/loop00066.png Binary files differnew file mode 100644 index 000000000..bc336e755 --- /dev/null +++ b/res-xhdpi/images/loop00066.png diff --git a/res-xhdpi/images/loop00067.png b/res-xhdpi/images/loop00067.png Binary files differnew file mode 100644 index 000000000..a4cdcaea4 --- /dev/null +++ b/res-xhdpi/images/loop00067.png diff --git a/res-xhdpi/images/loop00068.png b/res-xhdpi/images/loop00068.png Binary files differnew file mode 100644 index 000000000..65d41a280 --- /dev/null +++ b/res-xhdpi/images/loop00068.png diff --git a/res-xhdpi/images/loop00069.png b/res-xhdpi/images/loop00069.png Binary files differnew file mode 100644 index 000000000..5707b62c7 --- /dev/null +++ b/res-xhdpi/images/loop00069.png diff --git a/res-xhdpi/images/loop00070.png b/res-xhdpi/images/loop00070.png Binary files differnew file mode 100644 index 000000000..50ea15925 --- /dev/null +++ b/res-xhdpi/images/loop00070.png diff --git a/res-xhdpi/images/loop00071.png b/res-xhdpi/images/loop00071.png Binary files differnew file mode 100644 index 000000000..244a91060 --- /dev/null +++ b/res-xhdpi/images/loop00071.png diff --git a/res-xhdpi/images/loop00072.png b/res-xhdpi/images/loop00072.png Binary files differnew file mode 100644 index 000000000..e5ee2abe7 --- /dev/null +++ b/res-xhdpi/images/loop00072.png diff --git a/res-xhdpi/images/loop00073.png b/res-xhdpi/images/loop00073.png Binary files differnew file mode 100644 index 000000000..fced739ae --- /dev/null +++ b/res-xhdpi/images/loop00073.png diff --git a/res-xhdpi/images/loop00074.png b/res-xhdpi/images/loop00074.png Binary files differnew file mode 100644 index 000000000..1b739d346 --- /dev/null +++ b/res-xhdpi/images/loop00074.png diff --git a/res-xhdpi/images/loop00075.png b/res-xhdpi/images/loop00075.png Binary files differnew file mode 100644 index 000000000..989144f69 --- /dev/null +++ b/res-xhdpi/images/loop00075.png diff --git a/res-xhdpi/images/loop00076.png b/res-xhdpi/images/loop00076.png Binary files differnew file mode 100644 index 000000000..458c2a9a2 --- /dev/null +++ b/res-xhdpi/images/loop00076.png diff --git a/res-xhdpi/images/loop00077.png b/res-xhdpi/images/loop00077.png Binary files differnew file mode 100644 index 000000000..9cecb1d0d --- /dev/null +++ b/res-xhdpi/images/loop00077.png diff --git a/res-xhdpi/images/loop00078.png b/res-xhdpi/images/loop00078.png Binary files differnew file mode 100644 index 000000000..c2c8dee86 --- /dev/null +++ b/res-xhdpi/images/loop00078.png diff --git a/res-xhdpi/images/loop00079.png b/res-xhdpi/images/loop00079.png Binary files differnew file mode 100644 index 000000000..4f4fdd142 --- /dev/null +++ b/res-xhdpi/images/loop00079.png diff --git a/res-xhdpi/images/loop00080.png b/res-xhdpi/images/loop00080.png Binary files differnew file mode 100644 index 000000000..b224378de --- /dev/null +++ b/res-xhdpi/images/loop00080.png diff --git a/res-xhdpi/images/loop00081.png b/res-xhdpi/images/loop00081.png Binary files differnew file mode 100644 index 000000000..57d958755 --- /dev/null +++ b/res-xhdpi/images/loop00081.png diff --git a/res-xhdpi/images/loop00082.png b/res-xhdpi/images/loop00082.png Binary files differnew file mode 100644 index 000000000..c00f82a04 --- /dev/null +++ b/res-xhdpi/images/loop00082.png diff --git a/res-xhdpi/images/loop00083.png b/res-xhdpi/images/loop00083.png Binary files differnew file mode 100644 index 000000000..078311f09 --- /dev/null +++ b/res-xhdpi/images/loop00083.png diff --git a/res-xhdpi/images/loop00084.png b/res-xhdpi/images/loop00084.png Binary files differnew file mode 100644 index 000000000..cac170893 --- /dev/null +++ b/res-xhdpi/images/loop00084.png diff --git a/res-xhdpi/images/loop00085.png b/res-xhdpi/images/loop00085.png Binary files differnew file mode 100644 index 000000000..2ea4b0afc --- /dev/null +++ b/res-xhdpi/images/loop00085.png diff --git a/res-xhdpi/images/loop00086.png b/res-xhdpi/images/loop00086.png Binary files differnew file mode 100644 index 000000000..9ba6ca615 --- /dev/null +++ b/res-xhdpi/images/loop00086.png diff --git a/res-xhdpi/images/loop00087.png b/res-xhdpi/images/loop00087.png Binary files differnew file mode 100644 index 000000000..75694a387 --- /dev/null +++ b/res-xhdpi/images/loop00087.png diff --git a/res-xhdpi/images/loop00088.png b/res-xhdpi/images/loop00088.png Binary files differnew file mode 100644 index 000000000..971e50848 --- /dev/null +++ b/res-xhdpi/images/loop00088.png diff --git a/res-xhdpi/images/loop00089.png b/res-xhdpi/images/loop00089.png Binary files differnew file mode 100644 index 000000000..41b6ce6b1 --- /dev/null +++ b/res-xhdpi/images/loop00089.png diff --git a/res-xhdpi/images/loop00090.png b/res-xhdpi/images/loop00090.png Binary files differnew file mode 100644 index 000000000..f5bf7a73b --- /dev/null +++ b/res-xhdpi/images/loop00090.png diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png Binary files differindex eb34e94b3..9f7403753 100644 --- a/res-xhdpi/images/no_command_text.png +++ b/res-xhdpi/images/no_command_text.png diff --git a/res-xhdpi/images/progress_empty.png b/res-xhdpi/images/progress_empty.png Binary files differindex 72581832c..96c4bf6b3 100644 --- a/res-xhdpi/images/progress_empty.png +++ b/res-xhdpi/images/progress_empty.png diff --git a/res-xhdpi/images/progress_fill.png b/res-xhdpi/images/progress_fill.png Binary files differindex becf87bdf..1717be820 100644 --- a/res-xhdpi/images/progress_fill.png +++ b/res-xhdpi/images/progress_fill.png diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png Binary files differindex c87fd52b4..b8653eb95 100644 --- a/res-xxhdpi/images/erasing_text.png +++ b/res-xxhdpi/images/erasing_text.png diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png Binary files differindex 486e951df..d77ee5085 100644 --- a/res-xxhdpi/images/error_text.png +++ b/res-xxhdpi/images/error_text.png diff --git a/res-xxhdpi/images/icon_installing.png b/res-xxhdpi/images/icon_installing.png Binary files differdeleted file mode 100644 index 0fcfbc231..000000000 --- a/res-xxhdpi/images/icon_installing.png +++ /dev/null diff --git a/res-xxhdpi/images/installing_security_text.png b/res-xxhdpi/images/installing_security_text.png Binary files differnew file mode 100644 index 000000000..ca0c191d4 --- /dev/null +++ b/res-xxhdpi/images/installing_security_text.png diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png Binary files differindex ef6e8f3f0..a76a174f0 100644 --- a/res-xxhdpi/images/installing_text.png +++ b/res-xxhdpi/images/installing_text.png diff --git a/res-xxhdpi/images/loop00000.png b/res-xxhdpi/images/loop00000.png Binary files differnew file mode 100644 index 000000000..c5172629d --- /dev/null +++ b/res-xxhdpi/images/loop00000.png diff --git a/res-xxhdpi/images/loop00001.png b/res-xxhdpi/images/loop00001.png Binary files differnew file mode 100644 index 000000000..1b1ce7397 --- /dev/null +++ b/res-xxhdpi/images/loop00001.png diff --git a/res-xxhdpi/images/loop00002.png b/res-xxhdpi/images/loop00002.png Binary files differnew file mode 100644 index 000000000..e984a24c9 --- /dev/null +++ b/res-xxhdpi/images/loop00002.png diff --git a/res-xxhdpi/images/loop00003.png b/res-xxhdpi/images/loop00003.png Binary files differnew file mode 100644 index 000000000..b11dddcb8 --- /dev/null +++ b/res-xxhdpi/images/loop00003.png diff --git a/res-xxhdpi/images/loop00004.png b/res-xxhdpi/images/loop00004.png Binary files differnew file mode 100644 index 000000000..10272b271 --- /dev/null +++ b/res-xxhdpi/images/loop00004.png diff --git a/res-xxhdpi/images/loop00005.png b/res-xxhdpi/images/loop00005.png Binary files differnew file mode 100644 index 000000000..9558d7e68 --- /dev/null +++ b/res-xxhdpi/images/loop00005.png diff --git a/res-xxhdpi/images/loop00006.png b/res-xxhdpi/images/loop00006.png Binary files differnew file mode 100644 index 000000000..0e6c92d69 --- /dev/null +++ b/res-xxhdpi/images/loop00006.png diff --git a/res-xxhdpi/images/loop00007.png b/res-xxhdpi/images/loop00007.png Binary files differnew file mode 100644 index 000000000..0a353ad02 --- /dev/null +++ b/res-xxhdpi/images/loop00007.png diff --git a/res-xxhdpi/images/loop00008.png b/res-xxhdpi/images/loop00008.png Binary files differnew file mode 100644 index 000000000..2f0c1620c --- /dev/null +++ b/res-xxhdpi/images/loop00008.png diff --git a/res-xxhdpi/images/loop00009.png b/res-xxhdpi/images/loop00009.png Binary files differnew file mode 100644 index 000000000..960d683fa --- /dev/null +++ b/res-xxhdpi/images/loop00009.png diff --git a/res-xxhdpi/images/loop00010.png b/res-xxhdpi/images/loop00010.png Binary files differnew file mode 100644 index 000000000..b65c3011a --- /dev/null +++ b/res-xxhdpi/images/loop00010.png diff --git a/res-xxhdpi/images/loop00011.png b/res-xxhdpi/images/loop00011.png Binary files differnew file mode 100644 index 000000000..21444fa54 --- /dev/null +++ b/res-xxhdpi/images/loop00011.png diff --git a/res-xxhdpi/images/loop00012.png b/res-xxhdpi/images/loop00012.png Binary files differnew file mode 100644 index 000000000..587db0990 --- /dev/null +++ b/res-xxhdpi/images/loop00012.png diff --git a/res-xxhdpi/images/loop00013.png b/res-xxhdpi/images/loop00013.png Binary files differnew file mode 100644 index 000000000..57f2f66e2 --- /dev/null +++ b/res-xxhdpi/images/loop00013.png diff --git a/res-xxhdpi/images/loop00014.png b/res-xxhdpi/images/loop00014.png Binary files differnew file mode 100644 index 000000000..d308a6530 --- /dev/null +++ b/res-xxhdpi/images/loop00014.png diff --git a/res-xxhdpi/images/loop00015.png b/res-xxhdpi/images/loop00015.png Binary files differnew file mode 100644 index 000000000..3585facad --- /dev/null +++ b/res-xxhdpi/images/loop00015.png diff --git a/res-xxhdpi/images/loop00016.png b/res-xxhdpi/images/loop00016.png Binary files differnew file mode 100644 index 000000000..fd5089c44 --- /dev/null +++ b/res-xxhdpi/images/loop00016.png diff --git a/res-xxhdpi/images/loop00017.png b/res-xxhdpi/images/loop00017.png Binary files differnew file mode 100644 index 000000000..2c8c6a434 --- /dev/null +++ b/res-xxhdpi/images/loop00017.png diff --git a/res-xxhdpi/images/loop00018.png b/res-xxhdpi/images/loop00018.png Binary files differnew file mode 100644 index 000000000..23d7ca287 --- /dev/null +++ b/res-xxhdpi/images/loop00018.png diff --git a/res-xxhdpi/images/loop00019.png b/res-xxhdpi/images/loop00019.png Binary files differnew file mode 100644 index 000000000..cdefe2c55 --- /dev/null +++ b/res-xxhdpi/images/loop00019.png diff --git a/res-xxhdpi/images/loop00020.png b/res-xxhdpi/images/loop00020.png Binary files differnew file mode 100644 index 000000000..ae78e4cfe --- /dev/null +++ b/res-xxhdpi/images/loop00020.png diff --git a/res-xxhdpi/images/loop00021.png b/res-xxhdpi/images/loop00021.png Binary files differnew file mode 100644 index 000000000..ad83cfe04 --- /dev/null +++ b/res-xxhdpi/images/loop00021.png diff --git a/res-xxhdpi/images/loop00022.png b/res-xxhdpi/images/loop00022.png Binary files differnew file mode 100644 index 000000000..850076ac1 --- /dev/null +++ b/res-xxhdpi/images/loop00022.png diff --git a/res-xxhdpi/images/loop00023.png b/res-xxhdpi/images/loop00023.png Binary files differnew file mode 100644 index 000000000..cd30b3997 --- /dev/null +++ b/res-xxhdpi/images/loop00023.png diff --git a/res-xxhdpi/images/loop00024.png b/res-xxhdpi/images/loop00024.png Binary files differnew file mode 100644 index 000000000..e7ae4b277 --- /dev/null +++ b/res-xxhdpi/images/loop00024.png diff --git a/res-xxhdpi/images/loop00025.png b/res-xxhdpi/images/loop00025.png Binary files differnew file mode 100644 index 000000000..4e24bd118 --- /dev/null +++ b/res-xxhdpi/images/loop00025.png diff --git a/res-xxhdpi/images/loop00026.png b/res-xxhdpi/images/loop00026.png Binary files differnew file mode 100644 index 000000000..27713cce9 --- /dev/null +++ b/res-xxhdpi/images/loop00026.png diff --git a/res-xxhdpi/images/loop00027.png b/res-xxhdpi/images/loop00027.png Binary files differnew file mode 100644 index 000000000..34e4ade2e --- /dev/null +++ b/res-xxhdpi/images/loop00027.png diff --git a/res-xxhdpi/images/loop00028.png b/res-xxhdpi/images/loop00028.png Binary files differnew file mode 100644 index 000000000..0e6fdee75 --- /dev/null +++ b/res-xxhdpi/images/loop00028.png diff --git a/res-xxhdpi/images/loop00029.png b/res-xxhdpi/images/loop00029.png Binary files differnew file mode 100644 index 000000000..21c1c635b --- /dev/null +++ b/res-xxhdpi/images/loop00029.png diff --git a/res-xxhdpi/images/loop00030.png b/res-xxhdpi/images/loop00030.png Binary files differnew file mode 100644 index 000000000..984c24f9b --- /dev/null +++ b/res-xxhdpi/images/loop00030.png diff --git a/res-xxhdpi/images/loop00031.png b/res-xxhdpi/images/loop00031.png Binary files differnew file mode 100644 index 000000000..25fe1de26 --- /dev/null +++ b/res-xxhdpi/images/loop00031.png diff --git a/res-xxhdpi/images/loop00032.png b/res-xxhdpi/images/loop00032.png Binary files differnew file mode 100644 index 000000000..c089cb8a4 --- /dev/null +++ b/res-xxhdpi/images/loop00032.png diff --git a/res-xxhdpi/images/loop00033.png b/res-xxhdpi/images/loop00033.png Binary files differnew file mode 100644 index 000000000..82a2d9b54 --- /dev/null +++ b/res-xxhdpi/images/loop00033.png diff --git a/res-xxhdpi/images/loop00034.png b/res-xxhdpi/images/loop00034.png Binary files differnew file mode 100644 index 000000000..1aa76b94c --- /dev/null +++ b/res-xxhdpi/images/loop00034.png diff --git a/res-xxhdpi/images/loop00035.png b/res-xxhdpi/images/loop00035.png Binary files differnew file mode 100644 index 000000000..4399143e4 --- /dev/null +++ b/res-xxhdpi/images/loop00035.png diff --git a/res-xxhdpi/images/loop00036.png b/res-xxhdpi/images/loop00036.png Binary files differnew file mode 100644 index 000000000..975ae666e --- /dev/null +++ b/res-xxhdpi/images/loop00036.png diff --git a/res-xxhdpi/images/loop00037.png b/res-xxhdpi/images/loop00037.png Binary files differnew file mode 100644 index 000000000..dcf9a9050 --- /dev/null +++ b/res-xxhdpi/images/loop00037.png diff --git a/res-xxhdpi/images/loop00038.png b/res-xxhdpi/images/loop00038.png Binary files differnew file mode 100644 index 000000000..f10b8b723 --- /dev/null +++ b/res-xxhdpi/images/loop00038.png diff --git a/res-xxhdpi/images/loop00039.png b/res-xxhdpi/images/loop00039.png Binary files differnew file mode 100644 index 000000000..9c0d1e361 --- /dev/null +++ b/res-xxhdpi/images/loop00039.png diff --git a/res-xxhdpi/images/loop00040.png b/res-xxhdpi/images/loop00040.png Binary files differnew file mode 100644 index 000000000..b6b4908f6 --- /dev/null +++ b/res-xxhdpi/images/loop00040.png diff --git a/res-xxhdpi/images/loop00041.png b/res-xxhdpi/images/loop00041.png Binary files differnew file mode 100644 index 000000000..12a1a1e9a --- /dev/null +++ b/res-xxhdpi/images/loop00041.png diff --git a/res-xxhdpi/images/loop00042.png b/res-xxhdpi/images/loop00042.png Binary files differnew file mode 100644 index 000000000..f1fc35baf --- /dev/null +++ b/res-xxhdpi/images/loop00042.png diff --git a/res-xxhdpi/images/loop00043.png b/res-xxhdpi/images/loop00043.png Binary files differnew file mode 100644 index 000000000..50ac99e5e --- /dev/null +++ b/res-xxhdpi/images/loop00043.png diff --git a/res-xxhdpi/images/loop00044.png b/res-xxhdpi/images/loop00044.png Binary files differnew file mode 100644 index 000000000..f115dcc9b --- /dev/null +++ b/res-xxhdpi/images/loop00044.png diff --git a/res-xxhdpi/images/loop00045.png b/res-xxhdpi/images/loop00045.png Binary files differnew file mode 100644 index 000000000..adf7a671e --- /dev/null +++ b/res-xxhdpi/images/loop00045.png diff --git a/res-xxhdpi/images/loop00046.png b/res-xxhdpi/images/loop00046.png Binary files differnew file mode 100644 index 000000000..588eeb3fb --- /dev/null +++ b/res-xxhdpi/images/loop00046.png diff --git a/res-xxhdpi/images/loop00047.png b/res-xxhdpi/images/loop00047.png Binary files differnew file mode 100644 index 000000000..9dea7701d --- /dev/null +++ b/res-xxhdpi/images/loop00047.png diff --git a/res-xxhdpi/images/loop00048.png b/res-xxhdpi/images/loop00048.png Binary files differnew file mode 100644 index 000000000..d5eaeb126 --- /dev/null +++ b/res-xxhdpi/images/loop00048.png diff --git a/res-xxhdpi/images/loop00049.png b/res-xxhdpi/images/loop00049.png Binary files differnew file mode 100644 index 000000000..fb837295b --- /dev/null +++ b/res-xxhdpi/images/loop00049.png diff --git a/res-xxhdpi/images/loop00050.png b/res-xxhdpi/images/loop00050.png Binary files differnew file mode 100644 index 000000000..72441db4f --- /dev/null +++ b/res-xxhdpi/images/loop00050.png diff --git a/res-xxhdpi/images/loop00051.png b/res-xxhdpi/images/loop00051.png Binary files differnew file mode 100644 index 000000000..bf7170a53 --- /dev/null +++ b/res-xxhdpi/images/loop00051.png diff --git a/res-xxhdpi/images/loop00052.png b/res-xxhdpi/images/loop00052.png Binary files differnew file mode 100644 index 000000000..c512b56d4 --- /dev/null +++ b/res-xxhdpi/images/loop00052.png diff --git a/res-xxhdpi/images/loop00053.png b/res-xxhdpi/images/loop00053.png Binary files differnew file mode 100644 index 000000000..6ac3ca6f3 --- /dev/null +++ b/res-xxhdpi/images/loop00053.png diff --git a/res-xxhdpi/images/loop00054.png b/res-xxhdpi/images/loop00054.png Binary files differnew file mode 100644 index 000000000..ba194a6ad --- /dev/null +++ b/res-xxhdpi/images/loop00054.png diff --git a/res-xxhdpi/images/loop00055.png b/res-xxhdpi/images/loop00055.png Binary files differnew file mode 100644 index 000000000..9623f0d5c --- /dev/null +++ b/res-xxhdpi/images/loop00055.png diff --git a/res-xxhdpi/images/loop00056.png b/res-xxhdpi/images/loop00056.png Binary files differnew file mode 100644 index 000000000..e785e690f --- /dev/null +++ b/res-xxhdpi/images/loop00056.png diff --git a/res-xxhdpi/images/loop00057.png b/res-xxhdpi/images/loop00057.png Binary files differnew file mode 100644 index 000000000..9a5747ae3 --- /dev/null +++ b/res-xxhdpi/images/loop00057.png diff --git a/res-xxhdpi/images/loop00058.png b/res-xxhdpi/images/loop00058.png Binary files differnew file mode 100644 index 000000000..9a097cf12 --- /dev/null +++ b/res-xxhdpi/images/loop00058.png diff --git a/res-xxhdpi/images/loop00059.png b/res-xxhdpi/images/loop00059.png Binary files differnew file mode 100644 index 000000000..fee2db1e1 --- /dev/null +++ b/res-xxhdpi/images/loop00059.png diff --git a/res-xxhdpi/images/loop00060.png b/res-xxhdpi/images/loop00060.png Binary files differnew file mode 100644 index 000000000..0e00e709f --- /dev/null +++ b/res-xxhdpi/images/loop00060.png diff --git a/res-xxhdpi/images/loop00061.png b/res-xxhdpi/images/loop00061.png Binary files differnew file mode 100644 index 000000000..0ecce17bd --- /dev/null +++ b/res-xxhdpi/images/loop00061.png diff --git a/res-xxhdpi/images/loop00062.png b/res-xxhdpi/images/loop00062.png Binary files differnew file mode 100644 index 000000000..0a296d129 --- /dev/null +++ b/res-xxhdpi/images/loop00062.png diff --git a/res-xxhdpi/images/loop00063.png b/res-xxhdpi/images/loop00063.png Binary files differnew file mode 100644 index 000000000..56c3b8b9b --- /dev/null +++ b/res-xxhdpi/images/loop00063.png diff --git a/res-xxhdpi/images/loop00064.png b/res-xxhdpi/images/loop00064.png Binary files differnew file mode 100644 index 000000000..e6d639a3d --- /dev/null +++ b/res-xxhdpi/images/loop00064.png diff --git a/res-xxhdpi/images/loop00065.png b/res-xxhdpi/images/loop00065.png Binary files differnew file mode 100644 index 000000000..02e382b99 --- /dev/null +++ b/res-xxhdpi/images/loop00065.png diff --git a/res-xxhdpi/images/loop00066.png b/res-xxhdpi/images/loop00066.png Binary files differnew file mode 100644 index 000000000..fe89ed00a --- /dev/null +++ b/res-xxhdpi/images/loop00066.png diff --git a/res-xxhdpi/images/loop00067.png b/res-xxhdpi/images/loop00067.png Binary files differnew file mode 100644 index 000000000..a8f6ce50c --- /dev/null +++ b/res-xxhdpi/images/loop00067.png diff --git a/res-xxhdpi/images/loop00068.png b/res-xxhdpi/images/loop00068.png Binary files differnew file mode 100644 index 000000000..f9b7fb1c9 --- /dev/null +++ b/res-xxhdpi/images/loop00068.png diff --git a/res-xxhdpi/images/loop00069.png b/res-xxhdpi/images/loop00069.png Binary files differnew file mode 100644 index 000000000..d0dc50768 --- /dev/null +++ b/res-xxhdpi/images/loop00069.png diff --git a/res-xxhdpi/images/loop00070.png b/res-xxhdpi/images/loop00070.png Binary files differnew file mode 100644 index 000000000..63f9e4df1 --- /dev/null +++ b/res-xxhdpi/images/loop00070.png diff --git a/res-xxhdpi/images/loop00071.png b/res-xxhdpi/images/loop00071.png Binary files differnew file mode 100644 index 000000000..5ba39724f --- /dev/null +++ b/res-xxhdpi/images/loop00071.png diff --git a/res-xxhdpi/images/loop00072.png b/res-xxhdpi/images/loop00072.png Binary files differnew file mode 100644 index 000000000..de834e398 --- /dev/null +++ b/res-xxhdpi/images/loop00072.png diff --git a/res-xxhdpi/images/loop00073.png b/res-xxhdpi/images/loop00073.png Binary files differnew file mode 100644 index 000000000..4be2aed88 --- /dev/null +++ b/res-xxhdpi/images/loop00073.png diff --git a/res-xxhdpi/images/loop00074.png b/res-xxhdpi/images/loop00074.png Binary files differnew file mode 100644 index 000000000..235e9a228 --- /dev/null +++ b/res-xxhdpi/images/loop00074.png diff --git a/res-xxhdpi/images/loop00075.png b/res-xxhdpi/images/loop00075.png Binary files differnew file mode 100644 index 000000000..f6d806d5a --- /dev/null +++ b/res-xxhdpi/images/loop00075.png diff --git a/res-xxhdpi/images/loop00076.png b/res-xxhdpi/images/loop00076.png Binary files differnew file mode 100644 index 000000000..1e916d723 --- /dev/null +++ b/res-xxhdpi/images/loop00076.png diff --git a/res-xxhdpi/images/loop00077.png b/res-xxhdpi/images/loop00077.png Binary files differnew file mode 100644 index 000000000..0dbac748e --- /dev/null +++ b/res-xxhdpi/images/loop00077.png diff --git a/res-xxhdpi/images/loop00078.png b/res-xxhdpi/images/loop00078.png Binary files differnew file mode 100644 index 000000000..504d34a44 --- /dev/null +++ b/res-xxhdpi/images/loop00078.png diff --git a/res-xxhdpi/images/loop00079.png b/res-xxhdpi/images/loop00079.png Binary files differnew file mode 100644 index 000000000..51f4e8d7c --- /dev/null +++ b/res-xxhdpi/images/loop00079.png diff --git a/res-xxhdpi/images/loop00080.png b/res-xxhdpi/images/loop00080.png Binary files differnew file mode 100644 index 000000000..6ef03b82e --- /dev/null +++ b/res-xxhdpi/images/loop00080.png diff --git a/res-xxhdpi/images/loop00081.png b/res-xxhdpi/images/loop00081.png Binary files differnew file mode 100644 index 000000000..e2ebc394d --- /dev/null +++ b/res-xxhdpi/images/loop00081.png diff --git a/res-xxhdpi/images/loop00082.png b/res-xxhdpi/images/loop00082.png Binary files differnew file mode 100644 index 000000000..9de83a733 --- /dev/null +++ b/res-xxhdpi/images/loop00082.png diff --git a/res-xxhdpi/images/loop00083.png b/res-xxhdpi/images/loop00083.png Binary files differnew file mode 100644 index 000000000..c5c0099de --- /dev/null +++ b/res-xxhdpi/images/loop00083.png diff --git a/res-xxhdpi/images/loop00084.png b/res-xxhdpi/images/loop00084.png Binary files differnew file mode 100644 index 000000000..84c794f31 --- /dev/null +++ b/res-xxhdpi/images/loop00084.png diff --git a/res-xxhdpi/images/loop00085.png b/res-xxhdpi/images/loop00085.png Binary files differnew file mode 100644 index 000000000..29a40c628 --- /dev/null +++ b/res-xxhdpi/images/loop00085.png diff --git a/res-xxhdpi/images/loop00086.png b/res-xxhdpi/images/loop00086.png Binary files differnew file mode 100644 index 000000000..89a471735 --- /dev/null +++ b/res-xxhdpi/images/loop00086.png diff --git a/res-xxhdpi/images/loop00087.png b/res-xxhdpi/images/loop00087.png Binary files differnew file mode 100644 index 000000000..ef8d4d5db --- /dev/null +++ b/res-xxhdpi/images/loop00087.png diff --git a/res-xxhdpi/images/loop00088.png b/res-xxhdpi/images/loop00088.png Binary files differnew file mode 100644 index 000000000..5fc6c6224 --- /dev/null +++ b/res-xxhdpi/images/loop00088.png diff --git a/res-xxhdpi/images/loop00089.png b/res-xxhdpi/images/loop00089.png Binary files differnew file mode 100644 index 000000000..d6d99f42f --- /dev/null +++ b/res-xxhdpi/images/loop00089.png diff --git a/res-xxhdpi/images/loop00090.png b/res-xxhdpi/images/loop00090.png Binary files differnew file mode 100644 index 000000000..c5172629d --- /dev/null +++ b/res-xxhdpi/images/loop00090.png diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png Binary files differindex cc98bb18a..5e363e3a5 100644 --- a/res-xxhdpi/images/no_command_text.png +++ b/res-xxhdpi/images/no_command_text.png diff --git a/res-xxhdpi/images/progress_empty.png b/res-xxhdpi/images/progress_empty.png Binary files differindex 72581832c..96c4bf6b3 100644 --- a/res-xxhdpi/images/progress_empty.png +++ b/res-xxhdpi/images/progress_empty.png diff --git a/res-xxhdpi/images/progress_fill.png b/res-xxhdpi/images/progress_fill.png Binary files differindex becf87bdf..1717be820 100644 --- a/res-xxhdpi/images/progress_fill.png +++ b/res-xxhdpi/images/progress_fill.png diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png Binary files differindex 612e7a390..f4a466118 100644 --- a/res-xxxhdpi/images/erasing_text.png +++ b/res-xxxhdpi/images/erasing_text.png diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png Binary files differindex 50d2fadb5..317a7716f 100644 --- a/res-xxxhdpi/images/error_text.png +++ b/res-xxxhdpi/images/error_text.png diff --git a/res-xxxhdpi/images/icon_installing.png b/res-xxxhdpi/images/icon_installing.png Binary files differdeleted file mode 100644 index 0fcfbc231..000000000 --- a/res-xxxhdpi/images/icon_installing.png +++ /dev/null diff --git a/res-xxxhdpi/images/installing_security_text.png b/res-xxxhdpi/images/installing_security_text.png Binary files differnew file mode 100644 index 000000000..c99c9072a --- /dev/null +++ b/res-xxxhdpi/images/installing_security_text.png diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png Binary files differindex 9bd093bf4..91d833017 100644 --- a/res-xxxhdpi/images/installing_text.png +++ b/res-xxxhdpi/images/installing_text.png diff --git a/res-xxxhdpi/images/loop00000.png b/res-xxxhdpi/images/loop00000.png Binary files differnew file mode 100644 index 000000000..1bc9db5b1 --- /dev/null +++ b/res-xxxhdpi/images/loop00000.png diff --git a/res-xxxhdpi/images/loop00001.png b/res-xxxhdpi/images/loop00001.png Binary files differnew file mode 100644 index 000000000..f835b8567 --- /dev/null +++ b/res-xxxhdpi/images/loop00001.png diff --git a/res-xxxhdpi/images/loop00002.png b/res-xxxhdpi/images/loop00002.png Binary files differnew file mode 100644 index 000000000..e3bff326c --- /dev/null +++ b/res-xxxhdpi/images/loop00002.png diff --git a/res-xxxhdpi/images/loop00003.png b/res-xxxhdpi/images/loop00003.png Binary files differnew file mode 100644 index 000000000..d864c15d7 --- /dev/null +++ b/res-xxxhdpi/images/loop00003.png diff --git a/res-xxxhdpi/images/loop00004.png b/res-xxxhdpi/images/loop00004.png Binary files differnew file mode 100644 index 000000000..5d861c9b4 --- /dev/null +++ b/res-xxxhdpi/images/loop00004.png diff --git a/res-xxxhdpi/images/loop00005.png b/res-xxxhdpi/images/loop00005.png Binary files differnew file mode 100644 index 000000000..e9e860c3c --- /dev/null +++ b/res-xxxhdpi/images/loop00005.png diff --git a/res-xxxhdpi/images/loop00006.png b/res-xxxhdpi/images/loop00006.png Binary files differnew file mode 100644 index 000000000..d7c516e52 --- /dev/null +++ b/res-xxxhdpi/images/loop00006.png diff --git a/res-xxxhdpi/images/loop00007.png b/res-xxxhdpi/images/loop00007.png Binary files differnew file mode 100644 index 000000000..fa6d39716 --- /dev/null +++ b/res-xxxhdpi/images/loop00007.png diff --git a/res-xxxhdpi/images/loop00008.png b/res-xxxhdpi/images/loop00008.png Binary files differnew file mode 100644 index 000000000..888d3a278 --- /dev/null +++ b/res-xxxhdpi/images/loop00008.png diff --git a/res-xxxhdpi/images/loop00009.png b/res-xxxhdpi/images/loop00009.png Binary files differnew file mode 100644 index 000000000..9e6ead241 --- /dev/null +++ b/res-xxxhdpi/images/loop00009.png diff --git a/res-xxxhdpi/images/loop00010.png b/res-xxxhdpi/images/loop00010.png Binary files differnew file mode 100644 index 000000000..30e13e058 --- /dev/null +++ b/res-xxxhdpi/images/loop00010.png diff --git a/res-xxxhdpi/images/loop00011.png b/res-xxxhdpi/images/loop00011.png Binary files differnew file mode 100644 index 000000000..d8abc2bb5 --- /dev/null +++ b/res-xxxhdpi/images/loop00011.png diff --git a/res-xxxhdpi/images/loop00012.png b/res-xxxhdpi/images/loop00012.png Binary files differnew file mode 100644 index 000000000..2d88cfb6a --- /dev/null +++ b/res-xxxhdpi/images/loop00012.png diff --git a/res-xxxhdpi/images/loop00013.png b/res-xxxhdpi/images/loop00013.png Binary files differnew file mode 100644 index 000000000..0250f7494 --- /dev/null +++ b/res-xxxhdpi/images/loop00013.png diff --git a/res-xxxhdpi/images/loop00014.png b/res-xxxhdpi/images/loop00014.png Binary files differnew file mode 100644 index 000000000..c3d9239c9 --- /dev/null +++ b/res-xxxhdpi/images/loop00014.png diff --git a/res-xxxhdpi/images/loop00015.png b/res-xxxhdpi/images/loop00015.png Binary files differnew file mode 100644 index 000000000..644c9c6bd --- /dev/null +++ b/res-xxxhdpi/images/loop00015.png diff --git a/res-xxxhdpi/images/loop00016.png b/res-xxxhdpi/images/loop00016.png Binary files differnew file mode 100644 index 000000000..eff6e5b4e --- /dev/null +++ b/res-xxxhdpi/images/loop00016.png diff --git a/res-xxxhdpi/images/loop00017.png b/res-xxxhdpi/images/loop00017.png Binary files differnew file mode 100644 index 000000000..b472a86be --- /dev/null +++ b/res-xxxhdpi/images/loop00017.png diff --git a/res-xxxhdpi/images/loop00018.png b/res-xxxhdpi/images/loop00018.png Binary files differnew file mode 100644 index 000000000..b17b6ce12 --- /dev/null +++ b/res-xxxhdpi/images/loop00018.png diff --git a/res-xxxhdpi/images/loop00019.png b/res-xxxhdpi/images/loop00019.png Binary files differnew file mode 100644 index 000000000..d89b4da52 --- /dev/null +++ b/res-xxxhdpi/images/loop00019.png diff --git a/res-xxxhdpi/images/loop00020.png b/res-xxxhdpi/images/loop00020.png Binary files differnew file mode 100644 index 000000000..7e757b845 --- /dev/null +++ b/res-xxxhdpi/images/loop00020.png diff --git a/res-xxxhdpi/images/loop00021.png b/res-xxxhdpi/images/loop00021.png Binary files differnew file mode 100644 index 000000000..1a8ce3ec5 --- /dev/null +++ b/res-xxxhdpi/images/loop00021.png diff --git a/res-xxxhdpi/images/loop00022.png b/res-xxxhdpi/images/loop00022.png Binary files differnew file mode 100644 index 000000000..e9ab39a5f --- /dev/null +++ b/res-xxxhdpi/images/loop00022.png diff --git a/res-xxxhdpi/images/loop00023.png b/res-xxxhdpi/images/loop00023.png Binary files differnew file mode 100644 index 000000000..e1a7bf782 --- /dev/null +++ b/res-xxxhdpi/images/loop00023.png diff --git a/res-xxxhdpi/images/loop00024.png b/res-xxxhdpi/images/loop00024.png Binary files differnew file mode 100644 index 000000000..f77f70cff --- /dev/null +++ b/res-xxxhdpi/images/loop00024.png diff --git a/res-xxxhdpi/images/loop00025.png b/res-xxxhdpi/images/loop00025.png Binary files differnew file mode 100644 index 000000000..8348cdfca --- /dev/null +++ b/res-xxxhdpi/images/loop00025.png diff --git a/res-xxxhdpi/images/loop00026.png b/res-xxxhdpi/images/loop00026.png Binary files differnew file mode 100644 index 000000000..55fecc8bb --- /dev/null +++ b/res-xxxhdpi/images/loop00026.png diff --git a/res-xxxhdpi/images/loop00027.png b/res-xxxhdpi/images/loop00027.png Binary files differnew file mode 100644 index 000000000..f4edf0641 --- /dev/null +++ b/res-xxxhdpi/images/loop00027.png diff --git a/res-xxxhdpi/images/loop00028.png b/res-xxxhdpi/images/loop00028.png Binary files differnew file mode 100644 index 000000000..6dbe90446 --- /dev/null +++ b/res-xxxhdpi/images/loop00028.png diff --git a/res-xxxhdpi/images/loop00029.png b/res-xxxhdpi/images/loop00029.png Binary files differnew file mode 100644 index 000000000..764f27a05 --- /dev/null +++ b/res-xxxhdpi/images/loop00029.png diff --git a/res-xxxhdpi/images/loop00030.png b/res-xxxhdpi/images/loop00030.png Binary files differnew file mode 100644 index 000000000..2d2156930 --- /dev/null +++ b/res-xxxhdpi/images/loop00030.png diff --git a/res-xxxhdpi/images/loop00031.png b/res-xxxhdpi/images/loop00031.png Binary files differnew file mode 100644 index 000000000..e02db9c8b --- /dev/null +++ b/res-xxxhdpi/images/loop00031.png diff --git a/res-xxxhdpi/images/loop00032.png b/res-xxxhdpi/images/loop00032.png Binary files differnew file mode 100644 index 000000000..03f045684 --- /dev/null +++ b/res-xxxhdpi/images/loop00032.png diff --git a/res-xxxhdpi/images/loop00033.png b/res-xxxhdpi/images/loop00033.png Binary files differnew file mode 100644 index 000000000..5bdbbdbaf --- /dev/null +++ b/res-xxxhdpi/images/loop00033.png diff --git a/res-xxxhdpi/images/loop00034.png b/res-xxxhdpi/images/loop00034.png Binary files differnew file mode 100644 index 000000000..c8164e204 --- /dev/null +++ b/res-xxxhdpi/images/loop00034.png diff --git a/res-xxxhdpi/images/loop00035.png b/res-xxxhdpi/images/loop00035.png Binary files differnew file mode 100644 index 000000000..ed5721d7c --- /dev/null +++ b/res-xxxhdpi/images/loop00035.png diff --git a/res-xxxhdpi/images/loop00036.png b/res-xxxhdpi/images/loop00036.png Binary files differnew file mode 100644 index 000000000..08dffd26b --- /dev/null +++ b/res-xxxhdpi/images/loop00036.png diff --git a/res-xxxhdpi/images/loop00037.png b/res-xxxhdpi/images/loop00037.png Binary files differnew file mode 100644 index 000000000..583b665e4 --- /dev/null +++ b/res-xxxhdpi/images/loop00037.png diff --git a/res-xxxhdpi/images/loop00038.png b/res-xxxhdpi/images/loop00038.png Binary files differnew file mode 100644 index 000000000..cc2933d6a --- /dev/null +++ b/res-xxxhdpi/images/loop00038.png diff --git a/res-xxxhdpi/images/loop00039.png b/res-xxxhdpi/images/loop00039.png Binary files differnew file mode 100644 index 000000000..1f0496adb --- /dev/null +++ b/res-xxxhdpi/images/loop00039.png diff --git a/res-xxxhdpi/images/loop00040.png b/res-xxxhdpi/images/loop00040.png Binary files differnew file mode 100644 index 000000000..05bf33593 --- /dev/null +++ b/res-xxxhdpi/images/loop00040.png diff --git a/res-xxxhdpi/images/loop00041.png b/res-xxxhdpi/images/loop00041.png Binary files differnew file mode 100644 index 000000000..a868c7b2d --- /dev/null +++ b/res-xxxhdpi/images/loop00041.png diff --git a/res-xxxhdpi/images/loop00042.png b/res-xxxhdpi/images/loop00042.png Binary files differnew file mode 100644 index 000000000..7c7220f29 --- /dev/null +++ b/res-xxxhdpi/images/loop00042.png diff --git a/res-xxxhdpi/images/loop00043.png b/res-xxxhdpi/images/loop00043.png Binary files differnew file mode 100644 index 000000000..30336a77b --- /dev/null +++ b/res-xxxhdpi/images/loop00043.png diff --git a/res-xxxhdpi/images/loop00044.png b/res-xxxhdpi/images/loop00044.png Binary files differnew file mode 100644 index 000000000..80d373584 --- /dev/null +++ b/res-xxxhdpi/images/loop00044.png diff --git a/res-xxxhdpi/images/loop00045.png b/res-xxxhdpi/images/loop00045.png Binary files differnew file mode 100644 index 000000000..71a52c20c --- /dev/null +++ b/res-xxxhdpi/images/loop00045.png diff --git a/res-xxxhdpi/images/loop00046.png b/res-xxxhdpi/images/loop00046.png Binary files differnew file mode 100644 index 000000000..b3b37029d --- /dev/null +++ b/res-xxxhdpi/images/loop00046.png diff --git a/res-xxxhdpi/images/loop00047.png b/res-xxxhdpi/images/loop00047.png Binary files differnew file mode 100644 index 000000000..6ce2b37ab --- /dev/null +++ b/res-xxxhdpi/images/loop00047.png diff --git a/res-xxxhdpi/images/loop00048.png b/res-xxxhdpi/images/loop00048.png Binary files differnew file mode 100644 index 000000000..0b428cd73 --- /dev/null +++ b/res-xxxhdpi/images/loop00048.png diff --git a/res-xxxhdpi/images/loop00049.png b/res-xxxhdpi/images/loop00049.png Binary files differnew file mode 100644 index 000000000..53c1a4774 --- /dev/null +++ b/res-xxxhdpi/images/loop00049.png diff --git a/res-xxxhdpi/images/loop00050.png b/res-xxxhdpi/images/loop00050.png Binary files differnew file mode 100644 index 000000000..0e17bbdff --- /dev/null +++ b/res-xxxhdpi/images/loop00050.png diff --git a/res-xxxhdpi/images/loop00051.png b/res-xxxhdpi/images/loop00051.png Binary files differnew file mode 100644 index 000000000..78a99dcc7 --- /dev/null +++ b/res-xxxhdpi/images/loop00051.png diff --git a/res-xxxhdpi/images/loop00052.png b/res-xxxhdpi/images/loop00052.png Binary files differnew file mode 100644 index 000000000..efd0df559 --- /dev/null +++ b/res-xxxhdpi/images/loop00052.png diff --git a/res-xxxhdpi/images/loop00053.png b/res-xxxhdpi/images/loop00053.png Binary files differnew file mode 100644 index 000000000..0c417eef9 --- /dev/null +++ b/res-xxxhdpi/images/loop00053.png diff --git a/res-xxxhdpi/images/loop00054.png b/res-xxxhdpi/images/loop00054.png Binary files differnew file mode 100644 index 000000000..072077e6f --- /dev/null +++ b/res-xxxhdpi/images/loop00054.png diff --git a/res-xxxhdpi/images/loop00055.png b/res-xxxhdpi/images/loop00055.png Binary files differnew file mode 100644 index 000000000..693083ac8 --- /dev/null +++ b/res-xxxhdpi/images/loop00055.png diff --git a/res-xxxhdpi/images/loop00056.png b/res-xxxhdpi/images/loop00056.png Binary files differnew file mode 100644 index 000000000..07cbd75d1 --- /dev/null +++ b/res-xxxhdpi/images/loop00056.png diff --git a/res-xxxhdpi/images/loop00057.png b/res-xxxhdpi/images/loop00057.png Binary files differnew file mode 100644 index 000000000..cc98ab21a --- /dev/null +++ b/res-xxxhdpi/images/loop00057.png diff --git a/res-xxxhdpi/images/loop00058.png b/res-xxxhdpi/images/loop00058.png Binary files differnew file mode 100644 index 000000000..f55d21821 --- /dev/null +++ b/res-xxxhdpi/images/loop00058.png diff --git a/res-xxxhdpi/images/loop00059.png b/res-xxxhdpi/images/loop00059.png Binary files differnew file mode 100644 index 000000000..4bfed350a --- /dev/null +++ b/res-xxxhdpi/images/loop00059.png diff --git a/res-xxxhdpi/images/loop00060.png b/res-xxxhdpi/images/loop00060.png Binary files differnew file mode 100644 index 000000000..59f158b10 --- /dev/null +++ b/res-xxxhdpi/images/loop00060.png diff --git a/res-xxxhdpi/images/loop00061.png b/res-xxxhdpi/images/loop00061.png Binary files differnew file mode 100644 index 000000000..fd0dc558d --- /dev/null +++ b/res-xxxhdpi/images/loop00061.png diff --git a/res-xxxhdpi/images/loop00062.png b/res-xxxhdpi/images/loop00062.png Binary files differnew file mode 100644 index 000000000..2c316ce69 --- /dev/null +++ b/res-xxxhdpi/images/loop00062.png diff --git a/res-xxxhdpi/images/loop00063.png b/res-xxxhdpi/images/loop00063.png Binary files differnew file mode 100644 index 000000000..5b83c81cd --- /dev/null +++ b/res-xxxhdpi/images/loop00063.png diff --git a/res-xxxhdpi/images/loop00064.png b/res-xxxhdpi/images/loop00064.png Binary files differnew file mode 100644 index 000000000..ced0a9ab5 --- /dev/null +++ b/res-xxxhdpi/images/loop00064.png diff --git a/res-xxxhdpi/images/loop00065.png b/res-xxxhdpi/images/loop00065.png Binary files differnew file mode 100644 index 000000000..6e699dda6 --- /dev/null +++ b/res-xxxhdpi/images/loop00065.png diff --git a/res-xxxhdpi/images/loop00066.png b/res-xxxhdpi/images/loop00066.png Binary files differnew file mode 100644 index 000000000..8853cfffe --- /dev/null +++ b/res-xxxhdpi/images/loop00066.png diff --git a/res-xxxhdpi/images/loop00067.png b/res-xxxhdpi/images/loop00067.png Binary files differnew file mode 100644 index 000000000..24d11d14b --- /dev/null +++ b/res-xxxhdpi/images/loop00067.png diff --git a/res-xxxhdpi/images/loop00068.png b/res-xxxhdpi/images/loop00068.png Binary files differnew file mode 100644 index 000000000..d54fff0ba --- /dev/null +++ b/res-xxxhdpi/images/loop00068.png diff --git a/res-xxxhdpi/images/loop00069.png b/res-xxxhdpi/images/loop00069.png Binary files differnew file mode 100644 index 000000000..67f8d7884 --- /dev/null +++ b/res-xxxhdpi/images/loop00069.png diff --git a/res-xxxhdpi/images/loop00070.png b/res-xxxhdpi/images/loop00070.png Binary files differnew file mode 100644 index 000000000..d56fb78a5 --- /dev/null +++ b/res-xxxhdpi/images/loop00070.png diff --git a/res-xxxhdpi/images/loop00071.png b/res-xxxhdpi/images/loop00071.png Binary files differnew file mode 100644 index 000000000..e787b8a5a --- /dev/null +++ b/res-xxxhdpi/images/loop00071.png diff --git a/res-xxxhdpi/images/loop00072.png b/res-xxxhdpi/images/loop00072.png Binary files differnew file mode 100644 index 000000000..81f2e4c4b --- /dev/null +++ b/res-xxxhdpi/images/loop00072.png diff --git a/res-xxxhdpi/images/loop00073.png b/res-xxxhdpi/images/loop00073.png Binary files differnew file mode 100644 index 000000000..ad46ed162 --- /dev/null +++ b/res-xxxhdpi/images/loop00073.png diff --git a/res-xxxhdpi/images/loop00074.png b/res-xxxhdpi/images/loop00074.png Binary files differnew file mode 100644 index 000000000..d835a2bd3 --- /dev/null +++ b/res-xxxhdpi/images/loop00074.png diff --git a/res-xxxhdpi/images/loop00075.png b/res-xxxhdpi/images/loop00075.png Binary files differnew file mode 100644 index 000000000..aa35a84ba --- /dev/null +++ b/res-xxxhdpi/images/loop00075.png diff --git a/res-xxxhdpi/images/loop00076.png b/res-xxxhdpi/images/loop00076.png Binary files differnew file mode 100644 index 000000000..6ea547e29 --- /dev/null +++ b/res-xxxhdpi/images/loop00076.png diff --git a/res-xxxhdpi/images/loop00077.png b/res-xxxhdpi/images/loop00077.png Binary files differnew file mode 100644 index 000000000..c80938309 --- /dev/null +++ b/res-xxxhdpi/images/loop00077.png diff --git a/res-xxxhdpi/images/loop00078.png b/res-xxxhdpi/images/loop00078.png Binary files differnew file mode 100644 index 000000000..827a75c9f --- /dev/null +++ b/res-xxxhdpi/images/loop00078.png diff --git a/res-xxxhdpi/images/loop00079.png b/res-xxxhdpi/images/loop00079.png Binary files differnew file mode 100644 index 000000000..18dc1cf90 --- /dev/null +++ b/res-xxxhdpi/images/loop00079.png diff --git a/res-xxxhdpi/images/loop00080.png b/res-xxxhdpi/images/loop00080.png Binary files differnew file mode 100644 index 000000000..df06b5628 --- /dev/null +++ b/res-xxxhdpi/images/loop00080.png diff --git a/res-xxxhdpi/images/loop00081.png b/res-xxxhdpi/images/loop00081.png Binary files differnew file mode 100644 index 000000000..a5ba60379 --- /dev/null +++ b/res-xxxhdpi/images/loop00081.png diff --git a/res-xxxhdpi/images/loop00082.png b/res-xxxhdpi/images/loop00082.png Binary files differnew file mode 100644 index 000000000..e3298c5f3 --- /dev/null +++ b/res-xxxhdpi/images/loop00082.png diff --git a/res-xxxhdpi/images/loop00083.png b/res-xxxhdpi/images/loop00083.png Binary files differnew file mode 100644 index 000000000..c395662c4 --- /dev/null +++ b/res-xxxhdpi/images/loop00083.png diff --git a/res-xxxhdpi/images/loop00084.png b/res-xxxhdpi/images/loop00084.png Binary files differnew file mode 100644 index 000000000..f80af8e78 --- /dev/null +++ b/res-xxxhdpi/images/loop00084.png diff --git a/res-xxxhdpi/images/loop00085.png b/res-xxxhdpi/images/loop00085.png Binary files differnew file mode 100644 index 000000000..c89675830 --- /dev/null +++ b/res-xxxhdpi/images/loop00085.png diff --git a/res-xxxhdpi/images/loop00086.png b/res-xxxhdpi/images/loop00086.png Binary files differnew file mode 100644 index 000000000..97716923f --- /dev/null +++ b/res-xxxhdpi/images/loop00086.png diff --git a/res-xxxhdpi/images/loop00087.png b/res-xxxhdpi/images/loop00087.png Binary files differnew file mode 100644 index 000000000..e805dfa01 --- /dev/null +++ b/res-xxxhdpi/images/loop00087.png diff --git a/res-xxxhdpi/images/loop00088.png b/res-xxxhdpi/images/loop00088.png Binary files differnew file mode 100644 index 000000000..aa1a88e26 --- /dev/null +++ b/res-xxxhdpi/images/loop00088.png diff --git a/res-xxxhdpi/images/loop00089.png b/res-xxxhdpi/images/loop00089.png Binary files differnew file mode 100644 index 000000000..5bf7781d0 --- /dev/null +++ b/res-xxxhdpi/images/loop00089.png diff --git a/res-xxxhdpi/images/loop00090.png b/res-xxxhdpi/images/loop00090.png Binary files differnew file mode 100644 index 000000000..1bc9db5b1 --- /dev/null +++ b/res-xxxhdpi/images/loop00090.png diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png Binary files differindex 6354e6a99..b6eb964ba 100644 --- a/res-xxxhdpi/images/no_command_text.png +++ b/res-xxxhdpi/images/no_command_text.png diff --git a/res-xxxhdpi/images/progress_empty.png b/res-xxxhdpi/images/progress_empty.png Binary files differindex 72581832c..96c4bf6b3 100644 --- a/res-xxxhdpi/images/progress_empty.png +++ b/res-xxxhdpi/images/progress_empty.png diff --git a/res-xxxhdpi/images/progress_fill.png b/res-xxxhdpi/images/progress_fill.png Binary files differindex becf87bdf..1717be820 100644 --- a/res-xxxhdpi/images/progress_fill.png +++ b/res-xxxhdpi/images/progress_fill.png @@ -157,7 +157,7 @@ static int exec_cmd(const char* path, char* const argv[]) { return WEXITSTATUS(status); } -int format_volume(const char* volume) { +int format_volume(const char* volume, const char* directory) { Volume* v = volume_for_path(volume); if (v == NULL) { LOGE("unknown volume \"%s\"\n", volume); @@ -200,7 +200,7 @@ int format_volume(const char* volume) { } int result; if (strcmp(v->fs_type, "ext4") == 0) { - result = make_ext4fs(v->blk_device, length, volume, sehandle); + result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory); } else { /* Has to be f2fs because we checked earlier. */ if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { LOGE("format_volume: crypt footer + negative length (%zd) not supported on %s\n", length, v->fs_type); @@ -232,6 +232,10 @@ int format_volume(const char* volume) { return -1; } +int format_volume(const char* volume) { + return format_volume(volume, NULL); +} + int setup_install_mounts() { if (fstab == NULL) { LOGE("can't set up install mounts: no fstab loaded\n"); @@ -41,6 +41,12 @@ int ensure_path_unmounted(const char* path); // it is mounted. int format_volume(const char* volume); +// Reformat the given volume (must be the mount point only, eg +// "/cache"), no paths permitted. Attempts to unmount the volume if +// it is mounted. +// Copies 'directory' to root of the newly formatted volume +int format_volume(const char* volume, const char* directory); + // Ensure that all and only the volumes that packages expect to find // mounted (/tmp and /cache) are mounted. Returns 0 on success. int setup_install_mounts(); diff --git a/screen_ui.cpp b/screen_ui.cpp index 1d33269d2..369755438 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <dirent.h> #include <errno.h> #include <fcntl.h> #include <linux/input.h> @@ -40,8 +41,7 @@ #include "screen_ui.h" #include "ui.h" -static int char_width; -static int char_height; +#define TEXT_INDENT 4 // Return the current time as a double (including fractions of a second). static double now() { @@ -52,9 +52,9 @@ static double now() { ScreenRecoveryUI::ScreenRecoveryUI() : currentIcon(NONE), - installingFrame(0), locale(nullptr), - rtl_locale(false), + intro_done(false), + current_frame(0), progressBarType(EMPTY), progressScopeStart(0), progressScopeSize(0), @@ -73,83 +73,114 @@ ScreenRecoveryUI::ScreenRecoveryUI() : menu_items(0), menu_sel(0), file_viewer_text_(nullptr), - animation_fps(-1), - installing_frames(-1), + intro_frames(0), + loop_frames(0), + animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), - max_stage(-1) { + max_stage(-1), + updateMutex(PTHREAD_MUTEX_INITIALIZER), + rtl_locale(false) { +} - for (int i = 0; i < 5; i++) { - backgroundIcon[i] = nullptr; +GRSurface* ScreenRecoveryUI::GetCurrentFrame() { + if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { + return intro_done ? loopFrames[current_frame] : introFrames[current_frame]; } - pthread_mutex_init(&updateMutex, nullptr); + return error_icon; } -// Clear the screen and draw the currently selected background icon (if any). -// Should only be called with updateMutex locked. -void ScreenRecoveryUI::draw_background_locked(Icon icon) { - pagesIdentical = false; - gr_color(0, 0, 0, 255); - gr_clear(); +GRSurface* ScreenRecoveryUI::GetCurrentText() { + switch (currentIcon) { + case ERASING: return erasing_text; + case ERROR: return error_text; + case INSTALLING_UPDATE: return installing_text; + case NO_COMMAND: return no_command_text; + case NONE: abort(); + } +} - if (icon) { - GRSurface* surface = backgroundIcon[icon]; - if (icon == INSTALLING_UPDATE || icon == ERASING) { - surface = installation[installingFrame]; - } - GRSurface* text_surface = backgroundText[icon]; +int ScreenRecoveryUI::PixelsFromDp(int dp) { + return dp * density_; +} + +// Here's the intended layout: + +// | regular large +// ---------+-------------------- +// | 220dp 366dp +// icon | (200dp) (200dp) +// | 68dp 68dp +// text | (14sp) (14sp) +// | 32dp 32dp +// progress | (2dp) (2dp) +// | 194dp 340dp - int iconWidth = gr_get_width(surface); - int iconHeight = gr_get_height(surface); - int textWidth = gr_get_width(text_surface); - int textHeight = gr_get_height(text_surface); - int stageHeight = gr_get_height(stageMarkerEmpty); +// Note that "baseline" is actually the *top* of each icon (because that's how our drawing +// routines work), so that's the more useful measurement for calling code. - int sh = (max_stage >= 0) ? stageHeight : 0; +int ScreenRecoveryUI::GetAnimationBaseline() { + return GetTextBaseline() - PixelsFromDp(68) - gr_get_height(loopFrames[0]); +} - iconX = (gr_fb_width() - iconWidth) / 2; - iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2; +int ScreenRecoveryUI::GetTextBaseline() { + return GetProgressBaseline() - PixelsFromDp(32) - gr_get_height(installing_text); +} - int textX = (gr_fb_width() - textWidth) / 2; - int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40; +int ScreenRecoveryUI::GetProgressBaseline() { + return gr_fb_height() - PixelsFromDp(is_large_ ? 340 : 194) - gr_get_height(progressBarFill); +} - gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY); - if (stageHeight > 0) { - int sw = gr_get_width(stageMarkerEmpty); +// Clear the screen and draw the currently selected background icon (if any). +// Should only be called with updateMutex locked. +void ScreenRecoveryUI::draw_background_locked() { + pagesIdentical = false; + gr_color(0, 0, 0, 255); + gr_clear(); + + if (currentIcon != NONE) { + if (max_stage != -1) { + int stage_height = gr_get_height(stageMarkerEmpty); + int stage_width = gr_get_width(stageMarkerEmpty); int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2; - int y = iconY + iconHeight + 20; + int y = gr_fb_height() - stage_height; for (int i = 0; i < max_stage; ++i) { - gr_blit((i < stage) ? stageMarkerFill : stageMarkerEmpty, - 0, 0, sw, stageHeight, x, y); - x += sw; + GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty; + gr_blit(stage_surface, 0, 0, stage_width, stage_height, x, y); + x += stage_width; } } + GRSurface* text_surface = GetCurrentText(); + int text_x = (gr_fb_width() - gr_get_width(text_surface)) / 2; + int text_y = GetTextBaseline(); gr_color(255, 255, 255, 255); - gr_texticon(textX, textY, text_surface); + gr_texticon(text_x, text_y, text_surface); } } -// Draw the progress bar (if any) on the screen. Does not flip pages. +// Draws the animation and progress bar (if any) on the screen. +// Does not flip pages. // Should only be called with updateMutex locked. -void ScreenRecoveryUI::draw_progress_locked() { - if (currentIcon == ERROR) return; - - if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { - GRSurface* icon = installation[installingFrame]; - gr_blit(icon, 0, 0, gr_get_width(icon), gr_get_height(icon), iconX, iconY); +void ScreenRecoveryUI::draw_foreground_locked() { + if (currentIcon != NONE) { + GRSurface* frame = GetCurrentFrame(); + int frame_width = gr_get_width(frame); + int frame_height = gr_get_height(frame); + int frame_x = (gr_fb_width() - frame_width) / 2; + int frame_y = GetAnimationBaseline(); + gr_blit(frame, 0, 0, frame_width, frame_height, frame_x, frame_y); } if (progressBarType != EMPTY) { - int iconHeight = gr_get_height(backgroundIcon[INSTALLING_UPDATE]); int width = gr_get_width(progressBarEmpty); int height = gr_get_height(progressBarEmpty); - int dx = (gr_fb_width() - width)/2; - int dy = (3*gr_fb_height() + iconHeight - 2*height)/4; + int progress_x = (gr_fb_width() - width)/2; + int progress_y = GetProgressBaseline(); // 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); + gr_fill(progress_x, progress_y, width, height); if (progressBarType == DETERMINATE) { float p = progressScopeStart + progress * progressScopeSize; @@ -158,18 +189,20 @@ void ScreenRecoveryUI::draw_progress_locked() { if (rtl_locale) { // Fill the progress bar from right to left. if (pos > 0) { - gr_blit(progressBarFill, width-pos, 0, pos, height, dx+width-pos, dy); + gr_blit(progressBarFill, width-pos, 0, pos, height, + progress_x+width-pos, progress_y); } if (pos < width-1) { - gr_blit(progressBarEmpty, 0, 0, width-pos, height, dx, dy); + gr_blit(progressBarEmpty, 0, 0, width-pos, height, progress_x, progress_y); } } else { // Fill the progress bar from left to right. if (pos > 0) { - gr_blit(progressBarFill, 0, 0, pos, height, dx, dy); + gr_blit(progressBarFill, 0, 0, pos, height, progress_x, progress_y); } if (pos < width-1) { - gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); + gr_blit(progressBarEmpty, pos, 0, width-pos, height, + progress_x+pos, progress_y); } } } @@ -213,14 +246,14 @@ void ScreenRecoveryUI::DrawHorizontalRule(int* y) { *y += 4; } -void ScreenRecoveryUI::DrawTextLine(int* y, const char* line, bool bold) { - gr_text(4, *y, line, bold); - *y += char_height + 4; +void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) { + gr_text(x, *y, line, bold); + *y += char_height_ + 4; } -void ScreenRecoveryUI::DrawTextLines(int* y, const char* const* lines) { +void ScreenRecoveryUI::DrawTextLines(int x, int* y, const char* const* lines) { for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) { - DrawTextLine(y, lines[i], false); + DrawTextLine(x, y, lines[i], false); } } @@ -239,8 +272,8 @@ static const char* LONG_PRESS_HELP[] = { // Should only be called with updateMutex locked. void ScreenRecoveryUI::draw_screen_locked() { if (!show_text) { - draw_background_locked(currentIcon); - draw_progress_locked(); + draw_background_locked(); + draw_foreground_locked(); } else { gr_color(0, 0, 0, 255); gr_clear(); @@ -251,14 +284,14 @@ void ScreenRecoveryUI::draw_screen_locked() { property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, ""); SetColor(INFO); - DrawTextLine(&y, "Android Recovery", true); + DrawTextLine(TEXT_INDENT, &y, "Android Recovery", true); for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) { - DrawTextLine(&y, chunk.c_str(), false); + DrawTextLine(TEXT_INDENT, &y, chunk.c_str(), false); } - DrawTextLines(&y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); + DrawTextLines(TEXT_INDENT, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); SetColor(HEADER); - DrawTextLines(&y, menu_headers_); + DrawTextLines(TEXT_INDENT, &y, menu_headers_); SetColor(MENU); DrawHorizontalRule(&y); @@ -267,7 +300,7 @@ void ScreenRecoveryUI::draw_screen_locked() { if (i == menu_sel) { // Draw the highlight bar. SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - gr_fill(0, y - 2, gr_fb_width(), y + char_height + 2); + gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2); // Bold white text for the selected item. SetColor(MENU_SEL_FG); gr_text(4, y, menu_[i], true); @@ -275,7 +308,7 @@ void ScreenRecoveryUI::draw_screen_locked() { } else { gr_text(4, y, menu_[i], false); } - y += char_height + 4; + y += char_height_ + 4; } DrawHorizontalRule(&y); } @@ -286,9 +319,9 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - char_height; + for (int ty = gr_fb_height() - char_height_; ty >= y && count < text_rows_; - ty -= char_height, ++count) { + ty -= char_height_, ++count) { gr_text(0, ty, text_[row], false); --row; if (row < 0) row = text_rows_ - 1; @@ -310,7 +343,7 @@ void ScreenRecoveryUI::update_progress_locked() { draw_screen_locked(); // Must redraw the whole screen pagesIdentical = true; } else { - draw_progress_locked(); // Draw only the progress bar and overlays + draw_foreground_locked(); // Draw only the progress bar and overlays } gr_flip(); } @@ -327,14 +360,23 @@ void ScreenRecoveryUI::ProgressThreadLoop() { double start = now(); pthread_mutex_lock(&updateMutex); - int redraw = 0; + bool redraw = false; // update the installation animation, if active // skip this if we have a text overlay (too expensive to update) - if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && - installing_frames > 0 && !show_text) { - installingFrame = (installingFrame + 1) % installing_frames; - redraw = 1; + if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && !show_text) { + if (!intro_done) { + if (current_frame == intro_frames - 1) { + intro_done = true; + current_frame = 0; + } else { + ++current_frame; + } + } else { + current_frame = (current_frame + 1) % loop_frames; + } + + redraw = true; } // move the progress bar forward on timed intervals, if configured @@ -345,7 +387,7 @@ void ScreenRecoveryUI::ProgressThreadLoop() { if (p > 1.0) p = 1.0; if (p > progress) { progress = p; - redraw = 1; + redraw = true; } } @@ -363,22 +405,14 @@ void ScreenRecoveryUI::ProgressThreadLoop() { void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) { int result = res_create_display_surface(filename, surface); if (result < 0) { - LOGE("missing bitmap %s\n(Code %d)\n", filename, result); - } -} - -void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, int* fps, - GRSurface*** surface) { - int result = res_create_multi_display_surface(filename, frames, fps, surface); - if (result < 0) { - LOGE("missing bitmap %s\n(Code %d)\n", filename, result); + LOGE("couldn't load bitmap %s (error %d)\n", filename, result); } } void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) { int result = res_create_localized_alpha_surface(filename, locale, surface); if (result < 0) { - LOGE("missing bitmap %s\n(Code %d)\n", filename, result); + LOGE("couldn't load bitmap %s (error %d)\n", filename, result); } } @@ -391,12 +425,25 @@ static char** Alloc2d(size_t rows, size_t cols) { return result; } +// Choose the right background string to display during update. +void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) { + if (security_update) { + LoadLocalizedBitmap("installing_security_text", &installing_text); + } else { + LoadLocalizedBitmap("installing_text", &installing_text); + } + Redraw(); +} + void ScreenRecoveryUI::Init() { gr_init(); - gr_font_size(&char_width, &char_height); - text_rows_ = gr_fb_height() / char_height; - text_cols_ = gr_fb_width() / char_width; + density_ = static_cast<float>(property_get_int32("ro.sf.lcd_density", 160)) / 160.f; + is_large_ = gr_fb_height() > PixelsFromDp(800); + + gr_font_size(&char_width_, &char_height_); + text_rows_ = gr_fb_height() / char_height_; + text_cols_ = gr_fb_width() / char_width_; text_ = Alloc2d(text_rows_, text_cols_ + 1); file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); @@ -405,31 +452,64 @@ void ScreenRecoveryUI::Init() { text_col_ = text_row_ = 0; text_top_ = 1; - backgroundIcon[NONE] = nullptr; - LoadBitmapArray("icon_installing", &installing_frames, &animation_fps, &installation); - backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : nullptr; - backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE]; - LoadBitmap("icon_error", &backgroundIcon[ERROR]); - backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; + LoadBitmap("icon_error", &error_icon); LoadBitmap("progress_empty", &progressBarEmpty); LoadBitmap("progress_fill", &progressBarFill); + LoadBitmap("stage_empty", &stageMarkerEmpty); LoadBitmap("stage_fill", &stageMarkerFill); - LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]); - LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]); - LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]); - LoadLocalizedBitmap("error_text", &backgroundText[ERROR]); + // Background text for "installing_update" could be "installing update" + // or "installing security update". It will be set after UI init according + // to commands in BCB. + installing_text = nullptr; + LoadLocalizedBitmap("erasing_text", &erasing_text); + LoadLocalizedBitmap("no_command_text", &no_command_text); + LoadLocalizedBitmap("error_text", &error_text); + + LoadAnimation(); pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this); RecoveryUI::Init(); } +void ScreenRecoveryUI::LoadAnimation() { + // How many frames of intro and loop do we have? + std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/res/images"), closedir); + dirent* de; + while ((de = readdir(dir.get())) != nullptr) { + int value; + if (sscanf(de->d_name, "intro%d", &value) == 1 && intro_frames < (value + 1)) { + intro_frames = value + 1; + } else if (sscanf(de->d_name, "loop%d", &value) == 1 && loop_frames < (value + 1)) { + loop_frames = value + 1; + } + } + + // It's okay to not have an intro. + if (intro_frames == 0) intro_done = true; + // But you must have an animation. + if (loop_frames == 0) abort(); + + introFrames = new GRSurface*[intro_frames]; + for (int i = 0; i < intro_frames; ++i) { + // TODO: remember the names above, so we don't have to hard-code the number of 0s. + LoadBitmap(android::base::StringPrintf("intro%05d", i).c_str(), &introFrames[i]); + } + + loopFrames = new GRSurface*[loop_frames]; + for (int i = 0; i < loop_frames; ++i) { + LoadBitmap(android::base::StringPrintf("loop%05d", i).c_str(), &loopFrames[i]); + } +} + void ScreenRecoveryUI::SetLocale(const char* new_locale) { - if (new_locale) { - this->locale = new_locale; + this->locale = new_locale; + this->rtl_locale = false; + + if (locale) { char* lang = strdup(locale); for (char* p = lang; *p; ++p) { if (*p == '_') { @@ -438,8 +518,7 @@ void ScreenRecoveryUI::SetLocale(const char* new_locale) { } } - // A bit cheesy: keep an explicit list of supported languages - // that are RTL. + // A bit cheesy: keep an explicit list of supported RTL languages. if (strcmp(lang, "ar") == 0 || // Arabic strcmp(lang, "fa") == 0 || // Persian (Farsi) strcmp(lang, "he") == 0 || // Hebrew (new language code) @@ -448,8 +527,6 @@ void ScreenRecoveryUI::SetLocale(const char* new_locale) { rtl_locale = true; } free(lang); - } else { - new_locale = nullptr; } } diff --git a/screen_ui.h b/screen_ui.h index 08a5f44a9..4319b76ce 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -34,6 +34,7 @@ class ScreenRecoveryUI : public RecoveryUI { // overall recovery state ("background image") void SetBackground(Icon icon); + void SetSystemUpdateText(bool security_update); // progress indicator void SetProgressType(ProgressType type); @@ -67,16 +68,28 @@ class ScreenRecoveryUI : public RecoveryUI { }; void SetColor(UIElement e); - private: + protected: Icon currentIcon; - int installingFrame; + const char* locale; - bool rtl_locale; + bool intro_done; + int current_frame; + + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. + float density_; + // True if we should use the large layout. + bool is_large_; + + GRSurface* error_icon; + + GRSurface* erasing_text; + GRSurface* error_text; + GRSurface* installing_text; + GRSurface* no_command_text; + + GRSurface** introFrames; + GRSurface** loopFrames; - pthread_mutex_t updateMutex; - GRSurface* backgroundIcon[5]; - GRSurface* backgroundText[5]; - GRSurface** installation; GRSurface* progressBarEmpty; GRSurface* progressBarFill; GRSurface* stageMarkerEmpty; @@ -109,21 +122,29 @@ class ScreenRecoveryUI : public RecoveryUI { pthread_t progress_thread_; - // The following two are parsed from the image file - // (e.g. '/res/images/icon_installing.png'). - int animation_fps; - int installing_frames; + // Number of intro frames and loop frames in the animation. + int intro_frames; + int loop_frames; - int iconX, iconY; + // Number of frames per sec (default: 30) for both parts of the animation. + int animation_fps; int stage, max_stage; - void draw_background_locked(Icon icon); - void draw_progress_locked(); + int char_width_; + int char_height_; + pthread_mutex_t updateMutex; + bool rtl_locale; + + void draw_background_locked(); + void draw_foreground_locked(); void draw_screen_locked(); void update_screen_locked(); void update_progress_locked(); + GRSurface* GetCurrentFrame(); + GRSurface* GetCurrentText(); + static void* ProgressThreadStartRoutine(void* data); void ProgressThreadLoop(); @@ -132,13 +153,18 @@ class ScreenRecoveryUI : public RecoveryUI { void PutChar(char); void ClearText(); - void DrawHorizontalRule(int* y); - void DrawTextLine(int* y, const char* line, bool bold); - void DrawTextLines(int* y, const char* const* lines); - + void LoadAnimation(); void LoadBitmap(const char* filename, GRSurface** surface); - void LoadBitmapArray(const char* filename, int* frames, int* fps, GRSurface*** surface); void LoadLocalizedBitmap(const char* filename, GRSurface** surface); + + int PixelsFromDp(int dp); + int GetAnimationBaseline(); + int GetProgressBaseline(); + int GetTextBaseline(); + + void DrawHorizontalRule(int* y); + void DrawTextLine(int x, int* y, const char* line, bool bold); + void DrawTextLines(int x, int* y, const char* const* lines); }; #endif // RECOVERY_UI_H diff --git a/tests/Android.mk b/tests/Android.mk index 7b004b2a0..279a6cb49 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -21,9 +21,13 @@ include $(CLEAR_VARS) LOCAL_CLANG := true LOCAL_MODULE := recovery_unit_test LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk -LOCAL_STATIC_LIBRARIES := libverifier +LOCAL_STATIC_LIBRARIES := \ + libverifier \ + libminui + LOCAL_SRC_FILES := unit/asn1_decoder_test.cpp LOCAL_SRC_FILES += unit/recovery_test.cpp +LOCAL_SRC_FILES += unit/locale_test.cpp LOCAL_C_INCLUDES := bootable/recovery LOCAL_SHARED_LIBRARIES := liblog include $(BUILD_NATIVE_TEST) @@ -31,12 +35,17 @@ include $(BUILD_NATIVE_TEST) # Component tests include $(CLEAR_VARS) LOCAL_CLANG := true +LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE := recovery_component_test LOCAL_C_INCLUDES := bootable/recovery -LOCAL_SRC_FILES := component/verifier_test.cpp +LOCAL_SRC_FILES := \ + component/verifier_test.cpp \ + component/applypatch_test.cpp LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_STATIC_LIBRARIES := \ + libapplypatch \ + libotafault \ libbase \ libverifier \ libcrypto_utils_static \ @@ -44,6 +53,8 @@ LOCAL_STATIC_LIBRARIES := \ libminui \ libminzip \ libcutils \ + libbz \ + libz \ libc testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/recovery diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h new file mode 100644 index 000000000..3490f6805 --- /dev/null +++ b/tests/common/test_constants.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agree to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _OTA_TEST_CONSTANTS_H +#define _OTA_TEST_CONSTANTS_H + +#if defined(__LP64__) +#define NATIVE_TEST_PATH "/nativetest64" +#else +#define NATIVE_TEST_PATH "/nativetest" +#endif + +#endif diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp new file mode 100644 index 000000000..b44ddd17c --- /dev/null +++ b/tests/component/applypatch_test.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agree to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <gtest/gtest.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/statvfs.h> +#include <sys/types.h> +#include <time.h> + +#include <string> + +#include <android-base/file.h> +#include <android-base/stringprintf.h> +#include <android-base/test_utils.h> + +#include "applypatch/applypatch.h" +#include "common/test_constants.h" +#include "openssl/sha.h" +#include "print_sha1.h" + +static const std::string DATA_PATH = getenv("ANDROID_DATA"); +static const std::string TESTDATA_PATH = "/recovery/testdata"; +static const std::string WORK_FS = "/data"; + +static std::string sha1sum(const std::string& fname) { + uint8_t digest[SHA_DIGEST_LENGTH]; + std::string data; + android::base::ReadFileToString(fname, &data); + + SHA1((const uint8_t*)data.c_str(), data.size(), digest); + return print_sha1(digest); +} + +static void mangle_file(const std::string& fname) { + FILE* fh = fopen(&fname[0], "w"); + int r; + for (int i=0; i < 1024; i++) { + r = rand(); + fwrite(&r, sizeof(short), 1, fh); + } + fclose(fh); +} + +static bool file_cmp(std::string& f1, std::string& f2) { + std::string c1; + std::string c2; + android::base::ReadFileToString(f1, &c1); + android::base::ReadFileToString(f2, &c2); + return c1 == c2; +} + +static std::string from_testdata_base(const std::string fname) { + return android::base::StringPrintf("%s%s%s/%s", + &DATA_PATH[0], + &NATIVE_TEST_PATH[0], + &TESTDATA_PATH[0], + &fname[0]); +} + +class ApplyPatchTest : public ::testing::Test { + public: + static void SetUpTestCase() { + // set up files + old_file = from_testdata_base("old.file"); + new_file = from_testdata_base("new.file"); + patch_file = from_testdata_base("patch.bsdiff"); + rand_file = "/cache/applypatch_test_rand.file"; + cache_file = "/cache/saved.file"; + + // write stuff to rand_file + android::base::WriteStringToFile("hello", rand_file); + + // set up SHA constants + old_sha1 = sha1sum(old_file); + new_sha1 = sha1sum(new_file); + srand(time(NULL)); + bad_sha1_a = android::base::StringPrintf("%040x", rand()); + bad_sha1_b = android::base::StringPrintf("%040x", rand()); + + struct stat st; + stat(&new_file[0], &st); + new_size = st.st_size; + } + + static std::string old_file; + static std::string new_file; + static std::string rand_file; + static std::string cache_file; + static std::string patch_file; + + static std::string old_sha1; + static std::string new_sha1; + static std::string bad_sha1_a; + static std::string bad_sha1_b; + + static size_t new_size; +}; + +std::string ApplyPatchTest::old_file; +std::string ApplyPatchTest::new_file; + +static void cp(std::string src, std::string tgt) { + std::string cmd = android::base::StringPrintf("cp %s %s", + &src[0], + &tgt[0]); + system(&cmd[0]); +} + +static void backup_old() { + cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file); +} + +static void restore_old() { + cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file); +} + +class ApplyPatchCacheTest : public ApplyPatchTest { + public: + virtual void SetUp() { + backup_old(); + } + + virtual void TearDown() { + restore_old(); + } +}; + +class ApplyPatchFullTest : public ApplyPatchCacheTest { + public: + static void SetUpTestCase() { + ApplyPatchTest::SetUpTestCase(); + unsigned long free_kb = FreeSpaceForFile(&WORK_FS[0]); + ASSERT_GE(free_kb * 1024, new_size * 3 / 2); + output_f = new TemporaryFile(); + output_loc = std::string(output_f->path); + + struct FileContents fc; + + ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc)); + Value* patch1 = new Value(); + patch1->type = VAL_BLOB; + patch1->size = fc.data.size(); + patch1->data = static_cast<char*>(malloc(fc.data.size())); + memcpy(patch1->data, fc.data.data(), fc.data.size()); + patches.push_back(patch1); + + ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc)); + Value* patch2 = new Value(); + patch2->type = VAL_BLOB; + patch2->size = fc.st.st_size; + patch2->data = static_cast<char*>(malloc(fc.data.size())); + memcpy(patch2->data, fc.data.data(), fc.data.size()); + patches.push_back(patch2); + } + static void TearDownTestCase() { + delete output_f; + for (auto it = patches.begin(); it != patches.end(); ++it) { + free((*it)->data); + delete *it; + } + patches.clear(); + } + + static std::vector<Value*> patches; + static TemporaryFile* output_f; + static std::string output_loc; +}; + +class ApplyPatchDoubleCacheTest : public ApplyPatchFullTest { + public: + virtual void SetUp() { + ApplyPatchCacheTest::SetUp(); + cp(cache_file, "/cache/reallysaved.file"); + } + + virtual void TearDown() { + cp("/cache/reallysaved.file", cache_file); + ApplyPatchCacheTest::TearDown(); + } +}; + +std::string ApplyPatchTest::rand_file; +std::string ApplyPatchTest::patch_file; +std::string ApplyPatchTest::cache_file; +std::string ApplyPatchTest::old_sha1; +std::string ApplyPatchTest::new_sha1; +std::string ApplyPatchTest::bad_sha1_a; +std::string ApplyPatchTest::bad_sha1_b; + +size_t ApplyPatchTest::new_size; + +std::vector<Value*> ApplyPatchFullTest::patches; +TemporaryFile* ApplyPatchFullTest::output_f; +std::string ApplyPatchFullTest::output_loc; + +TEST_F(ApplyPatchTest, CheckModeSingle) { + char* s = &old_sha1[0]; + ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s)); +} + +TEST_F(ApplyPatchTest, CheckModeMultiple) { + char* argv[3] = { + &bad_sha1_a[0], + &old_sha1[0], + &bad_sha1_b[0] + }; + ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv)); +} + +TEST_F(ApplyPatchTest, CheckModeFailure) { + char* argv[2] = { + &bad_sha1_a[0], + &bad_sha1_b[0] + }; + ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) { + mangle_file(old_file); + char* s = &old_sha1[0]; + ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) { + mangle_file(old_file); + char* argv[3] = { + &bad_sha1_a[0], + &old_sha1[0], + &bad_sha1_b[0] + }; + ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) { + mangle_file(old_file); + char* argv[2] = { + &bad_sha1_a[0], + &bad_sha1_b[0] + }; + ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) { + unlink(&old_file[0]); + char* s = &old_sha1[0]; + ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) { + unlink(&old_file[0]); + char* argv[3] = { + &bad_sha1_a[0], + &old_sha1[0], + &bad_sha1_b[0] + }; + ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv)); +} + +TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) { + unlink(&old_file[0]); + char* argv[2] = { + &bad_sha1_a[0], + &bad_sha1_b[0] + }; + ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv)); +} + +TEST_F(ApplyPatchFullTest, ApplyInPlace) { + std::vector<char*> sha1s; + sha1s.push_back(&bad_sha1_a[0]); + sha1s.push_back(&old_sha1[0]); + + int ap_result = applypatch(&old_file[0], + "-", + &new_sha1[0], + new_size, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_EQ(0, ap_result); + ASSERT_TRUE(file_cmp(old_file, new_file)); + // reapply, applypatch is idempotent so it should succeed + ap_result = applypatch(&old_file[0], + "-", + &new_sha1[0], + new_size, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_EQ(0, ap_result); + ASSERT_TRUE(file_cmp(old_file, new_file)); +} + +TEST_F(ApplyPatchFullTest, ApplyInNewLocation) { + std::vector<char*> sha1s; + sha1s.push_back(&bad_sha1_a[0]); + sha1s.push_back(&old_sha1[0]); + int ap_result = applypatch(&old_file[0], + &output_loc[0], + &new_sha1[0], + new_size, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_EQ(0, ap_result); + ASSERT_TRUE(file_cmp(output_loc, new_file)); + ap_result = applypatch(&old_file[0], + &output_loc[0], + &new_sha1[0], + new_size, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_EQ(0, ap_result); + ASSERT_TRUE(file_cmp(output_loc, new_file)); +} + +TEST_F(ApplyPatchFullTest, ApplyCorruptedInNewLocation) { + mangle_file(old_file); + std::vector<char*> sha1s; + sha1s.push_back(&bad_sha1_a[0]); + sha1s.push_back(&old_sha1[0]); + int ap_result = applypatch(&old_file[0], + &output_loc[0], + &new_sha1[0], + new_size, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_EQ(0, ap_result); + ASSERT_TRUE(file_cmp(output_loc, new_file)); + ap_result = applypatch(&old_file[0], + &output_loc[0], + &new_sha1[0], + new_size, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_EQ(0, ap_result); + ASSERT_TRUE(file_cmp(output_loc, new_file)); +} + +TEST_F(ApplyPatchDoubleCacheTest, ApplyDoubleCorruptedInNewLocation) { + mangle_file(old_file); + mangle_file(cache_file); + + std::vector<char*> sha1s; + sha1s.push_back(&bad_sha1_a[0]); + sha1s.push_back(&old_sha1[0]); + int ap_result = applypatch(&old_file[0], + &output_loc[0], + &new_sha1[0], + new_size, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_NE(0, ap_result); + ASSERT_FALSE(file_cmp(output_loc, new_file)); + ap_result = applypatch(&old_file[0], + &output_loc[0], + &new_sha1[0], + new_size, + 2, + sha1s.data(), + patches.data(), + nullptr); + ASSERT_NE(0, ap_result); + ASSERT_FALSE(file_cmp(output_loc, new_file)); +} diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index b5d70327e..d7166dfaf 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -31,16 +31,11 @@ #include <android-base/stringprintf.h> #include "common.h" +#include "common/test_constants.h" #include "minzip/SysUtil.h" #include "ui.h" #include "verifier.h" -#if defined(__LP64__) -#define NATIVE_TEST_PATH "/nativetest64" -#else -#define NATIVE_TEST_PATH "/nativetest" -#endif - static const char* DATA_PATH = getenv("ANDROID_DATA"); static const char* TESTDATA_PATH = "/recovery/testdata/"; @@ -51,6 +46,7 @@ class MockUI : public RecoveryUI { void SetStage(int, int) { } void SetLocale(const char*) { } void SetBackground(Icon /*icon*/) { } + void SetSystemUpdateText(bool /*security_update*/) { } void SetProgressType(ProgressType /*determinate*/) { } void ShowProgress(float /*portion*/, float /*seconds*/) { } diff --git a/tests/testdata/new.file b/tests/testdata/new.file Binary files differnew file mode 100644 index 000000000..cdeb8fd50 --- /dev/null +++ b/tests/testdata/new.file diff --git a/tests/testdata/old.file b/tests/testdata/old.file Binary files differnew file mode 100644 index 000000000..166c8732e --- /dev/null +++ b/tests/testdata/old.file diff --git a/tests/testdata/patch.bsdiff b/tests/testdata/patch.bsdiff Binary files differnew file mode 100644 index 000000000..b78d38573 --- /dev/null +++ b/tests/testdata/patch.bsdiff diff --git a/tests/unit/locale_test.cpp b/tests/unit/locale_test.cpp new file mode 100644 index 000000000..0e515f8c1 --- /dev/null +++ b/tests/unit/locale_test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "minui/minui.h" + +TEST(LocaleTest, Misc) { + EXPECT_TRUE(matches_locale("zh_CN", "zh_CN_#Hans")); + EXPECT_TRUE(matches_locale("zh", "zh_CN_#Hans")); + EXPECT_FALSE(matches_locale("zh_HK", "zh_CN_#Hans")); + EXPECT_TRUE(matches_locale("en_GB", "en_GB")); + EXPECT_TRUE(matches_locale("en", "en_GB")); + EXPECT_FALSE(matches_locale("en_GB", "en")); + EXPECT_FALSE(matches_locale("en_GB", "en_US")); +} diff --git a/tools/recovery_l10n/res/layout/main.xml b/tools/recovery_l10n/res/layout/main.xml index 0900b1102..05a16e1e4 100644 --- a/tools/recovery_l10n/res/layout/main.xml +++ b/tools/recovery_l10n/res/layout/main.xml @@ -19,7 +19,9 @@ <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="#ffffffff" + android:fontFamily="sans-serif-medium" + android:textColor="#fff5f5f5" + android:textSize="14sp" android:background="#ff000000" android:maxWidth="480px" android:gravity="center" diff --git a/tools/recovery_l10n/res/values/strings.xml b/tools/recovery_l10n/res/values/strings.xml index f6193ab17..971e038d3 100644 --- a/tools/recovery_l10n/res/values/strings.xml +++ b/tools/recovery_l10n/res/values/strings.xml @@ -13,18 +13,18 @@ <!-- Displayed on the screen beneath the animated android while the system is installing an update. [CHAR LIMIT=60] --> - <string name="recovery_installing">Installing system update\u2026</string> + <string name="recovery_installing">Installing system update</string> <!-- Displayed on the screen beneath the animated android while the system is erasing a partition (either a data wipe aka "factory reset", or a cache wipe). [CHAR LIMIT=60] --> - <string name="recovery_erasing">Erasing\u2026</string> + <string name="recovery_erasing">Erasing</string> <!-- Displayed on the screen when the user has gotten into recovery mode without a command to run. Will not normally happen, but users (especially developers) may boot into recovery mode manually via special key combinations. [CHAR LIMIT=60] --> - <string name="recovery_no_command">No command.</string> + <string name="recovery_no_command">No command</string> <!-- Displayed on the triangle-! screen when a system update installation or data wipe procedure encounters an error. [CHAR @@ -33,6 +33,6 @@ <!-- Displayed on the screen beneath the animation while the system is installing a security update. [CHAR LIMIT=60] --> - <string name="recovery_installing_security">Installing security update\u2026</string> + <string name="recovery_installing_security">Installing security update</string> </resources> diff --git a/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java b/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java index 3f2bebe60..817a3ad7d 100644 --- a/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java +++ b/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java @@ -149,12 +149,9 @@ public class Main extends Activity { String[] localeNames = getAssets().getLocales(); Arrays.sort(localeNames); ArrayList<Locale> locales = new ArrayList<Locale>(); - for (String ln : localeNames) { - int u = ln.indexOf('_'); - if (u >= 0) { - Log.i(TAG, "locale = " + ln); - locales.add(new Locale(ln.substring(0, u), ln.substring(u+1))); - } + for (String localeName : localeNames) { + Log.i(TAG, "locale = " + localeName); + locales.add(Locale.forLanguageTag(localeName)); } final Runnable seq = buildSequence(locales.toArray(new Locale[0])); @@ -39,6 +39,7 @@ class RecoveryUI { // Set the overall recovery state ("background image"). enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR }; virtual void SetBackground(Icon icon) = 0; + virtual void SetSystemUpdateText(bool security_update) = 0; // --- progress indicator --- enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE }; diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index 6422cb2f4..09cfdfca5 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -15,6 +15,15 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +LOCAL_CLANG := true +LOCAL_SRC_FILES := bootloader_message_writer.cpp +LOCAL_MODULE := libbootloader_message_writer +LOCAL_STATIC_LIBRARIES := libbase libfs_mgr +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) LOCAL_CLANG := true @@ -24,7 +33,8 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. LOCAL_MODULE := uncrypt -LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils +LOCAL_STATIC_LIBRARIES := libbootloader_message_writer libbase \ + liblog libfs_mgr libcutils \ LOCAL_INIT_RC := uncrypt.rc diff --git a/uncrypt/bootloader_message_writer.cpp b/uncrypt/bootloader_message_writer.cpp new file mode 100644 index 000000000..3bb106aa0 --- /dev/null +++ b/uncrypt/bootloader_message_writer.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/system_properties.h> + +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> +#include <fs_mgr.h> + +#include "bootloader.h" + +static struct fstab* read_fstab(std::string* err) { + // The fstab path is always "/fstab.${ro.hardware}". + std::string fstab_path = "/fstab."; + char value[PROP_VALUE_MAX]; + if (__system_property_get("ro.hardware", value) == 0) { + *err = "failed to get ro.hardware"; + return nullptr; + } + fstab_path += value; + struct fstab* fstab = fs_mgr_read_fstab(fstab_path.c_str()); + if (fstab == nullptr) { + *err = "failed to read " + fstab_path; + } + return fstab; +} + +static std::string get_misc_blk_device(std::string* err) { + struct fstab* fstab = read_fstab(err); + if (fstab == nullptr) { + return ""; + } + fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab, "/misc"); + if (record == nullptr) { + *err = "failed to find /misc partition"; + return ""; + } + return record->blk_device; +} + +static bool write_bootloader_message(const bootloader_message& boot, std::string* err) { + std::string misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY | O_SYNC)); + if (fd.get() == -1) { + *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(), + strerror(errno)); + return false; + } + if (!android::base::WriteFully(fd.get(), &boot, sizeof(boot))) { + *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(), + strerror(errno)); + return false; + } + // TODO: O_SYNC and fsync duplicates each other? + if (fsync(fd.get()) == -1) { + *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(), + strerror(errno)); + return false; + } + return true; +} + +bool clear_bootloader_message(std::string* err) { + bootloader_message boot = {}; + return write_bootloader_message(boot, err); +} + +bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) { + bootloader_message boot = {}; + strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); + strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); + for (const auto& s : options) { + strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery)); + if (s.back() != '\n') { + strlcat(boot.recovery, "\n", sizeof(boot.recovery)); + } + } + return write_bootloader_message(boot, err); +} + +extern "C" bool write_bootloader_message(const char* options) { + std::string err; + return write_bootloader_message({options}, &err); +} diff --git a/uncrypt/include/bootloader_message_writer.h b/uncrypt/include/bootloader_message_writer.h new file mode 100644 index 000000000..e0ca3f44a --- /dev/null +++ b/uncrypt/include/bootloader_message_writer.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BOOTLOADER_MESSAGE_WRITER_H +#define BOOTLOADER_MESSAGE_WRITER_H + +#ifdef __cplusplus +#include <string> +#include <vector> + +bool clear_bootloader_message(std::string* err); + +bool write_bootloader_message(const std::vector<std::string>& options, std::string* err); + +#else +#include <stdbool.h> + +// C Interface. +bool write_bootloader_message(const char* options); +#endif + +#endif // BOOTLOADER_MESSAGE_WRITER_H diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index a1de6a182..5697712aa 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -39,6 +39,53 @@ // Recovery can take this block map file and retrieve the underlying // file data to use as an update package. +/** + * In addition to the uncrypt work, uncrypt also takes care of setting and + * clearing the bootloader control block (BCB) at /misc partition. + * + * uncrypt is triggered as init services on demand. It uses socket to + * communicate with its caller (i.e. system_server). The socket is managed by + * init (i.e. created prior to the service starts, and destroyed when uncrypt + * exits). + * + * Below is the uncrypt protocol. + * + * a. caller b. init c. uncrypt + * --------------- ------------ -------------- + * a1. ctl.start: + * setup-bcb / + * clear-bcb / + * uncrypt + * + * b2. create socket at + * /dev/socket/uncrypt + * + * c3. listen and accept + * + * a4. send a 4-byte int + * (message length) + * c5. receive message length + * a6. send message + * c7. receive message + * c8. <do the work; may send + * the progress> + * a9. <may handle progress> + * c10. <upon finishing> + * send "100" or "-1" + * + * a11. receive status code + * a12. send a 4-byte int to + * ack the receive of the + * final status code + * c13. receive and exit + * + * b14. destroy the socket + * + * Note that a12 and c13 are necessary to ensure a11 happens before the socket + * gets destroyed in b14. + */ + +#include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <inttypes.h> @@ -49,6 +96,7 @@ #include <stdlib.h> #include <string.h> #include <sys/mman.h> +#include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> @@ -62,23 +110,32 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <bootloader_message_writer.h> #include <cutils/android_reboot.h> #include <cutils/properties.h> +#include <cutils/sockets.h> #include <fs_mgr.h> #define LOG_TAG "uncrypt" #include <log/log.h> -#include "bootloader.h" - #define WINDOW_SIZE 5 +// uncrypt provides three services: SETUP_BCB, CLEAR_BCB and UNCRYPT. +// +// SETUP_BCB and CLEAR_BCB services use socket communication and do not rely +// on /cache partitions. They will handle requests to reboot into recovery +// (for applying updates for non-A/B devices, or factory resets for all +// devices). +// +// UNCRYPT service still needs files on /cache partition (UNCRYPT_PATH_FILE +// and CACHE_BLOCK_MAP). It will be working (and needed) only for non-A/B +// devices, on which /cache partitions always exist. static const std::string CACHE_BLOCK_MAP = "/cache/recovery/block.map"; -static const std::string COMMAND_FILE = "/cache/recovery/command"; -static const std::string STATUS_FILE = "/cache/recovery/uncrypt_status"; static const std::string UNCRYPT_PATH_FILE = "/cache/recovery/uncrypt_file"; +static const std::string UNCRYPT_SOCKET = "uncrypt"; -static struct fstab* fstab = NULL; +static struct fstab* fstab = nullptr; static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) { if (TEMP_FAILURE_RETRY(lseek64(wfd, offset, SEEK_SET)) == -1) { @@ -152,6 +209,11 @@ static const char* find_block_device(const char* path, bool* encryptable, bool* return NULL; } +static bool write_status_to_socket(int status, int socket) { + int status_out = htonl(status); + return android::base::WriteFully(socket, &status_out, sizeof(int)); +} + // Parse uncrypt_file to find the update package name. static bool find_uncrypt_package(const std::string& uncrypt_path_file, std::string* package_name) { CHECK(package_name != nullptr); @@ -167,7 +229,7 @@ static bool find_uncrypt_package(const std::string& uncrypt_path_file, std::stri } static int produce_block_map(const char* path, const char* map_file, const char* blk_dev, - bool encrypted, int status_fd) { + bool encrypted, int socket) { std::string err; if (!android::base::RemoveFileIfExists(map_file, &err)) { ALOGE("failed to remove the existing map file %s: %s", map_file, err.c_str()); @@ -181,9 +243,9 @@ static int produce_block_map(const char* path, const char* map_file, const char* return -1; } - // Make sure we can write to the status_file. - if (!android::base::WriteStringToFd("0\n", status_fd)) { - ALOGE("failed to update \"%s\"\n", STATUS_FILE.c_str()); + // Make sure we can write to the socket. + if (!write_status_to_socket(0, socket)) { + ALOGE("failed to write to socket %d\n", socket); return -1; } @@ -236,8 +298,8 @@ static int produce_block_map(const char* path, const char* map_file, const char* // Update the status file, progress must be between [0, 99]. int progress = static_cast<int>(100 * (double(pos) / double(sb.st_size))); if (progress > last_progress) { - last_progress = progress; - android::base::WriteStringToFd(std::to_string(progress) + "\n", status_fd); + last_progress = progress; + write_status_to_socket(progress, socket); } if ((tail+1) % WINDOW_SIZE == head) { @@ -350,54 +412,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* return 0; } -static std::string get_misc_blk_device() { - struct fstab* fstab = read_fstab(); - if (fstab == nullptr) { - return ""; - } - for (int i = 0; i < fstab->num_entries; ++i) { - fstab_rec* v = &fstab->recs[i]; - if (v->mount_point != nullptr && strcmp(v->mount_point, "/misc") == 0) { - return v->blk_device; - } - } - return ""; -} - -static int write_bootloader_message(const bootloader_message* in) { - std::string misc_blk_device = get_misc_blk_device(); - if (misc_blk_device.empty()) { - ALOGE("failed to find /misc partition."); - return -1; - } - android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY | O_SYNC)); - if (fd == -1) { - ALOGE("failed to open %s: %s", misc_blk_device.c_str(), strerror(errno)); - return -1; - } - if (!android::base::WriteFully(fd, in, sizeof(*in))) { - ALOGE("failed to write %s: %s", misc_blk_device.c_str(), strerror(errno)); - return -1; - } - // TODO: O_SYNC and fsync() duplicates each other? - if (fsync(fd) == -1) { - ALOGE("failed to fsync %s: %s", misc_blk_device.c_str(), strerror(errno)); - return -1; - } - return 0; -} - -static void reboot_to_recovery() { - ALOGI("rebooting to recovery"); - property_set("sys.powerctl", "reboot,recovery"); - while (true) { - pause(); - } - ALOGE("reboot didn't succeed?"); -} - -static int uncrypt(const char* input_path, const char* map_file, int status_fd) { - +static int uncrypt(const char* input_path, const char* map_file, const int socket) { ALOGI("update package is \"%s\"", input_path); // Turn the name of the file we're supposed to convert into an @@ -408,10 +423,6 @@ static int uncrypt(const char* input_path, const char* map_file, int status_fd) return 1; } - if (read_fstab() == NULL) { - return 1; - } - bool encryptable; bool encrypted; const char* blk_dev = find_block_device(path, &encryptable, &encrypted); @@ -435,7 +446,7 @@ static int uncrypt(const char* input_path, const char* map_file, int status_fd) // and /sdcard we leave the file alone. if (strncmp(path, "/data/", 6) == 0) { ALOGI("writing block map %s", map_file); - if (produce_block_map(path, map_file, blk_dev, encrypted, status_fd) != 0) { + if (produce_block_map(path, map_file, blk_dev, encrypted, socket) != 0) { return 1; } } @@ -443,102 +454,141 @@ static int uncrypt(const char* input_path, const char* map_file, int status_fd) return 0; } -static int uncrypt_wrapper(const char* input_path, const char* map_file, - const std::string& status_file) { - // The pipe has been created by the system server. - android::base::unique_fd status_fd(open(status_file.c_str(), - O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR)); - if (status_fd == -1) { - ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno)); - return 1; - } - +static bool uncrypt_wrapper(const char* input_path, const char* map_file, const int socket) { std::string package; if (input_path == nullptr) { if (!find_uncrypt_package(UNCRYPT_PATH_FILE, &package)) { - android::base::WriteStringToFd("-1\n", status_fd); - return 1; + write_status_to_socket(-1, socket); + return false; } input_path = package.c_str(); } CHECK(map_file != nullptr); - int status = uncrypt(input_path, map_file, status_fd); + int status = uncrypt(input_path, map_file, socket); if (status != 0) { - android::base::WriteStringToFd("-1\n", status_fd); - return 1; + write_status_to_socket(-1, socket); + return false; } - android::base::WriteStringToFd("100\n", status_fd); - return 0; + write_status_to_socket(100, socket); + return true; } -static int clear_bcb(const std::string& status_file) { - android::base::unique_fd status_fd(open(status_file.c_str(), - O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR)); - if (status_fd == -1) { - ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno)); - return 1; - } - bootloader_message boot = {}; - if (write_bootloader_message(&boot) != 0) { - android::base::WriteStringToFd("-1\n", status_fd); - return 1; +static bool clear_bcb(const int socket) { + std::string err; + if (!clear_bootloader_message(&err)) { + ALOGE("failed to clear bootloader message: %s", err.c_str()); + write_status_to_socket(-1, socket); + return false; } - android::base::WriteStringToFd("100\n", status_fd); - return 0; + write_status_to_socket(100, socket); + return true; } -static int setup_bcb(const std::string& command_file, const std::string& status_file) { - android::base::unique_fd status_fd(open(status_file.c_str(), - O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR)); - if (status_fd == -1) { - ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno)); - return 1; +static bool setup_bcb(const int socket) { + // c5. receive message length + int length; + if (!android::base::ReadFully(socket, &length, 4)) { + ALOGE("failed to read the length: %s", strerror(errno)); + return false; } + length = ntohl(length); + + // c7. receive message std::string content; - if (!android::base::ReadFileToString(command_file, &content)) { - ALOGE("failed to read \"%s\": %s", command_file.c_str(), strerror(errno)); - android::base::WriteStringToFd("-1\n", status_fd); - return 1; + content.resize(length); + if (!android::base::ReadFully(socket, &content[0], length)) { + ALOGE("failed to read the length: %s", strerror(errno)); + return false; } - bootloader_message boot = {}; - strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); - strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); - strlcat(boot.recovery, content.c_str(), sizeof(boot.recovery)); - if (write_bootloader_message(&boot) != 0) { - ALOGE("failed to set bootloader message"); - android::base::WriteStringToFd("-1\n", status_fd); - return 1; + ALOGI(" received command: [%s] (%zu)", content.c_str(), content.size()); + + // c8. setup the bcb command + std::string err; + if (!write_bootloader_message({content}, &err)) { + ALOGE("failed to set bootloader message: %s", err.c_str()); + write_status_to_socket(-1, socket); + return false; } - android::base::WriteStringToFd("100\n", status_fd); - return 0; + // c10. send "100" status + write_status_to_socket(100, socket); + return true; } static void usage(const char* exename) { fprintf(stderr, "Usage of %s:\n", exename); fprintf(stderr, "%s [<package_path> <map_file>] Uncrypt ota package.\n", exename); - fprintf(stderr, "%s --reboot Clear BCB data and reboot to recovery.\n", exename); fprintf(stderr, "%s --clear-bcb Clear BCB data in misc partition.\n", exename); fprintf(stderr, "%s --setup-bcb Setup BCB data by command file.\n", exename); } int main(int argc, char** argv) { - if (argc == 2) { - if (strcmp(argv[1], "--reboot") == 0) { - reboot_to_recovery(); - } else if (strcmp(argv[1], "--clear-bcb") == 0) { - return clear_bcb(STATUS_FILE); - } else if (strcmp(argv[1], "--setup-bcb") == 0) { - return setup_bcb(COMMAND_FILE, STATUS_FILE); - } - } else if (argc == 1 || argc == 3) { - const char* input_path = nullptr; - const char* map_file = CACHE_BLOCK_MAP.c_str(); - if (argc == 3) { - input_path = argv[1]; - map_file = argv[2]; - } - return uncrypt_wrapper(input_path, map_file, STATUS_FILE); + enum { UNCRYPT, SETUP_BCB, CLEAR_BCB } action; + const char* input_path = nullptr; + const char* map_file = CACHE_BLOCK_MAP.c_str(); + + if (argc == 2 && strcmp(argv[1], "--clear-bcb") == 0) { + action = CLEAR_BCB; + } else if (argc == 2 && strcmp(argv[1], "--setup-bcb") == 0) { + action = SETUP_BCB; + } else if (argc == 1) { + action = UNCRYPT; + } else if (argc == 3) { + input_path = argv[1]; + map_file = argv[2]; + action = UNCRYPT; + } else { + usage(argv[0]); + return 2; + } + + if ((fstab = read_fstab()) == nullptr) { + return 1; + } + + // c3. The socket is created by init when starting the service. uncrypt + // will use the socket to communicate with its caller. + android::base::unique_fd service_socket(android_get_control_socket(UNCRYPT_SOCKET.c_str())); + if (service_socket == -1) { + ALOGE("failed to open socket \"%s\": %s", UNCRYPT_SOCKET.c_str(), strerror(errno)); + return 1; + } + fcntl(service_socket, F_SETFD, FD_CLOEXEC); + + if (listen(service_socket, 1) == -1) { + ALOGE("failed to listen on socket %d: %s", service_socket.get(), strerror(errno)); + return 1; + } + + android::base::unique_fd socket_fd(accept4(service_socket, nullptr, nullptr, SOCK_CLOEXEC)); + if (socket_fd == -1) { + ALOGE("failed to accept on socket %d: %s", service_socket.get(), strerror(errno)); + return 1; + } + + bool success = false; + switch (action) { + case UNCRYPT: + success = uncrypt_wrapper(input_path, map_file, socket_fd); + break; + case SETUP_BCB: + success = setup_bcb(socket_fd); + break; + case CLEAR_BCB: + success = clear_bcb(socket_fd); + break; + default: // Should never happen. + ALOGE("Invalid uncrypt action code: %d", action); + return 1; + } + + // c13. Read a 4-byte code from the client before uncrypt exits. This is to + // ensure the client to receive the last status code before the socket gets + // destroyed. + int code; + if (android::base::ReadFully(socket_fd, &code, 4)) { + ALOGI(" received %d, exiting now", code); + } else { + ALOGE("failed to read the code: %s", strerror(errno)); } - usage(argv[0]); - return 2; + return success ? 0 : 1; } diff --git a/uncrypt/uncrypt.rc b/uncrypt/uncrypt.rc index b07c1dada..52f564eb6 100644 --- a/uncrypt/uncrypt.rc +++ b/uncrypt/uncrypt.rc @@ -1,19 +1,17 @@ service uncrypt /system/bin/uncrypt class main - disabled - oneshot - -service pre-recovery /system/bin/uncrypt --reboot - class main + socket uncrypt stream 600 system system disabled oneshot service setup-bcb /system/bin/uncrypt --setup-bcb class main + socket uncrypt stream 600 system system disabled oneshot service clear-bcb /system/bin/uncrypt --clear-bcb class main + socket uncrypt stream 600 system system disabled - oneshot
\ No newline at end of file + oneshot diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 12a549dba..f00bc4bff 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -44,6 +44,7 @@ #include "applypatch/applypatch.h" #include "edify/expr.h" +#include "error_code.h" #include "install.h" #include "openssl/sha.h" #include "minzip/Hash.h" @@ -68,6 +69,8 @@ struct RangeSet { std::vector<size_t> pos; // Actual limit is INT_MAX. }; +static CauseCode failure_type = kNoCause; +static bool is_retry = false; static std::map<std::string, RangeSet> stash_map; static void parse_range(const std::string& range_text, RangeSet& rs) { @@ -145,6 +148,7 @@ static int read_all(int fd, uint8_t* data, size_t size) { while (so_far < size) { ssize_t r = TEMP_FAILURE_RETRY(ota_read(fd, data+so_far, size-so_far)); if (r == -1) { + failure_type = kFreadFailure; fprintf(stderr, "read failed: %s\n", strerror(errno)); return -1; } @@ -162,6 +166,7 @@ static int write_all(int fd, const uint8_t* data, size_t size) { while (written < size) { ssize_t w = TEMP_FAILURE_RETRY(ota_write(fd, data+written, size-written)); if (w == -1) { + failure_type = kFwriteFailure; fprintf(stderr, "write failed: %s\n", strerror(errno)); return -1; } @@ -175,9 +180,25 @@ static int write_all(int fd, const std::vector<uint8_t>& buffer, size_t size) { return write_all(fd, buffer.data(), size); } +static bool discard_blocks(int fd, off64_t offset, uint64_t size) { + // Don't discard blocks unless the update is a retry run. + if (!is_retry) { + return true; + } + + uint64_t args[2] = {static_cast<uint64_t>(offset), size}; + int status = ioctl(fd, BLKDISCARD, &args); + if (status == -1) { + fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno)); + return false; + } + return true; +} + static bool check_lseek(int fd, off64_t offset, int whence) { off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence)); if (rc == -1) { + failure_type = kLseekFailure; fprintf(stderr, "lseek64 failed: %s\n", strerror(errno)); return false; } @@ -233,10 +254,15 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { rss->p_remain = (rss->tgt.pos[rss->p_block * 2 + 1] - rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; - if (!check_lseek(rss->fd, (off64_t)rss->tgt.pos[rss->p_block*2] * BLOCKSIZE, - SEEK_SET)) { + off64_t offset = static_cast<off64_t>(rss->tgt.pos[rss->p_block*2]) * BLOCKSIZE; + if (!discard_blocks(rss->fd, offset, rss->p_remain)) { + break; + } + + if (!check_lseek(rss->fd, offset, SEEK_SET)) { break; } + } else { // we can't write any more; return how many bytes have // been written so far. @@ -342,11 +368,15 @@ static int WriteBlocks(const RangeSet& tgt, const std::vector<uint8_t>& buffer, size_t p = 0; for (size_t i = 0; i < tgt.count; ++i) { - if (!check_lseek(fd, (off64_t) tgt.pos[i * 2] * BLOCKSIZE, SEEK_SET)) { + off64_t offset = static_cast<off64_t>(tgt.pos[i * 2]) * BLOCKSIZE; + size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE; + if (!discard_blocks(fd, offset, size)) { return -1; } - size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE; + if (!check_lseek(fd, offset, SEEK_SET)) { + return -1; + } if (write_all(fd, data + p, size) == -1) { return -1; @@ -373,6 +403,7 @@ struct CommandParameters { bool isunresumable; int version; size_t written; + size_t stashed; NewThreadInfo nti; pthread_t thread; std::vector<uint8_t> buffer; @@ -645,6 +676,7 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks } if (ota_fsync(fd) == -1) { + failure_type = kFsyncFailure; fprintf(stderr, "fsync \"%s\" failed: %s\n", fn.c_str(), strerror(errno)); return -1; } @@ -659,11 +691,13 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks android::base::unique_fd dfd(TEMP_FAILURE_RETRY(ota_open(dname.c_str(), O_RDONLY | O_DIRECTORY))); if (dfd == -1) { + failure_type = kFileOpenFailure; fprintf(stderr, "failed to open \"%s\" failed: %s\n", dname.c_str(), strerror(errno)); return -1; } if (ota_fsync(dfd) == -1) { + failure_type = kFsyncFailure; fprintf(stderr, "fsync \"%s\" failed: %s\n", dname.c_str(), strerror(errno)); return -1; } @@ -692,19 +726,21 @@ static int CreateStash(State* state, int maxblocks, const char* blockdev, std::s int res = stat(dirname.c_str(), &sb); if (res == -1 && errno != ENOENT) { - ErrorAbort(state, "stat \"%s\" failed: %s\n", dirname.c_str(), strerror(errno)); + ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s\n", + dirname.c_str(), strerror(errno)); return -1; } else if (res != 0) { fprintf(stderr, "creating stash %s\n", dirname.c_str()); res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE); if (res != 0) { - ErrorAbort(state, "mkdir \"%s\" failed: %s\n", dirname.c_str(), strerror(errno)); + ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s\n", + dirname.c_str(), strerror(errno)); return -1; } if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) { - ErrorAbort(state, "not enough space for stash\n"); + ErrorAbort(state, kStashCreationFailure, "not enough space for stash\n"); return -1; } @@ -724,7 +760,8 @@ static int CreateStash(State* state, int maxblocks, const char* blockdev, std::s size = maxblocks * BLOCKSIZE - size; if (size > 0 && CacheSizeCheck(size) != 0) { - ErrorAbort(state, "not enough space for stash (%d more needed)\n", size); + ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%d more needed)\n", + size); return -1; } @@ -774,6 +811,7 @@ static int SaveStash(CommandParameters& params, const std::string& base, } fprintf(stderr, "stashing %zu blocks to %s\n", blocks, id.c_str()); + params.stashed += blocks; return WriteStash(base, id, blocks, buffer, false, nullptr); } @@ -970,6 +1008,7 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& return -1; } + params.stashed += src_blocks; // Can be deleted when the write has completed if (!stash_exists) { params.freestash = srchash; @@ -1087,7 +1126,13 @@ static int PerformCommandZero(CommandParameters& params) { if (params.canwrite) { for (size_t i = 0; i < tgt.count; ++i) { - if (!check_lseek(params.fd, (off64_t) tgt.pos[i * 2] * BLOCKSIZE, SEEK_SET)) { + off64_t offset = static_cast<off64_t>(tgt.pos[i * 2]) * BLOCKSIZE; + size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE; + if (!discard_blocks(params.fd, offset, size)) { + return -1; + } + + if (!check_lseek(params.fd, offset, SEEK_SET)) { return -1; } @@ -1126,7 +1171,12 @@ static int PerformCommandNew(CommandParameters& params) { rss.p_block = 0; rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; - if (!check_lseek(params.fd, (off64_t) tgt.pos[0] * BLOCKSIZE, SEEK_SET)) { + off64_t offset = static_cast<off64_t>(tgt.pos[0]) * BLOCKSIZE; + if (!discard_blocks(params.fd, offset, tgt.size * BLOCKSIZE)) { + return -1; + } + + if (!check_lseek(params.fd, offset, SEEK_SET)) { return -1; } @@ -1204,16 +1254,27 @@ static int PerformCommandDiff(CommandParameters& params) { rss.p_block = 0; rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; - if (!check_lseek(params.fd, (off64_t) tgt.pos[0] * BLOCKSIZE, SEEK_SET)) { + off64_t offset = static_cast<off64_t>(tgt.pos[0]) * BLOCKSIZE; + if (!discard_blocks(params.fd, offset, rss.p_remain)) { + return -1; + } + + if (!check_lseek(params.fd, offset, SEEK_SET)) { return -1; } if (params.cmdname[0] == 'i') { // imgdiff - ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, - &RangeSinkWrite, &rss, nullptr, nullptr); + if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, + &RangeSinkWrite, &rss, nullptr, nullptr) != 0) { + fprintf(stderr, "Failed to apply image patch.\n"); + return -1; + } } else { - ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0, - &RangeSinkWrite, &rss, nullptr); + if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, + 0, &RangeSinkWrite, &rss, nullptr) != 0) { + fprintf(stderr, "Failed to apply bsdiff patch.\n"); + return -1; + } } // We expect the output of the patcher to fill the tgt ranges exactly. @@ -1322,6 +1383,10 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg params.canwrite = !dryrun; fprintf(stderr, "performing %s\n", dryrun ? "verification" : "update"); + if (state->is_retry) { + is_retry = true; + fprintf(stderr, "This update is a retry.\n"); + } Value* blockdev_filename = nullptr; Value* transfer_list_value = nullptr; @@ -1339,19 +1404,21 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg std::unique_ptr<Value, decltype(&FreeValue)> patch_data_fn_holder(patch_data_fn, FreeValue); if (blockdev_filename->type != VAL_STRING) { - ErrorAbort(state, "blockdev_filename argument to %s must be string", name); + ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", + name); return StringValue(strdup("")); } if (transfer_list_value->type != VAL_BLOB) { - ErrorAbort(state, "transfer_list argument to %s must be blob", name); + ErrorAbort(state, kArgsParsingFailure, "transfer_list argument to %s must be blob", name); return StringValue(strdup("")); } if (new_data_fn->type != VAL_STRING) { - ErrorAbort(state, "new_data_fn argument to %s must be string", name); + ErrorAbort(state, kArgsParsingFailure, "new_data_fn argument to %s must be string", name); return StringValue(strdup("")); } if (patch_data_fn->type != VAL_STRING) { - ErrorAbort(state, "patch_data_fn argument to %s must be string", name); + ErrorAbort(state, kArgsParsingFailure, "patch_data_fn argument to %s must be string", + name); return StringValue(strdup("")); } @@ -1409,7 +1476,8 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg const std::string transfer_list(transfer_list_value->data, transfer_list_value->size); std::vector<std::string> lines = android::base::Split(transfer_list, "\n"); if (lines.size() < 2) { - ErrorAbort(state, "too few lines in the transfer list [%zd]\n", lines.size()); + ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zd]\n", + lines.size()); return StringValue(strdup("")); } @@ -1424,7 +1492,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg // Second line in transfer list is the total number of blocks we expect to write int total_blocks; if (!android::base::ParseInt(lines[1].c_str(), &total_blocks, 0)) { - ErrorAbort(state, "unexpected block count [%s]\n", lines[1].c_str()); + ErrorAbort(state, kArgsParsingFailure, "unexpected block count [%s]\n", lines[1].c_str()); return StringValue(strdup("")); } @@ -1435,7 +1503,8 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg size_t start = 2; if (params.version >= 2) { if (lines.size() < 4) { - ErrorAbort(state, "too few lines in the transfer list [%zu]\n", lines.size()); + ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]\n", + lines.size()); return StringValue(strdup("")); } @@ -1445,7 +1514,8 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg // Fourth line is the maximum number of blocks that will be stashed simultaneously int stash_max_blocks; if (!android::base::ParseInt(lines[3].c_str(), &stash_max_blocks, 0)) { - ErrorAbort(state, "unexpected maximum stash blocks [%s]\n", lines[3].c_str()); + ErrorAbort(state, kArgsParsingFailure, "unexpected maximum stash blocks [%s]\n", + lines[3].c_str()); return StringValue(strdup("")); } @@ -1499,6 +1569,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg if (params.canwrite) { if (ota_fsync(params.fd) == -1) { + failure_type = kFsyncFailure; fprintf(stderr, "fsync failed: %s\n", strerror(errno)); goto pbiudone; } @@ -1511,8 +1582,17 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg pthread_join(params.thread, nullptr); fprintf(stderr, "wrote %zu blocks; expected %d\n", params.written, total_blocks); + fprintf(stderr, "stashed %zu blocks\n", params.stashed); fprintf(stderr, "max alloc needed was %zu\n", params.buffer.size()); + const char* partition = strrchr(blockdev_filename->data, '/'); + if (partition != nullptr && *(partition+1) != 0) { + fprintf(cmd_pipe, "log bytes_written_%s: %zu\n", partition + 1, + params.written * BLOCKSIZE); + fprintf(cmd_pipe, "log bytes_stashed_%s: %zu\n", partition + 1, + params.stashed * BLOCKSIZE); + fflush(cmd_pipe); + } // Delete stash only after successfully completing the update, as it // may contain blocks needed to complete the update later. DeleteStash(params.stashbase); @@ -1524,6 +1604,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg pbiudone: if (ota_fsync(params.fd) == -1) { + failure_type = kFsyncFailure; fprintf(stderr, "fsync failed: %s\n", strerror(errno)); } // params.fd will be automatically closed because it's a unique_fd. @@ -1534,6 +1615,10 @@ pbiudone: DeleteStash(params.stashbase); } + if (failure_type != kNoCause && state->cause_code == kNoCause) { + state->cause_code = failure_type; + } + return StringValue(rc == 0 ? strdup("t") : strdup("")); } @@ -1639,17 +1724,19 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) FreeValue); if (blockdev_filename->type != VAL_STRING) { - ErrorAbort(state, "blockdev_filename argument to %s must be string", name); + ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", + name); return StringValue(strdup("")); } if (ranges->type != VAL_STRING) { - ErrorAbort(state, "ranges argument to %s must be string", name); + ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); return StringValue(strdup("")); } android::base::unique_fd fd(ota_open(blockdev_filename->data, O_RDWR)); if (fd == -1) { - ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno)); + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", blockdev_filename->data, + strerror(errno)); return StringValue(strdup("")); } @@ -1662,14 +1749,15 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) std::vector<uint8_t> buffer(BLOCKSIZE); for (size_t i = 0; i < rs.count; ++i) { if (!check_lseek(fd, (off64_t)rs.pos[i*2] * BLOCKSIZE, SEEK_SET)) { - ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data, strerror(errno)); + ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data, + strerror(errno)); return StringValue(strdup("")); } for (size_t j = rs.pos[i*2]; j < rs.pos[i*2+1]; ++j) { if (read_all(fd, buffer, BLOCKSIZE) == -1) { - ErrorAbort(state, "failed to read %s: %s", blockdev_filename->data, - strerror(errno)); + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data, + strerror(errno)); return StringValue(strdup("")); } @@ -1696,13 +1784,14 @@ Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[]) std::unique_ptr<Value, decltype(&FreeValue)> filename(arg_filename, FreeValue); if (filename->type != VAL_STRING) { - ErrorAbort(state, "filename argument to %s must be string", name); + ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); return StringValue(strdup("")); } android::base::unique_fd fd(ota_open(arg_filename->data, O_RDONLY)); if (fd == -1) { - ErrorAbort(state, "open \"%s\" failed: %s", arg_filename->data, strerror(errno)); + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data, + strerror(errno)); return StringValue(strdup("")); } @@ -1710,7 +1799,8 @@ Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[]) std::vector<uint8_t> block0_buffer(BLOCKSIZE); if (ReadBlocks(blk0, block0_buffer, fd) == -1) { - ErrorAbort(state, "failed to read %s: %s", arg_filename->data, strerror(errno)); + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data, + strerror(errno)); return StringValue(strdup("")); } @@ -1745,11 +1835,11 @@ Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[ std::unique_ptr<Value, decltype(&FreeValue)> ranges(arg_ranges, FreeValue); if (filename->type != VAL_STRING) { - ErrorAbort(state, "filename argument to %s must be string", name); + ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); return StringValue(strdup("")); } if (ranges->type != VAL_STRING) { - ErrorAbort(state, "ranges argument to %s must be string", name); + ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); return StringValue(strdup("")); } @@ -1760,19 +1850,20 @@ Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[ fec::io fh(filename->data, O_RDWR); if (!fh) { - ErrorAbort(state, "fec_open \"%s\" failed: %s", filename->data, strerror(errno)); + ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data, + strerror(errno)); return StringValue(strdup("")); } if (!fh.has_ecc() || !fh.has_verity()) { - ErrorAbort(state, "unable to use metadata to correct errors"); + ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors"); return StringValue(strdup("")); } fec_status status; if (!fh.get_status(status)) { - ErrorAbort(state, "failed to read FEC status"); + ErrorAbort(state, kLibfecFailure, "failed to read FEC status"); return StringValue(strdup("")); } @@ -1789,8 +1880,8 @@ Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[ } if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) { - ErrorAbort(state, "failed to recover %s (block %zu): %s", filename->data, - j, strerror(errno)); + ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", + filename->data, j, strerror(errno)); return StringValue(strdup("")); } diff --git a/updater/install.cpp b/updater/install.cpp index 3e7e9285a..4c4886d51 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -48,9 +48,10 @@ #include "cutils/misc.h" #include "cutils/properties.h" #include "edify/expr.h" -#include "openssl/sha.h" +#include "error_code.h" #include "minzip/DirUtil.h" #include "mounts.h" +#include "openssl/sha.h" #include "ota_io.h" #include "updater.h" #include "install.h" @@ -109,7 +110,7 @@ char* PrintSha1(const uint8_t* digest) { Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 4 && argc != 5) { - return ErrorAbort(state, "%s() expects 4-5 args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 4-5 args, got %d", name, argc); } char* fs_type; char* partition_type; @@ -132,20 +133,21 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { } if (strlen(fs_type) == 0) { - ErrorAbort(state, "fs_type argument to %s() can't be empty", name); + ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name); goto done; } if (strlen(partition_type) == 0) { - ErrorAbort(state, "partition_type argument to %s() can't be empty", + ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty", name); goto done; } if (strlen(location) == 0) { - ErrorAbort(state, "location argument to %s() can't be empty", name); + ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name); goto done; } if (strlen(mount_point) == 0) { - ErrorAbort(state, "mount_point argument to %s() can't be empty", name); + ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty", + name); goto done; } @@ -189,14 +191,14 @@ done: Value* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 1) { - return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } char* mount_point; if (ReadArgs(state, argv, 1, &mount_point) < 0) { return NULL; } if (strlen(mount_point) == 0) { - ErrorAbort(state, "mount_point argument to unmount() can't be empty"); + ErrorAbort(state, kArgsParsingFailure, "mount_point argument to unmount() can't be empty"); goto done; } @@ -219,14 +221,14 @@ done: Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 1) { - return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } char* mount_point; if (ReadArgs(state, argv, 1, &mount_point) < 0) { return NULL; } if (strlen(mount_point) == 0) { - ErrorAbort(state, "mount_point argument to unmount() can't be empty"); + ErrorAbort(state, kArgsParsingFailure, "mount_point argument to unmount() can't be empty"); goto done; } @@ -276,7 +278,7 @@ static int exec_cmd(const char* path, char* const argv[]) { Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 5) { - return ErrorAbort(state, "%s() expects 5 args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 5 args, got %d", name, argc); } char* fs_type; char* partition_type; @@ -289,21 +291,22 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { } if (strlen(fs_type) == 0) { - ErrorAbort(state, "fs_type argument to %s() can't be empty", name); + ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name); goto done; } if (strlen(partition_type) == 0) { - ErrorAbort(state, "partition_type argument to %s() can't be empty", + ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty", name); goto done; } if (strlen(location) == 0) { - ErrorAbort(state, "location argument to %s() can't be empty", name); + ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name); goto done; } if (strlen(mount_point) == 0) { - ErrorAbort(state, "mount_point argument to %s() can't be empty", name); + ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty", + name); goto done; } @@ -349,7 +352,7 @@ done: Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 2) { - return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } char* src_name; @@ -359,21 +362,21 @@ Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { return NULL; } if (strlen(src_name) == 0) { - ErrorAbort(state, "src_name argument to %s() can't be empty", name); + ErrorAbort(state, kArgsParsingFailure, "src_name argument to %s() can't be empty", name); goto done; } if (strlen(dst_name) == 0) { - ErrorAbort(state, "dst_name argument to %s() can't be empty", name); + ErrorAbort(state, kArgsParsingFailure, "dst_name argument to %s() can't be empty", name); goto done; } if (make_parents(dst_name) != 0) { - ErrorAbort(state, "Creating parent of %s failed, error %s", + ErrorAbort(state, kFileRenameFailure, "Creating parent of %s failed, error %s", dst_name, strerror(errno)); } else if (access(dst_name, F_OK) == 0 && access(src_name, F_OK) != 0) { // File was already moved result = dst_name; } else if (rename(src_name, dst_name) != 0) { - ErrorAbort(state, "Rename of %s to %s failed, error %s", + ErrorAbort(state, kFileRenameFailure, "Rename of %s to %s failed, error %s", src_name, dst_name, strerror(errno)); } else { result = dst_name; @@ -416,7 +419,7 @@ Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { Value* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } char* frac_str; char* sec_str; @@ -437,7 +440,7 @@ Value* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) { Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { - return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } char* frac_str; if (ReadArgs(state, argv, 1, &frac_str) < 0) { @@ -456,7 +459,7 @@ Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) { Value* PackageExtractDirFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } char* zip_path; char* dest_path; @@ -484,7 +487,7 @@ Value* PackageExtractDirFn(const char* name, State* state, Value* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 1 || argc > 2) { - return ErrorAbort(state, "%s() expects 1 or 2 args, got %d", + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %d", name, argc); } bool success = false; @@ -592,7 +595,7 @@ static int make_parents(char* name) { // unlinks any previously existing src1, src2, etc before creating symlinks. Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc == 0) { - return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1+ args, got %d", name, argc); } char* target; target = Evaluate(state, argv[0]); @@ -628,7 +631,7 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { } free(srcs); if (bad) { - return ErrorAbort(state, "%s: some symlinks failed", name); + return ErrorAbort(state, kSymlinkFailure, "%s: some symlinks failed", name); } return StringValue(strdup("")); } @@ -852,14 +855,16 @@ static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv bool recursive = (strcmp(name, "set_metadata_recursive") == 0); if ((argc % 2) != 1) { - return ErrorAbort(state, "%s() expects an odd number of arguments, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, + "%s() expects an odd number of arguments, got %d", name, argc); } char** args = ReadVarArgs(state, argc, argv); if (args == NULL) return NULL; if (lstat(args[0], &sb) == -1) { - result = ErrorAbort(state, "%s: Error on lstat of \"%s\": %s", name, args[0], strerror(errno)); + result = ErrorAbort(state, kSetMetadataFailure, "%s: Error on lstat of \"%s\": %s", + name, args[0], strerror(errno)); goto done; } @@ -888,7 +893,7 @@ done: } if (bad > 0) { - return ErrorAbort(state, "%s: some changes failed", name); + return ErrorAbort(state, kSetMetadataFailure, "%s: some changes failed", name); } return StringValue(strdup("")); @@ -896,7 +901,7 @@ done: Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { - return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } char* key = Evaluate(state, argv[0]); if (key == NULL) return NULL; @@ -925,20 +930,22 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { struct stat st; if (stat(filename, &st) < 0) { - ErrorAbort(state, "%s: failed to stat \"%s\": %s", name, filename, strerror(errno)); + ErrorAbort(state, kFileGetPropFailure, "%s: failed to stat \"%s\": %s", name, filename, + strerror(errno)); goto done; } #define MAX_FILE_GETPROP_SIZE 65536 if (st.st_size > MAX_FILE_GETPROP_SIZE) { - ErrorAbort(state, "%s too large for %s (max %d)", filename, name, MAX_FILE_GETPROP_SIZE); + ErrorAbort(state, kFileGetPropFailure, "%s too large for %s (max %d)", filename, name, + MAX_FILE_GETPROP_SIZE); goto done; } buffer = reinterpret_cast<char*>(malloc(st.st_size+1)); if (buffer == NULL) { - ErrorAbort(state, "%s: failed to alloc %zu bytes", name, + ErrorAbort(state, kFileGetPropFailure, "%s: failed to alloc %zu bytes", name, static_cast<size_t>(st.st_size+1)); goto done; } @@ -946,12 +953,13 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { FILE* f; f = ota_fopen(filename, "rb"); if (f == NULL) { - ErrorAbort(state, "%s: failed to open %s: %s", name, filename, strerror(errno)); + ErrorAbort(state, kFileOpenFailure, "%s: failed to open %s: %s", name, filename, + strerror(errno)); goto done; } if (ota_fread(buffer, 1, st.st_size, f) != static_cast<size_t>(st.st_size)) { - ErrorAbort(state, "%s: failed to read %zu bytes from %s", + ErrorAbort(state, kFreadFailure, "%s: failed to read %zu bytes from %s", name, static_cast<size_t>(st.st_size), filename); ota_fclose(f); goto done; @@ -1015,7 +1023,8 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, size_t bytes; if (!android::base::ParseUint(bytes_str, &bytes)) { - ErrorAbort(state, "%s(): can't parse \"%s\" as byte count\n\n", name, bytes_str); + ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count\n\n", + name, bytes_str); free(bytes_str); return nullptr; } @@ -1027,9 +1036,8 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 6 || (argc % 2) == 1) { - return ErrorAbort(state, "%s(): expected at least 6 args and an " - "even number, got %d", - name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 6 args and an " + "even number, got %d", name, argc); } char* source_filename; @@ -1043,7 +1051,8 @@ Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { size_t target_size; if (!android::base::ParseUint(target_size_str, &target_size)) { - ErrorAbort(state, "%s(): can't parse \"%s\" as byte count", name, target_size_str); + ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count", + name, target_size_str); free(source_filename); free(target_filename); free(target_sha1); @@ -1067,11 +1076,11 @@ Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { for (int i = 0; i < patchcount; ++i) { if (patch_shas[i]->type != VAL_STRING) { - ErrorAbort(state, "%s(): sha-1 #%d is not string", name, i); + ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name, i); return nullptr; } if (patches[i]->type != VAL_BLOB) { - ErrorAbort(state, "%s(): patch #%d is not blob", name, i); + ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name, i); return nullptr; } } @@ -1094,7 +1103,7 @@ Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { Value* ApplyPatchCheckFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 1) { - return ErrorAbort(state, "%s(): expected at least 1 arg, got %d", + return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 1 arg, got %d", name, argc); } @@ -1139,7 +1148,7 @@ Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) { Value* WipeCacheFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 0) { - return ErrorAbort(state, "%s() expects no args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %d", name, argc); } fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "wipe_cache\n"); return StringValue(strdup("t")); @@ -1147,7 +1156,7 @@ Value* WipeCacheFn(const char* name, State* state, int argc, Expr* argv[]) { Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 1) { - return ErrorAbort(state, "%s() expects at least 1 arg", name); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); } char** args = ReadVarArgs(state, argc, argv); if (args == NULL) { @@ -1201,7 +1210,7 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { // Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 1) { - return ErrorAbort(state, "%s() expects at least 1 arg", name); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name); } std::unique_ptr<Value*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc, argv), free); @@ -1249,7 +1258,7 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { // is actually a FileContents*). Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { - return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } char* filename; if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; @@ -1285,7 +1294,7 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { // partition. Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } char* filename; @@ -1311,7 +1320,7 @@ Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { sleep(5); free(property); - ErrorAbort(state, "%s() failed to reboot", name); + ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name); return NULL; } @@ -1327,7 +1336,7 @@ Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { // bytes. Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } char* filename; @@ -1357,7 +1366,7 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { // is the block device for the misc partition. Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 1) { - return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); } char* filename; @@ -1375,7 +1384,7 @@ Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { - return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); } char* filename; @@ -1397,7 +1406,7 @@ Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 0) { - return ErrorAbort(state, "%s() expects no args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %d", name, argc); } UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); fprintf(ui->cmd_pipe, "enable_reboot\n"); @@ -1406,12 +1415,12 @@ Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc == 0) { - return ErrorAbort(state, "%s() expects args, got %d", name, argc); + return ErrorAbort(state, kArgsParsingFailure, "%s() expects args, got %d", name, argc); } char** args = ReadVarArgs(state, argc, argv); if (args == NULL) { - return ErrorAbort(state, "%s() could not read args", name); + return ErrorAbort(state, kArgsParsingFailure, "%s() could not read args", name); } char** args2 = reinterpret_cast<char**>(malloc(sizeof(char*) * (argc+1))); @@ -1429,7 +1438,8 @@ Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) { free(args2[0]); free(args2); if (result != 0) { - return ErrorAbort(state, "%s() returned error code %d", name, result); + return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", + name, result); } return StringValue(strdup("t")); } diff --git a/updater/updater.cpp b/updater/updater.cpp index 0497d6a85..c222cee0d 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -51,7 +51,7 @@ int main(int argc, char** argv) { setbuf(stdout, NULL); setbuf(stderr, NULL); - if (argc != 4) { + if (argc != 4 && argc != 5) { printf("unexpected number of arguments (%d)\n", argc); return 1; } @@ -145,6 +145,14 @@ int main(int argc, char** argv) { state.script = script; state.errmsg = NULL; + if (argc == 5) { + if (strcmp(argv[4], "retry") == 0) { + state.is_retry = true; + } else { + printf("unexpected argument: %s", argv[4]); + } + } + char* result = Evaluate(&state, root); if (have_eio_error) { @@ -159,11 +167,28 @@ int main(int argc, char** argv) { printf("script aborted: %s\n", state.errmsg); char* line = strtok(state.errmsg, "\n"); while (line) { + // Parse the error code in abort message. + // Example: "E30: This package is for bullhead devices." + if (*line == 'E') { + if (sscanf(line, "E%u: ", &state.error_code) != 1) { + printf("Failed to parse error code: [%s]\n", line); + } + } fprintf(cmd_pipe, "ui_print %s\n", line); line = strtok(NULL, "\n"); } fprintf(cmd_pipe, "ui_print\n"); } + + if (state.error_code != kNoError) { + fprintf(cmd_pipe, "log error: %d\n", state.error_code); + // Cause code should provide additional information about the abort; + // report only when an error exists. + if (state.cause_code != kNoCause) { + fprintf(cmd_pipe, "log cause: %d\n", state.cause_code); + } + } + free(state.errmsg); return 7; } else { diff --git a/verifier.cpp b/verifier.cpp index 1d6cf811a..996a1fdf9 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -28,11 +28,14 @@ #include "asn1_decoder.h" #include "common.h" +#include "print_sha1.h" #include "ui.h" #include "verifier.h" extern RecoveryUI* ui; +static constexpr size_t MiB = 1024 * 1024; + /* * Simple version of PKCS#7 SignedData extraction. This extracts the * signature OCTET STRING to be used for signature verification. @@ -188,8 +191,6 @@ int verify_file(unsigned char* addr, size_t length, } } -#define BUFFER_SIZE 4096 - bool need_sha1 = false; bool need_sha256 = false; for (const auto& key : keys) { @@ -207,8 +208,10 @@ int verify_file(unsigned char* addr, size_t length, double frac = -1.0; size_t so_far = 0; while (so_far < signed_len) { - size_t size = signed_len - so_far; - if (size > BUFFER_SIZE) size = BUFFER_SIZE; + // On a Nexus 5X, experiment showed 16MiB beat 1MiB by 6% faster for a + // 1196MiB full OTA and 60% for an 89MiB incremental OTA. + // http://b/28135231. + size_t size = std::min(signed_len - so_far, 16 * MiB); if (need_sha1) SHA1_Update(&sha1_ctx, addr + so_far, size); if (need_sha256) SHA256_Update(&sha256_ctx, addr + so_far, size); @@ -229,9 +232,14 @@ int verify_file(unsigned char* addr, size_t length, uint8_t* sig_der = nullptr; size_t sig_der_length = 0; + uint8_t* signature = eocd + eocd_size - signature_start; size_t signature_size = signature_start - FOOTER_SIZE; - if (!read_pkcs7(eocd + eocd_size - signature_start, signature_size, &sig_der, - &sig_der_length)) { + + LOGI("signature (offset: 0x%zx, length: %zu): %s\n", + length - signature_start, signature_size, + print_hex(signature, signature_size).c_str()); + + if (!read_pkcs7(signature, signature_size, &sig_der, &sig_der_length)) { LOGE("Could not find signature DER block\n"); return VERIFY_FAILURE; } @@ -286,6 +294,13 @@ int verify_file(unsigned char* addr, size_t length, } i++; } + + if (need_sha1) { + LOGI("SHA-1 digest: %s\n", print_hex(sha1, SHA_DIGEST_LENGTH).c_str()); + } + if (need_sha256) { + LOGI("SHA-256 digest: %s\n", print_hex(sha256, SHA256_DIGEST_LENGTH).c_str()); + } free(sig_der); LOGE("failed to verify whole-file signature\n"); return VERIFY_FAILURE; diff --git a/wear_touch.cpp b/wear_touch.cpp new file mode 100644 index 000000000..f22d40b88 --- /dev/null +++ b/wear_touch.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" +#include "wear_touch.h" + +#include <dirent.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include <linux/input.h> + +#define DEVICE_PATH "/dev/input" + +WearSwipeDetector::WearSwipeDetector(int low, int high, OnSwipeCallback callback, void* cookie): + mLowThreshold(low), + mHighThreshold(high), + mCallback(callback), + mCookie(cookie), + mCurrentSlot(-1) { + pthread_create(&mThread, NULL, touch_thread, this); +} + +WearSwipeDetector::~WearSwipeDetector() { +} + +void WearSwipeDetector::detect(int dx, int dy) { + enum SwipeDirection direction; + + if (abs(dy) < mLowThreshold && abs(dx) > mHighThreshold) { + direction = dx < 0 ? LEFT : RIGHT; + } else if (abs(dx) < mLowThreshold && abs(dy) > mHighThreshold) { + direction = dy < 0 ? UP : DOWN; + } else { + LOGD("Ignore %d %d\n", dx, dy); + return; + } + + LOGD("Swipe direction=%d\n", direction); + mCallback(mCookie, direction); +} + +void WearSwipeDetector::process(struct input_event *event) { + if (mCurrentSlot < 0) { + mCallback(mCookie, UP); + mCurrentSlot = 0; + } + + if (event->type == EV_ABS) { + if (event->code == ABS_MT_SLOT) + mCurrentSlot = event->value; + + // Ignore other fingers + if (mCurrentSlot > 0) { + return; + } + + switch (event->code) { + case ABS_MT_POSITION_X: + mX = event->value; + mFingerDown = true; + break; + + case ABS_MT_POSITION_Y: + mY = event->value; + mFingerDown = true; + break; + + case ABS_MT_TRACKING_ID: + if (event->value < 0) + mFingerDown = false; + break; + } + } else if (event->type == EV_SYN) { + if (event->code == SYN_REPORT) { + if (mFingerDown && !mSwiping) { + mStartX = mX; + mStartY = mY; + mSwiping = true; + } else if (!mFingerDown && mSwiping) { + mSwiping = false; + detect(mX - mStartX, mY - mStartY); + } + } + } +} + +void WearSwipeDetector::run() { + int fd = findDevice(DEVICE_PATH); + if (fd < 0) { + LOGE("no input devices found\n"); + return; + } + + struct input_event event; + while (read(fd, &event, sizeof(event)) == sizeof(event)) { + process(&event); + } + + close(fd); +} + +void* WearSwipeDetector::touch_thread(void* cookie) { + ((WearSwipeDetector*)cookie)->run(); + return NULL; +} + +#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) + +int WearSwipeDetector::openDevice(const char *device) { + int fd = open(device, O_RDONLY); + if (fd < 0) { + LOGE("could not open %s, %s\n", device, strerror(errno)); + return false; + } + + char name[80]; + name[sizeof(name) - 1] = '\0'; + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + LOGE("could not get device name for %s, %s\n", device, strerror(errno)); + name[0] = '\0'; + } + + uint8_t bits[512]; + memset(bits, 0, sizeof(bits)); + int ret = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits); + if (ret > 0) { + if (test_bit(ABS_MT_POSITION_X, bits) && test_bit(ABS_MT_POSITION_Y, bits)) { + LOGD("Found %s %s\n", device, name); + return fd; + } + } + + close(fd); + return -1; +} + +int WearSwipeDetector::findDevice(const char* path) { + DIR* dir = opendir(path); + if (dir == NULL) { + LOGE("Could not open directory %s", path); + return false; + } + + struct dirent* entry; + int ret = -1; + while (ret < 0 && (entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.') continue; + + char device[PATH_MAX]; + device[PATH_MAX-1] = '\0'; + snprintf(device, PATH_MAX-1, "%s/%s", path, entry->d_name); + + ret = openDevice(device); + } + + closedir(dir); + return ret; +} + diff --git a/wear_touch.h b/wear_touch.h new file mode 100644 index 000000000..9a1d3150c --- /dev/null +++ b/wear_touch.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __WEAR_TOUCH_H +#define __WEAR_TOUCH_H + +#include <pthread.h> + +class WearSwipeDetector { + +public: + enum SwipeDirection { UP, DOWN, RIGHT, LEFT }; + typedef void (*OnSwipeCallback)(void* cookie, enum SwipeDirection direction); + + WearSwipeDetector(int low, int high, OnSwipeCallback cb, void* cookie); + ~WearSwipeDetector(); + +private: + void run(); + void process(struct input_event *event); + void detect(int dx, int dy); + + pthread_t mThread; + static void* touch_thread(void* cookie); + + int findDevice(const char* path); + int openDevice(const char* device); + + int mLowThreshold; + int mHighThreshold; + + OnSwipeCallback mCallback; + void *mCookie; + + int mX; + int mY; + int mStartX; + int mStartY; + + int mCurrentSlot; + bool mFingerDown; + bool mSwiping; +}; + +#endif // __WEAR_TOUCH_H diff --git a/wear_ui.cpp b/wear_ui.cpp index 48278ff21..e078134ce 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -16,9 +16,7 @@ #include <errno.h> #include <fcntl.h> -#include <pthread.h> #include <stdarg.h> -#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> @@ -31,14 +29,10 @@ #include "common.h" #include "device.h" -#include "minui/minui.h" #include "wear_ui.h" -#include "ui.h" #include "cutils/properties.h" #include "android-base/strings.h" - -static int char_width; -static int char_height; +#include "android-base/stringprintf.h" // There's only (at most) one of these objects, and global callbacks // (for pthread_create, and the input event system) need to find it, @@ -65,7 +59,6 @@ WearRecoveryUI::WearRecoveryUI() : currentIcon(NONE), intro_done(false), current_frame(0), - rtl_locale(false), progressBarType(EMPTY), progressScopeStart(0), progressScopeSize(0), @@ -84,7 +77,6 @@ WearRecoveryUI::WearRecoveryUI() : for (size_t i = 0; i < 5; i++) backgroundIcon[i] = NULL; - pthread_mutex_init(&updateMutex, NULL); self = this; } @@ -148,41 +140,6 @@ void WearRecoveryUI::draw_progress_locked() } } -void WearRecoveryUI::SetColor(UIElement e) { - switch (e) { - case HEADER: - gr_color(247, 0, 6, 255); - break; - case MENU: - case MENU_SEL_BG: - gr_color(0, 106, 157, 255); - break; - case MENU_SEL_FG: - gr_color(255, 255, 255, 255); - break; - case LOG: - gr_color(249, 194, 0, 255); - break; - case TEXT_FILL: - gr_color(0, 0, 0, 160); - break; - default: - gr_color(255, 255, 255, 255); - break; - } -} - -void WearRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) { - gr_text(x, *y, line, bold); - *y += char_height + 4; -} - -void WearRecoveryUI::DrawTextLines(int x, int* y, const char* const* lines) { - for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) { - DrawTextLine(x, y, lines[i], false); - } -} - static const char* HEADERS[] = { "Swipe up/down to move.", "Swipe left/right to select.", @@ -221,7 +178,7 @@ void WearRecoveryUI::draw_screen_locked() if (menu_items > menu_end - menu_start) { sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items); gr_text(x+4, y, cur_selection_str, 1); - y += char_height+4; + y += char_height_+4; } // Menu begins here @@ -232,7 +189,7 @@ void WearRecoveryUI::draw_screen_locked() if (i == menu_sel) { // draw the highlight bar SetColor(MENU_SEL_BG); - gr_fill(x, y-2, gr_fb_width()-x, y+char_height+2); + gr_fill(x, y-2, gr_fb_width()-x, y+char_height_+2); // white text of selected item SetColor(MENU_SEL_FG); if (menu[i][0]) gr_text(x+4, y, menu[i], 1); @@ -240,7 +197,7 @@ void WearRecoveryUI::draw_screen_locked() } else { if (menu[i][0]) gr_text(x+4, y, menu[i], 0); } - y += char_height+4; + y += char_height_+4; } SetColor(MENU); y += 4; @@ -256,9 +213,9 @@ void WearRecoveryUI::draw_screen_locked() int ty; int row = (text_top+text_rows-1) % text_rows; size_t count = 0; - for (int ty = gr_fb_height() - char_height - outer_height; + for (int ty = gr_fb_height() - char_height_ - outer_height; ty > y+2 && count < text_rows; - ty -= char_height, ++count) { + ty -= char_height_, ++count) { gr_text(x+4, ty, text[row], 0); --row; if (row < 0) row = text_rows-1; @@ -288,7 +245,7 @@ void WearRecoveryUI::progress_loop() { if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && !show_text) { if (!intro_done) { - if (current_frame == intro_frames - 1) { + if (current_frame >= intro_frames - 1) { intro_done = true; current_frame = 0; } else { @@ -324,26 +281,19 @@ void WearRecoveryUI::progress_loop() { } } -void WearRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) { - int result = res_create_display_surface(filename, surface); - if (result < 0) { - LOGE("missing bitmap %s\n(Code %d)\n", filename, result); - } -} - void WearRecoveryUI::Init() { gr_init(); - gr_font_size(&char_width, &char_height); + gr_font_size(&char_width_, &char_height_); text_col = text_row = 0; - text_rows = (gr_fb_height()) / char_height; - visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height; + text_rows = (gr_fb_height()) / char_height_; + visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_; if (text_rows > kMaxRows) text_rows = kMaxRows; text_top = 1; - text_cols = (gr_fb_width() - (outer_width * 2)) / char_width; + text_cols = (gr_fb_width() - (outer_width * 2)) / char_width_; if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]); @@ -369,29 +319,6 @@ void WearRecoveryUI::Init() RecoveryUI::Init(); } -void WearRecoveryUI::SetLocale(const char* locale) { - if (locale) { - char* lang = strdup(locale); - for (char* p = lang; *p; ++p) { - if (*p == '_') { - *p = '\0'; - break; - } - } - - // A bit cheesy: keep an explicit list of supported languages - // that are RTL. - if (strcmp(lang, "ar") == 0 || // Arabic - strcmp(lang, "fa") == 0 || // Persian (Farsi) - strcmp(lang, "he") == 0 || // Hebrew (new language code) - strcmp(lang, "iw") == 0 || // Hebrew (old language code) - strcmp(lang, "ur") == 0) { // Urdu - rtl_locale = true; - } - free(lang); - } -} - void WearRecoveryUI::SetBackground(Icon icon) { pthread_mutex_lock(&updateMutex); @@ -653,3 +580,35 @@ void WearRecoveryUI::ClearText() { } pthread_mutex_unlock(&updateMutex); } + +void WearRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + PrintV(fmt, false, ap); + va_end(ap); +} + +void WearRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) { + std::string str; + android::base::StringAppendV(&str, fmt, ap); + + if (copy_to_stdout) { + fputs(str.c_str(), stdout); + } + + pthread_mutex_lock(&updateMutex); + if (text_rows > 0 && text_cols > 0) { + for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) { + if (*ptr == '\n' || text_col >= text_cols) { + text[text_row][text_col] = '\0'; + text_col = 0; + text_row = (text_row + 1) % text_rows; + if (text_row == text_top) text_top = (text_top + 1) % text_rows; + } + if (*ptr != '\n') text[text_row][text_col++] = *ptr; + } + text[text_row][text_col] = '\0'; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); +} @@ -17,19 +17,13 @@ #ifndef RECOVERY_WEAR_UI_H #define RECOVERY_WEAR_UI_H -#include <pthread.h> -#include <stdio.h> +#include "screen_ui.h" -#include "ui.h" -#include "minui/minui.h" - -class WearRecoveryUI : public RecoveryUI { +class WearRecoveryUI : public ScreenRecoveryUI { public: WearRecoveryUI(); void Init(); - void SetLocale(const char* locale); - // overall recovery state ("background image") void SetBackground(Icon icon); @@ -47,6 +41,7 @@ class WearRecoveryUI : public RecoveryUI { // printing messages void Print(const char* fmt, ...); + void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3); void ShowFile(const char* filename); void ShowFile(FILE* fp); @@ -58,9 +53,6 @@ class WearRecoveryUI : public RecoveryUI { void Redraw(); - enum UIElement { HEADER, MENU, MENU_SEL_BG, MENU_SEL_FG, LOG, TEXT_FILL }; - virtual void SetColor(UIElement e); - protected: int progress_bar_height, progress_bar_width; @@ -89,9 +81,6 @@ class WearRecoveryUI : public RecoveryUI { int current_frame; - bool rtl_locale; - - pthread_mutex_t updateMutex; GRSurface* backgroundIcon[5]; GRSurface* *introFrames; GRSurface* *loopFrames; @@ -128,11 +117,9 @@ class WearRecoveryUI : public RecoveryUI { void update_screen_locked(); static void* progress_thread(void* cookie); void progress_loop(); - void LoadBitmap(const char* filename, GRSurface** surface); void PutChar(char); void ClearText(); - void DrawTextLine(int x, int* y, const char* line, bool bold); - void DrawTextLines(int x, int* y, const char* const* lines); + void PrintV(const char*, bool, va_list); }; #endif // RECOVERY_WEAR_UI_H |