parent
59078fba41
commit
53fb6988af
12 changed files with 1188 additions and 0 deletions
@ -0,0 +1,120 @@ |
|||||||
|
CC = clang
|
||||||
|
CXX = clang++
|
||||||
|
|
||||||
|
|
||||||
|
PHONELIBS = ../phonelibs
|
||||||
|
|
||||||
|
WARN_FLAGS = -Werror=implicit-function-declaration \
|
||||||
|
-Werror=incompatible-pointer-types \
|
||||||
|
-Werror=int-conversion \
|
||||||
|
-Werror=return-type \
|
||||||
|
-Werror=format-extra-args
|
||||||
|
|
||||||
|
CFLAGS = -std=gnu11 -fPIC -O2 $(WARN_FLAGS)
|
||||||
|
CXXFLAGS = -std=c++11 -fPIC -O2 $(WARN_FLAGS)
|
||||||
|
|
||||||
|
NANOVG_FLAGS = -I$(PHONELIBS)/nanovg
|
||||||
|
|
||||||
|
OPENGL_LIBS = -lGLESv3
|
||||||
|
|
||||||
|
FRAMEBUFFER_LIBS = -lutils -lgui -lEGL
|
||||||
|
|
||||||
|
COMMON_OBJS = ../selfdrive/common/glutil.o \
|
||||||
|
../selfdrive/common/framebuffer.o \
|
||||||
|
$(PHONELIBS)/nanovg/nanovg.o \
|
||||||
|
../selfdrive/common/spinner.o \
|
||||||
|
opensans_semibold.o \
|
||||||
|
img_spinner_track.o \
|
||||||
|
img_spinner_comma.o
|
||||||
|
|
||||||
|
OPENPILOT_OBJS = installer_openpilot.o \
|
||||||
|
continue_openpilot.o \
|
||||||
|
$(COMMON_OBJS)
|
||||||
|
|
||||||
|
DASHCAM_OBJS = installer_dashcam.o \
|
||||||
|
continue_dashcam.o \
|
||||||
|
$(COMMON_OBJS)
|
||||||
|
|
||||||
|
DEPS := $(OPENPILOT_OBJS:.o=.d) $(DASHCAM_OBJS:.o=.d)
|
||||||
|
|
||||||
|
.PHONY: all |
||||||
|
all: installers/installer_dashcam installers/installer_openpilot |
||||||
|
|
||||||
|
installers/installer_openpilot: $(OPENPILOT_OBJS) |
||||||
|
@echo "[ LINK ] $@"
|
||||||
|
$(CXX) -fPIC -o '$@' $^ \
|
||||||
|
-s \
|
||||||
|
$(FRAMEBUFFER_LIBS) \
|
||||||
|
-L/system/vendor/lib64 \
|
||||||
|
$(OPENGL_LIBS) \
|
||||||
|
-lm -llog
|
||||||
|
|
||||||
|
installers/installer_dashcam: $(DASHCAM_OBJS) |
||||||
|
@echo "[ LINK ] $@"
|
||||||
|
$(CXX) -fPIC -o '$@' $^ \
|
||||||
|
-s \
|
||||||
|
$(FRAMEBUFFER_LIBS) \
|
||||||
|
-L/system/vendor/lib64 \
|
||||||
|
$(OPENGL_LIBS) \
|
||||||
|
-lm -llog
|
||||||
|
|
||||||
|
../selfdrive/common/framebuffer.o: ../selfdrive/common/framebuffer.cc |
||||||
|
@echo "[ CXX ] $@"
|
||||||
|
$(CXX) $(CXXFLAGS) -MMD \
|
||||||
|
-I$(PHONELIBS)/android_frameworks_native/include \
|
||||||
|
-I$(PHONELIBS)/android_system_core/include \
|
||||||
|
-I$(PHONELIBS)/android_hardware_libhardware/include \
|
||||||
|
-c -o '$@' '$<'
|
||||||
|
|
||||||
|
../selfdrive/common/spinner.o: ../selfdrive/common/spinner.c |
||||||
|
@echo "[ CXX ] $@"
|
||||||
|
$(CXX) $(CXXFLAGS) -MMD \
|
||||||
|
-I$(PHONELIBS)/android_frameworks_native/include \
|
||||||
|
-I$(PHONELIBS)/android_system_core/include \
|
||||||
|
-I$(PHONELIBS)/android_hardware_libhardware/include \
|
||||||
|
$(NANOVG_FLAGS) \
|
||||||
|
-c -o '$@' '$<'
|
||||||
|
|
||||||
|
opensans_semibold.o: ../selfdrive/assets/fonts/opensans_semibold.ttf |
||||||
|
@echo "[ bin2o ] $@"
|
||||||
|
cd '$(dir $<)' && ld -r -b binary '$(notdir $<)' -o '$(abspath $@)'
|
||||||
|
|
||||||
|
img_spinner_track.o: ../selfdrive/assets/img_spinner_track.png |
||||||
|
@echo "[ bin2o ] $@"
|
||||||
|
cd '$(dir $<)' && ld -r -b binary '$(notdir $<)' -o '$(abspath $@)'
|
||||||
|
|
||||||
|
img_spinner_comma.o: ../selfdrive/assets/img_spinner_comma.png |
||||||
|
@echo "[ bin2o ] $@"
|
||||||
|
cd '$(dir $<)' && ld -r -b binary '$(notdir $<)' -o '$(abspath $@)'
|
||||||
|
|
||||||
|
installer_openpilot.o: installer.c |
||||||
|
@echo "[ CC ] $@"
|
||||||
|
$(CC) $(CFLAGS) -MMD \
|
||||||
|
-I.. -I../selfdrive \
|
||||||
|
-DBRAND=openpilot \
|
||||||
|
-DBRANCH=release2 \
|
||||||
|
-c -o '$@' '$<'
|
||||||
|
|
||||||
|
installer_dashcam.o: installer.c |
||||||
|
@echo "[ CC ] $@"
|
||||||
|
$(CC) $(CFLAGS) -MMD \
|
||||||
|
-I.. -I../selfdrive \
|
||||||
|
-DBRAND=dashcam \
|
||||||
|
-DBRANCH=dashcam \
|
||||||
|
-c -o '$@' '$<'
|
||||||
|
|
||||||
|
%.o: %.c |
||||||
|
@echo "[ CC ] $@"
|
||||||
|
$(CC) $(CFLAGS) -MMD \
|
||||||
|
-I.. -I../selfdrive \
|
||||||
|
-c -o '$@' '$<'
|
||||||
|
|
||||||
|
%.o: %.sh |
||||||
|
@echo "[ bin2o ] $@"
|
||||||
|
cd '$(dir $<)' && ld -r -b binary '$(notdir $<)' -o '$(abspath $@)'
|
||||||
|
|
||||||
|
.PHONY: clean |
||||||
|
clean: |
||||||
|
rm -f installer_openpilot installer_dashcam $(OPENPILOT_OBJS) $(DASHCAM_OBJS) $(DEPS)
|
||||||
|
|
||||||
|
-include $(DEPS) |
@ -0,0 +1,4 @@ |
|||||||
|
#!/usr/bin/bash |
||||||
|
|
||||||
|
cd /data/openpilot |
||||||
|
exec ./launch_chffrplus.sh |
@ -0,0 +1,4 @@ |
|||||||
|
#!/usr/bin/bash |
||||||
|
|
||||||
|
cd /data/openpilot |
||||||
|
exec ./launch_openpilot.sh |
@ -0,0 +1,140 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdbool.h> |
||||||
|
#include <math.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <assert.h> |
||||||
|
#include <pthread.h> |
||||||
|
|
||||||
|
#include <sys/wait.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
|
||||||
|
#include <GLES3/gl3.h> |
||||||
|
#include <EGL/egl.h> |
||||||
|
#include <EGL/eglext.h> |
||||||
|
|
||||||
|
#include "common/util.h" |
||||||
|
#include "common/mat.h" |
||||||
|
#include "common/glutil.h" |
||||||
|
#include "common/framebuffer.h" |
||||||
|
#include "common/spinner.h" |
||||||
|
|
||||||
|
#ifndef BRAND |
||||||
|
#define BRAND openpilot |
||||||
|
#endif |
||||||
|
|
||||||
|
#define STR(X) #X |
||||||
|
#define STR2(X) STR(X) |
||||||
|
#define PASTE(A, B) A ## B |
||||||
|
#define PASTE2(A, B) PASTE(A, B) |
||||||
|
#define BRAND_S STR2(BRAND) |
||||||
|
#define BRANCH_S STR2(BRANCH) |
||||||
|
|
||||||
|
#define PRE_CHECKOUT_FOLDER "/system/comma/openpilot" |
||||||
|
#define GIT_CLONE_COMMAND "git clone https://github.com/commaai/openpilot.git "
|
||||||
|
|
||||||
|
|
||||||
|
extern const uint8_t str_continue[] asm("_binary_continue_" BRAND_S "_sh_start"); |
||||||
|
extern const uint8_t str_continue_end[] asm("_binary_continue_" BRAND_S "_sh_end"); |
||||||
|
|
||||||
|
static int use_pre_checkout() { |
||||||
|
int err; |
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
err = system("rm -rf /tmp/openpilot"); |
||||||
|
if(err) return 1; |
||||||
|
err = system("rm -rf /data/openpilot"); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
// Copy pre checkout into tmp so we can work on it
|
||||||
|
err = system("cp -rp " PRE_CHECKOUT_FOLDER " /tmp"); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
err = chdir("/tmp/openpilot"); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
// Checkout correct branch
|
||||||
|
err = system("git remote set-branches --add origin " BRANCH_S); |
||||||
|
if(err) return 1; |
||||||
|
err = system("git fetch origin " BRANCH_S); |
||||||
|
if(err) return 1; |
||||||
|
err = system("git checkout " BRANCH_S); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
// Move to final location
|
||||||
|
err = system("mv /tmp/openpilot /data"); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int fresh_clone() { |
||||||
|
int err; |
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
err = chdir("/tmp"); |
||||||
|
if(err) return 1; |
||||||
|
err = system("rm -rf /tmp/openpilot"); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
err = system(GIT_CLONE_COMMAND " -b " BRANCH_S " --depth=1 openpilot"); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
// Cleanup old folder in /data
|
||||||
|
err = system("rm -rf /data/openpilot"); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
// this won't move if /data/openpilot exists
|
||||||
|
err = system("mv /tmp/openpilot /data"); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int do_install() { |
||||||
|
int err; |
||||||
|
|
||||||
|
struct stat sb; |
||||||
|
if (stat(PRE_CHECKOUT_FOLDER, &sb) == 0 && S_ISDIR(sb.st_mode)) { |
||||||
|
printf("Pre-checkout found\n"); |
||||||
|
err = use_pre_checkout(); |
||||||
|
} else { |
||||||
|
printf("Doing fresh clone\n"); |
||||||
|
err = fresh_clone(); |
||||||
|
} |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
|
||||||
|
// Write continue.sh
|
||||||
|
FILE *of = fopen("/data/data/com.termux/files/continue.sh", "wb"); |
||||||
|
assert(of); |
||||||
|
|
||||||
|
size_t num = str_continue_end - str_continue; |
||||||
|
size_t num_written = fwrite(str_continue, 1, num, of); |
||||||
|
if (num != num_written) return 1; |
||||||
|
|
||||||
|
fclose(of); |
||||||
|
|
||||||
|
err = system("chmod +x /data/data/com.termux/files/continue.sh"); |
||||||
|
if(err) return 1; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void * run_spinner(void * args) { |
||||||
|
char *loading_msg = "Installing " BRAND_S; |
||||||
|
char *argv[2] = {NULL, loading_msg}; |
||||||
|
spin(2, argv); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int main() { |
||||||
|
pthread_t spinner_thread; |
||||||
|
assert(pthread_create(&spinner_thread, NULL, run_spinner, NULL) == 0); |
||||||
|
|
||||||
|
int status = do_install(); |
||||||
|
|
||||||
|
return status; |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
set -e |
||||||
|
|
||||||
|
ENVIRONMENT="${1}" |
||||||
|
if [ "${ENVIRONMENT}" != "staging" -a "${ENVIRONMENT}" != "prod" ]; then |
||||||
|
echo "usage: $0 <env>" >&2 |
||||||
|
echo " <env> = staging or prod" >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
SUFFIX="" |
||||||
|
if [ "${ENVIRONMENT}" != "prod" ]; then |
||||||
|
SUFFIX="_test" |
||||||
|
fi |
||||||
|
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" |
||||||
|
|
||||||
|
if [[ -z $(az account show 2>/dev/null) ]]; then |
||||||
|
echo "$(date --rfc-3339=s) LOGIN: azure" |
||||||
|
az login |
||||||
|
fi |
||||||
|
|
||||||
|
FILES=( |
||||||
|
installer_openpilot |
||||||
|
installer_dashcam |
||||||
|
) |
||||||
|
for FILE in ${FILES[@]}; do |
||||||
|
KEY="${FILE}${SUFFIX}" |
||||||
|
echo "$(date --rfc-3339=s) PUSHING: ${FILE} -> ${KEY}" |
||||||
|
az storage blob upload \ |
||||||
|
--account-name commadist \ |
||||||
|
--container-name neosupdate \ |
||||||
|
--name "${KEY}" \ |
||||||
|
--file "${FILE}" |
||||||
|
done |
@ -0,0 +1,3 @@ |
|||||||
|
version https://git-lfs.github.com/spec/v1 |
||||||
|
oid sha256:5345da01b3fc29935587d279916435e7c057e75277eece582234655ef2ae9237 |
||||||
|
size 538912 |
@ -0,0 +1,3 @@ |
|||||||
|
version https://git-lfs.github.com/spec/v1 |
||||||
|
oid sha256:a5cb802fe9e53dcba02d46b42db87bee43d5fee19a03006051fb44dff30dd24f |
||||||
|
size 538912 |
@ -0,0 +1,3 @@ |
|||||||
|
local/ |
||||||
|
prod/ |
||||||
|
staging/ |
@ -0,0 +1,98 @@ |
|||||||
|
CC = clang
|
||||||
|
CXX = clang++
|
||||||
|
|
||||||
|
PHONELIBS = ../../phonelibs
|
||||||
|
|
||||||
|
WARN_FLAGS = -Werror=implicit-function-declaration \
|
||||||
|
-Werror=incompatible-pointer-types \
|
||||||
|
-Werror=int-conversion \
|
||||||
|
-Werror=return-type \
|
||||||
|
-Werror=format-extra-args
|
||||||
|
|
||||||
|
CFLAGS = -std=gnu11 -g -fPIC -O2 $(WARN_FLAGS)
|
||||||
|
CXXFLAGS = -std=c++11 -g -fPIC -O2 $(WARN_FLAGS)
|
||||||
|
|
||||||
|
CURL_FLAGS = -I$(PHONELIBS)/curl/include
|
||||||
|
CURL_LIBS = $(PHONELIBS)/curl/lib/libcurl.a \
|
||||||
|
$(PHONELIBS)/zlib/lib/libz.a
|
||||||
|
|
||||||
|
BORINGSSL_FLAGS = -I$(PHONELIBS)/boringssl/include
|
||||||
|
BORINGSSL_LIBS = $(PHONELIBS)/boringssl/lib/libssl_static.a \
|
||||||
|
$(PHONELIBS)/boringssl/lib/libcrypto_static.a \
|
||||||
|
|
||||||
|
NANOVG_FLAGS = -I$(PHONELIBS)/nanovg
|
||||||
|
|
||||||
|
JSON11_FLAGS = -I$(PHONELIBS)/json11
|
||||||
|
|
||||||
|
OPENGL_LIBS = -lGLESv3
|
||||||
|
|
||||||
|
FRAMEBUFFER_LIBS = -lutils -lgui -lEGL
|
||||||
|
|
||||||
|
.PHONY: all |
||||||
|
all: updater |
||||||
|
|
||||||
|
OBJS = opensans_regular.ttf.o \
|
||||||
|
opensans_semibold.ttf.o \
|
||||||
|
opensans_bold.ttf.o \
|
||||||
|
../../selfdrive/common/touch.o \
|
||||||
|
../../selfdrive/common/framebuffer.o \
|
||||||
|
$(PHONELIBS)/json11/json11.o \
|
||||||
|
$(PHONELIBS)/nanovg/nanovg.o
|
||||||
|
|
||||||
|
DEPS := $(OBJS:.o=.d)
|
||||||
|
|
||||||
|
updater: updater.o $(OBJS) |
||||||
|
@echo "[ LINK ] $@"
|
||||||
|
$(CXX) $(CPPFLAGS) -fPIC -o 'updater' $^ \
|
||||||
|
$(FRAMEBUFFER_LIBS) \
|
||||||
|
$(CURL_LIBS) \
|
||||||
|
$(BORINGSSL_LIBS) \
|
||||||
|
-L/system/vendor/lib64 \
|
||||||
|
$(OPENGL_LIBS) \
|
||||||
|
-lcutils -lm -llog
|
||||||
|
strip updater
|
||||||
|
|
||||||
|
opensans_regular.ttf.o: ../../selfdrive/assets/fonts/opensans_regular.ttf |
||||||
|
@echo "[ bin2o ] $@"
|
||||||
|
cd '$(dir $<)' && ld -r -b binary '$(notdir $<)' -o '$(abspath $@)'
|
||||||
|
|
||||||
|
opensans_bold.ttf.o: ../../selfdrive/assets/fonts/opensans_bold.ttf |
||||||
|
@echo "[ bin2o ] $@"
|
||||||
|
cd '$(dir $<)' && ld -r -b binary '$(notdir $<)' -o '$(abspath $@)'
|
||||||
|
|
||||||
|
opensans_semibold.ttf.o: ../../selfdrive/assets/fonts/opensans_semibold.ttf |
||||||
|
@echo "[ bin2o ] $@"
|
||||||
|
cd '$(dir $<)' && ld -r -b binary '$(notdir $<)' -o '$(abspath $@)'
|
||||||
|
|
||||||
|
%.o: %.c |
||||||
|
mkdir -p $(@D)
|
||||||
|
@echo "[ CC ] $@"
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) \
|
||||||
|
-I../.. \
|
||||||
|
-I$(PHONELIBS)/android_frameworks_native/include \
|
||||||
|
-I$(PHONELIBS)/android_system_core/include \
|
||||||
|
-I$(PHONELIBS)/android_hardware_libhardware/include \
|
||||||
|
$(NANOVG_FLAGS) \
|
||||||
|
-c -o '$@' '$<'
|
||||||
|
|
||||||
|
%.o: %.cc |
||||||
|
mkdir -p $(@D)
|
||||||
|
@echo "[ CXX ] $@"
|
||||||
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) \
|
||||||
|
-I../../selfdrive \
|
||||||
|
-I../../ \
|
||||||
|
-I$(PHONELIBS)/android_frameworks_native/include \
|
||||||
|
-I$(PHONELIBS)/android_system_core/include \
|
||||||
|
-I$(PHONELIBS)/android_hardware_libhardware/include \
|
||||||
|
$(NANOVG_FLAGS) \
|
||||||
|
$(JSON11_FLAGS) \
|
||||||
|
$(CURL_FLAGS) \
|
||||||
|
$(BORINGSSL_FLAGS) \
|
||||||
|
-c -o '$@' '$<'
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: clean |
||||||
|
clean: |
||||||
|
rm -f $(OBJS) $(DEPS)
|
||||||
|
|
||||||
|
-include $(DEPS) |
@ -0,0 +1,7 @@ |
|||||||
|
{ |
||||||
|
"ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-efdf7de63b1aef63d68301e6175930991bf9a5927d16ec6fcc69287e2ee7ca4a.zip", |
||||||
|
"ota_hash": "efdf7de63b1aef63d68301e6175930991bf9a5927d16ec6fcc69287e2ee7ca4a", |
||||||
|
"recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-97c27e6ed04ed6bb0608b845a2d4100912093f9380c3f2ba6b56bccd608e5f6e.img", |
||||||
|
"recovery_len": 15861036, |
||||||
|
"recovery_hash": "97c27e6ed04ed6bb0608b845a2d4100912093f9380c3f2ba6b56bccd608e5f6e" |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
version https://git-lfs.github.com/spec/v1 |
||||||
|
oid sha256:9d5a44fd0dcf94172c1637c951e1da3b52d6a7049c9c12e14a02900a0f9e9aa4 |
||||||
|
size 2468632 |
@ -0,0 +1,768 @@ |
|||||||
|
#include <cstdio> |
||||||
|
#include <cstdlib> |
||||||
|
#include <cstring> |
||||||
|
#include <cassert> |
||||||
|
|
||||||
|
#include <unistd.h> |
||||||
|
#include <sys/stat.h> |
||||||
|
#include <sys/statvfs.h> |
||||||
|
|
||||||
|
#include <string> |
||||||
|
#include <sstream> |
||||||
|
#include <fstream> |
||||||
|
#include <mutex> |
||||||
|
#include <thread> |
||||||
|
|
||||||
|
#include <curl/curl.h> |
||||||
|
#include <openssl/sha.h> |
||||||
|
|
||||||
|
#include <GLES3/gl3.h> |
||||||
|
#include <EGL/egl.h> |
||||||
|
#include <EGL/eglext.h> |
||||||
|
|
||||||
|
#include "nanovg.h" |
||||||
|
#define NANOVG_GLES3_IMPLEMENTATION |
||||||
|
#include "nanovg_gl.h" |
||||||
|
#include "nanovg_gl_utils.h" |
||||||
|
|
||||||
|
#include "json11.hpp" |
||||||
|
|
||||||
|
#include "common/framebuffer.h" |
||||||
|
#include "common/touch.h" |
||||||
|
#include "common/utilpp.h" |
||||||
|
|
||||||
|
#define USER_AGENT "NEOSUpdater-0.2" |
||||||
|
|
||||||
|
#define MANIFEST_URL_EON_STAGING "https://github.com/commaai/eon-neos/raw/master/update.staging.json"
|
||||||
|
#define MANIFEST_URL_EON_LOCAL "http://192.168.5.1:8000/neosupdate/update.local.json"
|
||||||
|
#define MANIFEST_URL_EON "https://github.com/commaai/eon-neos/raw/master/update.json"
|
||||||
|
const char *manifest_url = MANIFEST_URL_EON; |
||||||
|
|
||||||
|
#define RECOVERY_DEV "/dev/block/bootdevice/by-name/recovery" |
||||||
|
#define RECOVERY_COMMAND "/cache/recovery/command" |
||||||
|
|
||||||
|
#define UPDATE_DIR "/data/neoupdate" |
||||||
|
|
||||||
|
extern const uint8_t bin_opensans_regular[] asm("_binary_opensans_regular_ttf_start"); |
||||||
|
extern const uint8_t bin_opensans_regular_end[] asm("_binary_opensans_regular_ttf_end"); |
||||||
|
extern const uint8_t bin_opensans_semibold[] asm("_binary_opensans_semibold_ttf_start"); |
||||||
|
extern const uint8_t bin_opensans_semibold_end[] asm("_binary_opensans_semibold_ttf_end"); |
||||||
|
extern const uint8_t bin_opensans_bold[] asm("_binary_opensans_bold_ttf_start"); |
||||||
|
extern const uint8_t bin_opensans_bold_end[] asm("_binary_opensans_bold_ttf_end"); |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
std::string sha256_file(std::string fn, size_t limit=0) { |
||||||
|
SHA256_CTX ctx; |
||||||
|
SHA256_Init(&ctx); |
||||||
|
|
||||||
|
FILE *file = fopen(fn.c_str(), "rb"); |
||||||
|
if (!file) return ""; |
||||||
|
|
||||||
|
const size_t buf_size = 8192; |
||||||
|
std::unique_ptr<char[]> buffer( new char[ buf_size ] ); |
||||||
|
|
||||||
|
bool read_limit = (limit != 0); |
||||||
|
while (true) { |
||||||
|
size_t read_size = buf_size; |
||||||
|
if (read_limit) read_size = std::min(read_size, limit); |
||||||
|
size_t bytes_read = fread(buffer.get(), 1, read_size, file); |
||||||
|
if (!bytes_read) break; |
||||||
|
|
||||||
|
SHA256_Update(&ctx, buffer.get(), bytes_read); |
||||||
|
|
||||||
|
if (read_limit) { |
||||||
|
limit -= bytes_read; |
||||||
|
if (limit == 0) break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t hash[SHA256_DIGEST_LENGTH]; |
||||||
|
SHA256_Final(hash, &ctx); |
||||||
|
|
||||||
|
fclose(file); |
||||||
|
|
||||||
|
return util::tohex(hash, sizeof(hash)); |
||||||
|
} |
||||||
|
|
||||||
|
size_t download_string_write(void *ptr, size_t size, size_t nmeb, void *up) { |
||||||
|
size_t sz = size * nmeb; |
||||||
|
((std::string*)up)->append((char*)ptr, sz); |
||||||
|
return sz; |
||||||
|
} |
||||||
|
|
||||||
|
std::string download_string(CURL *curl, std::string url) { |
||||||
|
std::string os; |
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); |
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); |
||||||
|
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); |
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT); |
||||||
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); |
||||||
|
curl_easy_setopt(curl, CURLOPT_RESUME_FROM, 0); |
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); |
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_string_write); |
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &os); |
||||||
|
CURLcode res = curl_easy_perform(curl); |
||||||
|
if (res != CURLE_OK) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
return os; |
||||||
|
} |
||||||
|
|
||||||
|
size_t download_file_write(void *ptr, size_t size, size_t nmeb, void *up) { |
||||||
|
return fwrite(ptr, size, nmeb, (FILE*)up); |
||||||
|
} |
||||||
|
|
||||||
|
int battery_capacity() { |
||||||
|
std::string bat_cap_s = util::read_file("/sys/class/power_supply/battery/capacity"); |
||||||
|
return atoi(bat_cap_s.c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
int battery_current() { |
||||||
|
std::string current_now_s = util::read_file("/sys/class/power_supply/battery/current_now"); |
||||||
|
return atoi(current_now_s.c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
bool check_battery() { |
||||||
|
int bat_cap = battery_capacity(); |
||||||
|
int current_now = battery_current(); |
||||||
|
return bat_cap > 35 || (current_now < 0 && bat_cap > 10); |
||||||
|
} |
||||||
|
|
||||||
|
bool check_space() { |
||||||
|
struct statvfs stat; |
||||||
|
if (statvfs("/data/", &stat) != 0) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
size_t space = stat.f_bsize * stat.f_bavail; |
||||||
|
return space > 2000000000ULL; // 2GB
|
||||||
|
} |
||||||
|
|
||||||
|
static void start_settings_activity(const char* name) { |
||||||
|
char launch_cmd[1024]; |
||||||
|
snprintf(launch_cmd, sizeof(launch_cmd), |
||||||
|
"am start -W --ez :settings:show_fragment_as_subsetting true -n 'com.android.settings/.%s'", name); |
||||||
|
system(launch_cmd); |
||||||
|
} |
||||||
|
|
||||||
|
struct Updater { |
||||||
|
bool do_exit = false; |
||||||
|
|
||||||
|
TouchState touch; |
||||||
|
|
||||||
|
int fb_w, fb_h; |
||||||
|
|
||||||
|
FramebufferState *fb = NULL; |
||||||
|
NVGcontext *vg = NULL; |
||||||
|
int font_regular; |
||||||
|
int font_semibold; |
||||||
|
int font_bold; |
||||||
|
|
||||||
|
std::thread update_thread_handle; |
||||||
|
|
||||||
|
std::mutex lock; |
||||||
|
|
||||||
|
// i hate state machines give me coroutines already
|
||||||
|
enum UpdateState { |
||||||
|
CONFIRMATION, |
||||||
|
LOW_BATTERY, |
||||||
|
RUNNING, |
||||||
|
ERROR, |
||||||
|
}; |
||||||
|
UpdateState state; |
||||||
|
|
||||||
|
std::string progress_text; |
||||||
|
float progress_frac; |
||||||
|
|
||||||
|
std::string error_text; |
||||||
|
|
||||||
|
std::string low_battery_text; |
||||||
|
std::string low_battery_title; |
||||||
|
std::string low_battery_context; |
||||||
|
std::string battery_cap_text; |
||||||
|
int min_battery_cap = 35; |
||||||
|
|
||||||
|
// button
|
||||||
|
int b_x, b_w, b_y, b_h; |
||||||
|
int balt_x; |
||||||
|
|
||||||
|
CURL *curl = NULL; |
||||||
|
|
||||||
|
Updater() { |
||||||
|
touch_init(&touch); |
||||||
|
|
||||||
|
fb = framebuffer_init("updater", 0x00001000, false, |
||||||
|
&fb_w, &fb_h); |
||||||
|
assert(fb); |
||||||
|
|
||||||
|
vg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG); |
||||||
|
assert(vg); |
||||||
|
|
||||||
|
font_regular = nvgCreateFontMem(vg, "opensans_regular", (unsigned char*)bin_opensans_regular, (bin_opensans_regular_end - bin_opensans_regular), 0); |
||||||
|
assert(font_regular >= 0); |
||||||
|
|
||||||
|
font_semibold = nvgCreateFontMem(vg, "opensans_semibold", (unsigned char*)bin_opensans_semibold, (bin_opensans_semibold_end - bin_opensans_semibold), 0); |
||||||
|
assert(font_semibold >= 0); |
||||||
|
|
||||||
|
font_bold = nvgCreateFontMem(vg, "opensans_bold", (unsigned char*)bin_opensans_bold, (bin_opensans_bold_end - bin_opensans_bold), 0); |
||||||
|
assert(font_bold >= 0); |
||||||
|
|
||||||
|
b_w = 640; |
||||||
|
balt_x = 200; |
||||||
|
b_x = fb_w-b_w-200; |
||||||
|
b_y = 720; |
||||||
|
b_h = 220; |
||||||
|
|
||||||
|
state = CONFIRMATION; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
int download_file_xferinfo(curl_off_t dltotal, curl_off_t dlno, |
||||||
|
curl_off_t ultotal, curl_off_t ulnow) { |
||||||
|
{ |
||||||
|
std::lock_guard<std::mutex> guard(lock); |
||||||
|
if (dltotal != 0) { |
||||||
|
progress_frac = (float) dlno / dltotal; |
||||||
|
} |
||||||
|
} |
||||||
|
// printf("info: %ld %ld %f\n", dltotal, dlno, progress_frac);
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool download_file(std::string url, std::string out_fn) { |
||||||
|
FILE *of = fopen(out_fn.c_str(), "ab"); |
||||||
|
assert(of); |
||||||
|
|
||||||
|
CURLcode res; |
||||||
|
long last_resume_from = 0; |
||||||
|
|
||||||
|
fseek(of, 0, SEEK_END); |
||||||
|
|
||||||
|
int tries = 4; |
||||||
|
|
||||||
|
bool ret = false; |
||||||
|
|
||||||
|
while (true) { |
||||||
|
long resume_from = ftell(of); |
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); |
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); |
||||||
|
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); |
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT); |
||||||
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); |
||||||
|
curl_easy_setopt(curl, CURLOPT_RESUME_FROM, resume_from); |
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_file_write); |
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, of); |
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); |
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); |
||||||
|
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, &Updater::download_file_xferinfo); |
||||||
|
|
||||||
|
CURLcode res = curl_easy_perform(curl); |
||||||
|
|
||||||
|
long response_code = 0; |
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); |
||||||
|
|
||||||
|
// double content_length = 0.0;
|
||||||
|
// curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length);
|
||||||
|
|
||||||
|
printf("download %s res %d, code %ld, resume from %ld\n", url.c_str(), res, response_code, resume_from); |
||||||
|
if (res == CURLE_OK) { |
||||||
|
ret = true; |
||||||
|
break; |
||||||
|
} else if (res == CURLE_HTTP_RETURNED_ERROR && response_code == 416) { |
||||||
|
// failed because the file is already complete?
|
||||||
|
ret = true; |
||||||
|
break; |
||||||
|
} else if (resume_from == last_resume_from) { |
||||||
|
// failed and dind't make make forward progress. only retry a couple times
|
||||||
|
tries--; |
||||||
|
if (tries <= 0) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
last_resume_from = resume_from; |
||||||
|
} |
||||||
|
// printf("res %d\n", res);
|
||||||
|
|
||||||
|
// printf("- %ld %f\n", response_code, content_length);
|
||||||
|
|
||||||
|
fclose(of); |
||||||
|
|
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
void set_progress(std::string text) { |
||||||
|
std::lock_guard<std::mutex> guard(lock); |
||||||
|
progress_text = text; |
||||||
|
} |
||||||
|
|
||||||
|
void set_error(std::string text) { |
||||||
|
std::lock_guard<std::mutex> guard(lock); |
||||||
|
error_text = text; |
||||||
|
state = ERROR; |
||||||
|
} |
||||||
|
|
||||||
|
void set_battery_low() { |
||||||
|
std::lock_guard<std::mutex> guard(lock); |
||||||
|
state = LOW_BATTERY; |
||||||
|
} |
||||||
|
|
||||||
|
void set_running() { |
||||||
|
std::lock_guard<std::mutex> guard(lock); |
||||||
|
state = RUNNING; |
||||||
|
} |
||||||
|
|
||||||
|
std::string stage_download(std::string url, std::string hash, std::string name) { |
||||||
|
std::string out_fn = UPDATE_DIR "/" + util::base_name(url); |
||||||
|
|
||||||
|
set_progress("Downloading " + name + "..."); |
||||||
|
bool r = download_file(url, out_fn); |
||||||
|
if (!r) { |
||||||
|
set_error("failed to download " + name); |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
set_progress("Verifying " + name + "..."); |
||||||
|
std::string fn_hash = sha256_file(out_fn); |
||||||
|
printf("got %s hash: %s\n", name.c_str(), hash.c_str()); |
||||||
|
if (fn_hash != hash) { |
||||||
|
set_error(name + " was corrupt"); |
||||||
|
unlink(out_fn.c_str()); |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
return out_fn; |
||||||
|
} |
||||||
|
|
||||||
|
void run_stages() { |
||||||
|
curl = curl_easy_init(); |
||||||
|
assert(curl); |
||||||
|
|
||||||
|
if (!check_battery()) { |
||||||
|
set_battery_low(); |
||||||
|
int battery_cap = battery_capacity(); |
||||||
|
while(battery_cap < min_battery_cap) { |
||||||
|
battery_cap = battery_capacity(); |
||||||
|
battery_cap_text = std::to_string(battery_cap); |
||||||
|
usleep(1000000); |
||||||
|
} |
||||||
|
set_running(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!check_space()) { |
||||||
|
set_error("2GB of free space required to update"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
mkdir(UPDATE_DIR, 0777); |
||||||
|
|
||||||
|
const int EON = (access("/EON", F_OK) != -1); |
||||||
|
|
||||||
|
set_progress("Finding latest version..."); |
||||||
|
std::string manifest_s; |
||||||
|
if (EON) { |
||||||
|
manifest_s = download_string(curl, manifest_url); |
||||||
|
} else { |
||||||
|
// don't update NEO
|
||||||
|
exit(0); |
||||||
|
} |
||||||
|
|
||||||
|
printf("manifest: %s\n", manifest_s.c_str()); |
||||||
|
|
||||||
|
std::string err; |
||||||
|
auto manifest = json11::Json::parse(manifest_s, err); |
||||||
|
if (manifest.is_null() || !err.empty()) { |
||||||
|
set_error("failed to load update manifest"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
std::string ota_url = manifest["ota_url"].string_value(); |
||||||
|
std::string ota_hash = manifest["ota_hash"].string_value(); |
||||||
|
|
||||||
|
std::string recovery_url = manifest["recovery_url"].string_value(); |
||||||
|
std::string recovery_hash = manifest["recovery_hash"].string_value(); |
||||||
|
int recovery_len = manifest["recovery_len"].int_value(); |
||||||
|
|
||||||
|
// std::string installer_url = manifest["installer_url"].string_value();
|
||||||
|
// std::string installer_hash = manifest["installer_hash"].string_value();
|
||||||
|
|
||||||
|
if (ota_url.empty() || ota_hash.empty()) { |
||||||
|
set_error("invalid update manifest"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// std::string installer_fn = stage_download(installer_url, installer_hash, "installer");
|
||||||
|
// if (installer_fn.empty()) {
|
||||||
|
// //error'd
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
std::string recovery_fn; |
||||||
|
if (recovery_url.empty() || recovery_hash.empty() || recovery_len == 0) { |
||||||
|
set_progress("Skipping recovery flash..."); |
||||||
|
} else { |
||||||
|
// only download the recovery if it differs from what's flashed
|
||||||
|
set_progress("Checking recovery..."); |
||||||
|
std::string existing_recovery_hash = sha256_file(RECOVERY_DEV, recovery_len); |
||||||
|
printf("existing recovery hash: %s\n", existing_recovery_hash.c_str()); |
||||||
|
|
||||||
|
if (existing_recovery_hash != recovery_hash) { |
||||||
|
recovery_fn = stage_download(recovery_url, recovery_hash, "recovery"); |
||||||
|
if (recovery_fn.empty()) { |
||||||
|
// error'd
|
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string ota_fn = stage_download(ota_url, ota_hash, "update"); |
||||||
|
if (ota_fn.empty()) { |
||||||
|
//error'd
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!check_battery()) { |
||||||
|
set_battery_low(); |
||||||
|
int battery_cap = battery_capacity(); |
||||||
|
while(battery_cap < min_battery_cap) { |
||||||
|
battery_cap = battery_capacity(); |
||||||
|
battery_cap_text = std::to_string(battery_cap); |
||||||
|
usleep(1000000); |
||||||
|
} |
||||||
|
set_running(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!recovery_fn.empty()) { |
||||||
|
// flash recovery
|
||||||
|
set_progress("Flashing recovery..."); |
||||||
|
|
||||||
|
FILE *flash_file = fopen(recovery_fn.c_str(), "rb"); |
||||||
|
if (!flash_file) { |
||||||
|
set_error("failed to flash recovery"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
FILE *recovery_dev = fopen(RECOVERY_DEV, "w+b"); |
||||||
|
if (!recovery_dev) { |
||||||
|
fclose(flash_file); |
||||||
|
set_error("failed to flash recovery"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const size_t buf_size = 4096; |
||||||
|
std::unique_ptr<char[]> buffer( new char[ buf_size ] ); |
||||||
|
|
||||||
|
while (true) { |
||||||
|
size_t bytes_read = fread(buffer.get(), 1, buf_size, flash_file); |
||||||
|
if (!bytes_read) break; |
||||||
|
|
||||||
|
size_t bytes_written = fwrite(buffer.get(), 1, bytes_read, recovery_dev); |
||||||
|
if (bytes_read != bytes_written) { |
||||||
|
fclose(recovery_dev); |
||||||
|
fclose(flash_file); |
||||||
|
set_error("failed to flash recovery: write failed"); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fclose(recovery_dev); |
||||||
|
fclose(flash_file); |
||||||
|
|
||||||
|
set_progress("Verifying flash..."); |
||||||
|
std::string new_recovery_hash = sha256_file(RECOVERY_DEV, recovery_len); |
||||||
|
printf("new recovery hash: %s\n", new_recovery_hash.c_str()); |
||||||
|
|
||||||
|
if (new_recovery_hash != recovery_hash) { |
||||||
|
set_error("recovery flash corrupted"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// write arguments to recovery
|
||||||
|
FILE *cmd_file = fopen(RECOVERY_COMMAND, "wb"); |
||||||
|
if (!cmd_file) { |
||||||
|
set_error("failed to reboot into recovery"); |
||||||
|
return; |
||||||
|
} |
||||||
|
fprintf(cmd_file, "--update_package=%s\n", ota_fn.c_str()); |
||||||
|
fclose(cmd_file); |
||||||
|
|
||||||
|
set_progress("Rebooting"); |
||||||
|
|
||||||
|
// remove the continue.sh so we come back into the setup.
|
||||||
|
// maybe we should go directly into the installer, but what if we don't come back with internet? :/
|
||||||
|
//unlink("/data/data/com.termux/files/continue.sh");
|
||||||
|
|
||||||
|
// TODO: this should be generic between android versions
|
||||||
|
// IPowerManager.reboot(confirm=false, reason="recovery", wait=true)
|
||||||
|
system("service call power 16 i32 0 s16 recovery i32 1"); |
||||||
|
while(1) pause(); |
||||||
|
|
||||||
|
// execl("/system/bin/reboot", "recovery");
|
||||||
|
// set_error("failed to reboot into recovery");
|
||||||
|
} |
||||||
|
|
||||||
|
void draw_ack_screen(const char *title, const char *message, const char *button, const char *altbutton) { |
||||||
|
nvgFillColor(vg, nvgRGBA(255,255,255,255)); |
||||||
|
nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); |
||||||
|
|
||||||
|
nvgFontFace(vg, "opensans_bold"); |
||||||
|
nvgFontSize(vg, 120.0f); |
||||||
|
nvgTextBox(vg, 110, 220, fb_w-240, title, NULL); |
||||||
|
|
||||||
|
nvgFontFace(vg, "opensans_regular"); |
||||||
|
nvgFontSize(vg, 86.0f); |
||||||
|
nvgTextBox(vg, 130, 380, fb_w-260, message, NULL); |
||||||
|
|
||||||
|
// draw button
|
||||||
|
if (button) { |
||||||
|
nvgBeginPath(vg); |
||||||
|
nvgFillColor(vg, nvgRGBA(8, 8, 8, 255)); |
||||||
|
nvgRoundedRect(vg, b_x, b_y, b_w, b_h, 20); |
||||||
|
nvgFill(vg); |
||||||
|
|
||||||
|
nvgFillColor(vg, nvgRGBA(255, 255, 255, 255)); |
||||||
|
nvgFontFace(vg, "opensans_semibold"); |
||||||
|
nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); |
||||||
|
nvgText(vg, b_x+b_w/2, b_y+b_h/2, button, NULL); |
||||||
|
|
||||||
|
nvgBeginPath(vg); |
||||||
|
nvgStrokeColor(vg, nvgRGBA(255, 255, 255, 50)); |
||||||
|
nvgStrokeWidth(vg, 5); |
||||||
|
nvgRoundedRect(vg, b_x, b_y, b_w, b_h, 20); |
||||||
|
nvgStroke(vg); |
||||||
|
} |
||||||
|
|
||||||
|
// draw button
|
||||||
|
if (altbutton) { |
||||||
|
nvgBeginPath(vg); |
||||||
|
nvgFillColor(vg, nvgRGBA(8, 8, 8, 255)); |
||||||
|
nvgRoundedRect(vg, balt_x, b_y, b_w, b_h, 20); |
||||||
|
nvgFill(vg); |
||||||
|
|
||||||
|
nvgFillColor(vg, nvgRGBA(255, 255, 255, 255)); |
||||||
|
nvgFontFace(vg, "opensans_semibold"); |
||||||
|
nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE); |
||||||
|
nvgText(vg, balt_x+b_w/2, b_y+b_h/2, altbutton, NULL); |
||||||
|
|
||||||
|
nvgBeginPath(vg); |
||||||
|
nvgStrokeColor(vg, nvgRGBA(255, 255, 255, 50)); |
||||||
|
nvgStrokeWidth(vg, 5); |
||||||
|
nvgRoundedRect(vg, balt_x, b_y, b_w, b_h, 20); |
||||||
|
nvgStroke(vg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void draw_battery_screen() { |
||||||
|
low_battery_title = "Low Battery"; |
||||||
|
low_battery_text = "Please connect EON to your charger. Update will continue once EON battery reaches 35%."; |
||||||
|
low_battery_context = "Current battery charge: " + battery_cap_text + "%"; |
||||||
|
|
||||||
|
nvgFillColor(vg, nvgRGBA(255,255,255,255)); |
||||||
|
nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); |
||||||
|
|
||||||
|
nvgFontFace(vg, "opensans_bold"); |
||||||
|
nvgFontSize(vg, 120.0f); |
||||||
|
nvgTextBox(vg, 110, 220, fb_w-240, low_battery_title.c_str(), NULL); |
||||||
|
|
||||||
|
nvgFontFace(vg, "opensans_regular"); |
||||||
|
nvgFontSize(vg, 86.0f); |
||||||
|
nvgTextBox(vg, 130, 380, fb_w-260, low_battery_text.c_str(), NULL); |
||||||
|
|
||||||
|
nvgFontFace(vg, "opensans_bold"); |
||||||
|
nvgFontSize(vg, 86.0f); |
||||||
|
nvgTextBox(vg, 130, 700, fb_w-260, low_battery_context.c_str(), NULL); |
||||||
|
} |
||||||
|
|
||||||
|
void draw_progress_screen() { |
||||||
|
// draw progress message
|
||||||
|
nvgFontSize(vg, 64.0f); |
||||||
|
nvgFillColor(vg, nvgRGBA(255,255,255,255)); |
||||||
|
nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); |
||||||
|
nvgFontFace(vg, "opensans_bold"); |
||||||
|
nvgFontSize(vg, 86.0f); |
||||||
|
nvgTextBox(vg, 0, 380, fb_w, progress_text.c_str(), NULL); |
||||||
|
|
||||||
|
// draw progress bar
|
||||||
|
{ |
||||||
|
int progress_width = 1000; |
||||||
|
int progress_x = fb_w/2-progress_width/2; |
||||||
|
int progress_y = 520; |
||||||
|
int progress_height = 50; |
||||||
|
|
||||||
|
int powerprompt_y = 312; |
||||||
|
nvgFontFace(vg, "opensans_regular"); |
||||||
|
nvgFontSize(vg, 64.0f); |
||||||
|
nvgText(vg, fb_w/2, 740, "Ensure EON is connected to power.", NULL); |
||||||
|
|
||||||
|
NVGpaint paint = nvgBoxGradient( |
||||||
|
vg, progress_x + 1, progress_y + 1, |
||||||
|
progress_width - 2, progress_height, 3, 4, nvgRGB(27, 27, 27), nvgRGB(27, 27, 27)); |
||||||
|
nvgBeginPath(vg); |
||||||
|
nvgRoundedRect(vg, progress_x, progress_y, progress_width, progress_height, 12); |
||||||
|
nvgFillPaint(vg, paint); |
||||||
|
nvgFill(vg); |
||||||
|
|
||||||
|
float value = std::min(std::max(0.0f, progress_frac), 1.0f); |
||||||
|
int bar_pos = ((progress_width - 2) * value); |
||||||
|
|
||||||
|
paint = nvgBoxGradient( |
||||||
|
vg, progress_x, progress_y, |
||||||
|
bar_pos+1.5f, progress_height-1, 3, 4, |
||||||
|
nvgRGB(245, 245, 245), nvgRGB(105, 105, 105)); |
||||||
|
|
||||||
|
nvgBeginPath(vg); |
||||||
|
nvgRoundedRect( |
||||||
|
vg, progress_x+1, progress_y+1, |
||||||
|
bar_pos, progress_height-2, 12); |
||||||
|
nvgFillPaint(vg, paint); |
||||||
|
nvgFill(vg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ui_draw() { |
||||||
|
std::lock_guard<std::mutex> guard(lock); |
||||||
|
|
||||||
|
nvgBeginFrame(vg, fb_w, fb_h, 1.0f); |
||||||
|
|
||||||
|
switch (state) { |
||||||
|
case CONFIRMATION: |
||||||
|
draw_ack_screen("An update to NEOS is required.", |
||||||
|
"Your device will now be reset and upgraded. You may want to connect to wifi as download is around 1 GB. Existing data on device should not be lost.", |
||||||
|
"Continue", |
||||||
|
"Connect to WiFi"); |
||||||
|
break; |
||||||
|
case LOW_BATTERY: |
||||||
|
draw_battery_screen(); |
||||||
|
break; |
||||||
|
case RUNNING: |
||||||
|
draw_progress_screen(); |
||||||
|
break; |
||||||
|
case ERROR: |
||||||
|
draw_ack_screen("There was an error", (error_text).c_str(), NULL, "Reboot"); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
nvgEndFrame(vg); |
||||||
|
} |
||||||
|
|
||||||
|
void ui_update() { |
||||||
|
std::lock_guard<std::mutex> guard(lock); |
||||||
|
|
||||||
|
switch (state) { |
||||||
|
case ERROR: |
||||||
|
case CONFIRMATION: { |
||||||
|
int touch_x = -1, touch_y = -1; |
||||||
|
int res = touch_poll(&touch, &touch_x, &touch_y, 0); |
||||||
|
if (res == 1 && !is_settings_active()) { |
||||||
|
if (touch_x >= b_x && touch_x < b_x+b_w && touch_y >= b_y && touch_y < b_y+b_h) { |
||||||
|
if (state == CONFIRMATION) { |
||||||
|
state = RUNNING; |
||||||
|
update_thread_handle = std::thread(&Updater::run_stages, this); |
||||||
|
} |
||||||
|
} |
||||||
|
if (touch_x >= balt_x && touch_x < balt_x+b_w && touch_y >= b_y && touch_y < b_y+b_h) { |
||||||
|
if (state == CONFIRMATION) { |
||||||
|
start_settings_activity("Settings$WifiSettingsActivity"); |
||||||
|
} else if (state == ERROR) { |
||||||
|
do_exit = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void go() { |
||||||
|
while (!do_exit) { |
||||||
|
ui_update(); |
||||||
|
|
||||||
|
glClearColor(0.08, 0.08, 0.08, 1.0); |
||||||
|
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); |
||||||
|
|
||||||
|
// background
|
||||||
|
nvgBeginPath(vg); |
||||||
|
NVGpaint bg = nvgLinearGradient(vg, fb_w, 0, fb_w, fb_h, |
||||||
|
nvgRGBA(0, 0, 0, 0), nvgRGBA(0, 0, 0, 255)); |
||||||
|
nvgFillPaint(vg, bg); |
||||||
|
nvgRect(vg, 0, 0, fb_w, fb_h); |
||||||
|
nvgFill(vg); |
||||||
|
|
||||||
|
glEnable(GL_BLEND); |
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
||||||
|
|
||||||
|
ui_draw(); |
||||||
|
|
||||||
|
glDisable(GL_BLEND); |
||||||
|
|
||||||
|
framebuffer_swap(fb); |
||||||
|
|
||||||
|
assert(glGetError() == GL_NO_ERROR); |
||||||
|
|
||||||
|
// no simple way to do 30fps vsync with surfaceflinger...
|
||||||
|
usleep(30000); |
||||||
|
} |
||||||
|
|
||||||
|
if (update_thread_handle.joinable()) { |
||||||
|
update_thread_handle.join(); |
||||||
|
} |
||||||
|
|
||||||
|
system("service call power 16 i32 0 i32 0 i32 1"); |
||||||
|
} |
||||||
|
|
||||||
|
bool is_settings_active() { |
||||||
|
FILE *fp; |
||||||
|
char sys_output[4096]; |
||||||
|
|
||||||
|
fp = popen("/bin/dumpsys window windows", "r"); |
||||||
|
if (fp == NULL) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool active = false; |
||||||
|
while (fgets(sys_output, sizeof(sys_output), fp) != NULL) { |
||||||
|
if (strstr(sys_output, "mCurrentFocus=null") != NULL) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (strstr(sys_output, "mCurrentFocus=Window") != NULL) { |
||||||
|
active = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pclose(fp); |
||||||
|
|
||||||
|
return active; |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
int main(int argc, char *argv[]) { |
||||||
|
if (argc > 1) { |
||||||
|
if (strcmp(argv[1], "local") == 0) { |
||||||
|
manifest_url = MANIFEST_URL_EON_LOCAL; |
||||||
|
} else if (strcmp(argv[1], "staging") == 0) { |
||||||
|
manifest_url = MANIFEST_URL_EON_STAGING; |
||||||
|
} else { |
||||||
|
manifest_url = argv[1]; |
||||||
|
} |
||||||
|
} |
||||||
|
printf("updating from %s\n", manifest_url); |
||||||
|
Updater updater; |
||||||
|
updater.go(); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue