Merge \"Minor minadbd cleanup.\"
am: a49c8a19da

Change-Id: Ie69f3c7d96292ad16f888fc63ee40bcd7c900a09
diff --git a/Android.mk b/Android.mk
index 384eefe..dcc11c5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -51,6 +51,7 @@
     ui.cpp \
     verifier.cpp \
     wear_ui.cpp \
+    wear_touch.cpp \
 
 LOCAL_MODULE := recovery
 
diff --git a/adb_install.cpp b/adb_install.cpp
index 60616ca..b05fda1 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -105,7 +105,7 @@
                 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 f5aed76..0c06d6b 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -152,40 +152,45 @@
             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;
-            }
+            // 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();
 
-            // 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);
+                int ret;
+                ret = inflateInit2(&strm, -15);
+                if (ret != Z_OK) {
+                    printf("failed to init source inflation: %d\n", ret);
+                    return -1;
+                }
 
-            if (bonus_size) {
-                memcpy(expanded_source.data() + (expanded_len - bonus_size),
-                       bonus_data->data, bonus_size);
+                // 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);
+                }
             }
 
             // Next, apply the bsdiff patch (in memory) to the uncompressed
@@ -209,33 +214,37 @@
             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;
+            {
+                std::vector<unsigned char>& temp_data = expanded_source;
 
-                if (sink(temp_data.data(), have, token) != have) {
-                    printf("failed to write %zd compressed bytes to output\n",
-                           have);
+                // 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 fd1a987..2465b07 100644
--- a/device.cpp
+++ b/device.cpp
@@ -25,6 +25,7 @@
     "Wipe cache partition",
     "Mount /system",
     "View recovery logs",
+    "Run graphics test",
     "Power off",
     NULL
 };
@@ -43,7 +44,8 @@
     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;
   }
 }
diff --git a/device.h b/device.h
index f74b6b0..5017782 100644
--- a/device.h
+++ b/device.h
@@ -68,6 +68,7 @@
         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 038dec0..71cf765 100644
--- a/edify/Android.mk
+++ b/edify/Android.mk
@@ -22,6 +22,8 @@
 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-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 c34342f..ecb1bea 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 @@
     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 @@
     return args;
 }
 
-// Use printf-style arguments to compose an error message to put into
-// *state.  Returns NULL.
-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);
+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 = buffer;
-    return NULL;
+    state->errmsg = strdup(buffer.c_str());
+    return;
+}
+
+// Use printf-style arguments to compose an error message to put into
+// *state.  Returns nullptr.
+Value* ErrorAbort(State* state, const char* format, ...) {
+    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 36f8e96..8863479 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 @@
     // 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 @@
 
 // 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 0000000..259319a
--- /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 9106db5..3d07290 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 @@
 
 #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 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 @@
     //
     //   - 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 @@
             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 @@
 }
 
 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 @@
         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 @@
     }
     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 @@
 
 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 @@
         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;
diff --git a/install.h b/install.h
index fd08e3c..66764f5 100644
--- a/install.h
+++ b/install.h
@@ -28,8 +28,8 @@
 // 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
old mode 100644
new mode 100755
index 3e777b4..6b435aa
--- 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 @@
   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 @@
 
   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 e3bc005..fb0bbe1 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -84,6 +84,8 @@
 // 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 8489d60..730b05f 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 @@
     return result;
 }
 
-static int matches_locale(const char* loc, const char* locale) {
-    if (locale == NULL) return 0;
+// 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;
 
-    if (strcmp(loc, locale) == 0) return 1;
+    // 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".
 
-    // 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".
-
-    int i;
-    for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i);
-    if (loc[i] == '_') return 0;
-
-    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 09ec876..e7dd17b 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -39,6 +39,11 @@
     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 @@
     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 @@
         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 @@
 
         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 50e385e..d0b1174 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 @@
     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 @@
 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 dd805e5..94be815 100644
--- a/otafault/ota_io.cpp
+++ b/otafault/ota_io.cpp
@@ -29,7 +29,6 @@
 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 @@
     }
 }
 
+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 fa3d7e0..c7c1f36 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 @@
     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 b4dc41b..79b99b1 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 @@
   { "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,7 +92,10 @@
 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";
@@ -107,7 +112,7 @@
 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 +543,7 @@
 
 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 +598,28 @@
     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 +974,37 @@
     }
 }
 
+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 +1063,7 @@
         }
 
         result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache,
-                                 TEMPORARY_INSTALL_FILE, false);
+                                 TEMPORARY_INSTALL_FILE, false, 0/*retry_count*/);
         break;
     }
 
@@ -1105,6 +1163,10 @@
                 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 +1408,7 @@
     bool just_exit = false;
     bool shutdown_after = false;
     int retry_count = 0;
+    bool security_update = false;
 
     int arg;
     int option_index;
@@ -1370,6 +1433,7 @@
         }
         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 +1460,9 @@
 
     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 +1525,21 @@
         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 8576a9b..1db3a2e 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
index 774244c..e500d02 100644
--- a/res-hdpi/images/erasing_text.png
+++ b/res-hdpi/images/erasing_text.png
Binary files differ
diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png
index 64a57ec..9a3597b 100644
--- a/res-hdpi/images/error_text.png
+++ b/res-hdpi/images/error_text.png
Binary files differ
diff --git a/res-hdpi/images/icon_installing.png b/res-hdpi/images/icon_installing.png
deleted file mode 100644
index 0fcfbc2..0000000
--- a/res-hdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/installing_security_text.png b/res-hdpi/images/installing_security_text.png
new file mode 100644
index 0000000..76e7474
--- /dev/null
+++ b/res-hdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png
index 33b54f1..5d2a5fa 100644
--- a/res-hdpi/images/installing_text.png
+++ b/res-hdpi/images/installing_text.png
Binary files differ
diff --git a/res-hdpi/images/loop00000.png b/res-hdpi/images/loop00000.png
new file mode 100644
index 0000000..030fa2b
--- /dev/null
+++ b/res-hdpi/images/loop00000.png
Binary files differ
diff --git a/res-hdpi/images/loop00001.png b/res-hdpi/images/loop00001.png
new file mode 100644
index 0000000..546a102
--- /dev/null
+++ b/res-hdpi/images/loop00001.png
Binary files differ
diff --git a/res-hdpi/images/loop00002.png b/res-hdpi/images/loop00002.png
new file mode 100644
index 0000000..262be3f
--- /dev/null
+++ b/res-hdpi/images/loop00002.png
Binary files differ
diff --git a/res-hdpi/images/loop00003.png b/res-hdpi/images/loop00003.png
new file mode 100644
index 0000000..1282fb3
--- /dev/null
+++ b/res-hdpi/images/loop00003.png
Binary files differ
diff --git a/res-hdpi/images/loop00004.png b/res-hdpi/images/loop00004.png
new file mode 100644
index 0000000..2ff7678
--- /dev/null
+++ b/res-hdpi/images/loop00004.png
Binary files differ
diff --git a/res-hdpi/images/loop00005.png b/res-hdpi/images/loop00005.png
new file mode 100644
index 0000000..20b4d81
--- /dev/null
+++ b/res-hdpi/images/loop00005.png
Binary files differ
diff --git a/res-hdpi/images/loop00006.png b/res-hdpi/images/loop00006.png
new file mode 100644
index 0000000..0f5b28d
--- /dev/null
+++ b/res-hdpi/images/loop00006.png
Binary files differ
diff --git a/res-hdpi/images/loop00007.png b/res-hdpi/images/loop00007.png
new file mode 100644
index 0000000..008acc8
--- /dev/null
+++ b/res-hdpi/images/loop00007.png
Binary files differ
diff --git a/res-hdpi/images/loop00008.png b/res-hdpi/images/loop00008.png
new file mode 100644
index 0000000..ca1309d
--- /dev/null
+++ b/res-hdpi/images/loop00008.png
Binary files differ
diff --git a/res-hdpi/images/loop00009.png b/res-hdpi/images/loop00009.png
new file mode 100644
index 0000000..b2730f1
--- /dev/null
+++ b/res-hdpi/images/loop00009.png
Binary files differ
diff --git a/res-hdpi/images/loop00010.png b/res-hdpi/images/loop00010.png
new file mode 100644
index 0000000..3867e9c
--- /dev/null
+++ b/res-hdpi/images/loop00010.png
Binary files differ
diff --git a/res-hdpi/images/loop00011.png b/res-hdpi/images/loop00011.png
new file mode 100644
index 0000000..2761d8f
--- /dev/null
+++ b/res-hdpi/images/loop00011.png
Binary files differ
diff --git a/res-hdpi/images/loop00012.png b/res-hdpi/images/loop00012.png
new file mode 100644
index 0000000..2d976ef
--- /dev/null
+++ b/res-hdpi/images/loop00012.png
Binary files differ
diff --git a/res-hdpi/images/loop00013.png b/res-hdpi/images/loop00013.png
new file mode 100644
index 0000000..5c96bb5
--- /dev/null
+++ b/res-hdpi/images/loop00013.png
Binary files differ
diff --git a/res-hdpi/images/loop00014.png b/res-hdpi/images/loop00014.png
new file mode 100644
index 0000000..d481ec5
--- /dev/null
+++ b/res-hdpi/images/loop00014.png
Binary files differ
diff --git a/res-hdpi/images/loop00015.png b/res-hdpi/images/loop00015.png
new file mode 100644
index 0000000..47716ed
--- /dev/null
+++ b/res-hdpi/images/loop00015.png
Binary files differ
diff --git a/res-hdpi/images/loop00016.png b/res-hdpi/images/loop00016.png
new file mode 100644
index 0000000..c0cffe8
--- /dev/null
+++ b/res-hdpi/images/loop00016.png
Binary files differ
diff --git a/res-hdpi/images/loop00017.png b/res-hdpi/images/loop00017.png
new file mode 100644
index 0000000..a0dc2e5
--- /dev/null
+++ b/res-hdpi/images/loop00017.png
Binary files differ
diff --git a/res-hdpi/images/loop00018.png b/res-hdpi/images/loop00018.png
new file mode 100644
index 0000000..c8eefc5
--- /dev/null
+++ b/res-hdpi/images/loop00018.png
Binary files differ
diff --git a/res-hdpi/images/loop00019.png b/res-hdpi/images/loop00019.png
new file mode 100644
index 0000000..0d9d8e0
--- /dev/null
+++ b/res-hdpi/images/loop00019.png
Binary files differ
diff --git a/res-hdpi/images/loop00020.png b/res-hdpi/images/loop00020.png
new file mode 100644
index 0000000..b4909a8
--- /dev/null
+++ b/res-hdpi/images/loop00020.png
Binary files differ
diff --git a/res-hdpi/images/loop00021.png b/res-hdpi/images/loop00021.png
new file mode 100644
index 0000000..b3c5274
--- /dev/null
+++ b/res-hdpi/images/loop00021.png
Binary files differ
diff --git a/res-hdpi/images/loop00022.png b/res-hdpi/images/loop00022.png
new file mode 100644
index 0000000..827c937
--- /dev/null
+++ b/res-hdpi/images/loop00022.png
Binary files differ
diff --git a/res-hdpi/images/loop00023.png b/res-hdpi/images/loop00023.png
new file mode 100644
index 0000000..84440fe
--- /dev/null
+++ b/res-hdpi/images/loop00023.png
Binary files differ
diff --git a/res-hdpi/images/loop00024.png b/res-hdpi/images/loop00024.png
new file mode 100644
index 0000000..cfc4c5b
--- /dev/null
+++ b/res-hdpi/images/loop00024.png
Binary files differ
diff --git a/res-hdpi/images/loop00025.png b/res-hdpi/images/loop00025.png
new file mode 100644
index 0000000..fd048fd
--- /dev/null
+++ b/res-hdpi/images/loop00025.png
Binary files differ
diff --git a/res-hdpi/images/loop00026.png b/res-hdpi/images/loop00026.png
new file mode 100644
index 0000000..6825187
--- /dev/null
+++ b/res-hdpi/images/loop00026.png
Binary files differ
diff --git a/res-hdpi/images/loop00027.png b/res-hdpi/images/loop00027.png
new file mode 100644
index 0000000..238dad6
--- /dev/null
+++ b/res-hdpi/images/loop00027.png
Binary files differ
diff --git a/res-hdpi/images/loop00028.png b/res-hdpi/images/loop00028.png
new file mode 100644
index 0000000..55e058d
--- /dev/null
+++ b/res-hdpi/images/loop00028.png
Binary files differ
diff --git a/res-hdpi/images/loop00029.png b/res-hdpi/images/loop00029.png
new file mode 100644
index 0000000..fc76137
--- /dev/null
+++ b/res-hdpi/images/loop00029.png
Binary files differ
diff --git a/res-hdpi/images/loop00030.png b/res-hdpi/images/loop00030.png
new file mode 100644
index 0000000..920634f
--- /dev/null
+++ b/res-hdpi/images/loop00030.png
Binary files differ
diff --git a/res-hdpi/images/loop00031.png b/res-hdpi/images/loop00031.png
new file mode 100644
index 0000000..f548464
--- /dev/null
+++ b/res-hdpi/images/loop00031.png
Binary files differ
diff --git a/res-hdpi/images/loop00032.png b/res-hdpi/images/loop00032.png
new file mode 100644
index 0000000..4cff5c4
--- /dev/null
+++ b/res-hdpi/images/loop00032.png
Binary files differ
diff --git a/res-hdpi/images/loop00033.png b/res-hdpi/images/loop00033.png
new file mode 100644
index 0000000..5d2d272
--- /dev/null
+++ b/res-hdpi/images/loop00033.png
Binary files differ
diff --git a/res-hdpi/images/loop00034.png b/res-hdpi/images/loop00034.png
new file mode 100644
index 0000000..b4d7341
--- /dev/null
+++ b/res-hdpi/images/loop00034.png
Binary files differ
diff --git a/res-hdpi/images/loop00035.png b/res-hdpi/images/loop00035.png
new file mode 100644
index 0000000..49025b8
--- /dev/null
+++ b/res-hdpi/images/loop00035.png
Binary files differ
diff --git a/res-hdpi/images/loop00036.png b/res-hdpi/images/loop00036.png
new file mode 100644
index 0000000..b3aa58d
--- /dev/null
+++ b/res-hdpi/images/loop00036.png
Binary files differ
diff --git a/res-hdpi/images/loop00037.png b/res-hdpi/images/loop00037.png
new file mode 100644
index 0000000..ff47e85
--- /dev/null
+++ b/res-hdpi/images/loop00037.png
Binary files differ
diff --git a/res-hdpi/images/loop00038.png b/res-hdpi/images/loop00038.png
new file mode 100644
index 0000000..8039b92
--- /dev/null
+++ b/res-hdpi/images/loop00038.png
Binary files differ
diff --git a/res-hdpi/images/loop00039.png b/res-hdpi/images/loop00039.png
new file mode 100644
index 0000000..e76d4bc
--- /dev/null
+++ b/res-hdpi/images/loop00039.png
Binary files differ
diff --git a/res-hdpi/images/loop00040.png b/res-hdpi/images/loop00040.png
new file mode 100644
index 0000000..963cce7
--- /dev/null
+++ b/res-hdpi/images/loop00040.png
Binary files differ
diff --git a/res-hdpi/images/loop00041.png b/res-hdpi/images/loop00041.png
new file mode 100644
index 0000000..dcd5f11
--- /dev/null
+++ b/res-hdpi/images/loop00041.png
Binary files differ
diff --git a/res-hdpi/images/loop00042.png b/res-hdpi/images/loop00042.png
new file mode 100644
index 0000000..72fe63a
--- /dev/null
+++ b/res-hdpi/images/loop00042.png
Binary files differ
diff --git a/res-hdpi/images/loop00043.png b/res-hdpi/images/loop00043.png
new file mode 100644
index 0000000..c109af8
--- /dev/null
+++ b/res-hdpi/images/loop00043.png
Binary files differ
diff --git a/res-hdpi/images/loop00044.png b/res-hdpi/images/loop00044.png
new file mode 100644
index 0000000..6648ec2
--- /dev/null
+++ b/res-hdpi/images/loop00044.png
Binary files differ
diff --git a/res-hdpi/images/loop00045.png b/res-hdpi/images/loop00045.png
new file mode 100644
index 0000000..90bf431
--- /dev/null
+++ b/res-hdpi/images/loop00045.png
Binary files differ
diff --git a/res-hdpi/images/loop00046.png b/res-hdpi/images/loop00046.png
new file mode 100644
index 0000000..50473f0
--- /dev/null
+++ b/res-hdpi/images/loop00046.png
Binary files differ
diff --git a/res-hdpi/images/loop00047.png b/res-hdpi/images/loop00047.png
new file mode 100644
index 0000000..db47023
--- /dev/null
+++ b/res-hdpi/images/loop00047.png
Binary files differ
diff --git a/res-hdpi/images/loop00048.png b/res-hdpi/images/loop00048.png
new file mode 100644
index 0000000..462a421
--- /dev/null
+++ b/res-hdpi/images/loop00048.png
Binary files differ
diff --git a/res-hdpi/images/loop00049.png b/res-hdpi/images/loop00049.png
new file mode 100644
index 0000000..f86af40
--- /dev/null
+++ b/res-hdpi/images/loop00049.png
Binary files differ
diff --git a/res-hdpi/images/loop00050.png b/res-hdpi/images/loop00050.png
new file mode 100644
index 0000000..8c0af52
--- /dev/null
+++ b/res-hdpi/images/loop00050.png
Binary files differ
diff --git a/res-hdpi/images/loop00051.png b/res-hdpi/images/loop00051.png
new file mode 100644
index 0000000..2360fc0
--- /dev/null
+++ b/res-hdpi/images/loop00051.png
Binary files differ
diff --git a/res-hdpi/images/loop00052.png b/res-hdpi/images/loop00052.png
new file mode 100644
index 0000000..dd52200
--- /dev/null
+++ b/res-hdpi/images/loop00052.png
Binary files differ
diff --git a/res-hdpi/images/loop00053.png b/res-hdpi/images/loop00053.png
new file mode 100644
index 0000000..c7f0c18
--- /dev/null
+++ b/res-hdpi/images/loop00053.png
Binary files differ
diff --git a/res-hdpi/images/loop00054.png b/res-hdpi/images/loop00054.png
new file mode 100644
index 0000000..7f16eff
--- /dev/null
+++ b/res-hdpi/images/loop00054.png
Binary files differ
diff --git a/res-hdpi/images/loop00055.png b/res-hdpi/images/loop00055.png
new file mode 100644
index 0000000..b9af0ce
--- /dev/null
+++ b/res-hdpi/images/loop00055.png
Binary files differ
diff --git a/res-hdpi/images/loop00056.png b/res-hdpi/images/loop00056.png
new file mode 100644
index 0000000..40b9e9b
--- /dev/null
+++ b/res-hdpi/images/loop00056.png
Binary files differ
diff --git a/res-hdpi/images/loop00057.png b/res-hdpi/images/loop00057.png
new file mode 100644
index 0000000..51068cb
--- /dev/null
+++ b/res-hdpi/images/loop00057.png
Binary files differ
diff --git a/res-hdpi/images/loop00058.png b/res-hdpi/images/loop00058.png
new file mode 100644
index 0000000..eba4486
--- /dev/null
+++ b/res-hdpi/images/loop00058.png
Binary files differ
diff --git a/res-hdpi/images/loop00059.png b/res-hdpi/images/loop00059.png
new file mode 100644
index 0000000..28761ac
--- /dev/null
+++ b/res-hdpi/images/loop00059.png
Binary files differ
diff --git a/res-hdpi/images/loop00060.png b/res-hdpi/images/loop00060.png
new file mode 100644
index 0000000..6532eb9
--- /dev/null
+++ b/res-hdpi/images/loop00060.png
Binary files differ
diff --git a/res-hdpi/images/loop00061.png b/res-hdpi/images/loop00061.png
new file mode 100644
index 0000000..fbe2e2e
--- /dev/null
+++ b/res-hdpi/images/loop00061.png
Binary files differ
diff --git a/res-hdpi/images/loop00062.png b/res-hdpi/images/loop00062.png
new file mode 100644
index 0000000..54341e3
--- /dev/null
+++ b/res-hdpi/images/loop00062.png
Binary files differ
diff --git a/res-hdpi/images/loop00063.png b/res-hdpi/images/loop00063.png
new file mode 100644
index 0000000..cfe9c80
--- /dev/null
+++ b/res-hdpi/images/loop00063.png
Binary files differ
diff --git a/res-hdpi/images/loop00064.png b/res-hdpi/images/loop00064.png
new file mode 100644
index 0000000..e1fe674
--- /dev/null
+++ b/res-hdpi/images/loop00064.png
Binary files differ
diff --git a/res-hdpi/images/loop00065.png b/res-hdpi/images/loop00065.png
new file mode 100644
index 0000000..efa35b6
--- /dev/null
+++ b/res-hdpi/images/loop00065.png
Binary files differ
diff --git a/res-hdpi/images/loop00066.png b/res-hdpi/images/loop00066.png
new file mode 100644
index 0000000..d8c20fe
--- /dev/null
+++ b/res-hdpi/images/loop00066.png
Binary files differ
diff --git a/res-hdpi/images/loop00067.png b/res-hdpi/images/loop00067.png
new file mode 100644
index 0000000..ddf1ea4
--- /dev/null
+++ b/res-hdpi/images/loop00067.png
Binary files differ
diff --git a/res-hdpi/images/loop00068.png b/res-hdpi/images/loop00068.png
new file mode 100644
index 0000000..827cfc6
--- /dev/null
+++ b/res-hdpi/images/loop00068.png
Binary files differ
diff --git a/res-hdpi/images/loop00069.png b/res-hdpi/images/loop00069.png
new file mode 100644
index 0000000..6ab833f
--- /dev/null
+++ b/res-hdpi/images/loop00069.png
Binary files differ
diff --git a/res-hdpi/images/loop00070.png b/res-hdpi/images/loop00070.png
new file mode 100644
index 0000000..a4cc06f
--- /dev/null
+++ b/res-hdpi/images/loop00070.png
Binary files differ
diff --git a/res-hdpi/images/loop00071.png b/res-hdpi/images/loop00071.png
new file mode 100644
index 0000000..96653c1
--- /dev/null
+++ b/res-hdpi/images/loop00071.png
Binary files differ
diff --git a/res-hdpi/images/loop00072.png b/res-hdpi/images/loop00072.png
new file mode 100644
index 0000000..44a15f8
--- /dev/null
+++ b/res-hdpi/images/loop00072.png
Binary files differ
diff --git a/res-hdpi/images/loop00073.png b/res-hdpi/images/loop00073.png
new file mode 100644
index 0000000..8352c7c
--- /dev/null
+++ b/res-hdpi/images/loop00073.png
Binary files differ
diff --git a/res-hdpi/images/loop00074.png b/res-hdpi/images/loop00074.png
new file mode 100644
index 0000000..914f1b7
--- /dev/null
+++ b/res-hdpi/images/loop00074.png
Binary files differ
diff --git a/res-hdpi/images/loop00075.png b/res-hdpi/images/loop00075.png
new file mode 100644
index 0000000..372b871
--- /dev/null
+++ b/res-hdpi/images/loop00075.png
Binary files differ
diff --git a/res-hdpi/images/loop00076.png b/res-hdpi/images/loop00076.png
new file mode 100644
index 0000000..ffbf285
--- /dev/null
+++ b/res-hdpi/images/loop00076.png
Binary files differ
diff --git a/res-hdpi/images/loop00077.png b/res-hdpi/images/loop00077.png
new file mode 100644
index 0000000..8dc6a40
--- /dev/null
+++ b/res-hdpi/images/loop00077.png
Binary files differ
diff --git a/res-hdpi/images/loop00078.png b/res-hdpi/images/loop00078.png
new file mode 100644
index 0000000..cf1ea61
--- /dev/null
+++ b/res-hdpi/images/loop00078.png
Binary files differ
diff --git a/res-hdpi/images/loop00079.png b/res-hdpi/images/loop00079.png
new file mode 100644
index 0000000..8674c82
--- /dev/null
+++ b/res-hdpi/images/loop00079.png
Binary files differ
diff --git a/res-hdpi/images/loop00080.png b/res-hdpi/images/loop00080.png
new file mode 100644
index 0000000..3d84259
--- /dev/null
+++ b/res-hdpi/images/loop00080.png
Binary files differ
diff --git a/res-hdpi/images/loop00081.png b/res-hdpi/images/loop00081.png
new file mode 100644
index 0000000..aed44c5
--- /dev/null
+++ b/res-hdpi/images/loop00081.png
Binary files differ
diff --git a/res-hdpi/images/loop00082.png b/res-hdpi/images/loop00082.png
new file mode 100644
index 0000000..a39769b
--- /dev/null
+++ b/res-hdpi/images/loop00082.png
Binary files differ
diff --git a/res-hdpi/images/loop00083.png b/res-hdpi/images/loop00083.png
new file mode 100644
index 0000000..905355d
--- /dev/null
+++ b/res-hdpi/images/loop00083.png
Binary files differ
diff --git a/res-hdpi/images/loop00084.png b/res-hdpi/images/loop00084.png
new file mode 100644
index 0000000..c86deea
--- /dev/null
+++ b/res-hdpi/images/loop00084.png
Binary files differ
diff --git a/res-hdpi/images/loop00085.png b/res-hdpi/images/loop00085.png
new file mode 100644
index 0000000..3744ab7
--- /dev/null
+++ b/res-hdpi/images/loop00085.png
Binary files differ
diff --git a/res-hdpi/images/loop00086.png b/res-hdpi/images/loop00086.png
new file mode 100644
index 0000000..0bb9b09
--- /dev/null
+++ b/res-hdpi/images/loop00086.png
Binary files differ
diff --git a/res-hdpi/images/loop00087.png b/res-hdpi/images/loop00087.png
new file mode 100644
index 0000000..83f97bd
--- /dev/null
+++ b/res-hdpi/images/loop00087.png
Binary files differ
diff --git a/res-hdpi/images/loop00088.png b/res-hdpi/images/loop00088.png
new file mode 100644
index 0000000..6fd3790
--- /dev/null
+++ b/res-hdpi/images/loop00088.png
Binary files differ
diff --git a/res-hdpi/images/loop00089.png b/res-hdpi/images/loop00089.png
new file mode 100644
index 0000000..09500f8
--- /dev/null
+++ b/res-hdpi/images/loop00089.png
Binary files differ
diff --git a/res-hdpi/images/loop00090.png b/res-hdpi/images/loop00090.png
new file mode 100644
index 0000000..030fa2b
--- /dev/null
+++ b/res-hdpi/images/loop00090.png
Binary files differ
diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png
index 9927ecb..a567ad1 100644
--- a/res-hdpi/images/no_command_text.png
+++ b/res-hdpi/images/no_command_text.png
Binary files differ
diff --git a/res-hdpi/images/progress_empty.png b/res-hdpi/images/progress_empty.png
index 7258183..96c4bf6 100644
--- a/res-hdpi/images/progress_empty.png
+++ b/res-hdpi/images/progress_empty.png
Binary files differ
diff --git a/res-hdpi/images/progress_fill.png b/res-hdpi/images/progress_fill.png
index becf87b..1717be8 100644
--- a/res-hdpi/images/progress_fill.png
+++ b/res-hdpi/images/progress_fill.png
Binary files differ
diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png
index fd86c3f..ad68a19 100644
--- a/res-mdpi/images/erasing_text.png
+++ b/res-mdpi/images/erasing_text.png
Binary files differ
diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png
index f1b44c9..8ea5acb 100644
--- a/res-mdpi/images/error_text.png
+++ b/res-mdpi/images/error_text.png
Binary files differ
diff --git a/res-mdpi/images/icon_installing.png b/res-mdpi/images/icon_installing.png
deleted file mode 100644
index 0fcfbc2..0000000
--- a/res-mdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/installing_security_text.png b/res-mdpi/images/installing_security_text.png
new file mode 100644
index 0000000..615b9b7
--- /dev/null
+++ b/res-mdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png
index 064b2a3..6cf8716 100644
--- a/res-mdpi/images/installing_text.png
+++ b/res-mdpi/images/installing_text.png
Binary files differ
diff --git a/res-mdpi/images/loop00000.png b/res-mdpi/images/loop00000.png
new file mode 100644
index 0000000..d7092b6
--- /dev/null
+++ b/res-mdpi/images/loop00000.png
Binary files differ
diff --git a/res-mdpi/images/loop00001.png b/res-mdpi/images/loop00001.png
new file mode 100644
index 0000000..e04a525
--- /dev/null
+++ b/res-mdpi/images/loop00001.png
Binary files differ
diff --git a/res-mdpi/images/loop00002.png b/res-mdpi/images/loop00002.png
new file mode 100644
index 0000000..e2a7831
--- /dev/null
+++ b/res-mdpi/images/loop00002.png
Binary files differ
diff --git a/res-mdpi/images/loop00003.png b/res-mdpi/images/loop00003.png
new file mode 100644
index 0000000..28f79bf
--- /dev/null
+++ b/res-mdpi/images/loop00003.png
Binary files differ
diff --git a/res-mdpi/images/loop00004.png b/res-mdpi/images/loop00004.png
new file mode 100644
index 0000000..e4bec80
--- /dev/null
+++ b/res-mdpi/images/loop00004.png
Binary files differ
diff --git a/res-mdpi/images/loop00005.png b/res-mdpi/images/loop00005.png
new file mode 100644
index 0000000..de673e0
--- /dev/null
+++ b/res-mdpi/images/loop00005.png
Binary files differ
diff --git a/res-mdpi/images/loop00006.png b/res-mdpi/images/loop00006.png
new file mode 100644
index 0000000..71d4203
--- /dev/null
+++ b/res-mdpi/images/loop00006.png
Binary files differ
diff --git a/res-mdpi/images/loop00007.png b/res-mdpi/images/loop00007.png
new file mode 100644
index 0000000..dee70b3
--- /dev/null
+++ b/res-mdpi/images/loop00007.png
Binary files differ
diff --git a/res-mdpi/images/loop00008.png b/res-mdpi/images/loop00008.png
new file mode 100644
index 0000000..9eccc7f
--- /dev/null
+++ b/res-mdpi/images/loop00008.png
Binary files differ
diff --git a/res-mdpi/images/loop00009.png b/res-mdpi/images/loop00009.png
new file mode 100644
index 0000000..d6672ac
--- /dev/null
+++ b/res-mdpi/images/loop00009.png
Binary files differ
diff --git a/res-mdpi/images/loop00010.png b/res-mdpi/images/loop00010.png
new file mode 100644
index 0000000..1bb8f5c
--- /dev/null
+++ b/res-mdpi/images/loop00010.png
Binary files differ
diff --git a/res-mdpi/images/loop00011.png b/res-mdpi/images/loop00011.png
new file mode 100644
index 0000000..849ce3d
--- /dev/null
+++ b/res-mdpi/images/loop00011.png
Binary files differ
diff --git a/res-mdpi/images/loop00012.png b/res-mdpi/images/loop00012.png
new file mode 100644
index 0000000..cee9dcf
--- /dev/null
+++ b/res-mdpi/images/loop00012.png
Binary files differ
diff --git a/res-mdpi/images/loop00013.png b/res-mdpi/images/loop00013.png
new file mode 100644
index 0000000..1ef61d7
--- /dev/null
+++ b/res-mdpi/images/loop00013.png
Binary files differ
diff --git a/res-mdpi/images/loop00014.png b/res-mdpi/images/loop00014.png
new file mode 100644
index 0000000..bc84637
--- /dev/null
+++ b/res-mdpi/images/loop00014.png
Binary files differ
diff --git a/res-mdpi/images/loop00015.png b/res-mdpi/images/loop00015.png
new file mode 100644
index 0000000..f5607f2
--- /dev/null
+++ b/res-mdpi/images/loop00015.png
Binary files differ
diff --git a/res-mdpi/images/loop00016.png b/res-mdpi/images/loop00016.png
new file mode 100644
index 0000000..235527c
--- /dev/null
+++ b/res-mdpi/images/loop00016.png
Binary files differ
diff --git a/res-mdpi/images/loop00017.png b/res-mdpi/images/loop00017.png
new file mode 100644
index 0000000..88307a6
--- /dev/null
+++ b/res-mdpi/images/loop00017.png
Binary files differ
diff --git a/res-mdpi/images/loop00018.png b/res-mdpi/images/loop00018.png
new file mode 100644
index 0000000..02472d7
--- /dev/null
+++ b/res-mdpi/images/loop00018.png
Binary files differ
diff --git a/res-mdpi/images/loop00019.png b/res-mdpi/images/loop00019.png
new file mode 100644
index 0000000..f06bdaa
--- /dev/null
+++ b/res-mdpi/images/loop00019.png
Binary files differ
diff --git a/res-mdpi/images/loop00020.png b/res-mdpi/images/loop00020.png
new file mode 100644
index 0000000..dc522c0
--- /dev/null
+++ b/res-mdpi/images/loop00020.png
Binary files differ
diff --git a/res-mdpi/images/loop00021.png b/res-mdpi/images/loop00021.png
new file mode 100644
index 0000000..3a53ee5
--- /dev/null
+++ b/res-mdpi/images/loop00021.png
Binary files differ
diff --git a/res-mdpi/images/loop00022.png b/res-mdpi/images/loop00022.png
new file mode 100644
index 0000000..09b8eea
--- /dev/null
+++ b/res-mdpi/images/loop00022.png
Binary files differ
diff --git a/res-mdpi/images/loop00023.png b/res-mdpi/images/loop00023.png
new file mode 100644
index 0000000..ebc677d
--- /dev/null
+++ b/res-mdpi/images/loop00023.png
Binary files differ
diff --git a/res-mdpi/images/loop00024.png b/res-mdpi/images/loop00024.png
new file mode 100644
index 0000000..a4fd8e5
--- /dev/null
+++ b/res-mdpi/images/loop00024.png
Binary files differ
diff --git a/res-mdpi/images/loop00025.png b/res-mdpi/images/loop00025.png
new file mode 100644
index 0000000..9435624
--- /dev/null
+++ b/res-mdpi/images/loop00025.png
Binary files differ
diff --git a/res-mdpi/images/loop00026.png b/res-mdpi/images/loop00026.png
new file mode 100644
index 0000000..b7e8081
--- /dev/null
+++ b/res-mdpi/images/loop00026.png
Binary files differ
diff --git a/res-mdpi/images/loop00027.png b/res-mdpi/images/loop00027.png
new file mode 100644
index 0000000..757d8ed
--- /dev/null
+++ b/res-mdpi/images/loop00027.png
Binary files differ
diff --git a/res-mdpi/images/loop00028.png b/res-mdpi/images/loop00028.png
new file mode 100644
index 0000000..8eefa3a
--- /dev/null
+++ b/res-mdpi/images/loop00028.png
Binary files differ
diff --git a/res-mdpi/images/loop00029.png b/res-mdpi/images/loop00029.png
new file mode 100644
index 0000000..8d890de
--- /dev/null
+++ b/res-mdpi/images/loop00029.png
Binary files differ
diff --git a/res-mdpi/images/loop00030.png b/res-mdpi/images/loop00030.png
new file mode 100644
index 0000000..8e0eeb6
--- /dev/null
+++ b/res-mdpi/images/loop00030.png
Binary files differ
diff --git a/res-mdpi/images/loop00031.png b/res-mdpi/images/loop00031.png
new file mode 100644
index 0000000..178b29d
--- /dev/null
+++ b/res-mdpi/images/loop00031.png
Binary files differ
diff --git a/res-mdpi/images/loop00032.png b/res-mdpi/images/loop00032.png
new file mode 100644
index 0000000..39192c7
--- /dev/null
+++ b/res-mdpi/images/loop00032.png
Binary files differ
diff --git a/res-mdpi/images/loop00033.png b/res-mdpi/images/loop00033.png
new file mode 100644
index 0000000..0647e50
--- /dev/null
+++ b/res-mdpi/images/loop00033.png
Binary files differ
diff --git a/res-mdpi/images/loop00034.png b/res-mdpi/images/loop00034.png
new file mode 100644
index 0000000..d6bc079
--- /dev/null
+++ b/res-mdpi/images/loop00034.png
Binary files differ
diff --git a/res-mdpi/images/loop00035.png b/res-mdpi/images/loop00035.png
new file mode 100644
index 0000000..68352e8
--- /dev/null
+++ b/res-mdpi/images/loop00035.png
Binary files differ
diff --git a/res-mdpi/images/loop00036.png b/res-mdpi/images/loop00036.png
new file mode 100644
index 0000000..92d9da2
--- /dev/null
+++ b/res-mdpi/images/loop00036.png
Binary files differ
diff --git a/res-mdpi/images/loop00037.png b/res-mdpi/images/loop00037.png
new file mode 100644
index 0000000..a0e4d33
--- /dev/null
+++ b/res-mdpi/images/loop00037.png
Binary files differ
diff --git a/res-mdpi/images/loop00038.png b/res-mdpi/images/loop00038.png
new file mode 100644
index 0000000..c523173
--- /dev/null
+++ b/res-mdpi/images/loop00038.png
Binary files differ
diff --git a/res-mdpi/images/loop00039.png b/res-mdpi/images/loop00039.png
new file mode 100644
index 0000000..aae7765
--- /dev/null
+++ b/res-mdpi/images/loop00039.png
Binary files differ
diff --git a/res-mdpi/images/loop00040.png b/res-mdpi/images/loop00040.png
new file mode 100644
index 0000000..af9e018
--- /dev/null
+++ b/res-mdpi/images/loop00040.png
Binary files differ
diff --git a/res-mdpi/images/loop00041.png b/res-mdpi/images/loop00041.png
new file mode 100644
index 0000000..8e089c2
--- /dev/null
+++ b/res-mdpi/images/loop00041.png
Binary files differ
diff --git a/res-mdpi/images/loop00042.png b/res-mdpi/images/loop00042.png
new file mode 100644
index 0000000..e3e3b8a
--- /dev/null
+++ b/res-mdpi/images/loop00042.png
Binary files differ
diff --git a/res-mdpi/images/loop00043.png b/res-mdpi/images/loop00043.png
new file mode 100644
index 0000000..cc8acba
--- /dev/null
+++ b/res-mdpi/images/loop00043.png
Binary files differ
diff --git a/res-mdpi/images/loop00044.png b/res-mdpi/images/loop00044.png
new file mode 100644
index 0000000..9a3a9b9
--- /dev/null
+++ b/res-mdpi/images/loop00044.png
Binary files differ
diff --git a/res-mdpi/images/loop00045.png b/res-mdpi/images/loop00045.png
new file mode 100644
index 0000000..ec5e3c4
--- /dev/null
+++ b/res-mdpi/images/loop00045.png
Binary files differ
diff --git a/res-mdpi/images/loop00046.png b/res-mdpi/images/loop00046.png
new file mode 100644
index 0000000..925e2b7
--- /dev/null
+++ b/res-mdpi/images/loop00046.png
Binary files differ
diff --git a/res-mdpi/images/loop00047.png b/res-mdpi/images/loop00047.png
new file mode 100644
index 0000000..62fff88
--- /dev/null
+++ b/res-mdpi/images/loop00047.png
Binary files differ
diff --git a/res-mdpi/images/loop00048.png b/res-mdpi/images/loop00048.png
new file mode 100644
index 0000000..46efe70
--- /dev/null
+++ b/res-mdpi/images/loop00048.png
Binary files differ
diff --git a/res-mdpi/images/loop00049.png b/res-mdpi/images/loop00049.png
new file mode 100644
index 0000000..678dce4
--- /dev/null
+++ b/res-mdpi/images/loop00049.png
Binary files differ
diff --git a/res-mdpi/images/loop00050.png b/res-mdpi/images/loop00050.png
new file mode 100644
index 0000000..cbc6fdb
--- /dev/null
+++ b/res-mdpi/images/loop00050.png
Binary files differ
diff --git a/res-mdpi/images/loop00051.png b/res-mdpi/images/loop00051.png
new file mode 100644
index 0000000..afa9066
--- /dev/null
+++ b/res-mdpi/images/loop00051.png
Binary files differ
diff --git a/res-mdpi/images/loop00052.png b/res-mdpi/images/loop00052.png
new file mode 100644
index 0000000..4d2d98c
--- /dev/null
+++ b/res-mdpi/images/loop00052.png
Binary files differ
diff --git a/res-mdpi/images/loop00053.png b/res-mdpi/images/loop00053.png
new file mode 100644
index 0000000..48136a5
--- /dev/null
+++ b/res-mdpi/images/loop00053.png
Binary files differ
diff --git a/res-mdpi/images/loop00054.png b/res-mdpi/images/loop00054.png
new file mode 100644
index 0000000..09f706a
--- /dev/null
+++ b/res-mdpi/images/loop00054.png
Binary files differ
diff --git a/res-mdpi/images/loop00055.png b/res-mdpi/images/loop00055.png
new file mode 100644
index 0000000..7565a1c
--- /dev/null
+++ b/res-mdpi/images/loop00055.png
Binary files differ
diff --git a/res-mdpi/images/loop00056.png b/res-mdpi/images/loop00056.png
new file mode 100644
index 0000000..2765831
--- /dev/null
+++ b/res-mdpi/images/loop00056.png
Binary files differ
diff --git a/res-mdpi/images/loop00057.png b/res-mdpi/images/loop00057.png
new file mode 100644
index 0000000..de440e0
--- /dev/null
+++ b/res-mdpi/images/loop00057.png
Binary files differ
diff --git a/res-mdpi/images/loop00058.png b/res-mdpi/images/loop00058.png
new file mode 100644
index 0000000..67d49c7
--- /dev/null
+++ b/res-mdpi/images/loop00058.png
Binary files differ
diff --git a/res-mdpi/images/loop00059.png b/res-mdpi/images/loop00059.png
new file mode 100644
index 0000000..a622f45
--- /dev/null
+++ b/res-mdpi/images/loop00059.png
Binary files differ
diff --git a/res-mdpi/images/loop00060.png b/res-mdpi/images/loop00060.png
new file mode 100644
index 0000000..06d6eec
--- /dev/null
+++ b/res-mdpi/images/loop00060.png
Binary files differ
diff --git a/res-mdpi/images/loop00061.png b/res-mdpi/images/loop00061.png
new file mode 100644
index 0000000..7f11945
--- /dev/null
+++ b/res-mdpi/images/loop00061.png
Binary files differ
diff --git a/res-mdpi/images/loop00062.png b/res-mdpi/images/loop00062.png
new file mode 100644
index 0000000..8197c94
--- /dev/null
+++ b/res-mdpi/images/loop00062.png
Binary files differ
diff --git a/res-mdpi/images/loop00063.png b/res-mdpi/images/loop00063.png
new file mode 100644
index 0000000..4093c9b
--- /dev/null
+++ b/res-mdpi/images/loop00063.png
Binary files differ
diff --git a/res-mdpi/images/loop00064.png b/res-mdpi/images/loop00064.png
new file mode 100644
index 0000000..d09bd1e
--- /dev/null
+++ b/res-mdpi/images/loop00064.png
Binary files differ
diff --git a/res-mdpi/images/loop00065.png b/res-mdpi/images/loop00065.png
new file mode 100644
index 0000000..cbb6c1b
--- /dev/null
+++ b/res-mdpi/images/loop00065.png
Binary files differ
diff --git a/res-mdpi/images/loop00066.png b/res-mdpi/images/loop00066.png
new file mode 100644
index 0000000..aed0a70
--- /dev/null
+++ b/res-mdpi/images/loop00066.png
Binary files differ
diff --git a/res-mdpi/images/loop00067.png b/res-mdpi/images/loop00067.png
new file mode 100644
index 0000000..dd0da79
--- /dev/null
+++ b/res-mdpi/images/loop00067.png
Binary files differ
diff --git a/res-mdpi/images/loop00068.png b/res-mdpi/images/loop00068.png
new file mode 100644
index 0000000..161802c
--- /dev/null
+++ b/res-mdpi/images/loop00068.png
Binary files differ
diff --git a/res-mdpi/images/loop00069.png b/res-mdpi/images/loop00069.png
new file mode 100644
index 0000000..4ee0372
--- /dev/null
+++ b/res-mdpi/images/loop00069.png
Binary files differ
diff --git a/res-mdpi/images/loop00070.png b/res-mdpi/images/loop00070.png
new file mode 100644
index 0000000..41a64ff
--- /dev/null
+++ b/res-mdpi/images/loop00070.png
Binary files differ
diff --git a/res-mdpi/images/loop00071.png b/res-mdpi/images/loop00071.png
new file mode 100644
index 0000000..c4793d7
--- /dev/null
+++ b/res-mdpi/images/loop00071.png
Binary files differ
diff --git a/res-mdpi/images/loop00072.png b/res-mdpi/images/loop00072.png
new file mode 100644
index 0000000..9399d19
--- /dev/null
+++ b/res-mdpi/images/loop00072.png
Binary files differ
diff --git a/res-mdpi/images/loop00073.png b/res-mdpi/images/loop00073.png
new file mode 100644
index 0000000..d4e55ad
--- /dev/null
+++ b/res-mdpi/images/loop00073.png
Binary files differ
diff --git a/res-mdpi/images/loop00074.png b/res-mdpi/images/loop00074.png
new file mode 100644
index 0000000..f29a0af
--- /dev/null
+++ b/res-mdpi/images/loop00074.png
Binary files differ
diff --git a/res-mdpi/images/loop00075.png b/res-mdpi/images/loop00075.png
new file mode 100644
index 0000000..020568e
--- /dev/null
+++ b/res-mdpi/images/loop00075.png
Binary files differ
diff --git a/res-mdpi/images/loop00076.png b/res-mdpi/images/loop00076.png
new file mode 100644
index 0000000..51a54cc
--- /dev/null
+++ b/res-mdpi/images/loop00076.png
Binary files differ
diff --git a/res-mdpi/images/loop00077.png b/res-mdpi/images/loop00077.png
new file mode 100644
index 0000000..f6e80a9
--- /dev/null
+++ b/res-mdpi/images/loop00077.png
Binary files differ
diff --git a/res-mdpi/images/loop00078.png b/res-mdpi/images/loop00078.png
new file mode 100644
index 0000000..9444521
--- /dev/null
+++ b/res-mdpi/images/loop00078.png
Binary files differ
diff --git a/res-mdpi/images/loop00079.png b/res-mdpi/images/loop00079.png
new file mode 100644
index 0000000..b1ef2c3
--- /dev/null
+++ b/res-mdpi/images/loop00079.png
Binary files differ
diff --git a/res-mdpi/images/loop00080.png b/res-mdpi/images/loop00080.png
new file mode 100644
index 0000000..8a911fb
--- /dev/null
+++ b/res-mdpi/images/loop00080.png
Binary files differ
diff --git a/res-mdpi/images/loop00081.png b/res-mdpi/images/loop00081.png
new file mode 100644
index 0000000..f848df4
--- /dev/null
+++ b/res-mdpi/images/loop00081.png
Binary files differ
diff --git a/res-mdpi/images/loop00082.png b/res-mdpi/images/loop00082.png
new file mode 100644
index 0000000..35b1325
--- /dev/null
+++ b/res-mdpi/images/loop00082.png
Binary files differ
diff --git a/res-mdpi/images/loop00083.png b/res-mdpi/images/loop00083.png
new file mode 100644
index 0000000..1571fb5
--- /dev/null
+++ b/res-mdpi/images/loop00083.png
Binary files differ
diff --git a/res-mdpi/images/loop00084.png b/res-mdpi/images/loop00084.png
new file mode 100644
index 0000000..92b5295
--- /dev/null
+++ b/res-mdpi/images/loop00084.png
Binary files differ
diff --git a/res-mdpi/images/loop00085.png b/res-mdpi/images/loop00085.png
new file mode 100644
index 0000000..cde8880
--- /dev/null
+++ b/res-mdpi/images/loop00085.png
Binary files differ
diff --git a/res-mdpi/images/loop00086.png b/res-mdpi/images/loop00086.png
new file mode 100644
index 0000000..45889e5
--- /dev/null
+++ b/res-mdpi/images/loop00086.png
Binary files differ
diff --git a/res-mdpi/images/loop00087.png b/res-mdpi/images/loop00087.png
new file mode 100644
index 0000000..9cad9aa
--- /dev/null
+++ b/res-mdpi/images/loop00087.png
Binary files differ
diff --git a/res-mdpi/images/loop00088.png b/res-mdpi/images/loop00088.png
new file mode 100644
index 0000000..dcf98c8
--- /dev/null
+++ b/res-mdpi/images/loop00088.png
Binary files differ
diff --git a/res-mdpi/images/loop00089.png b/res-mdpi/images/loop00089.png
new file mode 100644
index 0000000..584cb89
--- /dev/null
+++ b/res-mdpi/images/loop00089.png
Binary files differ
diff --git a/res-mdpi/images/loop00090.png b/res-mdpi/images/loop00090.png
new file mode 100644
index 0000000..d7092b6
--- /dev/null
+++ b/res-mdpi/images/loop00090.png
Binary files differ
diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png
index 1f29b89..5cc6b37 100644
--- a/res-mdpi/images/no_command_text.png
+++ b/res-mdpi/images/no_command_text.png
Binary files differ
diff --git a/res-mdpi/images/progress_empty.png b/res-mdpi/images/progress_empty.png
index 7258183..96c4bf6 100644
--- a/res-mdpi/images/progress_empty.png
+++ b/res-mdpi/images/progress_empty.png
Binary files differ
diff --git a/res-mdpi/images/progress_fill.png b/res-mdpi/images/progress_fill.png
index becf87b..1717be8 100644
--- a/res-mdpi/images/progress_fill.png
+++ b/res-mdpi/images/progress_fill.png
Binary files differ
diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png
index f88e0e6..01176e8 100644
--- a/res-xhdpi/images/erasing_text.png
+++ b/res-xhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png
index c3a4cc6..4f890fa 100644
--- a/res-xhdpi/images/error_text.png
+++ b/res-xhdpi/images/error_text.png
Binary files differ
diff --git a/res-xhdpi/images/icon_installing.png b/res-xhdpi/images/icon_installing.png
deleted file mode 100644
index 0fcfbc2..0000000
--- a/res-xhdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/installing_security_text.png b/res-xhdpi/images/installing_security_text.png
new file mode 100644
index 0000000..6192832
--- /dev/null
+++ b/res-xhdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png
index a4dacd0..4413529 100644
--- a/res-xhdpi/images/installing_text.png
+++ b/res-xhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xhdpi/images/loop00000.png b/res-xhdpi/images/loop00000.png
new file mode 100644
index 0000000..f5bf7a7
--- /dev/null
+++ b/res-xhdpi/images/loop00000.png
Binary files differ
diff --git a/res-xhdpi/images/loop00001.png b/res-xhdpi/images/loop00001.png
new file mode 100644
index 0000000..95c14eb
--- /dev/null
+++ b/res-xhdpi/images/loop00001.png
Binary files differ
diff --git a/res-xhdpi/images/loop00002.png b/res-xhdpi/images/loop00002.png
new file mode 100644
index 0000000..5910fd1
--- /dev/null
+++ b/res-xhdpi/images/loop00002.png
Binary files differ
diff --git a/res-xhdpi/images/loop00003.png b/res-xhdpi/images/loop00003.png
new file mode 100644
index 0000000..e6861d2
--- /dev/null
+++ b/res-xhdpi/images/loop00003.png
Binary files differ
diff --git a/res-xhdpi/images/loop00004.png b/res-xhdpi/images/loop00004.png
new file mode 100644
index 0000000..453cdc6
--- /dev/null
+++ b/res-xhdpi/images/loop00004.png
Binary files differ
diff --git a/res-xhdpi/images/loop00005.png b/res-xhdpi/images/loop00005.png
new file mode 100644
index 0000000..12157c9
--- /dev/null
+++ b/res-xhdpi/images/loop00005.png
Binary files differ
diff --git a/res-xhdpi/images/loop00006.png b/res-xhdpi/images/loop00006.png
new file mode 100644
index 0000000..5e78385
--- /dev/null
+++ b/res-xhdpi/images/loop00006.png
Binary files differ
diff --git a/res-xhdpi/images/loop00007.png b/res-xhdpi/images/loop00007.png
new file mode 100644
index 0000000..c69abf4
--- /dev/null
+++ b/res-xhdpi/images/loop00007.png
Binary files differ
diff --git a/res-xhdpi/images/loop00008.png b/res-xhdpi/images/loop00008.png
new file mode 100644
index 0000000..78c3b99
--- /dev/null
+++ b/res-xhdpi/images/loop00008.png
Binary files differ
diff --git a/res-xhdpi/images/loop00009.png b/res-xhdpi/images/loop00009.png
new file mode 100644
index 0000000..e510b6b
--- /dev/null
+++ b/res-xhdpi/images/loop00009.png
Binary files differ
diff --git a/res-xhdpi/images/loop00010.png b/res-xhdpi/images/loop00010.png
new file mode 100644
index 0000000..9d775fa
--- /dev/null
+++ b/res-xhdpi/images/loop00010.png
Binary files differ
diff --git a/res-xhdpi/images/loop00011.png b/res-xhdpi/images/loop00011.png
new file mode 100644
index 0000000..36c0195
--- /dev/null
+++ b/res-xhdpi/images/loop00011.png
Binary files differ
diff --git a/res-xhdpi/images/loop00012.png b/res-xhdpi/images/loop00012.png
new file mode 100644
index 0000000..ac65096
--- /dev/null
+++ b/res-xhdpi/images/loop00012.png
Binary files differ
diff --git a/res-xhdpi/images/loop00013.png b/res-xhdpi/images/loop00013.png
new file mode 100644
index 0000000..e3fdaaf
--- /dev/null
+++ b/res-xhdpi/images/loop00013.png
Binary files differ
diff --git a/res-xhdpi/images/loop00014.png b/res-xhdpi/images/loop00014.png
new file mode 100644
index 0000000..6e85108
--- /dev/null
+++ b/res-xhdpi/images/loop00014.png
Binary files differ
diff --git a/res-xhdpi/images/loop00015.png b/res-xhdpi/images/loop00015.png
new file mode 100644
index 0000000..9e60329
--- /dev/null
+++ b/res-xhdpi/images/loop00015.png
Binary files differ
diff --git a/res-xhdpi/images/loop00016.png b/res-xhdpi/images/loop00016.png
new file mode 100644
index 0000000..68417aa
--- /dev/null
+++ b/res-xhdpi/images/loop00016.png
Binary files differ
diff --git a/res-xhdpi/images/loop00017.png b/res-xhdpi/images/loop00017.png
new file mode 100644
index 0000000..4ac5dde
--- /dev/null
+++ b/res-xhdpi/images/loop00017.png
Binary files differ
diff --git a/res-xhdpi/images/loop00018.png b/res-xhdpi/images/loop00018.png
new file mode 100644
index 0000000..d651128
--- /dev/null
+++ b/res-xhdpi/images/loop00018.png
Binary files differ
diff --git a/res-xhdpi/images/loop00019.png b/res-xhdpi/images/loop00019.png
new file mode 100644
index 0000000..3742735
--- /dev/null
+++ b/res-xhdpi/images/loop00019.png
Binary files differ
diff --git a/res-xhdpi/images/loop00020.png b/res-xhdpi/images/loop00020.png
new file mode 100644
index 0000000..04489a1
--- /dev/null
+++ b/res-xhdpi/images/loop00020.png
Binary files differ
diff --git a/res-xhdpi/images/loop00021.png b/res-xhdpi/images/loop00021.png
new file mode 100644
index 0000000..59c7016
--- /dev/null
+++ b/res-xhdpi/images/loop00021.png
Binary files differ
diff --git a/res-xhdpi/images/loop00022.png b/res-xhdpi/images/loop00022.png
new file mode 100644
index 0000000..0b9a59f
--- /dev/null
+++ b/res-xhdpi/images/loop00022.png
Binary files differ
diff --git a/res-xhdpi/images/loop00023.png b/res-xhdpi/images/loop00023.png
new file mode 100644
index 0000000..31abae7
--- /dev/null
+++ b/res-xhdpi/images/loop00023.png
Binary files differ
diff --git a/res-xhdpi/images/loop00024.png b/res-xhdpi/images/loop00024.png
new file mode 100644
index 0000000..98d8ee3
--- /dev/null
+++ b/res-xhdpi/images/loop00024.png
Binary files differ
diff --git a/res-xhdpi/images/loop00025.png b/res-xhdpi/images/loop00025.png
new file mode 100644
index 0000000..9f074d2
--- /dev/null
+++ b/res-xhdpi/images/loop00025.png
Binary files differ
diff --git a/res-xhdpi/images/loop00026.png b/res-xhdpi/images/loop00026.png
new file mode 100644
index 0000000..063fca2
--- /dev/null
+++ b/res-xhdpi/images/loop00026.png
Binary files differ
diff --git a/res-xhdpi/images/loop00027.png b/res-xhdpi/images/loop00027.png
new file mode 100644
index 0000000..67e503a
--- /dev/null
+++ b/res-xhdpi/images/loop00027.png
Binary files differ
diff --git a/res-xhdpi/images/loop00028.png b/res-xhdpi/images/loop00028.png
new file mode 100644
index 0000000..7e76be8
--- /dev/null
+++ b/res-xhdpi/images/loop00028.png
Binary files differ
diff --git a/res-xhdpi/images/loop00029.png b/res-xhdpi/images/loop00029.png
new file mode 100644
index 0000000..4902f6b
--- /dev/null
+++ b/res-xhdpi/images/loop00029.png
Binary files differ
diff --git a/res-xhdpi/images/loop00030.png b/res-xhdpi/images/loop00030.png
new file mode 100644
index 0000000..387b893
--- /dev/null
+++ b/res-xhdpi/images/loop00030.png
Binary files differ
diff --git a/res-xhdpi/images/loop00031.png b/res-xhdpi/images/loop00031.png
new file mode 100644
index 0000000..ad11628
--- /dev/null
+++ b/res-xhdpi/images/loop00031.png
Binary files differ
diff --git a/res-xhdpi/images/loop00032.png b/res-xhdpi/images/loop00032.png
new file mode 100644
index 0000000..7d809e6
--- /dev/null
+++ b/res-xhdpi/images/loop00032.png
Binary files differ
diff --git a/res-xhdpi/images/loop00033.png b/res-xhdpi/images/loop00033.png
new file mode 100644
index 0000000..59fcdc1
--- /dev/null
+++ b/res-xhdpi/images/loop00033.png
Binary files differ
diff --git a/res-xhdpi/images/loop00034.png b/res-xhdpi/images/loop00034.png
new file mode 100644
index 0000000..cb4301c
--- /dev/null
+++ b/res-xhdpi/images/loop00034.png
Binary files differ
diff --git a/res-xhdpi/images/loop00035.png b/res-xhdpi/images/loop00035.png
new file mode 100644
index 0000000..6b16878
--- /dev/null
+++ b/res-xhdpi/images/loop00035.png
Binary files differ
diff --git a/res-xhdpi/images/loop00036.png b/res-xhdpi/images/loop00036.png
new file mode 100644
index 0000000..3aa7850
--- /dev/null
+++ b/res-xhdpi/images/loop00036.png
Binary files differ
diff --git a/res-xhdpi/images/loop00037.png b/res-xhdpi/images/loop00037.png
new file mode 100644
index 0000000..a60e851
--- /dev/null
+++ b/res-xhdpi/images/loop00037.png
Binary files differ
diff --git a/res-xhdpi/images/loop00038.png b/res-xhdpi/images/loop00038.png
new file mode 100644
index 0000000..50107f3
--- /dev/null
+++ b/res-xhdpi/images/loop00038.png
Binary files differ
diff --git a/res-xhdpi/images/loop00039.png b/res-xhdpi/images/loop00039.png
new file mode 100644
index 0000000..c85201e
--- /dev/null
+++ b/res-xhdpi/images/loop00039.png
Binary files differ
diff --git a/res-xhdpi/images/loop00040.png b/res-xhdpi/images/loop00040.png
new file mode 100644
index 0000000..6ae1612
--- /dev/null
+++ b/res-xhdpi/images/loop00040.png
Binary files differ
diff --git a/res-xhdpi/images/loop00041.png b/res-xhdpi/images/loop00041.png
new file mode 100644
index 0000000..7602b04
--- /dev/null
+++ b/res-xhdpi/images/loop00041.png
Binary files differ
diff --git a/res-xhdpi/images/loop00042.png b/res-xhdpi/images/loop00042.png
new file mode 100644
index 0000000..054da6d
--- /dev/null
+++ b/res-xhdpi/images/loop00042.png
Binary files differ
diff --git a/res-xhdpi/images/loop00043.png b/res-xhdpi/images/loop00043.png
new file mode 100644
index 0000000..d28be8b
--- /dev/null
+++ b/res-xhdpi/images/loop00043.png
Binary files differ
diff --git a/res-xhdpi/images/loop00044.png b/res-xhdpi/images/loop00044.png
new file mode 100644
index 0000000..8327126
--- /dev/null
+++ b/res-xhdpi/images/loop00044.png
Binary files differ
diff --git a/res-xhdpi/images/loop00045.png b/res-xhdpi/images/loop00045.png
new file mode 100644
index 0000000..d749e22
--- /dev/null
+++ b/res-xhdpi/images/loop00045.png
Binary files differ
diff --git a/res-xhdpi/images/loop00046.png b/res-xhdpi/images/loop00046.png
new file mode 100644
index 0000000..60025d1
--- /dev/null
+++ b/res-xhdpi/images/loop00046.png
Binary files differ
diff --git a/res-xhdpi/images/loop00047.png b/res-xhdpi/images/loop00047.png
new file mode 100644
index 0000000..b0be5c6
--- /dev/null
+++ b/res-xhdpi/images/loop00047.png
Binary files differ
diff --git a/res-xhdpi/images/loop00048.png b/res-xhdpi/images/loop00048.png
new file mode 100644
index 0000000..be926d9
--- /dev/null
+++ b/res-xhdpi/images/loop00048.png
Binary files differ
diff --git a/res-xhdpi/images/loop00049.png b/res-xhdpi/images/loop00049.png
new file mode 100644
index 0000000..4560854
--- /dev/null
+++ b/res-xhdpi/images/loop00049.png
Binary files differ
diff --git a/res-xhdpi/images/loop00050.png b/res-xhdpi/images/loop00050.png
new file mode 100644
index 0000000..967dd87
--- /dev/null
+++ b/res-xhdpi/images/loop00050.png
Binary files differ
diff --git a/res-xhdpi/images/loop00051.png b/res-xhdpi/images/loop00051.png
new file mode 100644
index 0000000..c169859
--- /dev/null
+++ b/res-xhdpi/images/loop00051.png
Binary files differ
diff --git a/res-xhdpi/images/loop00052.png b/res-xhdpi/images/loop00052.png
new file mode 100644
index 0000000..27c2383
--- /dev/null
+++ b/res-xhdpi/images/loop00052.png
Binary files differ
diff --git a/res-xhdpi/images/loop00053.png b/res-xhdpi/images/loop00053.png
new file mode 100644
index 0000000..cd2ca21
--- /dev/null
+++ b/res-xhdpi/images/loop00053.png
Binary files differ
diff --git a/res-xhdpi/images/loop00054.png b/res-xhdpi/images/loop00054.png
new file mode 100644
index 0000000..588586b
--- /dev/null
+++ b/res-xhdpi/images/loop00054.png
Binary files differ
diff --git a/res-xhdpi/images/loop00055.png b/res-xhdpi/images/loop00055.png
new file mode 100644
index 0000000..0984d01
--- /dev/null
+++ b/res-xhdpi/images/loop00055.png
Binary files differ
diff --git a/res-xhdpi/images/loop00056.png b/res-xhdpi/images/loop00056.png
new file mode 100644
index 0000000..bab2998
--- /dev/null
+++ b/res-xhdpi/images/loop00056.png
Binary files differ
diff --git a/res-xhdpi/images/loop00057.png b/res-xhdpi/images/loop00057.png
new file mode 100644
index 0000000..4acfce5
--- /dev/null
+++ b/res-xhdpi/images/loop00057.png
Binary files differ
diff --git a/res-xhdpi/images/loop00058.png b/res-xhdpi/images/loop00058.png
new file mode 100644
index 0000000..d49fea4
--- /dev/null
+++ b/res-xhdpi/images/loop00058.png
Binary files differ
diff --git a/res-xhdpi/images/loop00059.png b/res-xhdpi/images/loop00059.png
new file mode 100644
index 0000000..fdd75c6
--- /dev/null
+++ b/res-xhdpi/images/loop00059.png
Binary files differ
diff --git a/res-xhdpi/images/loop00060.png b/res-xhdpi/images/loop00060.png
new file mode 100644
index 0000000..06ac591
--- /dev/null
+++ b/res-xhdpi/images/loop00060.png
Binary files differ
diff --git a/res-xhdpi/images/loop00061.png b/res-xhdpi/images/loop00061.png
new file mode 100644
index 0000000..63be536
--- /dev/null
+++ b/res-xhdpi/images/loop00061.png
Binary files differ
diff --git a/res-xhdpi/images/loop00062.png b/res-xhdpi/images/loop00062.png
new file mode 100644
index 0000000..e25c906
--- /dev/null
+++ b/res-xhdpi/images/loop00062.png
Binary files differ
diff --git a/res-xhdpi/images/loop00063.png b/res-xhdpi/images/loop00063.png
new file mode 100644
index 0000000..1fcaefe
--- /dev/null
+++ b/res-xhdpi/images/loop00063.png
Binary files differ
diff --git a/res-xhdpi/images/loop00064.png b/res-xhdpi/images/loop00064.png
new file mode 100644
index 0000000..fe373d0
--- /dev/null
+++ b/res-xhdpi/images/loop00064.png
Binary files differ
diff --git a/res-xhdpi/images/loop00065.png b/res-xhdpi/images/loop00065.png
new file mode 100644
index 0000000..c5feed6
--- /dev/null
+++ b/res-xhdpi/images/loop00065.png
Binary files differ
diff --git a/res-xhdpi/images/loop00066.png b/res-xhdpi/images/loop00066.png
new file mode 100644
index 0000000..bc336e7
--- /dev/null
+++ b/res-xhdpi/images/loop00066.png
Binary files differ
diff --git a/res-xhdpi/images/loop00067.png b/res-xhdpi/images/loop00067.png
new file mode 100644
index 0000000..a4cdcae
--- /dev/null
+++ b/res-xhdpi/images/loop00067.png
Binary files differ
diff --git a/res-xhdpi/images/loop00068.png b/res-xhdpi/images/loop00068.png
new file mode 100644
index 0000000..65d41a2
--- /dev/null
+++ b/res-xhdpi/images/loop00068.png
Binary files differ
diff --git a/res-xhdpi/images/loop00069.png b/res-xhdpi/images/loop00069.png
new file mode 100644
index 0000000..5707b62
--- /dev/null
+++ b/res-xhdpi/images/loop00069.png
Binary files differ
diff --git a/res-xhdpi/images/loop00070.png b/res-xhdpi/images/loop00070.png
new file mode 100644
index 0000000..50ea159
--- /dev/null
+++ b/res-xhdpi/images/loop00070.png
Binary files differ
diff --git a/res-xhdpi/images/loop00071.png b/res-xhdpi/images/loop00071.png
new file mode 100644
index 0000000..244a910
--- /dev/null
+++ b/res-xhdpi/images/loop00071.png
Binary files differ
diff --git a/res-xhdpi/images/loop00072.png b/res-xhdpi/images/loop00072.png
new file mode 100644
index 0000000..e5ee2ab
--- /dev/null
+++ b/res-xhdpi/images/loop00072.png
Binary files differ
diff --git a/res-xhdpi/images/loop00073.png b/res-xhdpi/images/loop00073.png
new file mode 100644
index 0000000..fced739
--- /dev/null
+++ b/res-xhdpi/images/loop00073.png
Binary files differ
diff --git a/res-xhdpi/images/loop00074.png b/res-xhdpi/images/loop00074.png
new file mode 100644
index 0000000..1b739d3
--- /dev/null
+++ b/res-xhdpi/images/loop00074.png
Binary files differ
diff --git a/res-xhdpi/images/loop00075.png b/res-xhdpi/images/loop00075.png
new file mode 100644
index 0000000..989144f
--- /dev/null
+++ b/res-xhdpi/images/loop00075.png
Binary files differ
diff --git a/res-xhdpi/images/loop00076.png b/res-xhdpi/images/loop00076.png
new file mode 100644
index 0000000..458c2a9
--- /dev/null
+++ b/res-xhdpi/images/loop00076.png
Binary files differ
diff --git a/res-xhdpi/images/loop00077.png b/res-xhdpi/images/loop00077.png
new file mode 100644
index 0000000..9cecb1d
--- /dev/null
+++ b/res-xhdpi/images/loop00077.png
Binary files differ
diff --git a/res-xhdpi/images/loop00078.png b/res-xhdpi/images/loop00078.png
new file mode 100644
index 0000000..c2c8dee
--- /dev/null
+++ b/res-xhdpi/images/loop00078.png
Binary files differ
diff --git a/res-xhdpi/images/loop00079.png b/res-xhdpi/images/loop00079.png
new file mode 100644
index 0000000..4f4fdd1
--- /dev/null
+++ b/res-xhdpi/images/loop00079.png
Binary files differ
diff --git a/res-xhdpi/images/loop00080.png b/res-xhdpi/images/loop00080.png
new file mode 100644
index 0000000..b224378
--- /dev/null
+++ b/res-xhdpi/images/loop00080.png
Binary files differ
diff --git a/res-xhdpi/images/loop00081.png b/res-xhdpi/images/loop00081.png
new file mode 100644
index 0000000..57d9587
--- /dev/null
+++ b/res-xhdpi/images/loop00081.png
Binary files differ
diff --git a/res-xhdpi/images/loop00082.png b/res-xhdpi/images/loop00082.png
new file mode 100644
index 0000000..c00f82a
--- /dev/null
+++ b/res-xhdpi/images/loop00082.png
Binary files differ
diff --git a/res-xhdpi/images/loop00083.png b/res-xhdpi/images/loop00083.png
new file mode 100644
index 0000000..078311f
--- /dev/null
+++ b/res-xhdpi/images/loop00083.png
Binary files differ
diff --git a/res-xhdpi/images/loop00084.png b/res-xhdpi/images/loop00084.png
new file mode 100644
index 0000000..cac1708
--- /dev/null
+++ b/res-xhdpi/images/loop00084.png
Binary files differ
diff --git a/res-xhdpi/images/loop00085.png b/res-xhdpi/images/loop00085.png
new file mode 100644
index 0000000..2ea4b0a
--- /dev/null
+++ b/res-xhdpi/images/loop00085.png
Binary files differ
diff --git a/res-xhdpi/images/loop00086.png b/res-xhdpi/images/loop00086.png
new file mode 100644
index 0000000..9ba6ca6
--- /dev/null
+++ b/res-xhdpi/images/loop00086.png
Binary files differ
diff --git a/res-xhdpi/images/loop00087.png b/res-xhdpi/images/loop00087.png
new file mode 100644
index 0000000..75694a3
--- /dev/null
+++ b/res-xhdpi/images/loop00087.png
Binary files differ
diff --git a/res-xhdpi/images/loop00088.png b/res-xhdpi/images/loop00088.png
new file mode 100644
index 0000000..971e508
--- /dev/null
+++ b/res-xhdpi/images/loop00088.png
Binary files differ
diff --git a/res-xhdpi/images/loop00089.png b/res-xhdpi/images/loop00089.png
new file mode 100644
index 0000000..41b6ce6
--- /dev/null
+++ b/res-xhdpi/images/loop00089.png
Binary files differ
diff --git a/res-xhdpi/images/loop00090.png b/res-xhdpi/images/loop00090.png
new file mode 100644
index 0000000..f5bf7a7
--- /dev/null
+++ b/res-xhdpi/images/loop00090.png
Binary files differ
diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png
index eb34e94..9f74037 100644
--- a/res-xhdpi/images/no_command_text.png
+++ b/res-xhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xhdpi/images/progress_empty.png b/res-xhdpi/images/progress_empty.png
index 7258183..96c4bf6 100644
--- a/res-xhdpi/images/progress_empty.png
+++ b/res-xhdpi/images/progress_empty.png
Binary files differ
diff --git a/res-xhdpi/images/progress_fill.png b/res-xhdpi/images/progress_fill.png
index becf87b..1717be8 100644
--- a/res-xhdpi/images/progress_fill.png
+++ b/res-xhdpi/images/progress_fill.png
Binary files differ
diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png
index c87fd52..b8653eb 100644
--- a/res-xxhdpi/images/erasing_text.png
+++ b/res-xxhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png
index 486e951..d77ee50 100644
--- a/res-xxhdpi/images/error_text.png
+++ b/res-xxhdpi/images/error_text.png
Binary files differ
diff --git a/res-xxhdpi/images/icon_installing.png b/res-xxhdpi/images/icon_installing.png
deleted file mode 100644
index 0fcfbc2..0000000
--- a/res-xxhdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/installing_security_text.png b/res-xxhdpi/images/installing_security_text.png
new file mode 100644
index 0000000..ca0c191
--- /dev/null
+++ b/res-xxhdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png
index ef6e8f3..a76a174 100644
--- a/res-xxhdpi/images/installing_text.png
+++ b/res-xxhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00000.png b/res-xxhdpi/images/loop00000.png
new file mode 100644
index 0000000..c517262
--- /dev/null
+++ b/res-xxhdpi/images/loop00000.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00001.png b/res-xxhdpi/images/loop00001.png
new file mode 100644
index 0000000..1b1ce73
--- /dev/null
+++ b/res-xxhdpi/images/loop00001.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00002.png b/res-xxhdpi/images/loop00002.png
new file mode 100644
index 0000000..e984a24
--- /dev/null
+++ b/res-xxhdpi/images/loop00002.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00003.png b/res-xxhdpi/images/loop00003.png
new file mode 100644
index 0000000..b11dddc
--- /dev/null
+++ b/res-xxhdpi/images/loop00003.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00004.png b/res-xxhdpi/images/loop00004.png
new file mode 100644
index 0000000..10272b2
--- /dev/null
+++ b/res-xxhdpi/images/loop00004.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00005.png b/res-xxhdpi/images/loop00005.png
new file mode 100644
index 0000000..9558d7e
--- /dev/null
+++ b/res-xxhdpi/images/loop00005.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00006.png b/res-xxhdpi/images/loop00006.png
new file mode 100644
index 0000000..0e6c92d
--- /dev/null
+++ b/res-xxhdpi/images/loop00006.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00007.png b/res-xxhdpi/images/loop00007.png
new file mode 100644
index 0000000..0a353ad
--- /dev/null
+++ b/res-xxhdpi/images/loop00007.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00008.png b/res-xxhdpi/images/loop00008.png
new file mode 100644
index 0000000..2f0c162
--- /dev/null
+++ b/res-xxhdpi/images/loop00008.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00009.png b/res-xxhdpi/images/loop00009.png
new file mode 100644
index 0000000..960d683
--- /dev/null
+++ b/res-xxhdpi/images/loop00009.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00010.png b/res-xxhdpi/images/loop00010.png
new file mode 100644
index 0000000..b65c301
--- /dev/null
+++ b/res-xxhdpi/images/loop00010.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00011.png b/res-xxhdpi/images/loop00011.png
new file mode 100644
index 0000000..21444fa
--- /dev/null
+++ b/res-xxhdpi/images/loop00011.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00012.png b/res-xxhdpi/images/loop00012.png
new file mode 100644
index 0000000..587db09
--- /dev/null
+++ b/res-xxhdpi/images/loop00012.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00013.png b/res-xxhdpi/images/loop00013.png
new file mode 100644
index 0000000..57f2f66
--- /dev/null
+++ b/res-xxhdpi/images/loop00013.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00014.png b/res-xxhdpi/images/loop00014.png
new file mode 100644
index 0000000..d308a65
--- /dev/null
+++ b/res-xxhdpi/images/loop00014.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00015.png b/res-xxhdpi/images/loop00015.png
new file mode 100644
index 0000000..3585fac
--- /dev/null
+++ b/res-xxhdpi/images/loop00015.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00016.png b/res-xxhdpi/images/loop00016.png
new file mode 100644
index 0000000..fd5089c
--- /dev/null
+++ b/res-xxhdpi/images/loop00016.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00017.png b/res-xxhdpi/images/loop00017.png
new file mode 100644
index 0000000..2c8c6a4
--- /dev/null
+++ b/res-xxhdpi/images/loop00017.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00018.png b/res-xxhdpi/images/loop00018.png
new file mode 100644
index 0000000..23d7ca2
--- /dev/null
+++ b/res-xxhdpi/images/loop00018.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00019.png b/res-xxhdpi/images/loop00019.png
new file mode 100644
index 0000000..cdefe2c
--- /dev/null
+++ b/res-xxhdpi/images/loop00019.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00020.png b/res-xxhdpi/images/loop00020.png
new file mode 100644
index 0000000..ae78e4c
--- /dev/null
+++ b/res-xxhdpi/images/loop00020.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00021.png b/res-xxhdpi/images/loop00021.png
new file mode 100644
index 0000000..ad83cfe
--- /dev/null
+++ b/res-xxhdpi/images/loop00021.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00022.png b/res-xxhdpi/images/loop00022.png
new file mode 100644
index 0000000..850076a
--- /dev/null
+++ b/res-xxhdpi/images/loop00022.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00023.png b/res-xxhdpi/images/loop00023.png
new file mode 100644
index 0000000..cd30b39
--- /dev/null
+++ b/res-xxhdpi/images/loop00023.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00024.png b/res-xxhdpi/images/loop00024.png
new file mode 100644
index 0000000..e7ae4b2
--- /dev/null
+++ b/res-xxhdpi/images/loop00024.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00025.png b/res-xxhdpi/images/loop00025.png
new file mode 100644
index 0000000..4e24bd1
--- /dev/null
+++ b/res-xxhdpi/images/loop00025.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00026.png b/res-xxhdpi/images/loop00026.png
new file mode 100644
index 0000000..27713cc
--- /dev/null
+++ b/res-xxhdpi/images/loop00026.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00027.png b/res-xxhdpi/images/loop00027.png
new file mode 100644
index 0000000..34e4ade
--- /dev/null
+++ b/res-xxhdpi/images/loop00027.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00028.png b/res-xxhdpi/images/loop00028.png
new file mode 100644
index 0000000..0e6fdee
--- /dev/null
+++ b/res-xxhdpi/images/loop00028.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00029.png b/res-xxhdpi/images/loop00029.png
new file mode 100644
index 0000000..21c1c63
--- /dev/null
+++ b/res-xxhdpi/images/loop00029.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00030.png b/res-xxhdpi/images/loop00030.png
new file mode 100644
index 0000000..984c24f
--- /dev/null
+++ b/res-xxhdpi/images/loop00030.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00031.png b/res-xxhdpi/images/loop00031.png
new file mode 100644
index 0000000..25fe1de
--- /dev/null
+++ b/res-xxhdpi/images/loop00031.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00032.png b/res-xxhdpi/images/loop00032.png
new file mode 100644
index 0000000..c089cb8
--- /dev/null
+++ b/res-xxhdpi/images/loop00032.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00033.png b/res-xxhdpi/images/loop00033.png
new file mode 100644
index 0000000..82a2d9b
--- /dev/null
+++ b/res-xxhdpi/images/loop00033.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00034.png b/res-xxhdpi/images/loop00034.png
new file mode 100644
index 0000000..1aa76b9
--- /dev/null
+++ b/res-xxhdpi/images/loop00034.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00035.png b/res-xxhdpi/images/loop00035.png
new file mode 100644
index 0000000..4399143
--- /dev/null
+++ b/res-xxhdpi/images/loop00035.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00036.png b/res-xxhdpi/images/loop00036.png
new file mode 100644
index 0000000..975ae66
--- /dev/null
+++ b/res-xxhdpi/images/loop00036.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00037.png b/res-xxhdpi/images/loop00037.png
new file mode 100644
index 0000000..dcf9a90
--- /dev/null
+++ b/res-xxhdpi/images/loop00037.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00038.png b/res-xxhdpi/images/loop00038.png
new file mode 100644
index 0000000..f10b8b7
--- /dev/null
+++ b/res-xxhdpi/images/loop00038.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00039.png b/res-xxhdpi/images/loop00039.png
new file mode 100644
index 0000000..9c0d1e3
--- /dev/null
+++ b/res-xxhdpi/images/loop00039.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00040.png b/res-xxhdpi/images/loop00040.png
new file mode 100644
index 0000000..b6b4908
--- /dev/null
+++ b/res-xxhdpi/images/loop00040.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00041.png b/res-xxhdpi/images/loop00041.png
new file mode 100644
index 0000000..12a1a1e
--- /dev/null
+++ b/res-xxhdpi/images/loop00041.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00042.png b/res-xxhdpi/images/loop00042.png
new file mode 100644
index 0000000..f1fc35b
--- /dev/null
+++ b/res-xxhdpi/images/loop00042.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00043.png b/res-xxhdpi/images/loop00043.png
new file mode 100644
index 0000000..50ac99e
--- /dev/null
+++ b/res-xxhdpi/images/loop00043.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00044.png b/res-xxhdpi/images/loop00044.png
new file mode 100644
index 0000000..f115dcc
--- /dev/null
+++ b/res-xxhdpi/images/loop00044.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00045.png b/res-xxhdpi/images/loop00045.png
new file mode 100644
index 0000000..adf7a67
--- /dev/null
+++ b/res-xxhdpi/images/loop00045.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00046.png b/res-xxhdpi/images/loop00046.png
new file mode 100644
index 0000000..588eeb3
--- /dev/null
+++ b/res-xxhdpi/images/loop00046.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00047.png b/res-xxhdpi/images/loop00047.png
new file mode 100644
index 0000000..9dea770
--- /dev/null
+++ b/res-xxhdpi/images/loop00047.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00048.png b/res-xxhdpi/images/loop00048.png
new file mode 100644
index 0000000..d5eaeb1
--- /dev/null
+++ b/res-xxhdpi/images/loop00048.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00049.png b/res-xxhdpi/images/loop00049.png
new file mode 100644
index 0000000..fb83729
--- /dev/null
+++ b/res-xxhdpi/images/loop00049.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00050.png b/res-xxhdpi/images/loop00050.png
new file mode 100644
index 0000000..72441db
--- /dev/null
+++ b/res-xxhdpi/images/loop00050.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00051.png b/res-xxhdpi/images/loop00051.png
new file mode 100644
index 0000000..bf7170a
--- /dev/null
+++ b/res-xxhdpi/images/loop00051.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00052.png b/res-xxhdpi/images/loop00052.png
new file mode 100644
index 0000000..c512b56
--- /dev/null
+++ b/res-xxhdpi/images/loop00052.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00053.png b/res-xxhdpi/images/loop00053.png
new file mode 100644
index 0000000..6ac3ca6
--- /dev/null
+++ b/res-xxhdpi/images/loop00053.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00054.png b/res-xxhdpi/images/loop00054.png
new file mode 100644
index 0000000..ba194a6
--- /dev/null
+++ b/res-xxhdpi/images/loop00054.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00055.png b/res-xxhdpi/images/loop00055.png
new file mode 100644
index 0000000..9623f0d
--- /dev/null
+++ b/res-xxhdpi/images/loop00055.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00056.png b/res-xxhdpi/images/loop00056.png
new file mode 100644
index 0000000..e785e69
--- /dev/null
+++ b/res-xxhdpi/images/loop00056.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00057.png b/res-xxhdpi/images/loop00057.png
new file mode 100644
index 0000000..9a5747a
--- /dev/null
+++ b/res-xxhdpi/images/loop00057.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00058.png b/res-xxhdpi/images/loop00058.png
new file mode 100644
index 0000000..9a097cf
--- /dev/null
+++ b/res-xxhdpi/images/loop00058.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00059.png b/res-xxhdpi/images/loop00059.png
new file mode 100644
index 0000000..fee2db1
--- /dev/null
+++ b/res-xxhdpi/images/loop00059.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00060.png b/res-xxhdpi/images/loop00060.png
new file mode 100644
index 0000000..0e00e70
--- /dev/null
+++ b/res-xxhdpi/images/loop00060.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00061.png b/res-xxhdpi/images/loop00061.png
new file mode 100644
index 0000000..0ecce17
--- /dev/null
+++ b/res-xxhdpi/images/loop00061.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00062.png b/res-xxhdpi/images/loop00062.png
new file mode 100644
index 0000000..0a296d1
--- /dev/null
+++ b/res-xxhdpi/images/loop00062.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00063.png b/res-xxhdpi/images/loop00063.png
new file mode 100644
index 0000000..56c3b8b
--- /dev/null
+++ b/res-xxhdpi/images/loop00063.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00064.png b/res-xxhdpi/images/loop00064.png
new file mode 100644
index 0000000..e6d639a
--- /dev/null
+++ b/res-xxhdpi/images/loop00064.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00065.png b/res-xxhdpi/images/loop00065.png
new file mode 100644
index 0000000..02e382b
--- /dev/null
+++ b/res-xxhdpi/images/loop00065.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00066.png b/res-xxhdpi/images/loop00066.png
new file mode 100644
index 0000000..fe89ed0
--- /dev/null
+++ b/res-xxhdpi/images/loop00066.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00067.png b/res-xxhdpi/images/loop00067.png
new file mode 100644
index 0000000..a8f6ce5
--- /dev/null
+++ b/res-xxhdpi/images/loop00067.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00068.png b/res-xxhdpi/images/loop00068.png
new file mode 100644
index 0000000..f9b7fb1
--- /dev/null
+++ b/res-xxhdpi/images/loop00068.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00069.png b/res-xxhdpi/images/loop00069.png
new file mode 100644
index 0000000..d0dc507
--- /dev/null
+++ b/res-xxhdpi/images/loop00069.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00070.png b/res-xxhdpi/images/loop00070.png
new file mode 100644
index 0000000..63f9e4d
--- /dev/null
+++ b/res-xxhdpi/images/loop00070.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00071.png b/res-xxhdpi/images/loop00071.png
new file mode 100644
index 0000000..5ba3972
--- /dev/null
+++ b/res-xxhdpi/images/loop00071.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00072.png b/res-xxhdpi/images/loop00072.png
new file mode 100644
index 0000000..de834e3
--- /dev/null
+++ b/res-xxhdpi/images/loop00072.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00073.png b/res-xxhdpi/images/loop00073.png
new file mode 100644
index 0000000..4be2aed
--- /dev/null
+++ b/res-xxhdpi/images/loop00073.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00074.png b/res-xxhdpi/images/loop00074.png
new file mode 100644
index 0000000..235e9a2
--- /dev/null
+++ b/res-xxhdpi/images/loop00074.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00075.png b/res-xxhdpi/images/loop00075.png
new file mode 100644
index 0000000..f6d806d
--- /dev/null
+++ b/res-xxhdpi/images/loop00075.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00076.png b/res-xxhdpi/images/loop00076.png
new file mode 100644
index 0000000..1e916d7
--- /dev/null
+++ b/res-xxhdpi/images/loop00076.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00077.png b/res-xxhdpi/images/loop00077.png
new file mode 100644
index 0000000..0dbac74
--- /dev/null
+++ b/res-xxhdpi/images/loop00077.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00078.png b/res-xxhdpi/images/loop00078.png
new file mode 100644
index 0000000..504d34a
--- /dev/null
+++ b/res-xxhdpi/images/loop00078.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00079.png b/res-xxhdpi/images/loop00079.png
new file mode 100644
index 0000000..51f4e8d
--- /dev/null
+++ b/res-xxhdpi/images/loop00079.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00080.png b/res-xxhdpi/images/loop00080.png
new file mode 100644
index 0000000..6ef03b8
--- /dev/null
+++ b/res-xxhdpi/images/loop00080.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00081.png b/res-xxhdpi/images/loop00081.png
new file mode 100644
index 0000000..e2ebc39
--- /dev/null
+++ b/res-xxhdpi/images/loop00081.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00082.png b/res-xxhdpi/images/loop00082.png
new file mode 100644
index 0000000..9de83a7
--- /dev/null
+++ b/res-xxhdpi/images/loop00082.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00083.png b/res-xxhdpi/images/loop00083.png
new file mode 100644
index 0000000..c5c0099
--- /dev/null
+++ b/res-xxhdpi/images/loop00083.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00084.png b/res-xxhdpi/images/loop00084.png
new file mode 100644
index 0000000..84c794f
--- /dev/null
+++ b/res-xxhdpi/images/loop00084.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00085.png b/res-xxhdpi/images/loop00085.png
new file mode 100644
index 0000000..29a40c6
--- /dev/null
+++ b/res-xxhdpi/images/loop00085.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00086.png b/res-xxhdpi/images/loop00086.png
new file mode 100644
index 0000000..89a4717
--- /dev/null
+++ b/res-xxhdpi/images/loop00086.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00087.png b/res-xxhdpi/images/loop00087.png
new file mode 100644
index 0000000..ef8d4d5
--- /dev/null
+++ b/res-xxhdpi/images/loop00087.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00088.png b/res-xxhdpi/images/loop00088.png
new file mode 100644
index 0000000..5fc6c62
--- /dev/null
+++ b/res-xxhdpi/images/loop00088.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00089.png b/res-xxhdpi/images/loop00089.png
new file mode 100644
index 0000000..d6d99f4
--- /dev/null
+++ b/res-xxhdpi/images/loop00089.png
Binary files differ
diff --git a/res-xxhdpi/images/loop00090.png b/res-xxhdpi/images/loop00090.png
new file mode 100644
index 0000000..c517262
--- /dev/null
+++ b/res-xxhdpi/images/loop00090.png
Binary files differ
diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png
index cc98bb1..5e363e3 100644
--- a/res-xxhdpi/images/no_command_text.png
+++ b/res-xxhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xxhdpi/images/progress_empty.png b/res-xxhdpi/images/progress_empty.png
index 7258183..96c4bf6 100644
--- a/res-xxhdpi/images/progress_empty.png
+++ b/res-xxhdpi/images/progress_empty.png
Binary files differ
diff --git a/res-xxhdpi/images/progress_fill.png b/res-xxhdpi/images/progress_fill.png
index becf87b..1717be8 100644
--- a/res-xxhdpi/images/progress_fill.png
+++ b/res-xxhdpi/images/progress_fill.png
Binary files differ
diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png
index 612e7a3..f4a4661 100644
--- a/res-xxxhdpi/images/erasing_text.png
+++ b/res-xxxhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png
index 50d2fad..317a771 100644
--- a/res-xxxhdpi/images/error_text.png
+++ b/res-xxxhdpi/images/error_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/icon_installing.png b/res-xxxhdpi/images/icon_installing.png
deleted file mode 100644
index 0fcfbc2..0000000
--- a/res-xxxhdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/installing_security_text.png b/res-xxxhdpi/images/installing_security_text.png
new file mode 100644
index 0000000..c99c907
--- /dev/null
+++ b/res-xxxhdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png
index 9bd093b..91d8330 100644
--- a/res-xxxhdpi/images/installing_text.png
+++ b/res-xxxhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00000.png b/res-xxxhdpi/images/loop00000.png
new file mode 100644
index 0000000..1bc9db5
--- /dev/null
+++ b/res-xxxhdpi/images/loop00000.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00001.png b/res-xxxhdpi/images/loop00001.png
new file mode 100644
index 0000000..f835b85
--- /dev/null
+++ b/res-xxxhdpi/images/loop00001.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00002.png b/res-xxxhdpi/images/loop00002.png
new file mode 100644
index 0000000..e3bff32
--- /dev/null
+++ b/res-xxxhdpi/images/loop00002.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00003.png b/res-xxxhdpi/images/loop00003.png
new file mode 100644
index 0000000..d864c15
--- /dev/null
+++ b/res-xxxhdpi/images/loop00003.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00004.png b/res-xxxhdpi/images/loop00004.png
new file mode 100644
index 0000000..5d861c9
--- /dev/null
+++ b/res-xxxhdpi/images/loop00004.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00005.png b/res-xxxhdpi/images/loop00005.png
new file mode 100644
index 0000000..e9e860c
--- /dev/null
+++ b/res-xxxhdpi/images/loop00005.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00006.png b/res-xxxhdpi/images/loop00006.png
new file mode 100644
index 0000000..d7c516e
--- /dev/null
+++ b/res-xxxhdpi/images/loop00006.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00007.png b/res-xxxhdpi/images/loop00007.png
new file mode 100644
index 0000000..fa6d397
--- /dev/null
+++ b/res-xxxhdpi/images/loop00007.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00008.png b/res-xxxhdpi/images/loop00008.png
new file mode 100644
index 0000000..888d3a2
--- /dev/null
+++ b/res-xxxhdpi/images/loop00008.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00009.png b/res-xxxhdpi/images/loop00009.png
new file mode 100644
index 0000000..9e6ead2
--- /dev/null
+++ b/res-xxxhdpi/images/loop00009.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00010.png b/res-xxxhdpi/images/loop00010.png
new file mode 100644
index 0000000..30e13e0
--- /dev/null
+++ b/res-xxxhdpi/images/loop00010.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00011.png b/res-xxxhdpi/images/loop00011.png
new file mode 100644
index 0000000..d8abc2b
--- /dev/null
+++ b/res-xxxhdpi/images/loop00011.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00012.png b/res-xxxhdpi/images/loop00012.png
new file mode 100644
index 0000000..2d88cfb
--- /dev/null
+++ b/res-xxxhdpi/images/loop00012.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00013.png b/res-xxxhdpi/images/loop00013.png
new file mode 100644
index 0000000..0250f74
--- /dev/null
+++ b/res-xxxhdpi/images/loop00013.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00014.png b/res-xxxhdpi/images/loop00014.png
new file mode 100644
index 0000000..c3d9239
--- /dev/null
+++ b/res-xxxhdpi/images/loop00014.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00015.png b/res-xxxhdpi/images/loop00015.png
new file mode 100644
index 0000000..644c9c6
--- /dev/null
+++ b/res-xxxhdpi/images/loop00015.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00016.png b/res-xxxhdpi/images/loop00016.png
new file mode 100644
index 0000000..eff6e5b
--- /dev/null
+++ b/res-xxxhdpi/images/loop00016.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00017.png b/res-xxxhdpi/images/loop00017.png
new file mode 100644
index 0000000..b472a86
--- /dev/null
+++ b/res-xxxhdpi/images/loop00017.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00018.png b/res-xxxhdpi/images/loop00018.png
new file mode 100644
index 0000000..b17b6ce
--- /dev/null
+++ b/res-xxxhdpi/images/loop00018.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00019.png b/res-xxxhdpi/images/loop00019.png
new file mode 100644
index 0000000..d89b4da
--- /dev/null
+++ b/res-xxxhdpi/images/loop00019.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00020.png b/res-xxxhdpi/images/loop00020.png
new file mode 100644
index 0000000..7e757b8
--- /dev/null
+++ b/res-xxxhdpi/images/loop00020.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00021.png b/res-xxxhdpi/images/loop00021.png
new file mode 100644
index 0000000..1a8ce3e
--- /dev/null
+++ b/res-xxxhdpi/images/loop00021.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00022.png b/res-xxxhdpi/images/loop00022.png
new file mode 100644
index 0000000..e9ab39a
--- /dev/null
+++ b/res-xxxhdpi/images/loop00022.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00023.png b/res-xxxhdpi/images/loop00023.png
new file mode 100644
index 0000000..e1a7bf7
--- /dev/null
+++ b/res-xxxhdpi/images/loop00023.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00024.png b/res-xxxhdpi/images/loop00024.png
new file mode 100644
index 0000000..f77f70c
--- /dev/null
+++ b/res-xxxhdpi/images/loop00024.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00025.png b/res-xxxhdpi/images/loop00025.png
new file mode 100644
index 0000000..8348cdf
--- /dev/null
+++ b/res-xxxhdpi/images/loop00025.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00026.png b/res-xxxhdpi/images/loop00026.png
new file mode 100644
index 0000000..55fecc8
--- /dev/null
+++ b/res-xxxhdpi/images/loop00026.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00027.png b/res-xxxhdpi/images/loop00027.png
new file mode 100644
index 0000000..f4edf06
--- /dev/null
+++ b/res-xxxhdpi/images/loop00027.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00028.png b/res-xxxhdpi/images/loop00028.png
new file mode 100644
index 0000000..6dbe904
--- /dev/null
+++ b/res-xxxhdpi/images/loop00028.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00029.png b/res-xxxhdpi/images/loop00029.png
new file mode 100644
index 0000000..764f27a
--- /dev/null
+++ b/res-xxxhdpi/images/loop00029.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00030.png b/res-xxxhdpi/images/loop00030.png
new file mode 100644
index 0000000..2d21569
--- /dev/null
+++ b/res-xxxhdpi/images/loop00030.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00031.png b/res-xxxhdpi/images/loop00031.png
new file mode 100644
index 0000000..e02db9c
--- /dev/null
+++ b/res-xxxhdpi/images/loop00031.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00032.png b/res-xxxhdpi/images/loop00032.png
new file mode 100644
index 0000000..03f0456
--- /dev/null
+++ b/res-xxxhdpi/images/loop00032.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00033.png b/res-xxxhdpi/images/loop00033.png
new file mode 100644
index 0000000..5bdbbdb
--- /dev/null
+++ b/res-xxxhdpi/images/loop00033.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00034.png b/res-xxxhdpi/images/loop00034.png
new file mode 100644
index 0000000..c8164e2
--- /dev/null
+++ b/res-xxxhdpi/images/loop00034.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00035.png b/res-xxxhdpi/images/loop00035.png
new file mode 100644
index 0000000..ed5721d
--- /dev/null
+++ b/res-xxxhdpi/images/loop00035.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00036.png b/res-xxxhdpi/images/loop00036.png
new file mode 100644
index 0000000..08dffd2
--- /dev/null
+++ b/res-xxxhdpi/images/loop00036.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00037.png b/res-xxxhdpi/images/loop00037.png
new file mode 100644
index 0000000..583b665
--- /dev/null
+++ b/res-xxxhdpi/images/loop00037.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00038.png b/res-xxxhdpi/images/loop00038.png
new file mode 100644
index 0000000..cc2933d
--- /dev/null
+++ b/res-xxxhdpi/images/loop00038.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00039.png b/res-xxxhdpi/images/loop00039.png
new file mode 100644
index 0000000..1f0496a
--- /dev/null
+++ b/res-xxxhdpi/images/loop00039.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00040.png b/res-xxxhdpi/images/loop00040.png
new file mode 100644
index 0000000..05bf335
--- /dev/null
+++ b/res-xxxhdpi/images/loop00040.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00041.png b/res-xxxhdpi/images/loop00041.png
new file mode 100644
index 0000000..a868c7b
--- /dev/null
+++ b/res-xxxhdpi/images/loop00041.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00042.png b/res-xxxhdpi/images/loop00042.png
new file mode 100644
index 0000000..7c7220f
--- /dev/null
+++ b/res-xxxhdpi/images/loop00042.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00043.png b/res-xxxhdpi/images/loop00043.png
new file mode 100644
index 0000000..30336a7
--- /dev/null
+++ b/res-xxxhdpi/images/loop00043.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00044.png b/res-xxxhdpi/images/loop00044.png
new file mode 100644
index 0000000..80d3735
--- /dev/null
+++ b/res-xxxhdpi/images/loop00044.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00045.png b/res-xxxhdpi/images/loop00045.png
new file mode 100644
index 0000000..71a52c2
--- /dev/null
+++ b/res-xxxhdpi/images/loop00045.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00046.png b/res-xxxhdpi/images/loop00046.png
new file mode 100644
index 0000000..b3b3702
--- /dev/null
+++ b/res-xxxhdpi/images/loop00046.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00047.png b/res-xxxhdpi/images/loop00047.png
new file mode 100644
index 0000000..6ce2b37
--- /dev/null
+++ b/res-xxxhdpi/images/loop00047.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00048.png b/res-xxxhdpi/images/loop00048.png
new file mode 100644
index 0000000..0b428cd
--- /dev/null
+++ b/res-xxxhdpi/images/loop00048.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00049.png b/res-xxxhdpi/images/loop00049.png
new file mode 100644
index 0000000..53c1a47
--- /dev/null
+++ b/res-xxxhdpi/images/loop00049.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00050.png b/res-xxxhdpi/images/loop00050.png
new file mode 100644
index 0000000..0e17bbd
--- /dev/null
+++ b/res-xxxhdpi/images/loop00050.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00051.png b/res-xxxhdpi/images/loop00051.png
new file mode 100644
index 0000000..78a99dc
--- /dev/null
+++ b/res-xxxhdpi/images/loop00051.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00052.png b/res-xxxhdpi/images/loop00052.png
new file mode 100644
index 0000000..efd0df5
--- /dev/null
+++ b/res-xxxhdpi/images/loop00052.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00053.png b/res-xxxhdpi/images/loop00053.png
new file mode 100644
index 0000000..0c417ee
--- /dev/null
+++ b/res-xxxhdpi/images/loop00053.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00054.png b/res-xxxhdpi/images/loop00054.png
new file mode 100644
index 0000000..072077e
--- /dev/null
+++ b/res-xxxhdpi/images/loop00054.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00055.png b/res-xxxhdpi/images/loop00055.png
new file mode 100644
index 0000000..693083a
--- /dev/null
+++ b/res-xxxhdpi/images/loop00055.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00056.png b/res-xxxhdpi/images/loop00056.png
new file mode 100644
index 0000000..07cbd75
--- /dev/null
+++ b/res-xxxhdpi/images/loop00056.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00057.png b/res-xxxhdpi/images/loop00057.png
new file mode 100644
index 0000000..cc98ab2
--- /dev/null
+++ b/res-xxxhdpi/images/loop00057.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00058.png b/res-xxxhdpi/images/loop00058.png
new file mode 100644
index 0000000..f55d218
--- /dev/null
+++ b/res-xxxhdpi/images/loop00058.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00059.png b/res-xxxhdpi/images/loop00059.png
new file mode 100644
index 0000000..4bfed35
--- /dev/null
+++ b/res-xxxhdpi/images/loop00059.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00060.png b/res-xxxhdpi/images/loop00060.png
new file mode 100644
index 0000000..59f158b
--- /dev/null
+++ b/res-xxxhdpi/images/loop00060.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00061.png b/res-xxxhdpi/images/loop00061.png
new file mode 100644
index 0000000..fd0dc55
--- /dev/null
+++ b/res-xxxhdpi/images/loop00061.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00062.png b/res-xxxhdpi/images/loop00062.png
new file mode 100644
index 0000000..2c316ce
--- /dev/null
+++ b/res-xxxhdpi/images/loop00062.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00063.png b/res-xxxhdpi/images/loop00063.png
new file mode 100644
index 0000000..5b83c81
--- /dev/null
+++ b/res-xxxhdpi/images/loop00063.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00064.png b/res-xxxhdpi/images/loop00064.png
new file mode 100644
index 0000000..ced0a9a
--- /dev/null
+++ b/res-xxxhdpi/images/loop00064.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00065.png b/res-xxxhdpi/images/loop00065.png
new file mode 100644
index 0000000..6e699dd
--- /dev/null
+++ b/res-xxxhdpi/images/loop00065.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00066.png b/res-xxxhdpi/images/loop00066.png
new file mode 100644
index 0000000..8853cff
--- /dev/null
+++ b/res-xxxhdpi/images/loop00066.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00067.png b/res-xxxhdpi/images/loop00067.png
new file mode 100644
index 0000000..24d11d1
--- /dev/null
+++ b/res-xxxhdpi/images/loop00067.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00068.png b/res-xxxhdpi/images/loop00068.png
new file mode 100644
index 0000000..d54fff0
--- /dev/null
+++ b/res-xxxhdpi/images/loop00068.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00069.png b/res-xxxhdpi/images/loop00069.png
new file mode 100644
index 0000000..67f8d78
--- /dev/null
+++ b/res-xxxhdpi/images/loop00069.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00070.png b/res-xxxhdpi/images/loop00070.png
new file mode 100644
index 0000000..d56fb78
--- /dev/null
+++ b/res-xxxhdpi/images/loop00070.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00071.png b/res-xxxhdpi/images/loop00071.png
new file mode 100644
index 0000000..e787b8a
--- /dev/null
+++ b/res-xxxhdpi/images/loop00071.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00072.png b/res-xxxhdpi/images/loop00072.png
new file mode 100644
index 0000000..81f2e4c
--- /dev/null
+++ b/res-xxxhdpi/images/loop00072.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00073.png b/res-xxxhdpi/images/loop00073.png
new file mode 100644
index 0000000..ad46ed1
--- /dev/null
+++ b/res-xxxhdpi/images/loop00073.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00074.png b/res-xxxhdpi/images/loop00074.png
new file mode 100644
index 0000000..d835a2b
--- /dev/null
+++ b/res-xxxhdpi/images/loop00074.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00075.png b/res-xxxhdpi/images/loop00075.png
new file mode 100644
index 0000000..aa35a84
--- /dev/null
+++ b/res-xxxhdpi/images/loop00075.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00076.png b/res-xxxhdpi/images/loop00076.png
new file mode 100644
index 0000000..6ea547e
--- /dev/null
+++ b/res-xxxhdpi/images/loop00076.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00077.png b/res-xxxhdpi/images/loop00077.png
new file mode 100644
index 0000000..c809383
--- /dev/null
+++ b/res-xxxhdpi/images/loop00077.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00078.png b/res-xxxhdpi/images/loop00078.png
new file mode 100644
index 0000000..827a75c
--- /dev/null
+++ b/res-xxxhdpi/images/loop00078.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00079.png b/res-xxxhdpi/images/loop00079.png
new file mode 100644
index 0000000..18dc1cf
--- /dev/null
+++ b/res-xxxhdpi/images/loop00079.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00080.png b/res-xxxhdpi/images/loop00080.png
new file mode 100644
index 0000000..df06b56
--- /dev/null
+++ b/res-xxxhdpi/images/loop00080.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00081.png b/res-xxxhdpi/images/loop00081.png
new file mode 100644
index 0000000..a5ba603
--- /dev/null
+++ b/res-xxxhdpi/images/loop00081.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00082.png b/res-xxxhdpi/images/loop00082.png
new file mode 100644
index 0000000..e3298c5
--- /dev/null
+++ b/res-xxxhdpi/images/loop00082.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00083.png b/res-xxxhdpi/images/loop00083.png
new file mode 100644
index 0000000..c395662
--- /dev/null
+++ b/res-xxxhdpi/images/loop00083.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00084.png b/res-xxxhdpi/images/loop00084.png
new file mode 100644
index 0000000..f80af8e
--- /dev/null
+++ b/res-xxxhdpi/images/loop00084.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00085.png b/res-xxxhdpi/images/loop00085.png
new file mode 100644
index 0000000..c896758
--- /dev/null
+++ b/res-xxxhdpi/images/loop00085.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00086.png b/res-xxxhdpi/images/loop00086.png
new file mode 100644
index 0000000..9771692
--- /dev/null
+++ b/res-xxxhdpi/images/loop00086.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00087.png b/res-xxxhdpi/images/loop00087.png
new file mode 100644
index 0000000..e805dfa
--- /dev/null
+++ b/res-xxxhdpi/images/loop00087.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00088.png b/res-xxxhdpi/images/loop00088.png
new file mode 100644
index 0000000..aa1a88e
--- /dev/null
+++ b/res-xxxhdpi/images/loop00088.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00089.png b/res-xxxhdpi/images/loop00089.png
new file mode 100644
index 0000000..5bf7781
--- /dev/null
+++ b/res-xxxhdpi/images/loop00089.png
Binary files differ
diff --git a/res-xxxhdpi/images/loop00090.png b/res-xxxhdpi/images/loop00090.png
new file mode 100644
index 0000000..1bc9db5
--- /dev/null
+++ b/res-xxxhdpi/images/loop00090.png
Binary files differ
diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png
index 6354e6a..b6eb964 100644
--- a/res-xxxhdpi/images/no_command_text.png
+++ b/res-xxxhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/progress_empty.png b/res-xxxhdpi/images/progress_empty.png
index 7258183..96c4bf6 100644
--- a/res-xxxhdpi/images/progress_empty.png
+++ b/res-xxxhdpi/images/progress_empty.png
Binary files differ
diff --git a/res-xxxhdpi/images/progress_fill.png b/res-xxxhdpi/images/progress_fill.png
index becf87b..1717be8 100644
--- a/res-xxxhdpi/images/progress_fill.png
+++ b/res-xxxhdpi/images/progress_fill.png
Binary files differ
diff --git a/roots.cpp b/roots.cpp
index b06b9c6..2fec9ae 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -157,7 +157,7 @@
     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 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 @@
     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");
diff --git a/roots.h b/roots.h
index 6e3b243..a14b7d9 100644
--- a/roots.h
+++ b/roots.h
@@ -41,6 +41,12 @@
 // 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 1d33269..3697554 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 @@
 
 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 @@
     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;
+}
+
+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();
+    }
+}
+
+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
+
+// 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 ScreenRecoveryUI::GetAnimationBaseline() {
+    return GetTextBaseline() - PixelsFromDp(68) - gr_get_height(loopFrames[0]);
+}
+
+int ScreenRecoveryUI::GetTextBaseline() {
+    return GetProgressBaseline() - PixelsFromDp(32) - gr_get_height(installing_text);
+}
+
+int ScreenRecoveryUI::GetProgressBaseline() {
+    return gr_fb_height() - PixelsFromDp(is_large_ ? 340 : 194) - gr_get_height(progressBarFill);
 }
 
 // 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) {
+void ScreenRecoveryUI::draw_background_locked() {
     pagesIdentical = false;
     gr_color(0, 0, 0, 255);
     gr_clear();
 
-    if (icon) {
-        GRSurface* surface = backgroundIcon[icon];
-        if (icon == INSTALLING_UPDATE || icon == ERASING) {
-            surface = installation[installingFrame];
-        }
-        GRSurface* text_surface = backgroundText[icon];
-
-        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);
-
-        int sh = (max_stage >= 0) ? stageHeight : 0;
-
-        iconX = (gr_fb_width() - iconWidth) / 2;
-        iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2;
-
-        int textX = (gr_fb_width() - textWidth) / 2;
-        int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40;
-
-        gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
-        if (stageHeight > 0) {
-            int sw = gr_get_width(stageMarkerEmpty);
+    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 @@
             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 @@
     *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 @@
 // 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 @@
             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 @@
                 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 @@
                 } else {
                     gr_text(4, y, menu_[i], false);
                 }
-                y += char_height + 4;
+                y += char_height_ + 4;
             }
             DrawHorizontalRule(&y);
         }
@@ -286,9 +319,9 @@
         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 @@
         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 @@
         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 @@
             if (p > 1.0) p = 1.0;
             if (p > progress) {
                 progress = p;
-                redraw = 1;
+                redraw = true;
             }
         }
 
@@ -363,22 +405,14 @@
 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 @@
     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 @@
     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 @@
             }
         }
 
-        // 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 @@
             rtl_locale = true;
         }
         free(lang);
-    } else {
-        new_locale = nullptr;
     }
 }
 
diff --git a/screen_ui.h b/screen_ui.h
index 08a5f44..4319b76 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -34,6 +34,7 @@
 
     // overall recovery state ("background image")
     void SetBackground(Icon icon);
+    void SetSystemUpdateText(bool security_update);
 
     // progress indicator
     void SetProgressType(ProgressType type);
@@ -67,16 +68,28 @@
     };
     void SetColor(UIElement e);
 
-  private:
+  protected:
     Icon currentIcon;
-    int installingFrame;
-    const char* locale;
-    bool rtl_locale;
 
-    pthread_mutex_t updateMutex;
-    GRSurface* backgroundIcon[5];
-    GRSurface* backgroundText[5];
-    GRSurface** installation;
+    const char* 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;
+
     GRSurface* progressBarEmpty;
     GRSurface* progressBarFill;
     GRSurface* stageMarkerEmpty;
@@ -109,21 +122,29 @@
 
     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 @@
     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 7b004b2..279a6cb 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -21,9 +21,13 @@
 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 @@
 # 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 @@
     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 0000000..3490f68
--- /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 0000000..b44ddd1
--- /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 b5d7032..d7166df 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 @@
     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
new file mode 100644
index 0000000..cdeb8fd
--- /dev/null
+++ b/tests/testdata/new.file
Binary files differ
diff --git a/tests/testdata/old.file b/tests/testdata/old.file
new file mode 100644
index 0000000..166c873
--- /dev/null
+++ b/tests/testdata/old.file
Binary files differ
diff --git a/tests/testdata/patch.bsdiff b/tests/testdata/patch.bsdiff
new file mode 100644
index 0000000..b78d385
--- /dev/null
+++ b/tests/testdata/patch.bsdiff
Binary files differ
diff --git a/tests/unit/locale_test.cpp b/tests/unit/locale_test.cpp
new file mode 100644
index 0000000..0e515f8
--- /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 0900b11..05a16e1 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 f6193ab..971e038 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 3f2bebe..817a3ad 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 @@
         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]));
diff --git a/ui.h b/ui.h
index ca72911..82d95a3 100644
--- a/ui.h
+++ b/ui.h
@@ -39,6 +39,7 @@
     // 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 6422cb2..09cfdfc 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_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 0000000..3bb106a
--- /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 0000000..e0ca3f4
--- /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 a1de6a1..5697712 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 @@
     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 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 @@
         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 @@
         // 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 @@
     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 @@
         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 @@
     // 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 @@
     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;
+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;
     }
-    bootloader_message boot = {};
-    if (write_bootloader_message(&boot) != 0) {
-        android::base::WriteStringToFd("-1\n", status_fd);
-        return 1;
-    }
-    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;
     }
-    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));
+    }
+    return success ? 0 : 1;
 }
diff --git a/uncrypt/uncrypt.rc b/uncrypt/uncrypt.rc
index b07c1da..52f564e 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 12a549d..7aff7ff 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 @@
     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 @@
     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 @@
     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 @@
     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 @@
                 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 @@
 
     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 @@
     bool isunresumable;
     int version;
     size_t written;
+    size_t stashed;
     NewThreadInfo nti;
     pthread_t thread;
     std::vector<uint8_t> buffer;
@@ -645,6 +676,7 @@
     }
 
     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 @@
     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 @@
     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 @@
     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 @@
     }
 
     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 @@
                 return -1;
             }
 
+            params.stashed += src_blocks;
             // Can be deleted when the write has completed
             if (!stash_exists) {
                 params.freestash = srchash;
@@ -1087,7 +1126,13 @@
 
     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 @@
         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,7 +1254,12 @@
             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;
             }
 
@@ -1322,6 +1377,10 @@
     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 +1398,21 @@
     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 +1470,8 @@
     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 +1486,7 @@
     // 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 +1497,8 @@
     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 +1508,8 @@
         // 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 +1563,7 @@
 
         if (params.canwrite) {
             if (ota_fsync(params.fd) == -1) {
+                failure_type = kFsyncFailure;
                 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
                 goto pbiudone;
             }
@@ -1511,8 +1576,17 @@
         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 +1598,7 @@
 
 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 +1609,10 @@
         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 +1718,19 @@
             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 +1743,15 @@
     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 +1778,14 @@
     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 +1793,8 @@
     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 +1829,11 @@
     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 +1844,20 @@
     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 +1874,8 @@
             }
 
             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 11d5215..cf3e938 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"
@@ -110,7 +111,7 @@
 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;
@@ -133,20 +134,21 @@
     }
 
     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;
     }
 
@@ -190,14 +192,14 @@
 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;
     }
 
@@ -220,14 +222,14 @@
 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;
     }
 
@@ -277,7 +279,7 @@
 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;
@@ -290,21 +292,22 @@
     }
 
     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;
     }
 
@@ -350,7 +353,7 @@
 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;
@@ -360,21 +363,21 @@
         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;
@@ -417,7 +420,7 @@
 
 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;
@@ -438,7 +441,7 @@
 
 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) {
@@ -457,7 +460,7 @@
 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;
@@ -485,7 +488,7 @@
 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;
@@ -593,7 +596,7 @@
 //    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]);
@@ -629,7 +632,7 @@
     }
     free(srcs);
     if (bad) {
-        return ErrorAbort(state, "%s: some symlinks failed", name);
+        return ErrorAbort(state, kSymlinkFailure, "%s: some symlinks failed", name);
     }
     return StringValue(strdup(""));
 }
@@ -853,14 +856,16 @@
     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;
     }
 
@@ -889,7 +894,7 @@
     }
 
     if (bad > 0) {
-        return ErrorAbort(state, "%s: some changes failed", name);
+        return ErrorAbort(state, kSetMetadataFailure, "%s: some changes failed", name);
     }
 
     return StringValue(strdup(""));
@@ -897,7 +902,7 @@
 
 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;
@@ -926,20 +931,22 @@
 
     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;
     }
@@ -947,12 +954,13 @@
     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;
@@ -1016,7 +1024,8 @@
 
     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;
     }
@@ -1028,9 +1037,8 @@
 
 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;
@@ -1044,7 +1052,8 @@
 
     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);
@@ -1068,11 +1077,11 @@
 
     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;
         }
     }
@@ -1095,7 +1104,7 @@
 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);
     }
 
@@ -1140,7 +1149,7 @@
 
 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"));
@@ -1148,7 +1157,7 @@
 
 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) {
@@ -1202,7 +1211,7 @@
 //
 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);
@@ -1250,7 +1259,7 @@
 // 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;
@@ -1286,7 +1295,7 @@
 // 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;
@@ -1312,7 +1321,7 @@
 
     sleep(5);
     free(property);
-    ErrorAbort(state, "%s() failed to reboot", name);
+    ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name);
     return NULL;
 }
 
@@ -1328,7 +1337,7 @@
 // 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;
@@ -1358,7 +1367,7 @@
 // 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;
@@ -1376,7 +1385,7 @@
 
 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;
@@ -1398,7 +1407,7 @@
 
 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");
@@ -1407,12 +1416,12 @@
 
 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)));
@@ -1430,7 +1439,8 @@
     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 0497d6a..c222cee 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -51,7 +51,7 @@
     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 @@
     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 @@
             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 1d6cf81..996a1fd 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 @@
         }
     }
 
-#define BUFFER_SIZE 4096
-
     bool need_sha1 = false;
     bool need_sha256 = false;
     for (const auto& key : keys) {
@@ -207,8 +208,10 @@
     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 @@
     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 @@
         }
         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 0000000..f22d40b
--- /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 0000000..9a1d315
--- /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 48278ff..e078134 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 @@
     currentIcon(NONE),
     intro_done(false),
     current_frame(0),
-    rtl_locale(false),
     progressBarType(EMPTY),
     progressScopeStart(0),
     progressScopeSize(0),
@@ -84,7 +77,6 @@
     for (size_t i = 0; i < 5; i++)
         backgroundIcon[i] = NULL;
 
-    pthread_mutex_init(&updateMutex, NULL);
     self = this;
 }
 
@@ -148,41 +140,6 @@
     }
 }
 
-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 @@
             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 @@
                 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 @@
                 } 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 @@
         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 @@
         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::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 @@
     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 @@
     }
     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);
+}
diff --git a/wear_ui.h b/wear_ui.h
index 63c1b6e..e2d6fe0 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -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 @@
 
     // 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 @@
 
     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 @@
 
     int current_frame;
 
-    bool rtl_locale;
-
-    pthread_mutex_t updateMutex;
     GRSurface* backgroundIcon[5];
     GRSurface* *introFrames;
     GRSurface* *loopFrames;
@@ -128,11 +117,9 @@
     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