am b65a9023: (-s ours) am 7f89389a: am 7c511184: Merge "updater: Clean up char* with std::string."
* commit 'b65a90235e7d4fc3a2dbf68bb73ac323a88f1abc':
diff --git a/Android.mk b/Android.mk
index 74e7b1d..d96849f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -40,6 +40,7 @@
screen_ui.cpp \
ui.cpp \
verifier.cpp \
+ wear_ui.cpp \
LOCAL_MODULE := recovery
diff --git a/edify/Android.mk b/edify/Android.mk
index eb366c2..9b859d4 100644
--- a/edify/Android.mk
+++ b/edify/Android.mk
@@ -5,12 +5,7 @@
edify_src_files := \
lexer.l \
parser.y \
- expr.c
-
-# "-x c" forces the lex/yacc files to be compiled as c the build system
-# otherwise forces them to be c++. Need to also add an explicit -std because the
-# build system will soon default C++ to -std=c++11.
-edify_cflags := -x c -std=gnu89
+ expr.cpp
#
# Build the host-side command line tool
@@ -19,12 +14,13 @@
LOCAL_SRC_FILES := \
$(edify_src_files) \
- main.c
+ main.cpp
-LOCAL_CPPFLAGS := $(edify_cflags) -g -O0
+LOCAL_CPPFLAGS := -g -O0
LOCAL_MODULE := edify
LOCAL_YACCFLAGS := -v
LOCAL_CPPFLAGS += -Wno-unused-parameter
+LOCAL_CPPFLAGS += -Wno-deprecated-register
LOCAL_CLANG := true
include $(BUILD_HOST_EXECUTABLE)
@@ -36,8 +32,8 @@
LOCAL_SRC_FILES := $(edify_src_files)
-LOCAL_CPPFLAGS := $(edify_cflags)
-LOCAL_CPPFLAGS += -Wno-unused-parameter
+LOCAL_CPPFLAGS := -Wno-unused-parameter
+LOCAL_CPPFLAGS += -Wno-deprecated-register
LOCAL_MODULE := libedify
LOCAL_CLANG := true
diff --git a/edify/expr.c b/edify/expr.cpp
similarity index 92%
rename from edify/expr.c
rename to edify/expr.cpp
index 79f6282..cd1e087 100644
--- a/edify/expr.c
+++ b/edify/expr.cpp
@@ -51,7 +51,7 @@
Value* StringValue(char* str) {
if (str == NULL) return NULL;
- Value* v = malloc(sizeof(Value));
+ Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value)));
v->type = VAL_STRING;
v->size = strlen(str);
v->data = str;
@@ -68,7 +68,7 @@
if (argc == 0) {
return StringValue(strdup(""));
}
- char** strings = malloc(argc * sizeof(char*));
+ char** strings = reinterpret_cast<char**>(malloc(argc * sizeof(char*)));
int i;
for (i = 0; i < argc; ++i) {
strings[i] = NULL;
@@ -83,8 +83,9 @@
length += strlen(strings[i]);
}
- result = malloc(length+1);
- int p = 0;
+ result = reinterpret_cast<char*>(malloc(length+1));
+ int p;
+ p = 0;
for (i = 0; i < argc; ++i) {
strcpy(result+p, strings[i]);
p += strlen(strings[i]);
@@ -149,7 +150,7 @@
if (!b) {
int prefix_len;
int len = argv[i]->end - argv[i]->start;
- char* err_src = malloc(len + 20);
+ char* err_src = reinterpret_cast<char*>(malloc(len + 20));
strcpy(err_src, "assert failed: ");
prefix_len = strlen(err_src);
memcpy(err_src + prefix_len, state->script + argv[i]->start, len);
@@ -290,7 +291,8 @@
goto done;
}
- long r_int = strtol(right, &end, 10);
+ long r_int;
+ r_int = strtol(right, &end, 10);
if (right[0] == '\0' || *end != '\0') {
goto done;
}
@@ -325,11 +327,11 @@
Expr* Build(Function fn, YYLTYPE loc, int count, ...) {
va_list v;
va_start(v, count);
- Expr* e = malloc(sizeof(Expr));
+ Expr* e = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
e->fn = fn;
e->name = "(operator)";
e->argc = count;
- e->argv = malloc(count * sizeof(Expr*));
+ e->argv = reinterpret_cast<Expr**>(malloc(count * sizeof(Expr*)));
int i;
for (i = 0; i < count; ++i) {
e->argv[i] = va_arg(v, Expr*);
@@ -351,7 +353,7 @@
void RegisterFunction(const char* name, Function fn) {
if (fn_entries >= fn_size) {
fn_size = fn_size*2 + 1;
- fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction));
+ fn_table = reinterpret_cast<NamedFunction*>(realloc(fn_table, fn_size * sizeof(NamedFunction)));
}
fn_table[fn_entries].name = name;
fn_table[fn_entries].fn = fn;
@@ -371,8 +373,8 @@
Function FindFunction(const char* name) {
NamedFunction key;
key.name = name;
- NamedFunction* nf = bsearch(&key, fn_table, fn_entries,
- sizeof(NamedFunction), fn_entry_compare);
+ NamedFunction* nf = reinterpret_cast<NamedFunction*>(bsearch(&key, fn_table, fn_entries,
+ sizeof(NamedFunction), fn_entry_compare));
if (nf == NULL) {
return NULL;
}
@@ -401,7 +403,7 @@
// zero or more char** to put them in). If any expression evaluates
// to NULL, free the rest and return -1. Return 0 on success.
int ReadArgs(State* state, Expr* argv[], int count, ...) {
- char** args = malloc(count * sizeof(char*));
+ char** args = reinterpret_cast<char**>(malloc(count * sizeof(char*)));
va_list v;
va_start(v, count);
int i;
@@ -427,7 +429,7 @@
// zero or more Value** to put them in). If any expression evaluates
// to NULL, free the rest and return -1. Return 0 on success.
int ReadValueArgs(State* state, Expr* argv[], int count, ...) {
- Value** args = malloc(count * sizeof(Value*));
+ Value** args = reinterpret_cast<Value**>(malloc(count * sizeof(Value*)));
va_list v;
va_start(v, count);
int i;
@@ -494,7 +496,7 @@
// Use printf-style arguments to compose an error message to put into
// *state. Returns NULL.
Value* ErrorAbort(State* state, const char* format, ...) {
- char* buffer = malloc(4096);
+ char* buffer = reinterpret_cast<char*>(malloc(4096));
va_list v;
va_start(v, format);
vsnprintf(buffer, 4096, format, v);
diff --git a/edify/expr.h b/edify/expr.h
index a9ed2f9..36f8e96 100644
--- a/edify/expr.h
+++ b/edify/expr.h
@@ -21,10 +21,6 @@
#include "yydefs.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#define MAX_STRING_LEN 1024
typedef struct Expr Expr;
@@ -59,7 +55,7 @@
struct Expr {
Function fn;
- char* name;
+ const char* name;
int argc;
Expr** argv;
int start, end;
@@ -166,8 +162,4 @@
int parse_string(const char* str, Expr** root, int* error_count);
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
#endif // _EXPRESSION_H
diff --git a/edify/main.c b/edify/main.cpp
similarity index 100%
rename from edify/main.c
rename to edify/main.cpp
diff --git a/edify/parser.y b/edify/parser.y
index f8fb2d1..098a637 100644
--- a/edify/parser.y
+++ b/edify/parser.y
@@ -70,7 +70,7 @@
;
expr: STRING {
- $$ = malloc(sizeof(Expr));
+ $$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
$$->fn = Literal;
$$->name = $1;
$$->argc = 0;
@@ -91,7 +91,7 @@
| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); }
| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
| STRING '(' arglist ')' {
- $$ = malloc(sizeof(Expr));
+ $$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
$$->fn = FindFunction($1);
if ($$->fn == NULL) {
char buffer[256];
@@ -113,12 +113,12 @@
}
| expr {
$$.argc = 1;
- $$.argv = malloc(sizeof(Expr*));
+ $$.argv = reinterpret_cast<Expr**>(malloc(sizeof(Expr*)));
$$.argv[0] = $1;
}
| arglist ',' expr {
$$.argc = $1.argc + 1;
- $$.argv = realloc($$.argv, $$.argc * sizeof(Expr*));
+ $$.argv = reinterpret_cast<Expr**>(realloc($$.argv, $$.argc * sizeof(Expr*)));
$$.argv[$$.argc-1] = $3;
}
;
diff --git a/etc/init.rc b/etc/init.rc
index 0a4c6e9..dc18659 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -46,8 +46,8 @@
class_start default
# Load properties from /system/ + /factory after fs mount.
-on load_all_props_action
- load_all_props
+on load_system_props_action
+ load_system_props
on firmware_mounts_complete
rm /dev/.booting
@@ -62,7 +62,7 @@
# Load properties from /system/ + /factory after fs mount. Place
# this in another action so that the load will be scheduled after the prior
# issued fs triggers have completed.
- trigger load_all_props_action
+ trigger load_system_props_action
# Remove a file to wake up anything waiting for firmware
trigger firmware_mounts_complete
diff --git a/recovery.cpp b/recovery.cpp
index 515470f..c683bae 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -1036,11 +1036,15 @@
if (strncmp(update_package, "CACHE:", 6) == 0) {
int len = strlen(update_package) + 10;
char* modified_path = (char*)malloc(len);
- strlcpy(modified_path, "/cache/", len);
- strlcat(modified_path, update_package+6, len);
- printf("(replacing path \"%s\" with \"%s\")\n",
- update_package, modified_path);
- update_package = modified_path;
+ if (modified_path) {
+ strlcpy(modified_path, "/cache/", len);
+ strlcat(modified_path, update_package+6, len);
+ printf("(replacing path \"%s\" with \"%s\")\n",
+ update_package, modified_path);
+ update_package = modified_path;
+ }
+ else
+ printf("modified_path allocation failed\n");
}
}
printf("\n");
diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk
index e73c8f1..f31db42 100644
--- a/uncrypt/Android.mk
+++ b/uncrypt/Android.mk
@@ -20,6 +20,8 @@
LOCAL_SRC_FILES := uncrypt.cpp
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
+
LOCAL_MODULE := uncrypt
LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index 8785b29..aef4800 100644
--- a/uncrypt/uncrypt.cpp
+++ b/uncrypt/uncrypt.cpp
@@ -51,6 +51,8 @@
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
+
#include <base/file.h>
#include <base/strings.h>
#include <cutils/android_reboot.h>
@@ -60,6 +62,8 @@
#define LOG_TAG "uncrypt"
#include <log/log.h>
+#include "unique_fd.h"
+
#define WINDOW_SIZE 5
static const std::string cache_block_map = "/cache/recovery/block.map";
@@ -183,6 +187,7 @@
return -1;
}
FILE* mapf = fdopen(mapfd, "w");
+ unique_file mapf_holder(mapf);
// Make sure we can write to the status_file.
if (!android::base::WriteStringToFd("0\n", status_fd)) {
@@ -191,8 +196,7 @@
}
struct stat sb;
- int ret = stat(path, &sb);
- if (ret != 0) {
+ if (stat(path, &sb) != 0) {
ALOGE("failed to stat %s\n", path);
return -1;
}
@@ -221,15 +225,18 @@
size_t pos = 0;
int fd = open(path, O_RDONLY);
- if (fd < 0) {
+ unique_fd fd_holder(fd);
+ if (fd == -1) {
ALOGE("failed to open fd for reading: %s\n", strerror(errno));
return -1;
}
int wfd = -1;
+ unique_fd wfd_holder(wfd);
if (encrypted) {
wfd = open(blk_dev, O_WRONLY | O_SYNC);
- if (wfd < 0) {
+ wfd_holder = unique_fd(wfd);
+ if (wfd == -1) {
ALOGE("failed to open fd for writing: %s\n", strerror(errno));
return -1;
}
@@ -247,8 +254,7 @@
if ((tail+1) % WINDOW_SIZE == head) {
// write out head buffer
int block = head_block;
- ret = ioctl(fd, FIBMAP, &block);
- if (ret != 0) {
+ if (ioctl(fd, FIBMAP, &block) != 0) {
ALOGE("failed to find block %d\n", head_block);
return -1;
}
@@ -288,8 +294,7 @@
while (head != tail) {
// write out head buffer
int block = head_block;
- ret = ioctl(fd, FIBMAP, &block);
- if (ret != 0) {
+ if (ioctl(fd, FIBMAP, &block) != 0) {
ALOGE("failed to find block %d\n", head_block);
return -1;
}
@@ -313,14 +318,11 @@
ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno));
return -1;
}
- fclose(mapf);
- close(fd);
if (encrypted) {
if (fsync(wfd) == -1) {
ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno));
return -1;
}
- close(wfd);
}
return 0;
@@ -333,6 +335,8 @@
if (!v->mount_point) continue;
if (strcmp(v->mount_point, "/misc") == 0) {
int fd = open(v->blk_device, O_WRONLY | O_SYNC);
+ unique_fd fd_holder(fd);
+
uint8_t zeroes[1088]; // sizeof(bootloader_message) from recovery
memset(zeroes, 0, sizeof(zeroes));
@@ -349,10 +353,8 @@
}
if (fsync(fd) == -1) {
ALOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno));
- close(fd);
return;
}
- close(fd);
}
}
}
@@ -437,6 +439,7 @@
ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno));
return 1;
}
+ unique_fd status_fd_holder(status_fd);
if (argc == 3) {
// when command-line args are given this binary is being used
@@ -447,7 +450,6 @@
std::string package;
if (!find_uncrypt_package(package)) {
android::base::WriteStringToFd("-1\n", status_fd);
- close(status_fd);
return 1;
}
input_path = package.c_str();
@@ -457,12 +459,10 @@
int status = uncrypt(input_path, map_file, status_fd);
if (status != 0) {
android::base::WriteStringToFd("-1\n", status_fd);
- close(status_fd);
return 1;
}
android::base::WriteStringToFd("100\n", status_fd);
- close(status_fd);
}
return 0;
diff --git a/unique_fd.h b/unique_fd.h
new file mode 100644
index 0000000..98a7c7b
--- /dev/null
+++ b/unique_fd.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UNIQUE_FD_H
+#define UNIQUE_FD_H
+
+#include <stdio.h>
+
+#include <memory>
+
+class unique_fd {
+ public:
+ unique_fd(int fd) : fd_(fd) { }
+
+ unique_fd(unique_fd&& uf) {
+ fd_ = uf.fd_;
+ uf.fd_ = -1;
+ }
+
+ ~unique_fd() {
+ if (fd_ != -1) {
+ close(fd_);
+ }
+ }
+
+ int get() {
+ return fd_;
+ }
+
+ // Movable.
+ unique_fd& operator=(unique_fd&& uf) {
+ fd_ = uf.fd_;
+ uf.fd_ = -1;
+ return *this;
+ }
+
+ explicit operator bool() const {
+ return fd_ != -1;
+ }
+
+ private:
+ int fd_;
+
+ // Non-copyable.
+ unique_fd(const unique_fd&) = delete;
+ unique_fd& operator=(const unique_fd&) = delete;
+};
+
+// Custom deleter for unique_file to avoid fclose(NULL).
+struct safe_fclose {
+ void operator()(FILE *fp) const {
+ if (fp) {
+ fclose(fp);
+ };
+ }
+};
+
+using unique_file = std::unique_ptr<FILE, safe_fclose>;
+
+#endif // UNIQUE_FD_H
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
old mode 100644
new mode 100755
index 3a07da4..7da9adf
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -56,9 +56,9 @@
#define STASH_FILE_MODE 0600
typedef struct {
- int count;
- int size;
- int pos[0];
+ size_t count;
+ size_t size;
+ size_t pos[0]; // Actual limit is INT_MAX.
} RangeSet;
#define RANGESET_MAX_POINTS \
@@ -82,16 +82,17 @@
goto err;
}
+ errno = 0;
val = strtol(token, NULL, 0);
- if (val < 2 || val > RANGESET_MAX_POINTS) {
+ if (errno != 0 || val < 2 || val > RANGESET_MAX_POINTS) {
goto err;
} else if (val % 2) {
goto err; // must be even
}
num = (int) val;
- bufsize = sizeof(RangeSet) + num * sizeof(int);
+ bufsize = sizeof(RangeSet) + num * sizeof(size_t);
out = reinterpret_cast<RangeSet*>(malloc(bufsize));
@@ -103,41 +104,50 @@
out->count = num / 2;
out->size = 0;
- for (int i = 0; i < num; ++i) {
+ for (int i = 0; i < num; i += 2) {
token = strtok_r(NULL, ",", &save);
if (!token) {
goto err;
}
+ errno = 0;
val = strtol(token, NULL, 0);
- if (val < 0 || val > INT_MAX) {
+ if (errno != 0 || val < 0 || val > INT_MAX) {
goto err;
}
- out->pos[i] = (int) val;
+ out->pos[i] = static_cast<size_t>(val);
- if (i % 2) {
- if (out->pos[i - 1] >= out->pos[i]) {
- goto err; // empty or negative range
- }
+ token = strtok_r(NULL, ",", &save);
- if (out->size > INT_MAX - out->pos[i]) {
- goto err; // overflow
- }
-
- out->size += out->pos[i];
- } else {
- if (out->size < 0) {
- goto err;
- }
-
- out->size -= out->pos[i];
+ if (!token) {
+ goto err;
}
+
+ errno = 0;
+ val = strtol(token, NULL, 0);
+
+ if (errno != 0 || val < 0 || val > INT_MAX) {
+ goto err;
+ }
+
+ out->pos[i+1] = static_cast<size_t>(val);
+
+ if (out->pos[i] >= out->pos[i+1]) {
+ goto err; // empty or negative range
+ }
+
+ size_t rs = out->pos[i+1] - out->pos[i];
+ if (out->size > SIZE_MAX - rs) {
+ goto err; // overflow
+ }
+
+ out->size += rs;
}
- if (out->size <= 0) {
+ if (out->size == 0) {
goto err;
}
@@ -149,13 +159,13 @@
}
static bool range_overlaps(const RangeSet& r1, const RangeSet& r2) {
- for (int i = 0; i < r1.count; ++i) {
- int r1_0 = r1.pos[i * 2];
- int r1_1 = r1.pos[i * 2 + 1];
+ for (size_t i = 0; i < r1.count; ++i) {
+ size_t r1_0 = r1.pos[i * 2];
+ size_t r1_1 = r1.pos[i * 2 + 1];
- for (int j = 0; j < r2.count; ++j) {
- int r2_0 = r2.pos[j * 2];
- int r2_1 = r2.pos[j * 2 + 1];
+ for (size_t j = 0; j < r2.count; ++j) {
+ size_t r2_0 = r2.pos[j * 2];
+ size_t r2_1 = r2.pos[j * 2 + 1];
if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) {
return true;
@@ -219,7 +229,7 @@
typedef struct {
int fd;
RangeSet* tgt;
- int p_block;
+ size_t p_block;
size_t p_remain;
} RangeSinkState;
@@ -340,20 +350,18 @@
}
static int ReadBlocks(RangeSet* src, uint8_t* buffer, int fd) {
- int i;
size_t p = 0;
- size_t size;
if (!src || !buffer) {
return -1;
}
- for (i = 0; i < src->count; ++i) {
+ for (size_t i = 0; i < src->count; ++i) {
if (!check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
return -1;
}
- size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE;
+ size_t size = (src->pos[i * 2 + 1] - src->pos[i * 2]) * BLOCKSIZE;
if (read_all(fd, buffer + p, size) == -1) {
return -1;
@@ -366,20 +374,18 @@
}
static int WriteBlocks(RangeSet* tgt, uint8_t* buffer, int fd) {
- int i;
size_t p = 0;
- size_t size;
if (!tgt || !buffer) {
return -1;
}
- for (i = 0; i < tgt->count; ++i) {
+ for (size_t i = 0; i < tgt->count; ++i) {
if (!check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
return -1;
}
- size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE;
+ size_t size = (tgt->pos[i * 2 + 1] - tgt->pos[i * 2]) * BLOCKSIZE;
if (write_all(fd, buffer + p, size) == -1) {
return -1;
@@ -762,8 +768,8 @@
}
static int SaveStash(const std::string& base, char** wordsave, uint8_t** buffer,
- size_t* buffer_alloc, int fd, int usehash, bool* isunresumable) {
- if (!wordsave || !buffer || !buffer_alloc || !isunresumable) {
+ size_t* buffer_alloc, int fd, bool usehash) {
+ if (!wordsave || !buffer || !buffer_alloc) {
return -1;
}
@@ -1123,7 +1129,7 @@
}
return SaveStash(params->stashbase, ¶ms->cpos, ¶ms->buffer, ¶ms->bufsize,
- params->fd, (params->version >= 3), ¶ms->isunresumable);
+ params->fd, (params->version >= 3));
}
static int PerformCommandFree(CommandParameters* params) {
@@ -1140,8 +1146,6 @@
static int PerformCommandZero(CommandParameters* params) {
char* range = NULL;
- int i;
- int j;
int rc = -1;
RangeSet* tgt = NULL;
@@ -1164,12 +1168,12 @@
memset(params->buffer, 0, BLOCKSIZE);
if (params->canwrite) {
- for (i = 0; i < tgt->count; ++i) {
+ for (size_t i = 0; i < tgt->count; ++i) {
if (!check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
goto pczout;
}
- for (j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
+ for (size_t j = tgt->pos[i * 2]; j < tgt->pos[i * 2 + 1]; ++j) {
if (write_all(params->fd, params->buffer, BLOCKSIZE) == -1) {
goto pczout;
}
@@ -1359,7 +1363,6 @@
static int PerformCommandErase(CommandParameters* params) {
char* range = NULL;
- int i;
int rc = -1;
RangeSet* tgt = NULL;
struct stat st;
@@ -1395,7 +1398,7 @@
if (params->canwrite) {
fprintf(stderr, " erasing %d blocks\n", tgt->size);
- for (i = 0; i < tgt->count; ++i) {
+ for (size_t i = 0; i < tgt->count; ++i) {
// offset in bytes
blocks[0] = tgt->pos[i * 2] * (uint64_t) BLOCKSIZE;
// length in bytes
@@ -1852,15 +1855,14 @@
SHA_CTX ctx;
SHA_init(&ctx);
- int i, j;
- for (i = 0; i < rs->count; ++i) {
+ 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));
goto done;
}
- for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) {
+ 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));
diff --git a/wear_ui.cpp b/wear_ui.cpp
new file mode 100644
index 0000000..4ae42c4
--- /dev/null
+++ b/wear_ui.cpp
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2014 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 <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "common.h"
+#include "device.h"
+#include "minui/minui.h"
+#include "wear_ui.h"
+#include "ui.h"
+#include "cutils/properties.h"
+#include "base/strings.h"
+
+static int char_width;
+static int char_height;
+
+// There's only (at most) one of these objects, and global callbacks
+// (for pthread_create, and the input event system) need to find it,
+// so use a global variable.
+static WearRecoveryUI* self = NULL;
+
+// Return the current time as a double (including fractions of a second).
+static double now() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec + tv.tv_usec / 1000000.0;
+}
+
+WearRecoveryUI::WearRecoveryUI() :
+ progress_bar_height(3),
+ progress_bar_width(200),
+ progress_bar_y(259),
+ outer_height(0),
+ outer_width(0),
+ menu_unusable_rows(0),
+ intro_frames(22),
+ loop_frames(60),
+ currentIcon(NONE),
+ intro_done(false),
+ current_frame(0),
+ animation_fps(30),
+ rtl_locale(false),
+ progressBarType(EMPTY),
+ progressScopeStart(0),
+ progressScopeSize(0),
+ progress(0),
+ text_cols(0),
+ text_rows(0),
+ text_col(0),
+ text_row(0),
+ text_top(0),
+ show_text(false),
+ show_text_ever(false),
+ show_menu(false),
+ menu_items(0),
+ menu_sel(0) {
+
+ for (size_t i = 0; i < 5; i++)
+ backgroundIcon[i] = NULL;
+
+ pthread_mutex_init(&updateMutex, NULL);
+ self = this;
+}
+
+// Draw background frame on the screen. Does not flip pages.
+// Should only be called with updateMutex locked.
+void WearRecoveryUI::draw_background_locked(Icon icon)
+{
+ gr_color(0, 0, 0, 255);
+ gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+
+ if (icon) {
+ GRSurface* surface;
+ if (icon == INSTALLING_UPDATE || icon == ERASING) {
+ if (!intro_done) {
+ surface = introFrames[current_frame];
+ } else {
+ surface = loopFrames[current_frame];
+ }
+ }
+ else {
+ surface = backgroundIcon[icon];
+ }
+
+ int width = gr_get_width(surface);
+ int height = gr_get_height(surface);
+
+ int x = (gr_fb_width() - width) / 2;
+ int y = (gr_fb_height() - height) / 2;
+
+ gr_blit(surface, 0, 0, width, height, x, y);
+ }
+}
+
+// Draw the progress bar (if any) on the screen. Does not flip pages.
+// Should only be called with updateMutex locked.
+void WearRecoveryUI::draw_progress_locked()
+{
+ if (currentIcon == ERROR) return;
+ if (progressBarType != DETERMINATE) return;
+
+ int width = progress_bar_width;
+ int height = progress_bar_height;
+ int dx = (gr_fb_width() - width)/2;
+ int dy = progress_bar_y;
+
+ float p = progressScopeStart + progress * progressScopeSize;
+ int pos = (int) (p * width);
+
+ gr_color(0x43, 0x43, 0x43, 0xff);
+ gr_fill(dx, dy, dx + width, dy + height);
+
+ if (pos > 0) {
+ gr_color(0x02, 0xa8, 0xf3, 255);
+ if (rtl_locale) {
+ // Fill the progress bar from right to left.
+ gr_fill(dx + width - pos, dy, dx + width, dy + height);
+ } else {
+ // Fill the progress bar from left to right.
+ gr_fill(dx, dy, dx + pos, dy + height);
+ }
+ }
+}
+
+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.",
+ "",
+ NULL
+};
+
+void WearRecoveryUI::draw_screen_locked()
+{
+ draw_background_locked(currentIcon);
+ draw_progress_locked();
+ char cur_selection_str[50];
+
+ if (show_text) {
+ SetColor(TEXT_FILL);
+ gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+
+ int y = outer_height;
+ int x = outer_width;
+ if (show_menu) {
+ char recovery_fingerprint[PROPERTY_VALUE_MAX];
+ property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, "");
+ SetColor(HEADER);
+ DrawTextLine(x + 4, &y, "Android Recovery", true);
+ for (auto& chunk: android::base::Split(recovery_fingerprint, ":")) {
+ DrawTextLine(x +4, &y, chunk.c_str(), false);
+ }
+
+ // This is actually the help strings.
+ DrawTextLines(x + 4, &y, HEADERS);
+ SetColor(HEADER);
+ DrawTextLines(x + 4, &y, menu_headers_);
+
+ // Show the current menu item number in relation to total number if
+ // items don't fit on the screen.
+ if (menu_items > menu_end - menu_start) {
+ sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items);
+ gr_text(x+4, y, cur_selection_str, 1);
+ y += char_height+4;
+ }
+
+ // Menu begins here
+ SetColor(MENU);
+
+ for (int i = menu_start; i < menu_end; ++i) {
+
+ if (i == menu_sel) {
+ // draw the highlight bar
+ SetColor(MENU_SEL_BG);
+ gr_fill(x, y-2, gr_fb_width()-x, y+char_height+2);
+ // white text of selected item
+ SetColor(MENU_SEL_FG);
+ if (menu[i][0]) gr_text(x+4, y, menu[i], 1);
+ SetColor(MENU);
+ } else {
+ if (menu[i][0]) gr_text(x+4, y, menu[i], 0);
+ }
+ y += char_height+4;
+ }
+ SetColor(MENU);
+ y += 4;
+ gr_fill(0, y, gr_fb_width(), y+2);
+ y += 4;
+ }
+
+ SetColor(LOG);
+
+ // display from the bottom up, until we hit the top of the
+ // screen, the bottom of the menu, or we've displayed the
+ // entire text buffer.
+ int ty;
+ int row = (text_top+text_rows-1) % text_rows;
+ size_t count = 0;
+ for (int ty = gr_fb_height() - char_height - outer_height;
+ ty > y+2 && count < text_rows;
+ ty -= char_height, ++count) {
+ gr_text(x+4, ty, text[row], 0);
+ --row;
+ if (row < 0) row = text_rows-1;
+ }
+ }
+}
+
+void WearRecoveryUI::update_screen_locked()
+{
+ draw_screen_locked();
+ gr_flip();
+}
+
+// Keeps the progress bar updated, even when the process is otherwise busy.
+void* WearRecoveryUI::progress_thread(void *cookie) {
+ self->progress_loop();
+ return NULL;
+}
+
+void WearRecoveryUI::progress_loop() {
+ double interval = 1.0 / animation_fps;
+ for (;;) {
+ double start = now();
+ pthread_mutex_lock(&updateMutex);
+ int redraw = 0;
+
+ 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 = 1;
+ }
+
+ // move the progress bar forward on timed intervals, if configured
+ int duration = progressScopeDuration;
+ if (progressBarType == DETERMINATE && duration > 0) {
+ double elapsed = now() - progressScopeTime;
+ float p = 1.0 * elapsed / duration;
+ if (p > 1.0) p = 1.0;
+ if (p > progress) {
+ progress = p;
+ redraw = 1;
+ }
+ }
+
+ if (redraw)
+ update_screen_locked();
+
+ pthread_mutex_unlock(&updateMutex);
+ double end = now();
+ // minimum of 20ms delay between frames
+ double delay = interval - (end-start);
+ if (delay < 0.02) delay = 0.02;
+ usleep((long)(delay * 1000000));
+ }
+}
+
+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);
+
+ text_col = text_row = 0;
+ 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;
+ if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
+
+ LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
+ backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
+ LoadBitmap("icon_error", &backgroundIcon[ERROR]);
+ backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
+
+ introFrames = (GRSurface**)malloc(intro_frames * sizeof(GRSurface*));
+ for (int i = 0; i < intro_frames; ++i) {
+ char filename[40];
+ sprintf(filename, "intro%02d", i);
+ LoadBitmap(filename, introFrames + i);
+ }
+
+ loopFrames = (GRSurface**)malloc(loop_frames * sizeof(GRSurface*));
+ for (int i = 0; i < loop_frames; ++i) {
+ char filename[40];
+ sprintf(filename, "loop%02d", i);
+ LoadBitmap(filename, loopFrames + i);
+ }
+
+ pthread_create(&progress_t, NULL, progress_thread, NULL);
+ 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);
+ currentIcon = icon;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void WearRecoveryUI::SetProgressType(ProgressType type)
+{
+ pthread_mutex_lock(&updateMutex);
+ if (progressBarType != type) {
+ progressBarType = type;
+ }
+ progressScopeStart = 0;
+ progressScopeSize = 0;
+ progress = 0;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void WearRecoveryUI::ShowProgress(float portion, float seconds)
+{
+ pthread_mutex_lock(&updateMutex);
+ progressBarType = DETERMINATE;
+ progressScopeStart += progressScopeSize;
+ progressScopeSize = portion;
+ progressScopeTime = now();
+ progressScopeDuration = seconds;
+ progress = 0;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void WearRecoveryUI::SetProgress(float fraction)
+{
+ pthread_mutex_lock(&updateMutex);
+ if (fraction < 0.0) fraction = 0.0;
+ if (fraction > 1.0) fraction = 1.0;
+ if (progressBarType == DETERMINATE && fraction > progress) {
+ // Skip updates that aren't visibly different.
+ int width = progress_bar_width;
+ float scale = width * progressScopeSize;
+ if ((int) (progress * scale) != (int) (fraction * scale)) {
+ progress = fraction;
+ update_screen_locked();
+ }
+ }
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void WearRecoveryUI::SetStage(int current, int max)
+{
+}
+
+void WearRecoveryUI::Print(const char *fmt, ...)
+{
+ char buf[256];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, 256, fmt, ap);
+ va_end(ap);
+
+ fputs(buf, stdout);
+
+ // This can get called before ui_init(), so be careful.
+ pthread_mutex_lock(&updateMutex);
+ if (text_rows > 0 && text_cols > 0) {
+ char *ptr;
+ for (ptr = buf; *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);
+}
+
+void WearRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
+ int initial_selection) {
+ pthread_mutex_lock(&updateMutex);
+ if (text_rows > 0 && text_cols > 0) {
+ menu_headers_ = headers;
+ size_t i = 0;
+ for (; i < text_rows && items[i] != nullptr; i++) {
+ strncpy(menu[i], items[i], text_cols - 1);
+ menu[i][text_cols - 1] = '\0';
+ }
+ menu_items = i;
+ show_menu = 1;
+ menu_sel = initial_selection;
+ menu_start = 0;
+ menu_end = visible_text_rows - 1 - menu_unusable_rows;
+ if (menu_items <= menu_end)
+ menu_end = menu_items;
+ update_screen_locked();
+ }
+ pthread_mutex_unlock(&updateMutex);
+}
+
+int WearRecoveryUI::SelectMenu(int sel) {
+ int old_sel;
+ pthread_mutex_lock(&updateMutex);
+ if (show_menu > 0) {
+ old_sel = menu_sel;
+ menu_sel = sel;
+ if (menu_sel < 0) menu_sel = 0;
+ if (menu_sel >= menu_items) menu_sel = menu_items-1;
+ if (menu_sel < menu_start) {
+ menu_start--;
+ menu_end--;
+ } else if (menu_sel >= menu_end && menu_sel < menu_items) {
+ menu_end++;
+ menu_start++;
+ }
+ sel = menu_sel;
+ if (menu_sel != old_sel) update_screen_locked();
+ }
+ pthread_mutex_unlock(&updateMutex);
+ return sel;
+}
+
+void WearRecoveryUI::EndMenu() {
+ int i;
+ pthread_mutex_lock(&updateMutex);
+ if (show_menu > 0 && text_rows > 0 && text_cols > 0) {
+ show_menu = 0;
+ update_screen_locked();
+ }
+ pthread_mutex_unlock(&updateMutex);
+}
+
+bool WearRecoveryUI::IsTextVisible()
+{
+ pthread_mutex_lock(&updateMutex);
+ int visible = show_text;
+ pthread_mutex_unlock(&updateMutex);
+ return visible;
+}
+
+bool WearRecoveryUI::WasTextEverVisible()
+{
+ pthread_mutex_lock(&updateMutex);
+ int ever_visible = show_text_ever;
+ pthread_mutex_unlock(&updateMutex);
+ return ever_visible;
+}
+
+void WearRecoveryUI::ShowText(bool visible)
+{
+ pthread_mutex_lock(&updateMutex);
+ // Don't show text during ota install or factory reset
+ if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
+ pthread_mutex_unlock(&updateMutex);
+ return;
+ }
+ show_text = visible;
+ if (show_text) show_text_ever = 1;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void WearRecoveryUI::Redraw()
+{
+ pthread_mutex_lock(&updateMutex);
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void WearRecoveryUI::ShowFile(FILE* fp) {
+ std::vector<long> offsets;
+ offsets.push_back(ftell(fp));
+ ClearText();
+
+ struct stat sb;
+ fstat(fileno(fp), &sb);
+
+ bool show_prompt = false;
+ while (true) {
+ if (show_prompt) {
+ Print("--(%d%% of %d bytes)--",
+ static_cast<int>(100 * (double(ftell(fp)) / double(sb.st_size))),
+ static_cast<int>(sb.st_size));
+ Redraw();
+ while (show_prompt) {
+ show_prompt = false;
+ int key = WaitKey();
+ if (key == KEY_POWER || key == KEY_ENTER) {
+ return;
+ } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
+ if (offsets.size() <= 1) {
+ show_prompt = true;
+ } else {
+ offsets.pop_back();
+ fseek(fp, offsets.back(), SEEK_SET);
+ }
+ } else {
+ if (feof(fp)) {
+ return;
+ }
+ offsets.push_back(ftell(fp));
+ }
+ }
+ ClearText();
+ }
+
+ int ch = getc(fp);
+ if (ch == EOF) {
+ text_row = text_top = text_rows - 2;
+ show_prompt = true;
+ } else {
+ PutChar(ch);
+ if (text_col == 0 && text_row >= text_rows - 2) {
+ text_top = text_row;
+ show_prompt = true;
+ }
+ }
+ }
+}
+
+void WearRecoveryUI::PutChar(char ch) {
+ pthread_mutex_lock(&updateMutex);
+ if (ch != '\n') text[text_row][text_col++] = ch;
+ if (ch == '\n' || text_col >= text_cols) {
+ text_col = 0;
+ ++text_row;
+ }
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void WearRecoveryUI::ShowFile(const char* filename) {
+ FILE* fp = fopen_path(filename, "re");
+ if (fp == nullptr) {
+ Print(" Unable to open %s: %s\n", filename, strerror(errno));
+ return;
+ }
+ ShowFile(fp);
+ fclose(fp);
+}
+
+void WearRecoveryUI::ClearText() {
+ pthread_mutex_lock(&updateMutex);
+ text_col = 0;
+ text_row = 0;
+ text_top = 1;
+ for (size_t i = 0; i < text_rows; ++i) {
+ memset(text[i], 0, text_cols + 1);
+ }
+ pthread_mutex_unlock(&updateMutex);
+}
diff --git a/wear_ui.h b/wear_ui.h
new file mode 100644
index 0000000..839a264
--- /dev/null
+++ b/wear_ui.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RECOVERY_WEAR_UI_H
+#define RECOVERY_WEAR_UI_H
+
+#include <pthread.h>
+#include <stdio.h>
+
+#include "ui.h"
+#include "minui/minui.h"
+
+class WearRecoveryUI : public RecoveryUI {
+ public:
+ WearRecoveryUI();
+
+ void Init();
+ void SetLocale(const char* locale);
+
+ // overall recovery state ("background image")
+ void SetBackground(Icon icon);
+
+ // progress indicator
+ void SetProgressType(ProgressType type);
+ void ShowProgress(float portion, float seconds);
+ void SetProgress(float fraction);
+
+ void SetStage(int current, int max);
+
+ // text log
+ void ShowText(bool visible);
+ bool IsTextVisible();
+ bool WasTextEverVisible();
+
+ // printing messages
+ void Print(const char* fmt, ...);
+ void ShowFile(const char* filename);
+ void ShowFile(FILE* fp);
+
+ // menu display
+ void StartMenu(const char* const * headers, const char* const * items,
+ int initial_selection);
+ int SelectMenu(int sel);
+ void EndMenu();
+
+ 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;
+
+ // progress bar vertical position, it's centered horizontally
+ int progress_bar_y;
+
+ // outer of window
+ int outer_height, outer_width;
+
+ // Unusable rows when displaying the recovery menu, including the lines
+ // for headers (Android Recovery, build id and etc) and the bottom lines
+ // that may otherwise go out of the screen.
+ int menu_unusable_rows;
+
+ // number of intro frames (default: 22) and loop frames (default: 60)
+ int intro_frames;
+ int loop_frames;
+
+ private:
+ Icon currentIcon;
+
+ bool intro_done;
+
+ int current_frame;
+
+ int animation_fps;
+
+ bool rtl_locale;
+
+ pthread_mutex_t updateMutex;
+ GRSurface* backgroundIcon[5];
+ GRSurface* *introFrames;
+ GRSurface* *loopFrames;
+
+ ProgressType progressBarType;
+
+ float progressScopeStart, progressScopeSize, progress;
+ double progressScopeTime, progressScopeDuration;
+
+ static const int kMaxCols = 96;
+ static const int kMaxRows = 96;
+
+ // Log text overlay, displayed when a magic key is pressed
+ char text[kMaxRows][kMaxCols];
+ size_t text_cols, text_rows;
+ // Number of text rows seen on screen
+ int visible_text_rows;
+ size_t text_col, text_row, text_top;
+ bool show_text;
+ bool show_text_ever; // has show_text ever been true?
+
+ char menu[kMaxRows][kMaxCols];
+ bool show_menu;
+ const char* const* menu_headers_;
+ int menu_items, menu_sel;
+ int menu_start, menu_end;
+
+ pthread_t progress_t;
+
+ private:
+ void draw_background_locked(Icon icon);
+ void draw_progress_locked();
+ void draw_screen_locked();
+ 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);
+};
+
+#endif // RECOVERY_WEAR_UI_H