Update to AOSP 8.0 base
Change-Id: I29fe722b4eb9718765327902779046840a01433e
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
BasedOnStyle: Google
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
|
||||
ColumnLimit: 100
|
||||
CommentPragmas: NOLINT:.*
|
||||
DerivePointerAlignment: false
|
||||
IndentWidth: 2
|
||||
PointerAlignment: Left
|
||||
TabWidth: 2
|
||||
UseTab: Never
|
||||
PenaltyExcessCharacter: 32
|
||||
|
||||
Cpp11BracedListStyle: false
|
||||
+73
-19
@@ -104,24 +104,25 @@ LOCAL_CLANG := true
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
system/vold \
|
||||
system/extras/ext4_utils \
|
||||
system/extras \
|
||||
system/core/adb \
|
||||
system/core/libsparse \
|
||||
external/zlib
|
||||
external/zlib \
|
||||
$(LOCAL_PATH)/bootloader_message_twrp/include
|
||||
|
||||
LOCAL_C_INCLUDES += bionic
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
|
||||
LOCAL_C_INCLUDES += external/stlport/stlport external/openssl/include
|
||||
else
|
||||
LOCAL_C_INCLUDES += external/boringssl/include
|
||||
LOCAL_C_INCLUDES += external/boringssl/include external/libcxx/include
|
||||
endif
|
||||
|
||||
LOCAL_STATIC_LIBRARIES :=
|
||||
LOCAL_SHARED_LIBRARIES :=
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += libguitwrp
|
||||
LOCAL_SHARED_LIBRARIES += libaosprecovery libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libtwadbbu libbootloader_message
|
||||
LOCAL_SHARED_LIBRARIES += libcrecovery libtwadbbu libtwrpdigest
|
||||
LOCAL_SHARED_LIBRARIES += libaosprecovery libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libtwadbbu libbootloader_message_twrp
|
||||
LOCAL_SHARED_LIBRARIES += libcrecovery libtwadbbu libtwrpdigest libc++
|
||||
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libstlport
|
||||
@@ -132,13 +133,20 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0)
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/libmincrypt/includes
|
||||
LOCAL_CFLAGS += -DUSE_OLD_VERIFIER
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libc++ libcrypto
|
||||
LOCAL_SHARED_LIBRARIES += libcrypto
|
||||
endif
|
||||
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libbase
|
||||
endif
|
||||
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libziparchive
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libminzip
|
||||
LOCAL_CFLAGS += -DUSE_MINZIP
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard system/core/libsparse/Android.mk),)
|
||||
LOCAL_SHARED_LIBRARIES += libsparse
|
||||
endif
|
||||
@@ -161,14 +169,14 @@ ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
|
||||
endif
|
||||
LOCAL_C_INCLUDES += external/libselinux/include
|
||||
LOCAL_SHARED_LIBRARIES += libselinux
|
||||
ifneq ($(TARGET_USERIMAGES_USE_EXT4), true)
|
||||
LOCAL_CFLAGS += -DUSE_EXT4
|
||||
LOCAL_C_INCLUDES += system/extras/ext4_utils
|
||||
LOCAL_SHARED_LIBRARIES += libext4_utils
|
||||
ifneq ($(wildcard external/lz4/Android.mk),)
|
||||
LOCAL_STATIC_LIBRARIES += liblz4
|
||||
endif
|
||||
endif
|
||||
#ifneq ($(TARGET_USERIMAGES_USE_EXT4), true)
|
||||
# LOCAL_CFLAGS += -DUSE_EXT4
|
||||
# LOCAL_C_INCLUDES += system/extras/ext4_utils
|
||||
# LOCAL_SHARED_LIBRARIES += libext4_utils
|
||||
# ifneq ($(wildcard external/lz4/Android.mk),)
|
||||
# LOCAL_STATIC_LIBRARIES += liblz4
|
||||
# endif
|
||||
#endif
|
||||
|
||||
ifeq ($(AB_OTA_UPDATER),true)
|
||||
LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
|
||||
@@ -304,7 +312,7 @@ ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
endif
|
||||
endif
|
||||
WITH_CRYPTO_UTILS := \
|
||||
$(if $(wildcard system/core/libcrypto_utils/Android.mk),true)
|
||||
$(if $(wildcard system/core/libcrypto_utils/android_pubkey.c),true)
|
||||
ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true)
|
||||
LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
|
||||
endif
|
||||
@@ -370,7 +378,8 @@ LOCAL_ADDITIONAL_DEPENDENCIES += \
|
||||
mkfs.fat \
|
||||
permissive.sh \
|
||||
simg2img_twrp \
|
||||
libbootloader_message \
|
||||
libbootloader_message_twrp \
|
||||
init.recovery.hlthchrg.rc \
|
||||
init.recovery.service.rc
|
||||
|
||||
ifneq ($(TARGET_ARCH), arm64)
|
||||
@@ -589,6 +598,37 @@ else
|
||||
endif
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
# libmounts (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := mounts.cpp
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
|
||||
LOCAL_MODULE := libmounts
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# librecovery (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
install.cpp
|
||||
LOCAL_CFLAGS := -Wno-unused-parameter -Werror
|
||||
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
|
||||
|
||||
ifeq ($(AB_OTA_UPDATER),true)
|
||||
LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
|
||||
endif
|
||||
|
||||
LOCAL_MODULE := librecovery
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libminui \
|
||||
libvintf_recovery \
|
||||
libcrypto_utils \
|
||||
libcrypto \
|
||||
libbase
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# shared libaosprecovery for Apache code
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
@@ -596,7 +636,7 @@ include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libaosprecovery
|
||||
LOCAL_MODULE_TAGS := eng optional
|
||||
LOCAL_CFLAGS := -std=gnu++0x
|
||||
LOCAL_SRC_FILES := adb_install.cpp asn1_decoder.cpp legacy_property_service.cpp set_metadata.cpp tw_atomic.cpp installcommand.cpp
|
||||
LOCAL_SRC_FILES := adb_install.cpp legacy_property_service.cpp set_metadata.cpp tw_atomic.cpp installcommand.cpp zipwrap.cpp
|
||||
LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils libfusesideload libselinux libminzip
|
||||
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
|
||||
@@ -608,15 +648,23 @@ endif
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libmincrypttwrp
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/libmincrypt/includes
|
||||
LOCAL_SRC_FILES += verifier24/verifier.cpp
|
||||
LOCAL_SRC_FILES += verifier24/verifier.cpp verifier24/asn1_decoder.cpp
|
||||
LOCAL_CFLAGS += -DUSE_OLD_VERIFIER
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libcrypto libbase
|
||||
LOCAL_SRC_FILES += verifier.cpp
|
||||
LOCAL_SRC_FILES += verifier.cpp asn1_decoder.cpp
|
||||
endif
|
||||
ifeq ($(AB_OTA_UPDATER),true)
|
||||
LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
|
||||
endif
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
|
||||
LOCAL_SRC_FILES += otautil/ZipUtil.cpp otautil/SysUtil.cpp
|
||||
LOCAL_SHARED_LIBRARIES += libziparchive libext4_utils libcrypto libcrypto_utils
|
||||
LOCAL_STATIC_LIBRARIES += libvintf_recovery libfs_mgr liblogwrap libavb libvintf libtinyxml2 libz
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES +=
|
||||
else
|
||||
LOCAL_CFLAGS += -DUSE_MINZIP
|
||||
endif
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -638,6 +686,7 @@ include $(LOCAL_PATH)/tests/Android.mk \
|
||||
$(LOCAL_PATH)/edify/Android.mk \
|
||||
$(LOCAL_PATH)/otafault/Android.mk \
|
||||
$(LOCAL_PATH)/bootloader_message/Android.mk \
|
||||
$(LOCAL_PATH)/bootloader_message_twrp/Android.mk \
|
||||
$(LOCAL_PATH)/updater/Android.mk \
|
||||
$(LOCAL_PATH)/update_verifier/Android.mk \
|
||||
$(LOCAL_PATH)/applypatch/Android.mk
|
||||
@@ -647,6 +696,10 @@ ifeq ($(wildcard system/core/uncrypt/Android.mk),)
|
||||
endif
|
||||
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0)
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0)
|
||||
TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_WITH_DATA
|
||||
CLANG_TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_WITH_DATA
|
||||
endif
|
||||
include $(commands_recovery_local_path)/minadbd/Android.mk \
|
||||
$(commands_recovery_local_path)/minui/Android.mk
|
||||
else
|
||||
@@ -669,6 +722,7 @@ include $(commands_recovery_local_path)/injecttwrp/Android.mk \
|
||||
$(commands_recovery_local_path)/libcrecovery/Android.mk \
|
||||
$(commands_recovery_local_path)/libblkid/Android.mk \
|
||||
$(commands_recovery_local_path)/minuitwrp/Android.mk \
|
||||
$(commands_recovery_local_path)/otautil/Android.mk \
|
||||
$(commands_recovery_local_path)/openaes/Android.mk \
|
||||
$(commands_recovery_local_path)/toolbox/Android.mk \
|
||||
$(commands_recovery_local_path)/twrpTarMain/Android.mk \
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
**Team Win Recovery Project (TWRP)**
|
||||
|
||||
You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide").
|
||||
|
||||
|
||||
+13
-10
@@ -28,6 +28,8 @@
|
||||
|
||||
#include "ui.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "install.h"
|
||||
#include "common.h"
|
||||
#include "adb_install.h"
|
||||
#include "minadbd/fuse_adb_provider.h"
|
||||
#include "fuse_sideload.h"
|
||||
@@ -37,10 +39,7 @@
|
||||
#include "verifier.h"
|
||||
#endif
|
||||
|
||||
static RecoveryUI* ui = NULL;
|
||||
|
||||
void
|
||||
set_usb_driver(bool enabled) {
|
||||
static void set_usb_driver(bool enabled) {
|
||||
int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY);
|
||||
if (fd < 0) {
|
||||
/* These error messages show when built in older Android branches (e.g. Gingerbread)
|
||||
@@ -65,20 +64,18 @@ set_usb_driver(bool enabled) {
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stop_adbd() {
|
||||
static void stop_adbd() {
|
||||
printf("Stopping adbd...\n");
|
||||
property_set("ctl.stop", "adbd");
|
||||
set_usb_driver(false);
|
||||
}
|
||||
|
||||
bool is_ro_debuggable() {
|
||||
static bool is_ro_debuggable() {
|
||||
char value[PROPERTY_VALUE_MAX+1];
|
||||
return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1');
|
||||
}
|
||||
|
||||
void
|
||||
maybe_restart_adbd() {
|
||||
char value[PROPERTY_VALUE_MAX+1];
|
||||
static void maybe_restart_adbd() {
|
||||
if (is_ro_debuggable()) {
|
||||
printf("Restarting adbd...\n");
|
||||
set_usb_driver(true);
|
||||
@@ -96,6 +93,12 @@ apply_from_adb(const char* install_file, pid_t* child_pid) {
|
||||
stop_adbd();
|
||||
set_usb_driver(true);
|
||||
/*
|
||||
int apply_from_adb(RecoveryUI* ui, bool* wipe_cache, const char* install_file) {
|
||||
modified_flash = true;
|
||||
|
||||
stop_adbd(ui);
|
||||
set_usb_driver(ui, true);
|
||||
|
||||
ui->Print("\n\nNow send the package you want to apply\n"
|
||||
"to the device with \"adb sideload <filename>\"...\n");
|
||||
*/
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@
|
||||
|
||||
//class RecoveryUI;
|
||||
|
||||
void set_usb_driver(bool enabled);
|
||||
void maybe_restart_adbd();
|
||||
static void set_usb_driver(bool enabled);
|
||||
static void maybe_restart_adbd();
|
||||
int apply_from_adb(const char* install_file, pid_t* child_pid);
|
||||
|
||||
#endif
|
||||
|
||||
+151
-15
@@ -31,6 +31,7 @@ $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
|
||||
)
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(LOCAL_PATH)/include \
|
||||
external/bzip2 \
|
||||
external/zlib \
|
||||
$(commands_recovery_local_path)
|
||||
@@ -42,27 +43,99 @@ LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_C_INCLUDES += $(RECOVERY_PATH)
|
||||
LOCAL_STATIC_LIBRARIES += libbase libotafault libmtdutils libcrypto_static libbz libz
|
||||
|
||||
# libapplypatch (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
applypatch.cpp \
|
||||
bspatch.cpp \
|
||||
freecache.cpp \
|
||||
imgpatch.cpp
|
||||
LOCAL_MODULE := libapplypatch
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_PATH)/include \
|
||||
$(commands_recovery_local_path)
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libotafault \
|
||||
libbase \
|
||||
libcrypto \
|
||||
libbspatch \
|
||||
libbz \
|
||||
libz
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES += libmtdutils
|
||||
LOCAL_CFLAGS := \
|
||||
-DZLIB_CONST \
|
||||
-Werror
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# libimgpatch (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
bspatch.cpp \
|
||||
imgpatch.cpp
|
||||
LOCAL_MODULE := libimgpatch
|
||||
LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_PATH)/include \
|
||||
$(commands_recovery_local_path)
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libcrypto \
|
||||
libbspatch \
|
||||
libbase \
|
||||
libbz \
|
||||
libz
|
||||
LOCAL_CFLAGS := \
|
||||
-DZLIB_CONST \
|
||||
-Werror
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp
|
||||
# libimgpatch (host static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
bspatch.cpp \
|
||||
imgpatch.cpp
|
||||
LOCAL_MODULE := libimgpatch
|
||||
LOCAL_C_INCLUDES += $(RECOVERY_PATH)
|
||||
LOCAL_MODULE_HOST_OS := linux
|
||||
LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_PATH)/include \
|
||||
$(commands_recovery_local_path)
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libcrypto \
|
||||
libbspatch \
|
||||
libbase \
|
||||
libbz \
|
||||
libz
|
||||
LOCAL_CFLAGS := \
|
||||
-DZLIB_CONST \
|
||||
-Werror
|
||||
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||
endif # HOST_OS == linux
|
||||
|
||||
# libapplypatch_modes (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
applypatch_modes.cpp
|
||||
LOCAL_MODULE := libapplypatch_modes
|
||||
LOCAL_C_INCLUDES := $(commands_recovery_local_path)
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libapplypatch \
|
||||
libbase \
|
||||
libedify \
|
||||
libcrypto
|
||||
LOCAL_CFLAGS := -Werror
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_SRC_FILES := main.cpp
|
||||
# applypatch (target executable)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := applypatch_main.cpp
|
||||
LOCAL_MODULE := applypatch
|
||||
LOCAL_C_INCLUDES += $(RECOVERY_PATH)
|
||||
LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libcrypto_static libbz \
|
||||
@@ -70,15 +143,78 @@ LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libcrypt
|
||||
|
||||
LOCAL_SHARED_LIBRARIES += libz libcutils libc
|
||||
|
||||
LOCAL_C_INCLUDES := $(commands_recovery_local_path)
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libapplypatch_modes \
|
||||
libapplypatch \
|
||||
libbase \
|
||||
libedify \
|
||||
libotafault \
|
||||
libcrypto \
|
||||
libbspatch \
|
||||
libbz
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libbase \
|
||||
libz \
|
||||
libcutils
|
||||
LOCAL_CFLAGS := -Werror
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
libimgdiff_src_files := imgdiff.cpp
|
||||
|
||||
# libbsdiff is compiled with -D_FILE_OFFSET_BITS=64.
|
||||
libimgdiff_cflags := \
|
||||
-Werror \
|
||||
-D_FILE_OFFSET_BITS=64
|
||||
|
||||
libimgdiff_static_libraries := \
|
||||
libbsdiff \
|
||||
libdivsufsort \
|
||||
libdivsufsort64 \
|
||||
libziparchive \
|
||||
libutils \
|
||||
liblog \
|
||||
libbase \
|
||||
libz
|
||||
|
||||
# libimgdiff (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
$(libimgdiff_src_files)
|
||||
LOCAL_MODULE := libimgdiff
|
||||
LOCAL_CFLAGS := \
|
||||
$(libimgdiff_cflags)
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
$(libimgdiff_static_libraries)
|
||||
LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_SRC_FILES := imgdiff.cpp utils.cpp bsdiff.cpp
|
||||
# libimgdiff (host static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
$(libimgdiff_src_files)
|
||||
LOCAL_MODULE := libimgdiff
|
||||
LOCAL_CFLAGS := \
|
||||
$(libimgdiff_cflags)
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
$(libimgdiff_static_libraries)
|
||||
LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||
|
||||
# imgdiff (host static executable)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := imgdiff_main.cpp
|
||||
LOCAL_MODULE := imgdiff
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_C_INCLUDES += external/zlib external/bzip2
|
||||
LOCAL_STATIC_LIBRARIES += libz libbz
|
||||
|
||||
LOCAL_CFLAGS := -Werror
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libimgdiff \
|
||||
$(libimgdiff_static_libraries) \
|
||||
libbz
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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.
|
||||
|
||||
# This file is for building imgdiff in Chrome OS.
|
||||
|
||||
CPPFLAGS += -iquote.. -Iinclude
|
||||
CXXFLAGS += -std=c++11 -O3 -Wall -Werror
|
||||
LDLIBS += -lbz2 -lz
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: imgdiff libimgpatch.a
|
||||
|
||||
clean:
|
||||
rm -f *.o imgdiff libimgpatch.a
|
||||
|
||||
imgdiff: imgdiff.o bsdiff.o utils.o
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDLIBS) -o $@ $^
|
||||
|
||||
libimgpatch.a utils.o: CXXFLAGS += -fPIC
|
||||
libimgpatch.a: imgpatch.o bspatch.o utils.o
|
||||
${AR} rcs $@ $^
|
||||
+379
-623
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 "applypatch_modes.h"
|
||||
|
||||
// This program (applypatch) applies binary patches to files in a way that
|
||||
// is safe (the original file is not touched until we have the desired
|
||||
// replacement for it) and idempotent (it's okay to run this program
|
||||
// multiple times).
|
||||
//
|
||||
// See the comments to applypatch_modes() function.
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return applypatch_modes(argc, const_cast<const char**>(argv));
|
||||
}
|
||||
@@ -14,61 +14,72 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "applypatch_modes.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "applypatch.h"
|
||||
#include "edify/expr.h"
|
||||
#include "openssl/sha.h"
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
static int CheckMode(int argc, char** argv) {
|
||||
#include "applypatch/applypatch.h"
|
||||
#include "edify/expr.h"
|
||||
|
||||
static int CheckMode(int argc, const char** argv) {
|
||||
if (argc < 3) {
|
||||
return 2;
|
||||
}
|
||||
return applypatch_check(argv[2], argc-3, argv+3);
|
||||
std::vector<std::string> sha1;
|
||||
for (int i = 3; i < argc; i++) {
|
||||
sha1.push_back(argv[i]);
|
||||
}
|
||||
|
||||
return applypatch_check(argv[2], sha1);
|
||||
}
|
||||
|
||||
static int SpaceMode(int argc, char** argv) {
|
||||
static int SpaceMode(int argc, const char** argv) {
|
||||
if (argc != 3) {
|
||||
return 2;
|
||||
}
|
||||
char* endptr;
|
||||
size_t bytes = strtol(argv[2], &endptr, 10);
|
||||
if (bytes == 0 && endptr == argv[2]) {
|
||||
|
||||
size_t bytes;
|
||||
if (!android::base::ParseUint(argv[2], &bytes) || bytes == 0) {
|
||||
printf("can't parse \"%s\" as byte count\n\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
return CacheSizeCheck(bytes);
|
||||
}
|
||||
|
||||
// Parse arguments (which should be of the form "<sha1>:<filename>"
|
||||
// into the new parallel arrays *sha1s and *files.Returns true on
|
||||
// success.
|
||||
static bool ParsePatchArgs(int argc, char** argv, std::vector<char*>* sha1s,
|
||||
// Parse arguments (which should be of the form "<sha1>:<filename>" into the
|
||||
// new parallel arrays *sha1s and *files. Returns true on success.
|
||||
static bool ParsePatchArgs(int argc, const char** argv, std::vector<std::string>* sha1s,
|
||||
std::vector<FileContents>* files) {
|
||||
uint8_t digest[SHA_DIGEST_LENGTH];
|
||||
|
||||
if (sha1s == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
char* colon = strchr(argv[i], ':');
|
||||
if (colon == nullptr) {
|
||||
printf("no ':' in patch argument \"%s\"\n", argv[i]);
|
||||
std::vector<std::string> pieces = android::base::Split(argv[i], ":");
|
||||
if (pieces.size() != 2) {
|
||||
printf("failed to parse patch argument \"%s\"\n", argv[i]);
|
||||
return false;
|
||||
}
|
||||
*colon = '\0';
|
||||
++colon;
|
||||
if (ParseSha1(argv[i], digest) != 0) {
|
||||
|
||||
uint8_t digest[SHA_DIGEST_LENGTH];
|
||||
if (ParseSha1(pieces[0].c_str(), digest) != 0) {
|
||||
printf("failed to parse sha1 \"%s\"\n", argv[i]);
|
||||
return false;
|
||||
}
|
||||
|
||||
sha1s->push_back(argv[i]);
|
||||
sha1s->push_back(pieces[0]);
|
||||
FileContents fc;
|
||||
if (LoadFileContents(colon, &fc) != 0) {
|
||||
if (LoadFileContents(pieces[1].c_str(), &fc) != 0) {
|
||||
return false;
|
||||
}
|
||||
files->push_back(std::move(fc));
|
||||
@@ -81,20 +92,17 @@ static int FlashMode(const char* src_filename, const char* tgt_filename,
|
||||
return applypatch_flash(src_filename, tgt_filename, tgt_sha1, tgt_size);
|
||||
}
|
||||
|
||||
static int PatchMode(int argc, char** argv) {
|
||||
static int PatchMode(int argc, const char** argv) {
|
||||
FileContents bonusFc;
|
||||
Value bonusValue;
|
||||
Value* bonus = nullptr;
|
||||
Value bonus(VAL_INVALID, "");
|
||||
|
||||
if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
|
||||
if (LoadFileContents(argv[2], &bonusFc) != 0) {
|
||||
printf("failed to load bonus file %s\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
bonus = &bonusValue;
|
||||
bonus->type = VAL_BLOB;
|
||||
bonus->size = bonusFc.data.size();
|
||||
bonus->data = reinterpret_cast<char*>(bonusFc.data.data());
|
||||
bonus.type = VAL_BLOB;
|
||||
bonus.data = std::string(bonusFc.data.cbegin(), bonusFc.data.cend());
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
}
|
||||
@@ -103,42 +111,38 @@ static int PatchMode(int argc, char** argv) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
char* endptr;
|
||||
size_t target_size = strtol(argv[4], &endptr, 10);
|
||||
if (target_size == 0 && endptr == argv[4]) {
|
||||
size_t target_size;
|
||||
if (!android::base::ParseUint(argv[4], &target_size) || target_size == 0) {
|
||||
printf("can't parse \"%s\" as byte count\n\n", argv[4]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If no <src-sha1>:<patch> is provided, it is in flash mode.
|
||||
if (argc == 5) {
|
||||
if (bonus != nullptr) {
|
||||
if (bonus.type != VAL_INVALID) {
|
||||
printf("bonus file not supported in flash mode\n");
|
||||
return 1;
|
||||
}
|
||||
return FlashMode(argv[1], argv[2], argv[3], target_size);
|
||||
}
|
||||
std::vector<char*> sha1s;
|
||||
|
||||
std::vector<std::string> sha1s;
|
||||
std::vector<FileContents> files;
|
||||
if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) {
|
||||
printf("failed to parse patch args\n");
|
||||
return 1;
|
||||
}
|
||||
std::vector<Value> patches(files.size());
|
||||
std::vector<Value*> patch_ptrs(files.size());
|
||||
|
||||
std::vector<std::unique_ptr<Value>> patches;
|
||||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
patches[i].type = VAL_BLOB;
|
||||
patches[i].size = files[i].data.size();
|
||||
patches[i].data = reinterpret_cast<char*>(files[i].data.data());
|
||||
patch_ptrs[i] = &patches[i];
|
||||
patches.push_back(std::make_unique<Value>(
|
||||
VAL_BLOB, std::string(files[i].data.cbegin(), files[i].data.cend())));
|
||||
}
|
||||
return applypatch(argv[1], argv[2], argv[3], target_size,
|
||||
patch_ptrs.size(), sha1s.data(),
|
||||
patch_ptrs.data(), bonus);
|
||||
return applypatch(argv[1], argv[2], argv[3], target_size, sha1s, patches, &bonus);
|
||||
}
|
||||
|
||||
// This program applies binary patches to files in a way that is safe
|
||||
// (the original file is not touched until we have the desired
|
||||
// This program (applypatch) applies binary patches to files in a way that
|
||||
// is safe (the original file is not touched until we have the desired
|
||||
// replacement for it) and idempotent (it's okay to run this program
|
||||
// multiple times).
|
||||
//
|
||||
@@ -160,11 +164,11 @@ static int PatchMode(int argc, char** argv) {
|
||||
// - otherwise, or if any error is encountered, exits with non-zero
|
||||
// status.
|
||||
//
|
||||
// <src-file> (or <file> in check mode) may refer to an MTD partition
|
||||
// <src-file> (or <file> in check mode) may refer to an EMMC partition
|
||||
// to read the source data. See the comments for the
|
||||
// LoadMTDContents() function above for the format of such a filename.
|
||||
// LoadPartitionContents() function for the format of such a filename.
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int applypatch_modes(int argc, const char** argv) {
|
||||
if (argc < 2) {
|
||||
usage:
|
||||
printf(
|
||||
@@ -175,8 +179,8 @@ int main(int argc, char** argv) {
|
||||
" or %s -l\n"
|
||||
"\n"
|
||||
"Filenames may be of the form\n"
|
||||
" MTD:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
|
||||
"to specify reading from or writing to an MTD partition.\n\n",
|
||||
" EMMC:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
|
||||
"to specify reading from or writing to an EMMC partition.\n\n",
|
||||
argv[0], argv[0], argv[0], argv[0]);
|
||||
return 2;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 _APPLYPATCH_MODES_H
|
||||
#define _APPLYPATCH_MODES_H
|
||||
|
||||
int applypatch_modes(int argc, const char** argv);
|
||||
|
||||
#endif // _APPLYPATCH_MODES_H
|
||||
@@ -1,410 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Most of this code comes from bsdiff.c from the bsdiff-4.3
|
||||
* distribution, which is:
|
||||
*/
|
||||
|
||||
/*-
|
||||
* Copyright 2003-2005 Colin Percival
|
||||
* All rights reserved
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted providing that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <bzlib.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MIN(x,y) (((x)<(y)) ? (x) : (y))
|
||||
|
||||
static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h)
|
||||
{
|
||||
off_t i,j,k,x,tmp,jj,kk;
|
||||
|
||||
if(len<16) {
|
||||
for(k=start;k<start+len;k+=j) {
|
||||
j=1;x=V[I[k]+h];
|
||||
for(i=1;k+i<start+len;i++) {
|
||||
if(V[I[k+i]+h]<x) {
|
||||
x=V[I[k+i]+h];
|
||||
j=0;
|
||||
};
|
||||
if(V[I[k+i]+h]==x) {
|
||||
tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
|
||||
j++;
|
||||
};
|
||||
};
|
||||
for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
|
||||
if(j==1) I[k]=-1;
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
x=V[I[start+len/2]+h];
|
||||
jj=0;kk=0;
|
||||
for(i=start;i<start+len;i++) {
|
||||
if(V[I[i]+h]<x) jj++;
|
||||
if(V[I[i]+h]==x) kk++;
|
||||
};
|
||||
jj+=start;kk+=jj;
|
||||
|
||||
i=start;j=0;k=0;
|
||||
while(i<jj) {
|
||||
if(V[I[i]+h]<x) {
|
||||
i++;
|
||||
} else if(V[I[i]+h]==x) {
|
||||
tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
|
||||
j++;
|
||||
} else {
|
||||
tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
|
||||
k++;
|
||||
};
|
||||
};
|
||||
|
||||
while(jj+j<kk) {
|
||||
if(V[I[jj+j]+h]==x) {
|
||||
j++;
|
||||
} else {
|
||||
tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
|
||||
k++;
|
||||
};
|
||||
};
|
||||
|
||||
if(jj>start) split(I,V,start,jj-start,h);
|
||||
|
||||
for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
|
||||
if(jj==kk-1) I[jj]=-1;
|
||||
|
||||
if(start+len>kk) split(I,V,kk,start+len-kk,h);
|
||||
}
|
||||
|
||||
static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize)
|
||||
{
|
||||
off_t buckets[256];
|
||||
off_t i,h,len;
|
||||
|
||||
for(i=0;i<256;i++) buckets[i]=0;
|
||||
for(i=0;i<oldsize;i++) buckets[old[i]]++;
|
||||
for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
|
||||
for(i=255;i>0;i--) buckets[i]=buckets[i-1];
|
||||
buckets[0]=0;
|
||||
|
||||
for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
|
||||
I[0]=oldsize;
|
||||
for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
|
||||
V[oldsize]=0;
|
||||
for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
|
||||
I[0]=-1;
|
||||
|
||||
for(h=1;I[0]!=-(oldsize+1);h+=h) {
|
||||
len=0;
|
||||
for(i=0;i<oldsize+1;) {
|
||||
if(I[i]<0) {
|
||||
len-=I[i];
|
||||
i-=I[i];
|
||||
} else {
|
||||
if(len) I[i-len]=-len;
|
||||
len=V[I[i]]+1-i;
|
||||
split(I,V,i,len,h);
|
||||
i+=len;
|
||||
len=0;
|
||||
};
|
||||
};
|
||||
if(len) I[i-len]=-len;
|
||||
};
|
||||
|
||||
for(i=0;i<oldsize+1;i++) I[V[i]]=i;
|
||||
}
|
||||
|
||||
static off_t matchlen(u_char *olddata,off_t oldsize,u_char *newdata,off_t newsize)
|
||||
{
|
||||
off_t i;
|
||||
|
||||
for(i=0;(i<oldsize)&&(i<newsize);i++)
|
||||
if(olddata[i]!=newdata[i]) break;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static off_t search(off_t *I,u_char *old,off_t oldsize,
|
||||
u_char *newdata,off_t newsize,off_t st,off_t en,off_t *pos)
|
||||
{
|
||||
off_t x,y;
|
||||
|
||||
if(en-st<2) {
|
||||
x=matchlen(old+I[st],oldsize-I[st],newdata,newsize);
|
||||
y=matchlen(old+I[en],oldsize-I[en],newdata,newsize);
|
||||
|
||||
if(x>y) {
|
||||
*pos=I[st];
|
||||
return x;
|
||||
} else {
|
||||
*pos=I[en];
|
||||
return y;
|
||||
}
|
||||
};
|
||||
|
||||
x=st+(en-st)/2;
|
||||
if(memcmp(old+I[x],newdata,MIN(oldsize-I[x],newsize))<0) {
|
||||
return search(I,old,oldsize,newdata,newsize,x,en,pos);
|
||||
} else {
|
||||
return search(I,old,oldsize,newdata,newsize,st,x,pos);
|
||||
};
|
||||
}
|
||||
|
||||
static void offtout(off_t x,u_char *buf)
|
||||
{
|
||||
off_t y;
|
||||
|
||||
if(x<0) y=-x; else y=x;
|
||||
|
||||
buf[0]=y%256;y-=buf[0];
|
||||
y=y/256;buf[1]=y%256;y-=buf[1];
|
||||
y=y/256;buf[2]=y%256;y-=buf[2];
|
||||
y=y/256;buf[3]=y%256;y-=buf[3];
|
||||
y=y/256;buf[4]=y%256;y-=buf[4];
|
||||
y=y/256;buf[5]=y%256;y-=buf[5];
|
||||
y=y/256;buf[6]=y%256;y-=buf[6];
|
||||
y=y/256;buf[7]=y%256;
|
||||
|
||||
if(x<0) buf[7]|=0x80;
|
||||
}
|
||||
|
||||
// This is main() from bsdiff.c, with the following changes:
|
||||
//
|
||||
// - old, oldsize, newdata, newsize are arguments; we don't load this
|
||||
// data from files. old and newdata are owned by the caller; we
|
||||
// don't free them at the end.
|
||||
//
|
||||
// - the "I" block of memory is owned by the caller, who passes a
|
||||
// pointer to *I, which can be NULL. This way if we call
|
||||
// bsdiff() multiple times with the same 'old' data, we only do
|
||||
// the qsufsort() step the first time.
|
||||
//
|
||||
int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize,
|
||||
const char* patch_filename)
|
||||
{
|
||||
int fd;
|
||||
off_t *I;
|
||||
off_t scan,pos,len;
|
||||
off_t lastscan,lastpos,lastoffset;
|
||||
off_t oldscore,scsc;
|
||||
off_t s,Sf,lenf,Sb,lenb;
|
||||
off_t overlap,Ss,lens;
|
||||
off_t i;
|
||||
off_t dblen,eblen;
|
||||
u_char *db,*eb;
|
||||
u_char buf[8];
|
||||
u_char header[32];
|
||||
FILE * pf;
|
||||
BZFILE * pfbz2;
|
||||
int bz2err;
|
||||
|
||||
if (*IP == NULL) {
|
||||
off_t* V;
|
||||
*IP = reinterpret_cast<off_t*>(malloc((oldsize+1) * sizeof(off_t)));
|
||||
V = reinterpret_cast<off_t*>(malloc((oldsize+1) * sizeof(off_t)));
|
||||
qsufsort(*IP, V, old, oldsize);
|
||||
free(V);
|
||||
}
|
||||
I = *IP;
|
||||
|
||||
if(((db=reinterpret_cast<u_char*>(malloc(newsize+1)))==NULL) ||
|
||||
((eb=reinterpret_cast<u_char*>(malloc(newsize+1)))==NULL)) err(1,NULL);
|
||||
dblen=0;
|
||||
eblen=0;
|
||||
|
||||
/* Create the patch file */
|
||||
if ((pf = fopen(patch_filename, "w")) == NULL)
|
||||
err(1, "%s", patch_filename);
|
||||
|
||||
/* Header is
|
||||
0 8 "BSDIFF40"
|
||||
8 8 length of bzip2ed ctrl block
|
||||
16 8 length of bzip2ed diff block
|
||||
24 8 length of new file */
|
||||
/* File is
|
||||
0 32 Header
|
||||
32 ?? Bzip2ed ctrl block
|
||||
?? ?? Bzip2ed diff block
|
||||
?? ?? Bzip2ed extra block */
|
||||
memcpy(header,"BSDIFF40",8);
|
||||
offtout(0, header + 8);
|
||||
offtout(0, header + 16);
|
||||
offtout(newsize, header + 24);
|
||||
if (fwrite(header, 32, 1, pf) != 1)
|
||||
err(1, "fwrite(%s)", patch_filename);
|
||||
|
||||
/* Compute the differences, writing ctrl as we go */
|
||||
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
|
||||
errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
|
||||
scan=0;len=0;
|
||||
lastscan=0;lastpos=0;lastoffset=0;
|
||||
while(scan<newsize) {
|
||||
oldscore=0;
|
||||
|
||||
for(scsc=scan+=len;scan<newsize;scan++) {
|
||||
len=search(I,old,oldsize,newdata+scan,newsize-scan,
|
||||
0,oldsize,&pos);
|
||||
|
||||
for(;scsc<scan+len;scsc++)
|
||||
if((scsc+lastoffset<oldsize) &&
|
||||
(old[scsc+lastoffset] == newdata[scsc]))
|
||||
oldscore++;
|
||||
|
||||
if(((len==oldscore) && (len!=0)) ||
|
||||
(len>oldscore+8)) break;
|
||||
|
||||
if((scan+lastoffset<oldsize) &&
|
||||
(old[scan+lastoffset] == newdata[scan]))
|
||||
oldscore--;
|
||||
};
|
||||
|
||||
if((len!=oldscore) || (scan==newsize)) {
|
||||
s=0;Sf=0;lenf=0;
|
||||
for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
|
||||
if(old[lastpos+i]==newdata[lastscan+i]) s++;
|
||||
i++;
|
||||
if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
|
||||
};
|
||||
|
||||
lenb=0;
|
||||
if(scan<newsize) {
|
||||
s=0;Sb=0;
|
||||
for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
|
||||
if(old[pos-i]==newdata[scan-i]) s++;
|
||||
if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
|
||||
};
|
||||
};
|
||||
|
||||
if(lastscan+lenf>scan-lenb) {
|
||||
overlap=(lastscan+lenf)-(scan-lenb);
|
||||
s=0;Ss=0;lens=0;
|
||||
for(i=0;i<overlap;i++) {
|
||||
if(newdata[lastscan+lenf-overlap+i]==
|
||||
old[lastpos+lenf-overlap+i]) s++;
|
||||
if(newdata[scan-lenb+i]==
|
||||
old[pos-lenb+i]) s--;
|
||||
if(s>Ss) { Ss=s; lens=i+1; };
|
||||
};
|
||||
|
||||
lenf+=lens-overlap;
|
||||
lenb-=lens;
|
||||
};
|
||||
|
||||
for(i=0;i<lenf;i++)
|
||||
db[dblen+i]=newdata[lastscan+i]-old[lastpos+i];
|
||||
for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
|
||||
eb[eblen+i]=newdata[lastscan+lenf+i];
|
||||
|
||||
dblen+=lenf;
|
||||
eblen+=(scan-lenb)-(lastscan+lenf);
|
||||
|
||||
offtout(lenf,buf);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
|
||||
offtout((scan-lenb)-(lastscan+lenf),buf);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
|
||||
offtout((pos-lenb)-(lastpos+lenf),buf);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
|
||||
lastscan=scan-lenb;
|
||||
lastpos=pos-lenb;
|
||||
lastoffset=pos-scan;
|
||||
};
|
||||
};
|
||||
BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
|
||||
|
||||
/* Compute size of compressed ctrl data */
|
||||
if ((len = ftello(pf)) == -1)
|
||||
err(1, "ftello");
|
||||
offtout(len-32, header + 8);
|
||||
|
||||
/* Write compressed diff data */
|
||||
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
|
||||
errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
|
||||
|
||||
/* Compute size of compressed diff data */
|
||||
if ((newsize = ftello(pf)) == -1)
|
||||
err(1, "ftello");
|
||||
offtout(newsize - len, header + 16);
|
||||
|
||||
/* Write compressed extra data */
|
||||
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
|
||||
errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
|
||||
BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
|
||||
BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
|
||||
if (bz2err != BZ_OK)
|
||||
errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
|
||||
|
||||
/* Seek to the beginning, write the header, and close the file */
|
||||
if (fseeko(pf, 0, SEEK_SET))
|
||||
err(1, "fseeko");
|
||||
if (fwrite(header, 32, 1, pf) != 1)
|
||||
err(1, "fwrite(%s)", patch_filename);
|
||||
if (fclose(pf))
|
||||
err(1, "fclose");
|
||||
|
||||
/* Free the memory we used */
|
||||
free(db);
|
||||
free(eb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+21
-184
@@ -21,16 +21,12 @@
|
||||
// notice.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bzlib.h>
|
||||
#include <bspatch.h>
|
||||
|
||||
#include "applypatch/applypatch.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "applypatch.h"
|
||||
|
||||
void ShowBSDiffLicense() {
|
||||
puts("The bsdiff library used herein is:\n"
|
||||
@@ -64,184 +60,25 @@ void ShowBSDiffLicense() {
|
||||
);
|
||||
}
|
||||
|
||||
static off_t offtin(u_char *buf)
|
||||
{
|
||||
off_t y;
|
||||
|
||||
y=buf[7]&0x7F;
|
||||
y=y*256;y+=buf[6];
|
||||
y=y*256;y+=buf[5];
|
||||
y=y*256;y+=buf[4];
|
||||
y=y*256;y+=buf[3];
|
||||
y=y*256;y+=buf[2];
|
||||
y=y*256;y+=buf[1];
|
||||
y=y*256;y+=buf[0];
|
||||
|
||||
if(buf[7]&0x80) y=-y;
|
||||
|
||||
return y;
|
||||
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Value* patch,
|
||||
ssize_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx) {
|
||||
auto sha_sink = [&](const uint8_t* data, size_t len) {
|
||||
len = sink(data, len, token);
|
||||
if (ctx) SHA1_Update(ctx, data, len);
|
||||
return len;
|
||||
};
|
||||
return bsdiff::bspatch(old_data, old_size,
|
||||
reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
|
||||
patch->data.size(), sha_sink);
|
||||
}
|
||||
|
||||
int FillBuffer(unsigned char* buffer, int size, bz_stream* stream) {
|
||||
stream->next_out = (char*)buffer;
|
||||
stream->avail_out = size;
|
||||
while (stream->avail_out > 0) {
|
||||
int bzerr = BZ2_bzDecompress(stream);
|
||||
if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) {
|
||||
printf("bz error %d decompressing\n", bzerr);
|
||||
return -1;
|
||||
}
|
||||
if (stream->avail_out > 0) {
|
||||
printf("need %d more bytes\n", stream->avail_out);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch, ssize_t patch_offset,
|
||||
SinkFn sink, void* token, SHA_CTX* ctx) {
|
||||
|
||||
std::vector<unsigned char> new_data;
|
||||
if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset, &new_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sink(new_data.data(), new_data.size(), token) < static_cast<ssize_t>(new_data.size())) {
|
||||
printf("short write of output: %d (%s)\n", errno, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (ctx) SHA1_Update(ctx, new_data.data(), new_data.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch, ssize_t patch_offset,
|
||||
std::vector<unsigned char>* new_data) {
|
||||
// Patch data format:
|
||||
// 0 8 "BSDIFF40"
|
||||
// 8 8 X
|
||||
// 16 8 Y
|
||||
// 24 8 sizeof(newfile)
|
||||
// 32 X bzip2(control block)
|
||||
// 32+X Y bzip2(diff block)
|
||||
// 32+X+Y ??? bzip2(extra block)
|
||||
// with control block a set of triples (x,y,z) meaning "add x bytes
|
||||
// from oldfile to x bytes from the diff block; copy y bytes from the
|
||||
// extra block; seek forwards in oldfile by z bytes".
|
||||
|
||||
unsigned char* header = (unsigned char*) patch->data + patch_offset;
|
||||
if (memcmp(header, "BSDIFF40", 8) != 0) {
|
||||
printf("corrupt bsdiff patch file header (magic number)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t ctrl_len, data_len, new_size;
|
||||
ctrl_len = offtin(header+8);
|
||||
data_len = offtin(header+16);
|
||||
new_size = offtin(header+24);
|
||||
|
||||
if (ctrl_len < 0 || data_len < 0 || new_size < 0) {
|
||||
printf("corrupt patch file header (data lengths)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bzerr;
|
||||
|
||||
bz_stream cstream;
|
||||
cstream.next_in = patch->data + patch_offset + 32;
|
||||
cstream.avail_in = ctrl_len;
|
||||
cstream.bzalloc = NULL;
|
||||
cstream.bzfree = NULL;
|
||||
cstream.opaque = NULL;
|
||||
if ((bzerr = BZ2_bzDecompressInit(&cstream, 0, 0)) != BZ_OK) {
|
||||
printf("failed to bzinit control stream (%d)\n", bzerr);
|
||||
}
|
||||
|
||||
bz_stream dstream;
|
||||
dstream.next_in = patch->data + patch_offset + 32 + ctrl_len;
|
||||
dstream.avail_in = data_len;
|
||||
dstream.bzalloc = NULL;
|
||||
dstream.bzfree = NULL;
|
||||
dstream.opaque = NULL;
|
||||
if ((bzerr = BZ2_bzDecompressInit(&dstream, 0, 0)) != BZ_OK) {
|
||||
printf("failed to bzinit diff stream (%d)\n", bzerr);
|
||||
}
|
||||
|
||||
bz_stream estream;
|
||||
estream.next_in = patch->data + patch_offset + 32 + ctrl_len + data_len;
|
||||
estream.avail_in = patch->size - (patch_offset + 32 + ctrl_len + data_len);
|
||||
estream.bzalloc = NULL;
|
||||
estream.bzfree = NULL;
|
||||
estream.opaque = NULL;
|
||||
if ((bzerr = BZ2_bzDecompressInit(&estream, 0, 0)) != BZ_OK) {
|
||||
printf("failed to bzinit extra stream (%d)\n", bzerr);
|
||||
}
|
||||
|
||||
new_data->resize(new_size);
|
||||
|
||||
off_t oldpos = 0, newpos = 0;
|
||||
off_t ctrl[3];
|
||||
off_t len_read;
|
||||
int i;
|
||||
unsigned char buf[24];
|
||||
while (newpos < new_size) {
|
||||
// Read control data
|
||||
if (FillBuffer(buf, 24, &cstream) != 0) {
|
||||
printf("error while reading control stream\n");
|
||||
return 1;
|
||||
}
|
||||
ctrl[0] = offtin(buf);
|
||||
ctrl[1] = offtin(buf+8);
|
||||
ctrl[2] = offtin(buf+16);
|
||||
|
||||
if (ctrl[0] < 0 || ctrl[1] < 0) {
|
||||
printf("corrupt patch (negative byte counts)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (newpos + ctrl[0] > new_size) {
|
||||
printf("corrupt patch (new file overrun)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read diff string
|
||||
if (FillBuffer(new_data->data() + newpos, ctrl[0], &dstream) != 0) {
|
||||
printf("error while reading diff stream\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Add old data to diff string
|
||||
for (i = 0; i < ctrl[0]; ++i) {
|
||||
if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
|
||||
(*new_data)[newpos+i] += old_data[oldpos+i];
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust pointers
|
||||
newpos += ctrl[0];
|
||||
oldpos += ctrl[0];
|
||||
|
||||
// Sanity check
|
||||
if (newpos + ctrl[1] > new_size) {
|
||||
printf("corrupt patch (new file overrun)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read extra string
|
||||
if (FillBuffer(new_data->data() + newpos, ctrl[1], &estream) != 0) {
|
||||
printf("error while reading extra stream\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Adjust pointers
|
||||
newpos += ctrl[1];
|
||||
oldpos += ctrl[2];
|
||||
}
|
||||
|
||||
BZ2_bzDecompressEnd(&cstream);
|
||||
BZ2_bzDecompressEnd(&dstream);
|
||||
BZ2_bzDecompressEnd(&estream);
|
||||
return 0;
|
||||
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch,
|
||||
ssize_t patch_offset, std::vector<unsigned char>* new_data) {
|
||||
auto vector_sink = [new_data](const uint8_t* data, size_t len) {
|
||||
new_data->insert(new_data->end(), data, data + len);
|
||||
return len;
|
||||
};
|
||||
return bsdiff::bspatch(old_data, old_size,
|
||||
reinterpret_cast<const uint8_t*>(&patch->data[patch_offset]),
|
||||
patch->data.size(), vector_sink);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "applypatch.h"
|
||||
#include "applypatch/applypatch.h"
|
||||
|
||||
static int EliminateOpenFiles(std::set<std::string>* files) {
|
||||
std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir);
|
||||
|
||||
+693
-692
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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 "applypatch/imgdiff.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return imgdiff(argc, const_cast<const char**>(argv));
|
||||
}
|
||||
+213
-202
@@ -14,31 +14,41 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// See imgdiff.c in this directory for a description of the patch file
|
||||
// See imgdiff.cpp in this directory for a description of the patch file
|
||||
// format.
|
||||
|
||||
#include <applypatch/imgpatch.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "zlib.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "applypatch.h"
|
||||
#include "imgdiff.h"
|
||||
#include "utils.h"
|
||||
#include <applypatch/applypatch.h>
|
||||
#include <applypatch/imgdiff.h>
|
||||
#include <android-base/memory.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <zlib.h>
|
||||
|
||||
static inline int64_t Read8(const void *address) {
|
||||
return android::base::get_unaligned<int64_t>(address);
|
||||
}
|
||||
|
||||
static inline int32_t Read4(const void *address) {
|
||||
return android::base::get_unaligned<int32_t>(address);
|
||||
}
|
||||
|
||||
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
|
||||
const unsigned char* patch_data, ssize_t patch_size,
|
||||
SinkFn sink, void* token) {
|
||||
Value patch = {VAL_BLOB, patch_size,
|
||||
reinterpret_cast<char*>(const_cast<unsigned char*>(patch_data))};
|
||||
return ApplyImagePatch(
|
||||
old_data, old_size, &patch, sink, token, nullptr, nullptr);
|
||||
Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size));
|
||||
|
||||
return ApplyImagePatch(old_data, old_size, &patch, sink, token, nullptr, nullptr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -47,203 +57,204 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
|
||||
* file, and update the SHA context with the output data as well.
|
||||
* Return 0 on success.
|
||||
*/
|
||||
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch,
|
||||
SinkFn sink, void* token, SHA_CTX* ctx,
|
||||
const Value* bonus_data) {
|
||||
ssize_t pos = 12;
|
||||
char* header = patch->data;
|
||||
if (patch->size < 12) {
|
||||
printf("patch too short to contain header\n");
|
||||
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch,
|
||||
SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data) {
|
||||
if (patch->data.size() < 12) {
|
||||
printf("patch too short to contain header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
|
||||
// (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
|
||||
// CHUNK_GZIP.)
|
||||
size_t pos = 12;
|
||||
const char* header = &patch->data[0];
|
||||
if (memcmp(header, "IMGDIFF2", 8) != 0) {
|
||||
printf("corrupt patch file header (magic number)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int num_chunks = Read4(header + 8);
|
||||
|
||||
for (int i = 0; i < num_chunks; ++i) {
|
||||
// each chunk's header record starts with 4 bytes.
|
||||
if (pos + 4 > patch->data.size()) {
|
||||
printf("failed to read chunk %d record\n", i);
|
||||
return -1;
|
||||
}
|
||||
int type = Read4(&patch->data[pos]);
|
||||
pos += 4;
|
||||
|
||||
if (type == CHUNK_NORMAL) {
|
||||
const char* normal_header = &patch->data[pos];
|
||||
pos += 24;
|
||||
if (pos > patch->data.size()) {
|
||||
printf("failed to read chunk %d normal header data\n", i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
|
||||
// (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
|
||||
// CHUNK_GZIP.)
|
||||
if (memcmp(header, "IMGDIFF2", 8) != 0) {
|
||||
printf("corrupt patch file header (magic number)\n");
|
||||
size_t src_start = static_cast<size_t>(Read8(normal_header));
|
||||
size_t src_len = static_cast<size_t>(Read8(normal_header + 8));
|
||||
size_t patch_offset = static_cast<size_t>(Read8(normal_header + 16));
|
||||
|
||||
if (src_start + src_len > static_cast<size_t>(old_size)) {
|
||||
printf("source data too short\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, token, ctx);
|
||||
} else if (type == CHUNK_RAW) {
|
||||
const char* raw_header = &patch->data[pos];
|
||||
pos += 4;
|
||||
if (pos > patch->data.size()) {
|
||||
printf("failed to read chunk %d raw header data\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int num_chunks = Read4(header+8);
|
||||
ssize_t data_len = Read4(raw_header);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < num_chunks; ++i) {
|
||||
// each chunk's header record starts with 4 bytes.
|
||||
if (pos + 4 > patch->size) {
|
||||
printf("failed to read chunk %d record\n", i);
|
||||
return -1;
|
||||
if (pos + data_len > patch->data.size()) {
|
||||
printf("failed to read chunk %d raw data\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len);
|
||||
if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len, token) !=
|
||||
data_len) {
|
||||
printf("failed to write chunk %d raw data\n", i);
|
||||
return -1;
|
||||
}
|
||||
pos += data_len;
|
||||
} else if (type == CHUNK_DEFLATE) {
|
||||
// deflate chunks have an additional 60 bytes in their chunk header.
|
||||
const char* deflate_header = &patch->data[pos];
|
||||
pos += 60;
|
||||
if (pos > patch->data.size()) {
|
||||
printf("failed to read chunk %d deflate header data\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t src_start = static_cast<size_t>(Read8(deflate_header));
|
||||
size_t src_len = static_cast<size_t>(Read8(deflate_header + 8));
|
||||
size_t patch_offset = static_cast<size_t>(Read8(deflate_header + 16));
|
||||
size_t expanded_len = static_cast<size_t>(Read8(deflate_header + 24));
|
||||
size_t target_len = static_cast<size_t>(Read8(deflate_header + 32));
|
||||
int level = Read4(deflate_header + 40);
|
||||
int method = Read4(deflate_header + 44);
|
||||
int windowBits = Read4(deflate_header + 48);
|
||||
int memLevel = Read4(deflate_header + 52);
|
||||
int strategy = Read4(deflate_header + 56);
|
||||
|
||||
if (src_start + src_len > static_cast<size_t>(old_size)) {
|
||||
printf("source data too short\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Decompress the source data; the chunk header tells us exactly
|
||||
// how big we expect it to be when decompressed.
|
||||
|
||||
// Note: expanded_len will include the bonus data size if
|
||||
// the patch was constructed with bonus data. The
|
||||
// deflation will come up 'bonus_size' bytes short; these
|
||||
// must be appended from the bonus_data value.
|
||||
size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->data.size() : 0;
|
||||
|
||||
std::vector<unsigned char> expanded_source(expanded_len);
|
||||
|
||||
// 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 = old_data + src_start;
|
||||
strm.avail_out = expanded_len;
|
||||
strm.next_out = expanded_source.data();
|
||||
|
||||
int ret = inflateInit2(&strm, -15);
|
||||
if (ret != Z_OK) {
|
||||
printf("failed to init source inflation: %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
int type = Read4(patch->data + pos);
|
||||
pos += 4;
|
||||
|
||||
if (type == CHUNK_NORMAL) {
|
||||
char* normal_header = patch->data + pos;
|
||||
pos += 24;
|
||||
if (pos > patch->size) {
|
||||
printf("failed to read chunk %d normal header data\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t src_start = Read8(normal_header);
|
||||
size_t src_len = Read8(normal_header+8);
|
||||
size_t patch_offset = Read8(normal_header+16);
|
||||
|
||||
if (src_start + src_len > static_cast<size_t>(old_size)) {
|
||||
printf("source data too short\n");
|
||||
return -1;
|
||||
}
|
||||
ApplyBSDiffPatch(old_data + src_start, src_len,
|
||||
patch, patch_offset, sink, token, ctx);
|
||||
} else if (type == CHUNK_RAW) {
|
||||
char* raw_header = patch->data + pos;
|
||||
pos += 4;
|
||||
if (pos > patch->size) {
|
||||
printf("failed to read chunk %d raw header data\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t data_len = Read4(raw_header);
|
||||
|
||||
if (pos + data_len > patch->size) {
|
||||
printf("failed to read chunk %d raw data\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ctx) SHA1_Update(ctx, patch->data + pos, data_len);
|
||||
if (sink((unsigned char*)patch->data + pos,
|
||||
data_len, token) != data_len) {
|
||||
printf("failed to write chunk %d raw data\n", i);
|
||||
return -1;
|
||||
}
|
||||
pos += data_len;
|
||||
} else if (type == CHUNK_DEFLATE) {
|
||||
// deflate chunks have an additional 60 bytes in their chunk header.
|
||||
char* deflate_header = patch->data + pos;
|
||||
pos += 60;
|
||||
if (pos > patch->size) {
|
||||
printf("failed to read chunk %d deflate header data\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t src_start = Read8(deflate_header);
|
||||
size_t src_len = Read8(deflate_header+8);
|
||||
size_t patch_offset = Read8(deflate_header+16);
|
||||
size_t expanded_len = Read8(deflate_header+24);
|
||||
int level = Read4(deflate_header+40);
|
||||
int method = Read4(deflate_header+44);
|
||||
int windowBits = Read4(deflate_header+48);
|
||||
int memLevel = Read4(deflate_header+52);
|
||||
int strategy = Read4(deflate_header+56);
|
||||
|
||||
if (src_start + src_len > static_cast<size_t>(old_size)) {
|
||||
printf("source data too short\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Decompress the source data; the chunk header tells us exactly
|
||||
// how big we expect it to be when decompressed.
|
||||
|
||||
// Note: expanded_len will include the bonus data size if
|
||||
// the patch was constructed with bonus data. The
|
||||
// deflation will come up 'bonus_size' bytes short; these
|
||||
// must be appended from the bonus_data value.
|
||||
size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0;
|
||||
|
||||
std::vector<unsigned char> expanded_source(expanded_len);
|
||||
|
||||
// inflate() doesn't like strm.next_out being a nullptr even with
|
||||
// avail_out being zero (Z_STREAM_ERROR).
|
||||
if (expanded_len != 0) {
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = src_len;
|
||||
strm.next_in = (unsigned char*)(old_data + src_start);
|
||||
strm.avail_out = expanded_len;
|
||||
strm.next_out = expanded_source.data();
|
||||
|
||||
int ret;
|
||||
ret = inflateInit2(&strm, -15);
|
||||
if (ret != Z_OK) {
|
||||
printf("failed to init source inflation: %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Because we've provided enough room to accommodate the output
|
||||
// data, we expect one call to inflate() to suffice.
|
||||
ret = inflate(&strm, Z_SYNC_FLUSH);
|
||||
if (ret != Z_STREAM_END) {
|
||||
printf("source inflation returned %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
// We should have filled the output buffer exactly, except
|
||||
// for the bonus_size.
|
||||
if (strm.avail_out != bonus_size) {
|
||||
printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size);
|
||||
return -1;
|
||||
}
|
||||
inflateEnd(&strm);
|
||||
|
||||
if (bonus_size) {
|
||||
memcpy(expanded_source.data() + (expanded_len - bonus_size),
|
||||
bonus_data->data, bonus_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Next, apply the bsdiff patch (in memory) to the uncompressed
|
||||
// data.
|
||||
std::vector<unsigned char> uncompressed_target_data;
|
||||
if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len,
|
||||
patch, patch_offset,
|
||||
&uncompressed_target_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now compress the target data and append it to the output.
|
||||
|
||||
// we're done with the expanded_source data buffer, so we'll
|
||||
// reuse that memory to receive the output of deflate.
|
||||
if (expanded_source.size() < 32768U) {
|
||||
expanded_source.resize(32768U);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<unsigned char>& temp_data = expanded_source;
|
||||
|
||||
// now the deflate stream
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = uncompressed_target_data.size();
|
||||
strm.next_in = uncompressed_target_data.data();
|
||||
int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
|
||||
if (ret != Z_OK) {
|
||||
printf("failed to init uncompressed data deflation: %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
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 %ld compressed bytes to output\n",
|
||||
(long)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;
|
||||
// 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);
|
||||
|
||||
return 0;
|
||||
if (bonus_size) {
|
||||
memcpy(expanded_source.data() + (expanded_len - bonus_size), &bonus_data->data[0],
|
||||
bonus_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Next, apply the bsdiff patch (in memory) to the uncompressed data.
|
||||
std::vector<unsigned char> uncompressed_target_data;
|
||||
// TODO(senj): Remove the only usage of ApplyBSDiffPatchMem here,
|
||||
// replace it with ApplyBSDiffPatch with a custom sink function that
|
||||
// wraps the given sink function to stream output to save memory.
|
||||
if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset,
|
||||
&uncompressed_target_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (uncompressed_target_data.size() != target_len) {
|
||||
printf("expected target len to be %zu, but it's %zu\n", target_len,
|
||||
uncompressed_target_data.size());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now compress the target data and append it to the output.
|
||||
|
||||
// we're done with the expanded_source data buffer, so we'll
|
||||
// reuse that memory to receive the output of deflate.
|
||||
if (expanded_source.size() < 32768U) {
|
||||
expanded_source.resize(32768U);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<unsigned char>& temp_data = expanded_source;
|
||||
|
||||
// now the deflate stream
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = uncompressed_target_data.size();
|
||||
strm.next_in = uncompressed_target_data.data();
|
||||
int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
|
||||
if (ret != Z_OK) {
|
||||
printf("failed to init uncompressed data deflation: %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -17,11 +17,15 @@
|
||||
#ifndef _APPLYPATCH_H
|
||||
#define _APPLYPATCH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "openssl/sha.h"
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "edify/expr.h"
|
||||
|
||||
struct FileContents {
|
||||
@@ -39,33 +43,28 @@ struct FileContents {
|
||||
|
||||
typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
|
||||
|
||||
// applypatch.c
|
||||
// applypatch.cpp
|
||||
int ShowLicenses();
|
||||
size_t FreeSpaceForFile(const char* filename);
|
||||
int CacheSizeCheck(size_t bytes);
|
||||
int ParseSha1(const char* str, uint8_t* digest);
|
||||
|
||||
int applypatch_flash(const char* source_filename, const char* target_filename,
|
||||
const char* target_sha1_str, size_t target_size);
|
||||
int applypatch(const char* source_filename,
|
||||
const char* target_filename,
|
||||
const char* target_sha1_str,
|
||||
size_t target_size,
|
||||
int num_patches,
|
||||
char** const patch_sha1_str,
|
||||
Value** patch_data,
|
||||
Value* bonus_data);
|
||||
const std::vector<std::string>& patch_sha1_str,
|
||||
const std::vector<std::unique_ptr<Value>>& patch_data,
|
||||
const Value* bonus_data);
|
||||
int applypatch_check(const char* filename,
|
||||
int num_patches,
|
||||
char** const patch_sha1_str);
|
||||
const std::vector<std::string>& patch_sha1_str);
|
||||
int applypatch_flash(const char* source_filename, const char* target_filename,
|
||||
const char* target_sha1_str, size_t target_size);
|
||||
|
||||
int LoadFileContents(const char* filename, FileContents* file);
|
||||
int SaveFileContents(const char* filename, const FileContents* file);
|
||||
void FreeFileContents(FileContents* file);
|
||||
int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
|
||||
int num_patches);
|
||||
|
||||
// bsdiff.cpp
|
||||
// bspatch.cpp
|
||||
void ShowBSDiffLicense();
|
||||
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
|
||||
const Value* patch, ssize_t patch_offset,
|
||||
@@ -14,17 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _APPLYPATCH_IMGDIFF_H
|
||||
#define _APPLYPATCH_IMGDIFF_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// Image patch chunk types
|
||||
#define CHUNK_NORMAL 0
|
||||
#define CHUNK_GZIP 1 // version 1 only
|
||||
#define CHUNK_DEFLATE 2 // version 2 only
|
||||
#define CHUNK_RAW 3 // version 2 only
|
||||
#define CHUNK_NORMAL 0
|
||||
#define CHUNK_GZIP 1 // version 1 only
|
||||
#define CHUNK_DEFLATE 2 // version 2 only
|
||||
#define CHUNK_RAW 3 // version 2 only
|
||||
|
||||
// The gzip header size is actually variable, but we currently don't
|
||||
// support gzipped data with any of the optional fields, so for now it
|
||||
// will always be ten bytes. See RFC 1952 for the definition of the
|
||||
// gzip format.
|
||||
#define GZIP_HEADER_LEN 10
|
||||
static constexpr size_t GZIP_HEADER_LEN = 10;
|
||||
|
||||
// The gzip footer size really is fixed.
|
||||
#define GZIP_FOOTER_LEN 8
|
||||
static constexpr size_t GZIP_FOOTER_LEN = 8;
|
||||
|
||||
int imgdiff(int argc, const char** argv);
|
||||
|
||||
#endif // _APPLYPATCH_IMGDIFF_H
|
||||
@@ -14,13 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _IMGPATCH_H
|
||||
#define _IMGPATCH_H
|
||||
#ifndef _APPLYPATCH_IMGPATCH_H
|
||||
#define _APPLYPATCH_IMGPATCH_H
|
||||
|
||||
typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
|
||||
#include <sys/types.h>
|
||||
|
||||
using SinkFn = ssize_t (*)(const unsigned char*, ssize_t, void*);
|
||||
|
||||
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
|
||||
const unsigned char* patch_data, ssize_t patch_size,
|
||||
SinkFn sink, void* token);
|
||||
|
||||
#endif //_IMGPATCH_H
|
||||
#endif // _APPLYPATCH_IMGPATCH_H
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# This file is for libimgpatch in Chrome OS.
|
||||
|
||||
Name: libimgpatch
|
||||
Description: Apply imgdiff patch
|
||||
Version: 0.0.1
|
||||
Libs: -limgpatch -lbz2 -lz
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
/** Write a 4-byte value to f in little-endian order. */
|
||||
void Write4(int value, FILE* f) {
|
||||
fputc(value & 0xff, f);
|
||||
fputc((value >> 8) & 0xff, f);
|
||||
fputc((value >> 16) & 0xff, f);
|
||||
fputc((value >> 24) & 0xff, f);
|
||||
}
|
||||
|
||||
/** Write an 8-byte value to f in little-endian order. */
|
||||
void Write8(long long value, FILE* f) {
|
||||
fputc(value & 0xff, f);
|
||||
fputc((value >> 8) & 0xff, f);
|
||||
fputc((value >> 16) & 0xff, f);
|
||||
fputc((value >> 24) & 0xff, f);
|
||||
fputc((value >> 32) & 0xff, f);
|
||||
fputc((value >> 40) & 0xff, f);
|
||||
fputc((value >> 48) & 0xff, f);
|
||||
fputc((value >> 56) & 0xff, f);
|
||||
}
|
||||
|
||||
int Read2(void* pv) {
|
||||
unsigned char* p = reinterpret_cast<unsigned char*>(pv);
|
||||
return (int)(((unsigned int)p[1] << 8) |
|
||||
(unsigned int)p[0]);
|
||||
}
|
||||
|
||||
int Read4(void* pv) {
|
||||
unsigned char* p = reinterpret_cast<unsigned char*>(pv);
|
||||
return (int)(((unsigned int)p[3] << 24) |
|
||||
((unsigned int)p[2] << 16) |
|
||||
((unsigned int)p[1] << 8) |
|
||||
(unsigned int)p[0]);
|
||||
}
|
||||
|
||||
long long Read8(void* pv) {
|
||||
unsigned char* p = reinterpret_cast<unsigned char*>(pv);
|
||||
return (long long)(((unsigned long long)p[7] << 56) |
|
||||
((unsigned long long)p[6] << 48) |
|
||||
((unsigned long long)p[5] << 40) |
|
||||
((unsigned long long)p[4] << 32) |
|
||||
((unsigned long long)p[3] << 24) |
|
||||
((unsigned long long)p[2] << 16) |
|
||||
((unsigned long long)p[1] << 8) |
|
||||
(unsigned long long)p[0]);
|
||||
}
|
||||
+111
-144
@@ -14,178 +14,145 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asn1_decoder.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct asn1_context {
|
||||
size_t length;
|
||||
uint8_t* p;
|
||||
int app_type;
|
||||
} asn1_context_t;
|
||||
|
||||
|
||||
static const int kMaskConstructed = 0xE0;
|
||||
static const int kMaskTag = 0x7F;
|
||||
static const int kMaskAppType = 0x1F;
|
||||
|
||||
static const int kTagOctetString = 0x04;
|
||||
static const int kTagOid = 0x06;
|
||||
static const int kTagSequence = 0x30;
|
||||
static const int kTagSet = 0x31;
|
||||
static const int kTagConstructed = 0xA0;
|
||||
|
||||
asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length) {
|
||||
asn1_context_t* ctx = (asn1_context_t*) calloc(1, sizeof(asn1_context_t));
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ctx->p = buffer;
|
||||
ctx->length = length;
|
||||
return ctx;
|
||||
int asn1_context::peek_byte() const {
|
||||
if (length_ == 0) {
|
||||
return -1;
|
||||
}
|
||||
return *p_;
|
||||
}
|
||||
|
||||
void asn1_context_free(asn1_context_t* ctx) {
|
||||
free(ctx);
|
||||
int asn1_context::get_byte() {
|
||||
if (length_ == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int byte = *p_;
|
||||
p_++;
|
||||
length_--;
|
||||
return byte;
|
||||
}
|
||||
|
||||
static inline int peek_byte(asn1_context_t* ctx) {
|
||||
if (ctx->length <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return *ctx->p;
|
||||
bool asn1_context::skip_bytes(size_t num_skip) {
|
||||
if (length_ < num_skip) {
|
||||
return false;
|
||||
}
|
||||
p_ += num_skip;
|
||||
length_ -= num_skip;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int get_byte(asn1_context_t* ctx) {
|
||||
if (ctx->length <= 0) {
|
||||
return -1;
|
||||
}
|
||||
int byte = *ctx->p;
|
||||
ctx->p++;
|
||||
ctx->length--;
|
||||
return byte;
|
||||
}
|
||||
|
||||
static inline bool skip_bytes(asn1_context_t* ctx, size_t num_skip) {
|
||||
if (ctx->length < num_skip) {
|
||||
return false;
|
||||
}
|
||||
ctx->p += num_skip;
|
||||
ctx->length -= num_skip;
|
||||
bool asn1_context::decode_length(size_t* out_len) {
|
||||
int num_octets = get_byte();
|
||||
if (num_octets == -1) {
|
||||
return false;
|
||||
}
|
||||
if ((num_octets & 0x80) == 0x00) {
|
||||
*out_len = num_octets;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decode_length(asn1_context_t* ctx, size_t* out_len) {
|
||||
int num_octets = get_byte(ctx);
|
||||
if (num_octets == -1) {
|
||||
return false;
|
||||
}
|
||||
num_octets &= kMaskTag;
|
||||
if (static_cast<size_t>(num_octets) >= sizeof(size_t)) {
|
||||
return false;
|
||||
}
|
||||
size_t length = 0;
|
||||
for (int i = 0; i < num_octets; ++i) {
|
||||
int byte = get_byte();
|
||||
if (byte == -1) {
|
||||
return false;
|
||||
}
|
||||
if ((num_octets & 0x80) == 0x00) {
|
||||
*out_len = num_octets;
|
||||
return 1;
|
||||
}
|
||||
num_octets &= kMaskTag;
|
||||
if ((size_t)num_octets >= sizeof(size_t)) {
|
||||
return false;
|
||||
}
|
||||
size_t length = 0;
|
||||
for (int i = 0; i < num_octets; ++i) {
|
||||
int byte = get_byte(ctx);
|
||||
if (byte == -1) {
|
||||
return false;
|
||||
}
|
||||
length <<= 8;
|
||||
length += byte;
|
||||
}
|
||||
*out_len = length;
|
||||
return true;
|
||||
length <<= 8;
|
||||
length += byte;
|
||||
}
|
||||
*out_len = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the constructed type and advances the pointer. E.g. A0 -> 0
|
||||
*/
|
||||
asn1_context_t* asn1_constructed_get(asn1_context_t* ctx) {
|
||||
int type = get_byte(ctx);
|
||||
if (type == -1 || (type & kMaskConstructed) != kTagConstructed) {
|
||||
return NULL;
|
||||
}
|
||||
asn1_context* asn1_context::asn1_constructed_get() {
|
||||
int type = get_byte();
|
||||
if (type == -1 || (type & kMaskConstructed) != kTagConstructed) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t length;
|
||||
if (!decode_length(&length) || length > length_) {
|
||||
return nullptr;
|
||||
}
|
||||
asn1_context* app_ctx = new asn1_context(p_, length);
|
||||
app_ctx->app_type_ = type & kMaskAppType;
|
||||
return app_ctx;
|
||||
}
|
||||
|
||||
bool asn1_context::asn1_constructed_skip_all() {
|
||||
int byte = peek_byte();
|
||||
while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) {
|
||||
skip_bytes(1);
|
||||
size_t length;
|
||||
if (!decode_length(ctx, &length) || length > ctx->length) {
|
||||
return NULL;
|
||||
if (!decode_length(&length) || !skip_bytes(length)) {
|
||||
return false;
|
||||
}
|
||||
asn1_context_t* app_ctx = asn1_context_new(ctx->p, length);
|
||||
app_ctx->app_type = type & kMaskAppType;
|
||||
return app_ctx;
|
||||
byte = peek_byte();
|
||||
}
|
||||
return byte != -1;
|
||||
}
|
||||
|
||||
bool asn1_constructed_skip_all(asn1_context_t* ctx) {
|
||||
int byte = peek_byte(ctx);
|
||||
while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) {
|
||||
skip_bytes(ctx, 1);
|
||||
size_t length;
|
||||
if (!decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
|
||||
return false;
|
||||
}
|
||||
byte = peek_byte(ctx);
|
||||
}
|
||||
return byte != -1;
|
||||
int asn1_context::asn1_constructed_type() const {
|
||||
return app_type_;
|
||||
}
|
||||
|
||||
int asn1_constructed_type(asn1_context_t* ctx) {
|
||||
return ctx->app_type;
|
||||
asn1_context* asn1_context::asn1_sequence_get() {
|
||||
if ((get_byte() & kMaskTag) != kTagSequence) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t length;
|
||||
if (!decode_length(&length) || length > length_) {
|
||||
return nullptr;
|
||||
}
|
||||
return new asn1_context(p_, length);
|
||||
}
|
||||
|
||||
asn1_context_t* asn1_sequence_get(asn1_context_t* ctx) {
|
||||
if ((get_byte(ctx) & kMaskTag) != kTagSequence) {
|
||||
return NULL;
|
||||
}
|
||||
size_t length;
|
||||
if (!decode_length(ctx, &length) || length > ctx->length) {
|
||||
return NULL;
|
||||
}
|
||||
return asn1_context_new(ctx->p, length);
|
||||
asn1_context* asn1_context::asn1_set_get() {
|
||||
if ((get_byte() & kMaskTag) != kTagSet) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t length;
|
||||
if (!decode_length(&length) || length > length_) {
|
||||
return nullptr;
|
||||
}
|
||||
return new asn1_context(p_, length);
|
||||
}
|
||||
|
||||
asn1_context_t* asn1_set_get(asn1_context_t* ctx) {
|
||||
if ((get_byte(ctx) & kMaskTag) != kTagSet) {
|
||||
return NULL;
|
||||
}
|
||||
size_t length;
|
||||
if (!decode_length(ctx, &length) || length > ctx->length) {
|
||||
return NULL;
|
||||
}
|
||||
return asn1_context_new(ctx->p, length);
|
||||
bool asn1_context::asn1_sequence_next() {
|
||||
size_t length;
|
||||
if (get_byte() == -1 || !decode_length(&length) || !skip_bytes(length)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asn1_sequence_next(asn1_context_t* ctx) {
|
||||
size_t length;
|
||||
if (get_byte(ctx) == -1 || !decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
bool asn1_context::asn1_oid_get(const uint8_t** oid, size_t* length) {
|
||||
if (get_byte() != kTagOid) {
|
||||
return false;
|
||||
}
|
||||
if (!decode_length(length) || *length == 0 || *length > length_) {
|
||||
return false;
|
||||
}
|
||||
*oid = p_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length) {
|
||||
if (get_byte(ctx) != kTagOid) {
|
||||
return false;
|
||||
}
|
||||
if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
|
||||
return false;
|
||||
}
|
||||
*oid = ctx->p;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length) {
|
||||
if (get_byte(ctx) != kTagOctetString) {
|
||||
return false;
|
||||
}
|
||||
if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
|
||||
return false;
|
||||
}
|
||||
*octet_string = ctx->p;
|
||||
return true;
|
||||
bool asn1_context::asn1_octet_string_get(const uint8_t** octet_string, size_t* length) {
|
||||
if (get_byte() != kTagOctetString) {
|
||||
return false;
|
||||
}
|
||||
if (!decode_length(length) || *length == 0 || *length > length_) {
|
||||
return false;
|
||||
}
|
||||
*octet_string = p_;
|
||||
return true;
|
||||
}
|
||||
|
||||
+31
-12
@@ -14,23 +14,42 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ASN1_DECODER_H_
|
||||
#define ASN1_DECODER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct asn1_context asn1_context_t;
|
||||
class asn1_context {
|
||||
public:
|
||||
asn1_context(const uint8_t* buffer, size_t length) : p_(buffer), length_(length), app_type_(0) {}
|
||||
int asn1_constructed_type() const;
|
||||
asn1_context* asn1_constructed_get();
|
||||
bool asn1_constructed_skip_all();
|
||||
asn1_context* asn1_sequence_get();
|
||||
asn1_context* asn1_set_get();
|
||||
bool asn1_sequence_next();
|
||||
bool asn1_oid_get(const uint8_t** oid, size_t* length);
|
||||
bool asn1_octet_string_get(const uint8_t** octet_string, size_t* length);
|
||||
|
||||
asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length);
|
||||
void asn1_context_free(asn1_context_t* ctx);
|
||||
asn1_context_t* asn1_constructed_get(asn1_context_t* ctx);
|
||||
bool asn1_constructed_skip_all(asn1_context_t* ctx);
|
||||
int asn1_constructed_type(asn1_context_t* ctx);
|
||||
asn1_context_t* asn1_sequence_get(asn1_context_t* ctx);
|
||||
asn1_context_t* asn1_set_get(asn1_context_t* ctx);
|
||||
bool asn1_sequence_next(asn1_context_t* seq);
|
||||
bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length);
|
||||
bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length);
|
||||
private:
|
||||
static constexpr int kMaskConstructed = 0xE0;
|
||||
static constexpr int kMaskTag = 0x7F;
|
||||
static constexpr int kMaskAppType = 0x1F;
|
||||
|
||||
static constexpr int kTagOctetString = 0x04;
|
||||
static constexpr int kTagOid = 0x06;
|
||||
static constexpr int kTagSequence = 0x30;
|
||||
static constexpr int kTagSet = 0x31;
|
||||
static constexpr int kTagConstructed = 0xA0;
|
||||
|
||||
int peek_byte() const;
|
||||
int get_byte();
|
||||
bool skip_bytes(size_t num_skip);
|
||||
bool decode_length(size_t* out_len);
|
||||
|
||||
const uint8_t* p_;
|
||||
size_t length_;
|
||||
int app_type_;
|
||||
};
|
||||
|
||||
#endif /* ASN1_DECODER_H_ */
|
||||
|
||||
@@ -14,32 +14,16 @@
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 25; echo $$?),0)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_SRC_FILES := bootloader_message.cpp
|
||||
LOCAL_MODULE := libbootloader_message
|
||||
LOCAL_STATIC_LIBRARIES := libfs_mgr
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
endif
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_SRC_FILES := bootloader_message.cpp
|
||||
LOCAL_MODULE := libbootloader_message
|
||||
LOCAL_C_INCLUDES += bionic $(LOCAL_PATH)/include
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
|
||||
LOCAL_C_INCLUDES += external/stlport/stlport
|
||||
LOCAL_SHARED_LIBRARIES += libstlport
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libc++
|
||||
endif
|
||||
LOCAL_CFLAGS := -DEXCLUDE_FS_MGR
|
||||
# ignore bootloader's factory reset command even when written to /misc
|
||||
ifeq ($(TW_IGNORE_MISC_WIPE_DATA), true)
|
||||
LOCAL_CFLAGS += -DIGNORE_MISC_WIPE_DATA
|
||||
LOCAL_STATIC_LIBRARIES := libbase libfs_mgr
|
||||
LOCAL_CFLAGS := -Werror
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0)
|
||||
TARGET_GLOBAL_CFLAGS += -DUSE_OLD_BOOTLOADER_MESSAGE
|
||||
CLANG_TARGET_GLOBAL_CFLAGS += -DUSE_OLD_BOOTLOADER_MESSAGE
|
||||
endif
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
@@ -19,31 +19,18 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.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>
|
||||
*/
|
||||
#ifndef EXCLUDE_FS_MGR
|
||||
#include <fs_mgr.h>
|
||||
#endif
|
||||
|
||||
static std::string misc_blkdev;
|
||||
#ifdef USE_OLD_BOOTLOADER_MESSAGE
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
void set_misc_device(std::string name) {
|
||||
misc_blkdev = name;
|
||||
}
|
||||
|
||||
#ifndef EXCLUDE_FS_MGR
|
||||
static struct fstab* read_fstab(std::string* err) {
|
||||
// The fstab path is always "/fstab.${ro.hardware}".
|
||||
std::string fstab_path = "/fstab.";
|
||||
@@ -62,23 +49,28 @@ static struct fstab* read_fstab(std::string* err) {
|
||||
#endif
|
||||
|
||||
static std::string get_misc_blk_device(std::string* err) {
|
||||
#ifdef EXCLUDE_FS_MGR
|
||||
return misc_blkdev;
|
||||
#else
|
||||
#ifdef USE_OLD_BOOTLOADER_MESSAGE
|
||||
struct fstab* fstab = read_fstab(err);
|
||||
if (fstab == nullptr) {
|
||||
#else
|
||||
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
|
||||
fs_mgr_free_fstab);
|
||||
#endif
|
||||
if (!fstab) {
|
||||
*err = "failed to read default fstab";
|
||||
return "";
|
||||
}
|
||||
#ifdef USE_OLD_BOOTLOADER_MESSAGE
|
||||
fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
|
||||
#else
|
||||
fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab.get(), "/misc");
|
||||
#endif
|
||||
if (record == nullptr) {
|
||||
*err = "failed to find /misc partition";
|
||||
return "";
|
||||
}
|
||||
return record->blk_device;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// In recovery mode, recovery can get started and try to access the misc
|
||||
// device before the kernel has actually created it.
|
||||
static bool wait_for_device(const std::string& blk_device, std::string* err) {
|
||||
@@ -90,127 +82,94 @@ static bool wait_for_device(const std::string& blk_device, std::string* err) {
|
||||
struct stat buf;
|
||||
ret = stat(blk_device.c_str(), &buf);
|
||||
if (ret == -1) {
|
||||
char buffer[2048];
|
||||
sprintf(buffer, "failed to stat %s try %d: %s\n",
|
||||
blk_device.c_str(), tries, strerror(errno));
|
||||
*err += buffer;
|
||||
/*
|
||||
*err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
|
||||
blk_device.c_str(), tries, strerror(errno));
|
||||
*/
|
||||
sleep(1);
|
||||
}
|
||||
} while (ret && tries < 10);
|
||||
|
||||
if (ret) {
|
||||
*err += "failed to stat " + blk_device + "\n";
|
||||
/*
|
||||
*err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
|
||||
*/
|
||||
}
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
static bool read_misc_partition(void* p, size_t size, size_t offset, std::string* err) {
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
|
||||
size_t offset, std::string* err) {
|
||||
if (!wait_for_device(misc_blk_device, err)) {
|
||||
return false;
|
||||
}
|
||||
int fd(open(misc_blk_device.c_str(), O_RDONLY));
|
||||
if (fd < 0) {
|
||||
*err = "failed to open " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
/*
|
||||
android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
|
||||
if (fd == -1) {
|
||||
*err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
|
||||
*err = "failed to lseek " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
*err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if (read(fd, p, size) != size) {
|
||||
*err = "failed to read " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
if (!android::base::ReadFully(fd, p, size)) {
|
||||
*err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) {
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
*err = "no misc device set";
|
||||
return false;
|
||||
}
|
||||
int fd = (open(misc_blk_device.c_str(), O_WRONLY | O_SYNC));
|
||||
static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
|
||||
size_t offset, std::string* err) {
|
||||
android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
|
||||
if (fd == -1) {
|
||||
*err = "failed to open " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
/*
|
||||
*err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
|
||||
*err = "failed to lseek " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
*err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if (write(fd, p, size) != size) {
|
||||
*err = "failed to write " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
if (!android::base::WriteFully(fd, p, size)) {
|
||||
*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) == -1) {
|
||||
*err = "failed to fsync " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
*err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
|
||||
std::string* err) {
|
||||
return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
|
||||
BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
|
||||
}
|
||||
|
||||
bool read_bootloader_message(bootloader_message* boot, std::string* err) {
|
||||
return read_misc_partition(boot, sizeof(*boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
return read_bootloader_message_from(boot, misc_blk_device, err);
|
||||
}
|
||||
|
||||
bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device,
|
||||
std::string* err) {
|
||||
return write_misc_partition(&boot, sizeof(boot), misc_blk_device,
|
||||
BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
|
||||
}
|
||||
|
||||
bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
|
||||
return write_misc_partition(&boot, sizeof(boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
return write_bootloader_message_to(boot, misc_blk_device, err);
|
||||
}
|
||||
|
||||
bool clear_bootloader_message(std::string* err) {
|
||||
@@ -224,120 +183,72 @@ bool write_bootloader_message(const std::vector<std::string>& options, std::stri
|
||||
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
|
||||
for (const auto& s : options) {
|
||||
strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
|
||||
if (s.substr(s.size() - 1) != "\n") {
|
||||
if (s.back() != '\n') {
|
||||
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
|
||||
}
|
||||
}
|
||||
return write_bootloader_message(boot, err);
|
||||
}
|
||||
|
||||
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
|
||||
bootloader_message boot;
|
||||
if (!read_bootloader_message(&boot, err)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Zero out the entire fields.
|
||||
memset(boot.command, 0, sizeof(boot.command));
|
||||
memset(boot.recovery, 0, sizeof(boot.recovery));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool write_reboot_bootloader(std::string* err) {
|
||||
bootloader_message boot;
|
||||
if (!read_bootloader_message(&boot, err)) {
|
||||
return false;
|
||||
}
|
||||
if (boot.command[0] != '\0') {
|
||||
*err = "Bootloader command pending.";
|
||||
return false;
|
||||
}
|
||||
strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
|
||||
return write_bootloader_message(boot, err);
|
||||
}
|
||||
|
||||
bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
package_data->resize(size);
|
||||
return read_misc_partition(&(*package_data)[0], size, WIPE_PACKAGE_OFFSET_IN_MISC, err);
|
||||
return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
|
||||
WIPE_PACKAGE_OFFSET_IN_MISC, err);
|
||||
}
|
||||
|
||||
bool write_wipe_package(const std::string& package_data, std::string* err) {
|
||||
return write_misc_partition(package_data.data(), package_data.size(),
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device,
|
||||
WIPE_PACKAGE_OFFSET_IN_MISC, err);
|
||||
}
|
||||
|
||||
extern "C" bool write_reboot_bootloader(void) {
|
||||
std::string err;
|
||||
return write_reboot_bootloader(&err);
|
||||
}
|
||||
|
||||
extern "C" bool write_bootloader_message(const char* options) {
|
||||
std::string err;
|
||||
bootloader_message boot = {};
|
||||
memcpy(&boot, options, sizeof(boot));
|
||||
return write_bootloader_message(boot, &err);
|
||||
}
|
||||
|
||||
static const char *COMMAND_FILE = "/cache/recovery/command";
|
||||
static const int MAX_ARG_LENGTH = 4096;
|
||||
static const int MAX_ARGS = 100;
|
||||
|
||||
// command line args come from, in decreasing precedence:
|
||||
// - the actual command line
|
||||
// - the bootloader control block (one per line, after "recovery")
|
||||
// - the contents of COMMAND_FILE (one per line)
|
||||
void
|
||||
get_args(int *argc, char ***argv) {
|
||||
bootloader_message boot = {};
|
||||
std::string err;
|
||||
if (!read_bootloader_message(&boot, &err)) {
|
||||
printf("%s\n", err.c_str());
|
||||
// If fails, leave a zeroed bootloader_message.
|
||||
memset(&boot, 0, sizeof(boot));
|
||||
}
|
||||
//stage = strndup(boot.stage, sizeof(boot.stage));
|
||||
|
||||
if (boot.command[0] != 0 && boot.command[0] != 255) {
|
||||
printf("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
|
||||
}
|
||||
|
||||
if (boot.status[0] != 0 && boot.status[0] != 255) {
|
||||
printf("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
|
||||
}
|
||||
|
||||
// --- if arguments weren't supplied, look in the bootloader control block
|
||||
if (*argc <= 1) {
|
||||
boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
|
||||
const char *arg = strtok(boot.recovery, "\n");
|
||||
if (arg != NULL && !strcmp(arg, "recovery")) {
|
||||
*argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
|
||||
(*argv)[0] = strdup(arg);
|
||||
for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
|
||||
if ((arg = strtok(NULL, "\n")) == NULL) break;
|
||||
|
||||
// if the device does not have an own recovery key combo we just want to open TWRP after
|
||||
// walking through the factory reset screen - without actually doing a factory reset
|
||||
#ifdef IGNORE_MISC_WIPE_DATA
|
||||
if (!strcmp(arg, "--wipe_data")) {
|
||||
(*argv)[*argc] = "";
|
||||
*argc = *argc -1;
|
||||
printf("Bootloader arg \"%s\" ignored because TWRP was compiled with TW_IGNORE_MISC_WIPE_DATA\n", arg);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
(*argv)[*argc] = strdup(arg);
|
||||
}
|
||||
printf("Got arguments from boot message\n");
|
||||
} else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
|
||||
printf("Bad boot message\n\"%.20s\"\n", boot.recovery);
|
||||
}
|
||||
}
|
||||
|
||||
// --- if that doesn't work, try the command file (if we have /cache).
|
||||
if (*argc <= 1/* && has_cache*/) {
|
||||
FILE *fp = fopen(COMMAND_FILE, "r");
|
||||
if (fp != NULL) {
|
||||
char *token;
|
||||
char *argv0 = (*argv)[0];
|
||||
*argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
|
||||
(*argv)[0] = argv0; // use the same program name
|
||||
|
||||
char buf[MAX_ARG_LENGTH];
|
||||
for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
|
||||
if (!fgets(buf, sizeof(buf), fp)) break;
|
||||
token = strtok(buf, "\r\n");
|
||||
if (token != NULL) {
|
||||
(*argv)[*argc] = strdup(token); // Strip newline.
|
||||
} else {
|
||||
--*argc;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
printf("Got arguments from %s\n", COMMAND_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
// --> write the arguments we have back into the bootloader control block
|
||||
// always boot into recovery after this (until finish_recovery() is called)
|
||||
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
|
||||
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
|
||||
int i;
|
||||
for (i = 1; i < *argc; ++i) {
|
||||
strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
|
||||
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
|
||||
}
|
||||
if (!write_bootloader_message(boot, &err)) {
|
||||
printf("%s\n", err.c_str());
|
||||
}
|
||||
return write_bootloader_message({options}, &err);
|
||||
}
|
||||
|
||||
@@ -17,23 +17,21 @@
|
||||
#ifndef _BOOTLOADER_MESSAGE_H
|
||||
#define _BOOTLOADER_MESSAGE_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Spaces used by misc partition are as below:
|
||||
// 0 - 2K Bootloader Message
|
||||
// 2K - 16K Used by Vendor's bootloader
|
||||
// 0 - 2K For bootloader_message
|
||||
// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used
|
||||
// as bootloader_message_ab struct)
|
||||
// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
|
||||
// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
|
||||
// are not configurable without changing all of them.
|
||||
#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
|
||||
static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = BOARD_RECOVERY_BLDRMSG_OFFSET;
|
||||
static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024 + BOOTLOADER_MESSAGE_OFFSET_IN_MISC;
|
||||
#else
|
||||
static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
|
||||
static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
|
||||
#endif
|
||||
|
||||
/* Bootloader Message
|
||||
/* Bootloader Message (2-KiB)
|
||||
*
|
||||
* This structure describes the content of a block in flash
|
||||
* that is used for recovery and the bootloader to talk to
|
||||
@@ -44,8 +42,9 @@ static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
|
||||
* It is also updated by the bootloader when firmware update
|
||||
* is complete (to boot into recovery for any final cleanup)
|
||||
*
|
||||
* The status field is written by the bootloader after the
|
||||
* completion of an "update-radio" or "update-hboot" command.
|
||||
* The status field was used by the bootloader after the completion
|
||||
* of an "update-radio" or "update-hboot" command, which has been
|
||||
* deprecated since Froyo.
|
||||
*
|
||||
* The recovery field is only written by linux and used
|
||||
* for the system to send a message to recovery or the
|
||||
@@ -56,18 +55,17 @@ static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
|
||||
* package it is. If the value is of the format "#/#" (eg, "1/3"),
|
||||
* the UI will add a simple indicator of that status.
|
||||
*
|
||||
* The slot_suffix field is used for A/B implementations where the
|
||||
* bootloader does not set the androidboot.ro.boot.slot_suffix kernel
|
||||
* commandline parameter. This is used by fs_mgr to mount /system and
|
||||
* other partitions with the slotselect flag set in fstab. A/B
|
||||
* implementations are free to use all 32 bytes and may store private
|
||||
* data past the first NUL-byte in this field.
|
||||
* We used to have slot_suffix field for A/B boot control metadata in
|
||||
* this struct, which gets unintentionally cleared by recovery or
|
||||
* uncrypt. Move it into struct bootloader_message_ab to avoid the
|
||||
* issue.
|
||||
*/
|
||||
struct bootloader_message {
|
||||
char command[32];
|
||||
char status[32];
|
||||
char recovery[768];
|
||||
|
||||
#ifdef USE_OLD_BOOTLOADER_MESSAGE
|
||||
// The 'recovery' field used to be 1024 bytes. It has only ever
|
||||
// been used to store the recovery command line, so 768 bytes
|
||||
// should be plenty. We carve off the last 256 bytes to store the
|
||||
@@ -76,23 +74,156 @@ struct bootloader_message {
|
||||
char stage[32];
|
||||
char slot_suffix[32];
|
||||
char reserved[192];
|
||||
#else
|
||||
// The 'recovery' field used to be 1024 bytes. It has only ever
|
||||
// been used to store the recovery command line, so 768 bytes
|
||||
// should be plenty. We carve off the last 256 bytes to store the
|
||||
// stage string (for multistage packages) and possible future
|
||||
// expansion.
|
||||
char stage[32];
|
||||
|
||||
// The 'reserved' field used to be 224 bytes when it was initially
|
||||
// carved off from the 1024-byte recovery field. Bump it up to
|
||||
// 1184-byte so that the entire bootloader_message struct rounds up
|
||||
// to 2048-byte.
|
||||
char reserved[1184];
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* We must be cautious when changing the bootloader_message struct size,
|
||||
* because A/B-specific fields may end up with different offsets.
|
||||
*/
|
||||
#if !defined(USE_OLD_BOOTLOADER_MESSAGE) && ((__STDC_VERSION__ >= 201112L) || defined(__cplusplus))
|
||||
static_assert(sizeof(struct bootloader_message) == 2048,
|
||||
"struct bootloader_message size changes, which may break A/B devices");
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The A/B-specific bootloader message structure (4-KiB).
|
||||
*
|
||||
* We separate A/B boot control metadata from the regular bootloader
|
||||
* message struct and keep it here. Everything that's A/B-specific
|
||||
* stays after struct bootloader_message, which should be managed by
|
||||
* the A/B-bootloader or boot control HAL.
|
||||
*
|
||||
* The slot_suffix field is used for A/B implementations where the
|
||||
* bootloader does not set the androidboot.ro.boot.slot_suffix kernel
|
||||
* commandline parameter. This is used by fs_mgr to mount /system and
|
||||
* other partitions with the slotselect flag set in fstab. A/B
|
||||
* implementations are free to use all 32 bytes and may store private
|
||||
* data past the first NUL-byte in this field. It is encouraged, but
|
||||
* not mandatory, to use 'struct bootloader_control' described below.
|
||||
*/
|
||||
struct bootloader_message_ab {
|
||||
struct bootloader_message message;
|
||||
char slot_suffix[32];
|
||||
|
||||
// Round up the entire struct to 4096-byte.
|
||||
char reserved[2016];
|
||||
};
|
||||
|
||||
/**
|
||||
* Be cautious about the struct size change, in case we put anything post
|
||||
* bootloader_message_ab struct (b/29159185).
|
||||
*/
|
||||
#if !defined(USE_OLD_BOOTLOADER_MESSAGE) && ((__STDC_VERSION__ >= 201112L) || defined(__cplusplus))
|
||||
static_assert(sizeof(struct bootloader_message_ab) == 4096,
|
||||
"struct bootloader_message_ab size changes");
|
||||
#endif
|
||||
|
||||
#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */
|
||||
#define BOOT_CTRL_VERSION 1
|
||||
|
||||
struct slot_metadata {
|
||||
// Slot priority with 15 meaning highest priority, 1 lowest
|
||||
// priority and 0 the slot is unbootable.
|
||||
uint8_t priority : 4;
|
||||
// Number of times left attempting to boot this slot.
|
||||
uint8_t tries_remaining : 3;
|
||||
// 1 if this slot has booted successfully, 0 otherwise.
|
||||
uint8_t successful_boot : 1;
|
||||
// 1 if this slot is corrupted from a dm-verity corruption, 0
|
||||
// otherwise.
|
||||
uint8_t verity_corrupted : 1;
|
||||
// Reserved for further use.
|
||||
uint8_t reserved : 7;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Bootloader Control AB
|
||||
*
|
||||
* This struct can be used to manage A/B metadata. It is designed to
|
||||
* be put in the 'slot_suffix' field of the 'bootloader_message'
|
||||
* structure described above. It is encouraged to use the
|
||||
* 'bootloader_control' structure to store the A/B metadata, but not
|
||||
* mandatory.
|
||||
*/
|
||||
struct bootloader_control {
|
||||
// NUL terminated active slot suffix.
|
||||
char slot_suffix[4];
|
||||
// Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
|
||||
uint32_t magic;
|
||||
// Version of struct being used (see BOOT_CTRL_VERSION).
|
||||
uint8_t version;
|
||||
// Number of slots being managed.
|
||||
uint8_t nb_slot : 3;
|
||||
// Number of times left attempting to boot recovery.
|
||||
uint8_t recovery_tries_remaining : 3;
|
||||
// Ensure 4-bytes alignment for slot_info field.
|
||||
uint8_t reserved0[2];
|
||||
// Per-slot information. Up to 4 slots.
|
||||
struct slot_metadata slot_info[4];
|
||||
// Reserved for further use.
|
||||
uint8_t reserved1[8];
|
||||
// CRC32 of all 28 bytes preceding this field (little endian
|
||||
// format).
|
||||
uint32_t crc32_le;
|
||||
} __attribute__((packed));
|
||||
|
||||
#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
|
||||
static_assert(sizeof(struct bootloader_control) ==
|
||||
sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
|
||||
"struct bootloader_control has wrong size");
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Read bootloader message into boot. Error message will be set in err.
|
||||
bool read_bootloader_message(bootloader_message* boot, std::string* err);
|
||||
|
||||
// Read bootloader message from the specified misc device into boot.
|
||||
bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
|
||||
std::string* err);
|
||||
|
||||
// Write bootloader message to BCB.
|
||||
bool write_bootloader_message(const bootloader_message& boot, std::string* err);
|
||||
|
||||
// Write bootloader message to the specified BCB device.
|
||||
bool write_bootloader_message_to(const bootloader_message& boot,
|
||||
const std::string& misc_blk_device, std::string* err);
|
||||
|
||||
// Write bootloader message (boots into recovery with the options) to BCB. Will
|
||||
// set the command and recovery fields, and reset the rest.
|
||||
bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
|
||||
|
||||
// Update bootloader message (boots into recovery with the options) to BCB. Will
|
||||
// only update the command and recovery fields.
|
||||
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
|
||||
|
||||
// Clear BCB.
|
||||
bool clear_bootloader_message(std::string* err);
|
||||
|
||||
bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
|
||||
bool write_wipe_package(const std::string& package_data, std::string* err);
|
||||
// Writes the reboot-bootloader reboot reason to the bootloader_message.
|
||||
bool write_reboot_bootloader(std::string* err);
|
||||
|
||||
void set_misc_device(std::string name);
|
||||
void get_args(int *argc, char ***argv);
|
||||
// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC).
|
||||
bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
|
||||
|
||||
// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC).
|
||||
bool write_wipe_package(const std::string& package_data, std::string* err);
|
||||
|
||||
#else
|
||||
|
||||
@@ -100,6 +231,7 @@ void get_args(int *argc, char ***argv);
|
||||
|
||||
// C Interface.
|
||||
bool write_bootloader_message(const char* options);
|
||||
bool write_reboot_bootloader(void);
|
||||
|
||||
#endif // ifdef __cplusplus
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_SRC_FILES := bootloader_message.cpp
|
||||
LOCAL_MODULE := libbootloader_message_twrp
|
||||
LOCAL_C_INCLUDES += bionic $(LOCAL_PATH)/include
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 22; echo $$?),0)
|
||||
LOCAL_C_INCLUDES += external/stlport/stlport
|
||||
LOCAL_SHARED_LIBRARIES += libstlport
|
||||
else
|
||||
LOCAL_C_INCLUDES += external/libcxx/include
|
||||
LOCAL_SHARED_LIBRARIES += libc++
|
||||
endif
|
||||
LOCAL_CFLAGS := -Werror -std=c++11
|
||||
# ignore bootloader's factory reset command even when written to /misc
|
||||
ifeq ($(TW_IGNORE_MISC_WIPE_DATA), true)
|
||||
LOCAL_CFLAGS += -DIGNORE_MISC_WIPE_DATA
|
||||
endif
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* 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 <bootloader_message_twrp/bootloader_message.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
static std::string misc_blkdev;
|
||||
|
||||
void set_misc_device(const char* name) {
|
||||
misc_blkdev = name;
|
||||
}
|
||||
|
||||
static std::string get_misc_blk_device(std::string* err) {
|
||||
*err = "";
|
||||
return misc_blkdev;
|
||||
}
|
||||
|
||||
// In recovery mode, recovery can get started and try to access the misc
|
||||
// device before the kernel has actually created it.
|
||||
static bool wait_for_device(const std::string& blk_device, std::string* err) {
|
||||
int tries = 0;
|
||||
int ret;
|
||||
err->clear();
|
||||
do {
|
||||
++tries;
|
||||
struct stat buf;
|
||||
ret = stat(blk_device.c_str(), &buf);
|
||||
if (ret == -1) {
|
||||
char buffer[2048];
|
||||
sprintf(buffer, "failed to stat %s try %d: %s\n",
|
||||
blk_device.c_str(), tries, strerror(errno));
|
||||
*err += buffer;
|
||||
/*
|
||||
*err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
|
||||
blk_device.c_str(), tries, strerror(errno));
|
||||
*/
|
||||
sleep(1);
|
||||
}
|
||||
} while (ret && tries < 10);
|
||||
|
||||
if (ret) {
|
||||
*err += "failed to stat " + blk_device + "\n";
|
||||
/*
|
||||
*err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
|
||||
*/
|
||||
}
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
|
||||
size_t offset, std::string* err) {
|
||||
if (!wait_for_device(misc_blk_device, err)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd(open(misc_blk_device.c_str(), O_RDONLY));
|
||||
if (fd < 0) {
|
||||
*err = "failed to open " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
/*
|
||||
android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
|
||||
if (fd == -1) {
|
||||
*err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
|
||||
*err = "failed to lseek " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
|
||||
*err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((size_t)read(fd, p, size) != size) {
|
||||
*err = "failed to read " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
if (!android::base::ReadFully(fd, p, size)) {
|
||||
*err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) {
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
*err = "no misc device set";
|
||||
return false;
|
||||
}
|
||||
int fd = (open(misc_blk_device.c_str(), O_WRONLY | O_SYNC));
|
||||
if (fd == -1) {
|
||||
*err = "failed to open " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
/*
|
||||
static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
|
||||
size_t offset, std::string* err) {
|
||||
android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
|
||||
if (fd == -1) {
|
||||
*err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
|
||||
*err = "failed to lseek " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
*err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
if ((size_t)write(fd, p, size) != size) {
|
||||
*err = "failed to write " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
if (!android::base::WriteFully(fd, p, size)) {
|
||||
*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) == -1) {
|
||||
*err = "failed to fsync " + misc_blk_device + ": ";
|
||||
*err += strerror(errno);
|
||||
close(fd);
|
||||
/*
|
||||
if (fsync(fd) == -1) {
|
||||
*err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
|
||||
strerror(errno));
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
|
||||
std::string* err) {
|
||||
return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
|
||||
BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
|
||||
}
|
||||
|
||||
bool read_bootloader_message(bootloader_message* boot, std::string* err) {
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
return read_bootloader_message_from(boot, misc_blk_device, err);
|
||||
}
|
||||
|
||||
bool write_bootloader_message_to(const bootloader_message& boot, __unused const std::string& misc_blk_device,
|
||||
std::string* err) {
|
||||
return write_misc_partition(&boot, sizeof(boot),
|
||||
BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return write_bootloader_message_to(boot, misc_blk_device, err);
|
||||
}
|
||||
|
||||
// libc++ in 5.1 does not know how to handle a std::string* so this craziness is needed
|
||||
bool clear_bootloader_message(void* err) {
|
||||
std::string &s = *(static_cast<std::string*>(err));
|
||||
return clear_bootloader_message(&s);
|
||||
}
|
||||
|
||||
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.substr(s.size() - 1) != "\n") {
|
||||
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
|
||||
}
|
||||
}
|
||||
return write_bootloader_message(boot, err);
|
||||
}
|
||||
|
||||
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
|
||||
bootloader_message boot;
|
||||
if (!read_bootloader_message(&boot, err)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Zero out the entire fields.
|
||||
memset(boot.command, 0, sizeof(boot.command));
|
||||
memset(boot.recovery, 0, sizeof(boot.recovery));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool write_reboot_bootloader(std::string* err) {
|
||||
bootloader_message boot;
|
||||
if (!read_bootloader_message(&boot, err)) {
|
||||
return false;
|
||||
}
|
||||
if (boot.command[0] != '\0') {
|
||||
*err = "Bootloader command pending.";
|
||||
return false;
|
||||
}
|
||||
strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
|
||||
return write_bootloader_message(boot, err);
|
||||
}
|
||||
|
||||
bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
package_data->resize(size);
|
||||
return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
|
||||
WIPE_PACKAGE_OFFSET_IN_MISC, err);
|
||||
}
|
||||
|
||||
bool write_wipe_package(const std::string& package_data, std::string* err) {
|
||||
std::string misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
return write_misc_partition(package_data.data(), package_data.size(),
|
||||
WIPE_PACKAGE_OFFSET_IN_MISC, err);
|
||||
}
|
||||
|
||||
extern "C" bool write_reboot_bootloader(void) {
|
||||
std::string err;
|
||||
return write_reboot_bootloader(&err);
|
||||
}
|
||||
|
||||
extern "C" bool write_bootloader_message(const char* options) {
|
||||
std::string err;
|
||||
bootloader_message boot = {};
|
||||
memcpy(&boot, options, sizeof(boot));
|
||||
return write_bootloader_message(boot, &err);
|
||||
}
|
||||
|
||||
static const char *COMMAND_FILE = "/cache/recovery/command";
|
||||
static const int MAX_ARG_LENGTH = 4096;
|
||||
static const int MAX_ARGS = 100;
|
||||
|
||||
// command line args come from, in decreasing precedence:
|
||||
// - the actual command line
|
||||
// - the bootloader control block (one per line, after "recovery")
|
||||
// - the contents of COMMAND_FILE (one per line)
|
||||
void
|
||||
get_args(int *argc, char ***argv) {
|
||||
bootloader_message boot = {};
|
||||
std::string err;
|
||||
if (!read_bootloader_message(&boot, &err)) {
|
||||
printf("%s\n", err.c_str());
|
||||
// If fails, leave a zeroed bootloader_message.
|
||||
memset(&boot, 0, sizeof(boot));
|
||||
}
|
||||
//stage = strndup(boot.stage, sizeof(boot.stage));
|
||||
|
||||
if (boot.command[0] != 0 && boot.command[0] != 255) {
|
||||
printf("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
|
||||
}
|
||||
|
||||
if (boot.status[0] != 0 && boot.status[0] != 255) {
|
||||
printf("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
|
||||
}
|
||||
|
||||
// --- if arguments weren't supplied, look in the bootloader control block
|
||||
if (*argc <= 1) {
|
||||
boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
|
||||
const char *arg = strtok(boot.recovery, "\n");
|
||||
if (arg != NULL && !strcmp(arg, "recovery")) {
|
||||
*argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
|
||||
(*argv)[0] = strdup(arg);
|
||||
for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
|
||||
if ((arg = strtok(NULL, "\n")) == NULL) break;
|
||||
|
||||
// if the device does not have an own recovery key combo we just want to open TWRP after
|
||||
// walking through the factory reset screen - without actually doing a factory reset
|
||||
#ifdef IGNORE_MISC_WIPE_DATA
|
||||
if (!strcmp(arg, "--wipe_data")) {
|
||||
(*argv)[*argc] = "";
|
||||
*argc = *argc -1;
|
||||
printf("Bootloader arg \"%s\" ignored because TWRP was compiled with TW_IGNORE_MISC_WIPE_DATA\n", arg);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
(*argv)[*argc] = strdup(arg);
|
||||
}
|
||||
printf("Got arguments from boot message\n");
|
||||
} else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
|
||||
printf("Bad boot message\n\"%.20s\"\n", boot.recovery);
|
||||
}
|
||||
}
|
||||
|
||||
// --- if that doesn't work, try the command file (if we have /cache).
|
||||
if (*argc <= 1/* && has_cache*/) {
|
||||
FILE *fp = fopen(COMMAND_FILE, "r");
|
||||
if (fp != NULL) {
|
||||
char *token;
|
||||
char *argv0 = (*argv)[0];
|
||||
*argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
|
||||
(*argv)[0] = argv0; // use the same program name
|
||||
|
||||
char buf[MAX_ARG_LENGTH];
|
||||
for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
|
||||
if (!fgets(buf, sizeof(buf), fp)) break;
|
||||
token = strtok(buf, "\r\n");
|
||||
if (token != NULL) {
|
||||
(*argv)[*argc] = strdup(token); // Strip newline.
|
||||
} else {
|
||||
--*argc;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
printf("Got arguments from %s\n", COMMAND_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
// --> write the arguments we have back into the bootloader control block
|
||||
// always boot into recovery after this (until finish_recovery() is called)
|
||||
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
|
||||
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
|
||||
int i;
|
||||
for (i = 1; i < *argc; ++i) {
|
||||
strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
|
||||
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
|
||||
}
|
||||
if (!write_bootloader_message(boot, &err)) {
|
||||
printf("%s\n", err.c_str());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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_TWRP_H
|
||||
#define _BOOTLOADER_MESSAGE_TWRP_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Spaces used by misc partition are as below:
|
||||
// 0 - 2K For bootloader_message
|
||||
// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used
|
||||
// as bootloader_message_ab struct)
|
||||
// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
|
||||
// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
|
||||
// are not configurable without changing all of them.
|
||||
#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
|
||||
static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = BOARD_RECOVERY_BLDRMSG_OFFSET;
|
||||
static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024 + BOOTLOADER_MESSAGE_OFFSET_IN_MISC;
|
||||
#else
|
||||
static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
|
||||
static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
|
||||
#endif
|
||||
|
||||
/* Bootloader Message (2-KiB)
|
||||
*
|
||||
* This structure describes the content of a block in flash
|
||||
* that is used for recovery and the bootloader to talk to
|
||||
* each other.
|
||||
*
|
||||
* The command field is updated by linux when it wants to
|
||||
* reboot into recovery or to update radio or bootloader firmware.
|
||||
* It is also updated by the bootloader when firmware update
|
||||
* is complete (to boot into recovery for any final cleanup)
|
||||
*
|
||||
* The status field was used by the bootloader after the completion
|
||||
* of an "update-radio" or "update-hboot" command, which has been
|
||||
* deprecated since Froyo.
|
||||
*
|
||||
* The recovery field is only written by linux and used
|
||||
* for the system to send a message to recovery or the
|
||||
* other way around.
|
||||
*
|
||||
* The stage field is written by packages which restart themselves
|
||||
* multiple times, so that the UI can reflect which invocation of the
|
||||
* package it is. If the value is of the format "#/#" (eg, "1/3"),
|
||||
* the UI will add a simple indicator of that status.
|
||||
*
|
||||
* We used to have slot_suffix field for A/B boot control metadata in
|
||||
* this struct, which gets unintentionally cleared by recovery or
|
||||
* uncrypt. Move it into struct bootloader_message_ab to avoid the
|
||||
* issue.
|
||||
*/
|
||||
struct bootloader_message {
|
||||
char command[32];
|
||||
char status[32];
|
||||
char recovery[768];
|
||||
|
||||
// The 'recovery' field used to be 1024 bytes. It has only ever
|
||||
// been used to store the recovery command line, so 768 bytes
|
||||
// should be plenty. We carve off the last 256 bytes to store the
|
||||
// stage string (for multistage packages) and possible future
|
||||
// expansion.
|
||||
char stage[32];
|
||||
|
||||
// The 'reserved' field used to be 224 bytes when it was initially
|
||||
// carved off from the 1024-byte recovery field. Bump it up to
|
||||
// 1184-byte so that the entire bootloader_message struct rounds up
|
||||
// to 2048-byte.
|
||||
char reserved[1184];
|
||||
};
|
||||
|
||||
/**
|
||||
* We must be cautious when changing the bootloader_message struct size,
|
||||
* because A/B-specific fields may end up with different offsets.
|
||||
*/
|
||||
/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
|
||||
static_assert(sizeof(struct bootloader_message) == 2048,
|
||||
"struct bootloader_message size changes, which may break A/B devices");
|
||||
#endif*/
|
||||
|
||||
/**
|
||||
* The A/B-specific bootloader message structure (4-KiB).
|
||||
*
|
||||
* We separate A/B boot control metadata from the regular bootloader
|
||||
* message struct and keep it here. Everything that's A/B-specific
|
||||
* stays after struct bootloader_message, which should be managed by
|
||||
* the A/B-bootloader or boot control HAL.
|
||||
*
|
||||
* The slot_suffix field is used for A/B implementations where the
|
||||
* bootloader does not set the androidboot.ro.boot.slot_suffix kernel
|
||||
* commandline parameter. This is used by fs_mgr to mount /system and
|
||||
* other partitions with the slotselect flag set in fstab. A/B
|
||||
* implementations are free to use all 32 bytes and may store private
|
||||
* data past the first NUL-byte in this field. It is encouraged, but
|
||||
* not mandatory, to use 'struct bootloader_control' described below.
|
||||
*/
|
||||
struct bootloader_message_ab {
|
||||
struct bootloader_message message;
|
||||
char slot_suffix[32];
|
||||
|
||||
// Round up the entire struct to 4096-byte.
|
||||
char reserved[2016];
|
||||
};
|
||||
|
||||
/**
|
||||
* Be cautious about the struct size change, in case we put anything post
|
||||
* bootloader_message_ab struct (b/29159185).
|
||||
*/
|
||||
/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
|
||||
static_assert(sizeof(struct bootloader_message_ab) == 4096,
|
||||
"struct bootloader_message_ab size changes");
|
||||
#endif*/
|
||||
|
||||
#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */
|
||||
#define BOOT_CTRL_VERSION 1
|
||||
|
||||
struct slot_metadata {
|
||||
// Slot priority with 15 meaning highest priority, 1 lowest
|
||||
// priority and 0 the slot is unbootable.
|
||||
uint8_t priority : 4;
|
||||
// Number of times left attempting to boot this slot.
|
||||
uint8_t tries_remaining : 3;
|
||||
// 1 if this slot has booted successfully, 0 otherwise.
|
||||
uint8_t successful_boot : 1;
|
||||
// 1 if this slot is corrupted from a dm-verity corruption, 0
|
||||
// otherwise.
|
||||
uint8_t verity_corrupted : 1;
|
||||
// Reserved for further use.
|
||||
uint8_t reserved : 7;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Bootloader Control AB
|
||||
*
|
||||
* This struct can be used to manage A/B metadata. It is designed to
|
||||
* be put in the 'slot_suffix' field of the 'bootloader_message'
|
||||
* structure described above. It is encouraged to use the
|
||||
* 'bootloader_control' structure to store the A/B metadata, but not
|
||||
* mandatory.
|
||||
*/
|
||||
struct bootloader_control {
|
||||
// NUL terminated active slot suffix.
|
||||
char slot_suffix[4];
|
||||
// Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
|
||||
uint32_t magic;
|
||||
// Version of struct being used (see BOOT_CTRL_VERSION).
|
||||
uint8_t version;
|
||||
// Number of slots being managed.
|
||||
uint8_t nb_slot : 3;
|
||||
// Number of times left attempting to boot recovery.
|
||||
uint8_t recovery_tries_remaining : 3;
|
||||
// Ensure 4-bytes alignment for slot_info field.
|
||||
uint8_t reserved0[2];
|
||||
// Per-slot information. Up to 4 slots.
|
||||
struct slot_metadata slot_info[4];
|
||||
// Reserved for further use.
|
||||
uint8_t reserved1[8];
|
||||
// CRC32 of all 28 bytes preceding this field (little endian
|
||||
// format).
|
||||
uint32_t crc32_le;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
|
||||
static_assert(sizeof(struct bootloader_control) ==
|
||||
sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
|
||||
"struct bootloader_control has wrong size");
|
||||
#endif*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
// Read bootloader message into boot. Error message will be set in err.
|
||||
bool read_bootloader_message(bootloader_message* boot, std::string* err);
|
||||
|
||||
// Read bootloader message from the specified misc device into boot.
|
||||
bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
|
||||
std::string* err);
|
||||
|
||||
// Write bootloader message to BCB.
|
||||
bool write_bootloader_message(const bootloader_message& boot, std::string* err);
|
||||
|
||||
// Write bootloader message to the specified BCB device.
|
||||
bool write_bootloader_message_to(const bootloader_message& boot,
|
||||
const std::string& misc_blk_device, std::string* err);
|
||||
|
||||
// Write bootloader message (boots into recovery with the options) to BCB. Will
|
||||
// set the command and recovery fields, and reset the rest.
|
||||
bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
|
||||
|
||||
// Update bootloader message (boots into recovery with the options) to BCB. Will
|
||||
// only update the command and recovery fields.
|
||||
bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
|
||||
|
||||
// Clear BCB.
|
||||
bool clear_bootloader_message(void* err);
|
||||
bool clear_bootloader_message(std::string* err);
|
||||
|
||||
// Writes the reboot-bootloader reboot reason to the bootloader_message.
|
||||
bool write_reboot_bootloader(std::string* err);
|
||||
|
||||
// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC).
|
||||
bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
|
||||
|
||||
void set_misc_device(const char* name);
|
||||
void get_args(int *argc, char ***argv);
|
||||
|
||||
// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC).
|
||||
bool write_wipe_package(const std::string& package_data, std::string* err);
|
||||
|
||||
#else
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// C Interface.
|
||||
bool write_bootloader_message(const char* options);
|
||||
bool write_reboot_bootloader(void);
|
||||
|
||||
#endif // ifdef __cplusplus
|
||||
|
||||
#endif // _BOOTLOADER_MESSAGE_TWRP_H
|
||||
@@ -17,9 +17,9 @@
|
||||
#ifndef RECOVERY_COMMON_H
|
||||
#define RECOVERY_COMMON_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -40,18 +40,29 @@ extern "C" {
|
||||
#define STRINGIFY(x) #x
|
||||
#define EXPAND(x) STRINGIFY(x)
|
||||
|
||||
class RecoveryUI;
|
||||
|
||||
extern RecoveryUI* ui;
|
||||
extern bool modified_flash;
|
||||
//typedef struct fstab_rec Volume;
|
||||
|
||||
// The current stage, e.g. "1/2".
|
||||
extern std::string stage;
|
||||
|
||||
// The reason argument provided in "--reason=".
|
||||
extern const char* reason;
|
||||
|
||||
// fopen a file, mounting volumes and making parent dirs as necessary.
|
||||
FILE* fopen_path(const char *path, const char *mode);
|
||||
|
||||
void ui_print(const char* format, ...);
|
||||
|
||||
bool is_ro_debuggable();
|
||||
static bool is_ro_debuggable();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
bool reboot(const std::string& command);
|
||||
|
||||
#endif // RECOVERY_COMMON_H
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ Device::BuiltinAction Device::InvokeMenuItem(int menu_position) {
|
||||
return menu_position < 0 ? NO_ACTION : MENU_ACTIONS[menu_position];
|
||||
}
|
||||
|
||||
int Device::HandleMenuKey(int key, int visible) {
|
||||
int Device::HandleMenuKey(int key, bool visible) {
|
||||
if (!visible) {
|
||||
return kNoAction;
|
||||
}
|
||||
|
||||
@@ -20,96 +20,90 @@
|
||||
#include "ui.h"
|
||||
|
||||
class Device {
|
||||
public:
|
||||
Device(RecoveryUI* ui) : ui_(ui) { }
|
||||
virtual ~Device() { }
|
||||
public:
|
||||
explicit Device(RecoveryUI* ui) : ui_(ui) {}
|
||||
virtual ~Device() {}
|
||||
|
||||
// Called to obtain the UI object that should be used to display
|
||||
// the recovery user interface for this device. You should not
|
||||
// have called Init() on the UI object already, the caller will do
|
||||
// that after this method returns.
|
||||
virtual RecoveryUI* GetUI() { return ui_; }
|
||||
// Called to obtain the UI object that should be used to display the recovery user interface for
|
||||
// this device. You should not have called Init() on the UI object already, the caller will do
|
||||
// that after this method returns.
|
||||
virtual RecoveryUI* GetUI() {
|
||||
return ui_;
|
||||
}
|
||||
|
||||
// Called when recovery starts up (after the UI has been obtained
|
||||
// and initialized and after the arguments have been parsed, but
|
||||
// before anything else).
|
||||
virtual void StartRecovery() { };
|
||||
// Called when recovery starts up (after the UI has been obtained and initialized and after the
|
||||
// arguments have been parsed, but before anything else).
|
||||
virtual void StartRecovery() {};
|
||||
|
||||
// Called from the main thread when recovery is at the main menu
|
||||
// and waiting for input, and a key is pressed. (Note that "at"
|
||||
// the main menu does not necessarily mean the menu is visible;
|
||||
// recovery will be at the main menu with it invisible after an
|
||||
// unsuccessful operation [ie OTA package failure], or if recovery
|
||||
// is started with no command.)
|
||||
//
|
||||
// key is the code of the key just pressed. (You can call
|
||||
// IsKeyPressed() on the RecoveryUI object you returned from GetUI
|
||||
// if you want to find out if other keys are held down.)
|
||||
//
|
||||
// visible is true if the menu is visible.
|
||||
//
|
||||
// Return one of the defined constants below in order to:
|
||||
//
|
||||
// - move the menu highlight (kHighlight{Up,Down})
|
||||
// - invoke the highlighted item (kInvokeItem)
|
||||
// - do nothing (kNoAction)
|
||||
// - invoke a specific action (a menu position: any non-negative number)
|
||||
virtual int HandleMenuKey(int key, int visible);
|
||||
// Called from the main thread when recovery is at the main menu and waiting for input, and a key
|
||||
// is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible;
|
||||
// recovery will be at the main menu with it invisible after an unsuccessful operation [ie OTA
|
||||
// package failure], or if recovery is started with no command.)
|
||||
//
|
||||
// 'key' is the code of the key just pressed. (You can call IsKeyPressed() on the RecoveryUI
|
||||
// object you returned from GetUI if you want to find out if other keys are held down.)
|
||||
//
|
||||
// 'visible' is true if the menu is visible.
|
||||
//
|
||||
// Returns one of the defined constants below in order to:
|
||||
//
|
||||
// - move the menu highlight (kHighlight{Up,Down})
|
||||
// - invoke the highlighted item (kInvokeItem)
|
||||
// - do nothing (kNoAction)
|
||||
// - invoke a specific action (a menu position: any non-negative number)
|
||||
virtual int HandleMenuKey(int key, bool visible);
|
||||
|
||||
enum BuiltinAction {
|
||||
NO_ACTION = 0,
|
||||
REBOOT = 1,
|
||||
APPLY_SDCARD = 2,
|
||||
// APPLY_CACHE was 3.
|
||||
APPLY_ADB_SIDELOAD = 4,
|
||||
WIPE_DATA = 5,
|
||||
WIPE_CACHE = 6,
|
||||
REBOOT_BOOTLOADER = 7,
|
||||
SHUTDOWN = 8,
|
||||
VIEW_RECOVERY_LOGS = 9,
|
||||
MOUNT_SYSTEM = 10,
|
||||
RUN_GRAPHICS_TEST = 11,
|
||||
};
|
||||
enum BuiltinAction {
|
||||
NO_ACTION = 0,
|
||||
REBOOT = 1,
|
||||
APPLY_SDCARD = 2,
|
||||
// APPLY_CACHE was 3.
|
||||
APPLY_ADB_SIDELOAD = 4,
|
||||
WIPE_DATA = 5,
|
||||
WIPE_CACHE = 6,
|
||||
REBOOT_BOOTLOADER = 7,
|
||||
SHUTDOWN = 8,
|
||||
VIEW_RECOVERY_LOGS = 9,
|
||||
MOUNT_SYSTEM = 10,
|
||||
RUN_GRAPHICS_TEST = 11,
|
||||
};
|
||||
|
||||
// Return the list of menu items (an array of strings,
|
||||
// NULL-terminated). The menu_position passed to InvokeMenuItem
|
||||
// will correspond to the indexes into this array.
|
||||
virtual const char* const* GetMenuItems();
|
||||
// Return the list of menu items (an array of strings, NULL-terminated). The menu_position passed
|
||||
// to InvokeMenuItem will correspond to the indexes into this array.
|
||||
virtual const char* const* GetMenuItems();
|
||||
|
||||
// Perform a recovery action selected from the menu.
|
||||
// 'menu_position' will be the item number of the selected menu
|
||||
// item, or a non-negative number returned from
|
||||
// device_handle_key(). The menu will be hidden when this is
|
||||
// called; implementations can call ui_print() to print
|
||||
// information to the screen. If the menu position is one of the
|
||||
// builtin actions, you can just return the corresponding enum
|
||||
// value. If it is an action specific to your device, you
|
||||
// actually perform it here and return NO_ACTION.
|
||||
virtual BuiltinAction InvokeMenuItem(int menu_position);
|
||||
// Perform a recovery action selected from the menu. 'menu_position' will be the item number of
|
||||
// the selected menu item, or a non-negative number returned from HandleMenuKey(). The menu will
|
||||
// be hidden when this is called; implementations can call ui_print() to print information to the
|
||||
// screen. If the menu position is one of the builtin actions, you can just return the
|
||||
// corresponding enum value. If it is an action specific to your device, you actually perform it
|
||||
// here and return NO_ACTION.
|
||||
virtual BuiltinAction InvokeMenuItem(int menu_position);
|
||||
|
||||
static const int kNoAction = -1;
|
||||
static const int kHighlightUp = -2;
|
||||
static const int kHighlightDown = -3;
|
||||
static const int kInvokeItem = -4;
|
||||
static const int kNoAction = -1;
|
||||
static const int kHighlightUp = -2;
|
||||
static const int kHighlightDown = -3;
|
||||
static const int kInvokeItem = -4;
|
||||
|
||||
// Called before and after we do a wipe data/factory reset operation,
|
||||
// either via a reboot from the main system with the --wipe_data flag,
|
||||
// or when the user boots into recovery image manually and selects the
|
||||
// option from the menu, to perform whatever device-specific wiping
|
||||
// actions are needed.
|
||||
// Return true on success; returning false from PreWipeData will prevent
|
||||
// the regular wipe, and returning false from PostWipeData will cause
|
||||
// the wipe to be considered a failure.
|
||||
virtual bool PreWipeData() { return true; }
|
||||
virtual bool PostWipeData() { return true; }
|
||||
// Called before and after we do a wipe data/factory reset operation, either via a reboot from the
|
||||
// main system with the --wipe_data flag, or when the user boots into recovery image manually and
|
||||
// selects the option from the menu, to perform whatever device-specific wiping actions as needed.
|
||||
// Returns true on success; returning false from PreWipeData will prevent the regular wipe, and
|
||||
// returning false from PostWipeData will cause the wipe to be considered a failure.
|
||||
virtual bool PreWipeData() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
RecoveryUI* ui_;
|
||||
virtual bool PostWipeData() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
RecoveryUI* ui_;
|
||||
};
|
||||
|
||||
// The device-specific library must define this function (or the
|
||||
// default one will be used, if there is no device-specific library).
|
||||
// It returns the Device object that recovery should use.
|
||||
// The device-specific library must define this function (or the default one will be used, if there
|
||||
// is no device-specific library). It returns the Device object that recovery should use.
|
||||
Device* make_device();
|
||||
|
||||
#endif // _DEVICE_H
|
||||
|
||||
+22
-8
@@ -1,23 +1,36 @@
|
||||
# Copyright 2009 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
edify_src_files := \
|
||||
lexer.ll \
|
||||
parser.yy \
|
||||
expr.cpp
|
||||
lexer.ll \
|
||||
parser.yy \
|
||||
expr.cpp
|
||||
|
||||
#
|
||||
# Build the host-side command line tool
|
||||
# Build the host-side command line tool (host executable)
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(edify_src_files) \
|
||||
main.cpp
|
||||
$(edify_src_files) \
|
||||
edify_parser.cpp
|
||||
|
||||
LOCAL_CFLAGS := -Werror
|
||||
LOCAL_CPPFLAGS := -g -O0
|
||||
LOCAL_MODULE := edify
|
||||
LOCAL_MODULE := edify_parser
|
||||
LOCAL_YACCFLAGS := -v
|
||||
LOCAL_CPPFLAGS += -Wno-unused-parameter
|
||||
LOCAL_CPPFLAGS += -Wno-deprecated-register
|
||||
@@ -28,12 +41,13 @@ LOCAL_STATIC_LIBRARIES += libbase
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
#
|
||||
# Build the device-side library
|
||||
# Build the device-side library (static library)
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(edify_src_files)
|
||||
|
||||
LOCAL_CFLAGS := -Werror
|
||||
LOCAL_CPPFLAGS := -Wno-unused-parameter
|
||||
LOCAL_CPPFLAGS += -Wno-deprecated-register
|
||||
LOCAL_MODULE := libedify
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
edify
|
||||
=====
|
||||
|
||||
Update scripts (from donut onwards) are written in a new little
|
||||
scripting language ("edify") that is superficially somewhat similar to
|
||||
the old one ("amend"). This is a brief overview of the new language.
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is a host-side tool for validating a given edify script file.
|
||||
*
|
||||
* We used to have edify test cases here, which have been moved to
|
||||
* tests/component/edify_test.cpp.
|
||||
*
|
||||
* Caveat: It doesn't recognize functions defined through updater, which
|
||||
* makes the tool less useful. We should either extend the tool or remove it.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
#include "expr.h"
|
||||
|
||||
static void ExprDump(int depth, const std::unique_ptr<Expr>& n, const std::string& script) {
|
||||
printf("%*s", depth*2, "");
|
||||
printf("%s %p (%d-%d) \"%s\"\n",
|
||||
n->name.c_str(), n->fn, n->start, n->end,
|
||||
script.substr(n->start, n->end - n->start).c_str());
|
||||
for (size_t i = 0; i < n->argv.size(); ++i) {
|
||||
ExprDump(depth+1, n->argv[i], script);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
RegisterBuiltins();
|
||||
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <edify script>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string buffer;
|
||||
if (!android::base::ReadFileToString(argv[1], &buffer)) {
|
||||
printf("%s: failed to read %s: %s\n", argv[0], argv[1], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expr> root;
|
||||
int error_count = 0;
|
||||
int error = parse_string(buffer.data(), &root, &error_count);
|
||||
printf("parse returned %d; %d errors encountered\n", error, error_count);
|
||||
if (error == 0 || error_count > 0) {
|
||||
|
||||
ExprDump(0, root, buffer);
|
||||
|
||||
State state(buffer, nullptr);
|
||||
std::string result;
|
||||
if (!Evaluate(&state, root, &result)) {
|
||||
printf("result was NULL, message is: %s\n",
|
||||
(state.errmsg.empty() ? "(NULL)" : state.errmsg.c_str()));
|
||||
} else {
|
||||
printf("result is [%s]\n", result.c_str());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+250
-353
@@ -14,201 +14,172 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "expr.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "expr.h"
|
||||
|
||||
// Functions should:
|
||||
//
|
||||
// - return a malloc()'d string
|
||||
// - if Evaluate() on any argument returns NULL, return NULL.
|
||||
// - if Evaluate() on any argument returns nullptr, return nullptr.
|
||||
|
||||
int BooleanString(const char* s) {
|
||||
return s[0] != '\0';
|
||||
static bool BooleanString(const std::string& s) {
|
||||
return !s.empty();
|
||||
}
|
||||
|
||||
char* Evaluate(State* state, Expr* expr) {
|
||||
Value* v = expr->fn(expr->name, state, expr->argc, expr->argv);
|
||||
if (v == NULL) return NULL;
|
||||
bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result) {
|
||||
if (result == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Value> v(expr->fn(expr->name.c_str(), state, expr->argv));
|
||||
if (!v) {
|
||||
return false;
|
||||
}
|
||||
if (v->type != VAL_STRING) {
|
||||
ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type);
|
||||
FreeValue(v);
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
char* result = v->data;
|
||||
free(v);
|
||||
return result;
|
||||
|
||||
*result = v->data;
|
||||
return true;
|
||||
}
|
||||
|
||||
Value* EvaluateValue(State* state, Expr* expr) {
|
||||
return expr->fn(expr->name, state, expr->argc, expr->argv);
|
||||
Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr) {
|
||||
return expr->fn(expr->name.c_str(), state, expr->argv);
|
||||
}
|
||||
|
||||
Value* StringValue(char* str) {
|
||||
if (str == NULL) return NULL;
|
||||
Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value)));
|
||||
v->type = VAL_STRING;
|
||||
v->size = strlen(str);
|
||||
v->data = str;
|
||||
return v;
|
||||
}
|
||||
|
||||
void FreeValue(Value* v) {
|
||||
if (v == NULL) return;
|
||||
free(v->data);
|
||||
free(v);
|
||||
}
|
||||
|
||||
Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc == 0) {
|
||||
return StringValue(strdup(""));
|
||||
Value* StringValue(const char* str) {
|
||||
if (str == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
char** strings = reinterpret_cast<char**>(malloc(argc * sizeof(char*)));
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
strings[i] = NULL;
|
||||
return new Value(VAL_STRING, str);
|
||||
}
|
||||
|
||||
Value* StringValue(const std::string& str) {
|
||||
return StringValue(str.c_str());
|
||||
}
|
||||
|
||||
Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
if (argv.empty()) {
|
||||
return StringValue("");
|
||||
}
|
||||
char* result = NULL;
|
||||
int length = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
strings[i] = Evaluate(state, argv[i]);
|
||||
if (strings[i] == NULL) {
|
||||
goto done;
|
||||
std::string result;
|
||||
for (size_t i = 0; i < argv.size(); ++i) {
|
||||
std::string str;
|
||||
if (!Evaluate(state, argv[i], &str)) {
|
||||
return nullptr;
|
||||
}
|
||||
length += strlen(strings[i]);
|
||||
result += str;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
result[p] = '\0';
|
||||
|
||||
done:
|
||||
for (i = 0; i < argc; ++i) {
|
||||
free(strings[i]);
|
||||
}
|
||||
free(strings);
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 2 && argc != 3) {
|
||||
free(state->errmsg);
|
||||
state->errmsg = strdup("ifelse expects 2 or 3 arguments");
|
||||
return NULL;
|
||||
}
|
||||
char* cond = Evaluate(state, argv[0]);
|
||||
if (cond == NULL) {
|
||||
return NULL;
|
||||
Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
if (argv.size() != 2 && argv.size() != 3) {
|
||||
state->errmsg = "ifelse expects 2 or 3 arguments";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (BooleanString(cond) == true) {
|
||||
free(cond);
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
if (argc == 3) {
|
||||
free(cond);
|
||||
return EvaluateValue(state, argv[2]);
|
||||
} else {
|
||||
return StringValue(cond);
|
||||
}
|
||||
std::string cond;
|
||||
if (!Evaluate(state, argv[0], &cond)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!cond.empty()) {
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else if (argv.size() == 3) {
|
||||
return EvaluateValue(state, argv[2]);
|
||||
}
|
||||
|
||||
return StringValue("");
|
||||
}
|
||||
|
||||
Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* msg = NULL;
|
||||
if (argc > 0) {
|
||||
msg = Evaluate(state, argv[0]);
|
||||
}
|
||||
free(state->errmsg);
|
||||
if (msg) {
|
||||
Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string msg;
|
||||
if (!argv.empty() && Evaluate(state, argv[0], &msg)) {
|
||||
state->errmsg = msg;
|
||||
} else {
|
||||
state->errmsg = strdup("called abort()");
|
||||
state->errmsg = "called abort()";
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
char* v = Evaluate(state, argv[i]);
|
||||
if (v == NULL) {
|
||||
return NULL;
|
||||
Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
for (size_t i = 0; i < argv.size(); ++i) {
|
||||
std::string result;
|
||||
if (!Evaluate(state, argv[i], &result)) {
|
||||
return nullptr;
|
||||
}
|
||||
int b = BooleanString(v);
|
||||
free(v);
|
||||
if (!b) {
|
||||
int prefix_len;
|
||||
if (result.empty()) {
|
||||
int len = argv[i]->end - argv[i]->start;
|
||||
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);
|
||||
err_src[prefix_len + len] = '\0';
|
||||
free(state->errmsg);
|
||||
state->errmsg = err_src;
|
||||
return NULL;
|
||||
state->errmsg = "assert failed: " + state->script.substr(argv[i]->start, len);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return StringValue(strdup(""));
|
||||
return StringValue("");
|
||||
}
|
||||
|
||||
Value* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* val = Evaluate(state, argv[0]);
|
||||
if (val == NULL) {
|
||||
return NULL;
|
||||
Value* SleepFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string val;
|
||||
if (!Evaluate(state, argv[0], &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int v;
|
||||
if (!android::base::ParseInt(val.c_str(), &v, 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
int v = strtol(val, NULL, 10);
|
||||
sleep(v);
|
||||
|
||||
return StringValue(val);
|
||||
}
|
||||
|
||||
Value* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
int i;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
char* v = Evaluate(state, argv[i]);
|
||||
if (v == NULL) {
|
||||
return NULL;
|
||||
Value* StdoutFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
for (size_t i = 0; i < argv.size(); ++i) {
|
||||
std::string v;
|
||||
if (!Evaluate(state, argv[i], &v)) {
|
||||
return nullptr;
|
||||
}
|
||||
fputs(v, stdout);
|
||||
free(v);
|
||||
fputs(v.c_str(), stdout);
|
||||
}
|
||||
return StringValue(strdup(""));
|
||||
return StringValue("");
|
||||
}
|
||||
|
||||
Value* LogicalAndFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
if (BooleanString(left) == true) {
|
||||
free(left);
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string left;
|
||||
if (!Evaluate(state, argv[0], &left)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (BooleanString(left)) {
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
return StringValue(left);
|
||||
return StringValue("");
|
||||
}
|
||||
}
|
||||
|
||||
Value* LogicalOrFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
if (BooleanString(left) == false) {
|
||||
free(left);
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string left;
|
||||
if (!Evaluate(state, argv[0], &left)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!BooleanString(left)) {
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
return StringValue(left);
|
||||
@@ -216,174 +187,144 @@ Value* LogicalOrFn(const char* name, State* state,
|
||||
}
|
||||
|
||||
Value* LogicalNotFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
char* val = Evaluate(state, argv[0]);
|
||||
if (val == NULL) return NULL;
|
||||
bool bv = BooleanString(val);
|
||||
free(val);
|
||||
return StringValue(strdup(bv ? "" : "t"));
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string val;
|
||||
if (!Evaluate(state, argv[0], &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return StringValue(BooleanString(val) ? "" : "t");
|
||||
}
|
||||
|
||||
Value* SubstringFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
char* needle = Evaluate(state, argv[0]);
|
||||
if (needle == NULL) return NULL;
|
||||
char* haystack = Evaluate(state, argv[1]);
|
||||
if (haystack == NULL) {
|
||||
free(needle);
|
||||
return NULL;
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string needle;
|
||||
if (!Evaluate(state, argv[0], &needle)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* result = strdup(strstr(haystack, needle) ? "t" : "");
|
||||
free(needle);
|
||||
free(haystack);
|
||||
std::string haystack;
|
||||
if (!Evaluate(state, argv[1], &haystack)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string result = (haystack.find(needle) != std::string::npos) ? "t" : "";
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
char* right = Evaluate(state, argv[1]);
|
||||
if (right == NULL) {
|
||||
free(left);
|
||||
return NULL;
|
||||
Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string left;
|
||||
if (!Evaluate(state, argv[0], &left)) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string right;
|
||||
if (!Evaluate(state, argv[1], &right)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* result = strdup(strcmp(left, right) == 0 ? "t" : "");
|
||||
free(left);
|
||||
free(right);
|
||||
const char* result = (left == right) ? "t" : "";
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
char* left = Evaluate(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
char* right = Evaluate(state, argv[1]);
|
||||
if (right == NULL) {
|
||||
free(left);
|
||||
return NULL;
|
||||
Value* InequalityFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string left;
|
||||
if (!Evaluate(state, argv[0], &left)) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string right;
|
||||
if (!Evaluate(state, argv[1], &right)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* result = strdup(strcmp(left, right) != 0 ? "t" : "");
|
||||
free(left);
|
||||
free(right);
|
||||
const char* result = (left != right) ? "t" : "";
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
Value* left = EvaluateValue(state, argv[0]);
|
||||
if (left == NULL) return NULL;
|
||||
FreeValue(left);
|
||||
Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::unique_ptr<Value> left(EvaluateValue(state, argv[0]));
|
||||
if (!left) {
|
||||
return nullptr;
|
||||
}
|
||||
return EvaluateValue(state, argv[1]);
|
||||
}
|
||||
|
||||
Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
if (argc != 2) {
|
||||
free(state->errmsg);
|
||||
state->errmsg = strdup("less_than_int expects 2 arguments");
|
||||
return NULL;
|
||||
Value* LessThanIntFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
if (argv.size() != 2) {
|
||||
state->errmsg = "less_than_int expects 2 arguments";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* left;
|
||||
char* right;
|
||||
if (ReadArgs(state, argv, 2, &left, &right) < 0) return NULL;
|
||||
|
||||
bool result = false;
|
||||
char* end;
|
||||
|
||||
long l_int = strtol(left, &end, 10);
|
||||
if (left[0] == '\0' || *end != '\0') {
|
||||
goto done;
|
||||
std::vector<std::string> args;
|
||||
if (!ReadArgs(state, argv, &args)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
long r_int;
|
||||
r_int = strtol(right, &end, 10);
|
||||
if (right[0] == '\0' || *end != '\0') {
|
||||
goto done;
|
||||
// Parse up to at least long long or 64-bit integers.
|
||||
int64_t l_int;
|
||||
if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
|
||||
state->errmsg = "failed to parse int in " + args[0];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result = l_int < r_int;
|
||||
int64_t r_int;
|
||||
if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
|
||||
state->errmsg = "failed to parse int in " + args[1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
done:
|
||||
free(left);
|
||||
free(right);
|
||||
return StringValue(strdup(result ? "t" : ""));
|
||||
return StringValue(l_int < r_int ? "t" : "");
|
||||
}
|
||||
|
||||
Value* GreaterThanIntFn(const char* name, State* state,
|
||||
int argc, Expr* argv[]) {
|
||||
if (argc != 2) {
|
||||
free(state->errmsg);
|
||||
state->errmsg = strdup("greater_than_int expects 2 arguments");
|
||||
return NULL;
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
if (argv.size() != 2) {
|
||||
state->errmsg = "greater_than_int expects 2 arguments";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Expr* temp[2];
|
||||
temp[0] = argv[1];
|
||||
temp[1] = argv[0];
|
||||
|
||||
return LessThanIntFn(name, state, 2, temp);
|
||||
}
|
||||
|
||||
Value* Literal(const char* name, State* state, int argc, Expr* argv[]) {
|
||||
return StringValue(strdup(name));
|
||||
}
|
||||
|
||||
Expr* Build(Function fn, YYLTYPE loc, int count, ...) {
|
||||
va_list v;
|
||||
va_start(v, count);
|
||||
Expr* e = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
|
||||
e->fn = fn;
|
||||
e->name = "(operator)";
|
||||
e->argc = count;
|
||||
e->argv = reinterpret_cast<Expr**>(malloc(count * sizeof(Expr*)));
|
||||
int i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
e->argv[i] = va_arg(v, Expr*);
|
||||
std::vector<std::string> args;
|
||||
if (!ReadArgs(state, argv, &args)) {
|
||||
return nullptr;
|
||||
}
|
||||
va_end(v);
|
||||
e->start = loc.start;
|
||||
e->end = loc.end;
|
||||
return e;
|
||||
|
||||
// Parse up to at least long long or 64-bit integers.
|
||||
int64_t l_int;
|
||||
if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
|
||||
state->errmsg = "failed to parse int in " + args[0];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int64_t r_int;
|
||||
if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
|
||||
state->errmsg = "failed to parse int in " + args[1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return StringValue(l_int > r_int ? "t" : "");
|
||||
}
|
||||
|
||||
Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
return StringValue(name);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// the function table
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
static int fn_entries = 0;
|
||||
static int fn_size = 0;
|
||||
NamedFunction* fn_table = NULL;
|
||||
static std::unordered_map<std::string, Function> fn_table;
|
||||
|
||||
void RegisterFunction(const char* name, Function fn) {
|
||||
if (fn_entries >= fn_size) {
|
||||
fn_size = fn_size*2 + 1;
|
||||
fn_table = reinterpret_cast<NamedFunction*>(realloc(fn_table, fn_size * sizeof(NamedFunction)));
|
||||
void RegisterFunction(const std::string& name, Function fn) {
|
||||
fn_table[name] = fn;
|
||||
}
|
||||
|
||||
Function FindFunction(const std::string& name) {
|
||||
if (fn_table.find(name) == fn_table.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return fn_table[name];
|
||||
}
|
||||
fn_table[fn_entries].name = name;
|
||||
fn_table[fn_entries].fn = fn;
|
||||
++fn_entries;
|
||||
}
|
||||
|
||||
static int fn_entry_compare(const void* a, const void* b) {
|
||||
const char* na = ((const NamedFunction*)a)->name;
|
||||
const char* nb = ((const NamedFunction*)b)->name;
|
||||
return strcmp(na, nb);
|
||||
}
|
||||
|
||||
void FinishRegistration() {
|
||||
qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare);
|
||||
}
|
||||
|
||||
Function FindFunction(const char* name) {
|
||||
NamedFunction key;
|
||||
key.name = name;
|
||||
NamedFunction* nf = reinterpret_cast<NamedFunction*>(bsearch(&key, fn_table, fn_entries,
|
||||
sizeof(NamedFunction), fn_entry_compare));
|
||||
if (nf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return nf->fn;
|
||||
}
|
||||
|
||||
void RegisterBuiltins() {
|
||||
@@ -404,106 +345,56 @@ void RegisterBuiltins() {
|
||||
// convenience methods for functions
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// Evaluate the expressions in argv, giving 'count' char* (the ... is
|
||||
// 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 = reinterpret_cast<char**>(malloc(count * sizeof(char*)));
|
||||
va_list v;
|
||||
va_start(v, count);
|
||||
int i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
args[i] = Evaluate(state, argv[i]);
|
||||
if (args[i] == NULL) {
|
||||
va_end(v);
|
||||
int j;
|
||||
for (j = 0; j < i; ++j) {
|
||||
free(args[j]);
|
||||
}
|
||||
free(args);
|
||||
return -1;
|
||||
}
|
||||
*(va_arg(v, char**)) = args[i];
|
||||
}
|
||||
va_end(v);
|
||||
free(args);
|
||||
return 0;
|
||||
// Evaluate the expressions in argv, and put the results of strings in args. If any expression
|
||||
// evaluates to nullptr, return false. Return true on success.
|
||||
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::string>* args) {
|
||||
return ReadArgs(state, argv, args, 0, argv.size());
|
||||
}
|
||||
|
||||
// Evaluate the expressions in argv, giving 'count' Value* (the ... is
|
||||
// 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 = reinterpret_cast<Value**>(malloc(count * sizeof(Value*)));
|
||||
va_list v;
|
||||
va_start(v, count);
|
||||
int i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
args[i] = EvaluateValue(state, argv[i]);
|
||||
if (args[i] == NULL) {
|
||||
va_end(v);
|
||||
int j;
|
||||
for (j = 0; j < i; ++j) {
|
||||
FreeValue(args[j]);
|
||||
}
|
||||
free(args);
|
||||
return -1;
|
||||
}
|
||||
*(va_arg(v, Value**)) = args[i];
|
||||
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::string>* args, size_t start, size_t len) {
|
||||
if (args == nullptr) {
|
||||
return false;
|
||||
}
|
||||
va_end(v);
|
||||
free(args);
|
||||
return 0;
|
||||
if (start + len > argv.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = start; i < start + len; ++i) {
|
||||
std::string var;
|
||||
if (!Evaluate(state, argv[i], &var)) {
|
||||
args->clear();
|
||||
return false;
|
||||
}
|
||||
args->push_back(var);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Evaluate the expressions in argv, returning an array of char*
|
||||
// results. If any evaluate to NULL, free the rest and return NULL.
|
||||
// The caller is responsible for freeing the returned array and the
|
||||
// strings it contains.
|
||||
char** ReadVarArgs(State* state, int argc, Expr* argv[]) {
|
||||
char** args = (char**)malloc(argc * sizeof(char*));
|
||||
int i = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
args[i] = Evaluate(state, argv[i]);
|
||||
if (args[i] == NULL) {
|
||||
int j;
|
||||
for (j = 0; j < i; ++j) {
|
||||
free(args[j]);
|
||||
}
|
||||
free(args);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
// Evaluate the expressions in argv, and put the results of Value* in args. If any expression
|
||||
// evaluate to nullptr, return false. Return true on success.
|
||||
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::unique_ptr<Value>>* args) {
|
||||
return ReadValueArgs(state, argv, args, 0, argv.size());
|
||||
}
|
||||
|
||||
// Evaluate the expressions in argv, returning an array of Value*
|
||||
// results. If any evaluate to NULL, free the rest and return NULL.
|
||||
// The caller is responsible for freeing the returned array and the
|
||||
// Values it contains.
|
||||
Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]) {
|
||||
Value** args = (Value**)malloc(argc * sizeof(Value*));
|
||||
int i = 0;
|
||||
for (i = 0; i < argc; ++i) {
|
||||
args[i] = EvaluateValue(state, argv[i]);
|
||||
if (args[i] == NULL) {
|
||||
int j;
|
||||
for (j = 0; j < i; ++j) {
|
||||
FreeValue(args[j]);
|
||||
}
|
||||
free(args);
|
||||
return NULL;
|
||||
}
|
||||
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len) {
|
||||
if (args == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
static void ErrorAbortV(State* state, const char* format, va_list ap) {
|
||||
std::string buffer;
|
||||
android::base::StringAppendV(&buffer, format, ap);
|
||||
free(state->errmsg);
|
||||
state->errmsg = strdup(buffer.c_str());
|
||||
return;
|
||||
if (len == 0 || start + len > argv.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = start; i < start + len; ++i) {
|
||||
std::unique_ptr<Value> v(EvaluateValue(state, argv[i]));
|
||||
if (!v) {
|
||||
args->clear();
|
||||
return false;
|
||||
}
|
||||
args->push_back(std::move(v));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use printf-style arguments to compose an error message to put into
|
||||
@@ -511,7 +402,7 @@ static void ErrorAbortV(State* state, const char* format, va_list ap) {
|
||||
Value* ErrorAbort(State* state, const char* format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
ErrorAbortV(state, format, ap);
|
||||
android::base::StringAppendV(&state->errmsg, format, ap);
|
||||
va_end(ap);
|
||||
return nullptr;
|
||||
}
|
||||
@@ -519,8 +410,14 @@ Value* ErrorAbort(State* state, const char* format, ...) {
|
||||
Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
ErrorAbortV(state, format, ap);
|
||||
android::base::StringAppendV(&state->errmsg, format, ap);
|
||||
va_end(ap);
|
||||
state->cause_code = cause_code;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
State::State(const std::string& script, void* cookie) :
|
||||
script(script),
|
||||
cookie(cookie) {
|
||||
}
|
||||
|
||||
|
||||
+80
-104
@@ -19,27 +19,26 @@
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "error_code.h"
|
||||
#include "yydefs.h"
|
||||
|
||||
#define MAX_STRING_LEN 1024
|
||||
struct State {
|
||||
State(const std::string& script, void* cookie);
|
||||
|
||||
typedef struct Expr Expr;
|
||||
// The source of the original script.
|
||||
const std::string& script;
|
||||
|
||||
typedef struct {
|
||||
// Optional pointer to app-specific data; the core of edify never
|
||||
// uses this value.
|
||||
void* cookie;
|
||||
|
||||
// The source of the original script. Must be NULL-terminated,
|
||||
// and in writable memory (Evaluate may make temporary changes to
|
||||
// it but will restore it when done).
|
||||
char* script;
|
||||
|
||||
// The error message (if any) returned if the evaluation aborts.
|
||||
// Should be NULL initially, will be either NULL or a malloc'd
|
||||
// pointer after Evaluate() returns.
|
||||
char* errmsg;
|
||||
// Should be empty initially, will be either empty or a string that
|
||||
// Evaluate() returns.
|
||||
std::string errmsg;
|
||||
|
||||
// error code indicates the type of failure (e.g. failure to update system image)
|
||||
// during the OTA process.
|
||||
@@ -50,117 +49,95 @@ typedef struct {
|
||||
CauseCode cause_code = kNoCause;
|
||||
|
||||
bool is_retry = false;
|
||||
|
||||
} State;
|
||||
|
||||
#define VAL_STRING 1 // data will be NULL-terminated; size doesn't count null
|
||||
#define VAL_BLOB 2
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
ssize_t size;
|
||||
char* data;
|
||||
} Value;
|
||||
|
||||
typedef Value* (*Function)(const char* name, State* state,
|
||||
int argc, Expr* argv[]);
|
||||
|
||||
struct Expr {
|
||||
Function fn;
|
||||
const char* name;
|
||||
int argc;
|
||||
Expr** argv;
|
||||
int start, end;
|
||||
};
|
||||
|
||||
// Take one of the Expr*s passed to the function as an argument,
|
||||
// evaluate it, return the resulting Value. The caller takes
|
||||
// ownership of the returned Value.
|
||||
Value* EvaluateValue(State* state, Expr* expr);
|
||||
enum ValueType {
|
||||
VAL_INVALID = -1,
|
||||
VAL_STRING = 1,
|
||||
VAL_BLOB = 2,
|
||||
};
|
||||
|
||||
// Take one of the Expr*s passed to the function as an argument,
|
||||
// evaluate it, assert that it is a string, and return the resulting
|
||||
// char*. The caller takes ownership of the returned char*. This is
|
||||
// a convenience function for older functions that want to deal only
|
||||
// with strings.
|
||||
char* Evaluate(State* state, Expr* expr);
|
||||
struct Value {
|
||||
ValueType type;
|
||||
std::string data;
|
||||
|
||||
Value(ValueType type, const std::string& str) :
|
||||
type(type),
|
||||
data(str) {}
|
||||
};
|
||||
|
||||
struct Expr;
|
||||
|
||||
using Function = Value* (*)(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
|
||||
struct Expr {
|
||||
Function fn;
|
||||
std::string name;
|
||||
std::vector<std::unique_ptr<Expr>> argv;
|
||||
int start, end;
|
||||
|
||||
Expr(Function fn, const std::string& name, int start, int end) :
|
||||
fn(fn),
|
||||
name(name),
|
||||
start(start),
|
||||
end(end) {}
|
||||
};
|
||||
|
||||
// Evaluate the input expr, return the resulting Value.
|
||||
Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr);
|
||||
|
||||
// Evaluate the input expr, assert that it is a string, and update the result parameter. This
|
||||
// function returns true if the evaluation succeeds. This is a convenience function for older
|
||||
// functions that want to deal only with strings.
|
||||
bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result);
|
||||
|
||||
// Glue to make an Expr out of a literal.
|
||||
Value* Literal(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
|
||||
// Functions corresponding to various syntactic sugar operators.
|
||||
// ("concat" is also available as a builtin function, to concatenate
|
||||
// more than two strings.)
|
||||
Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
|
||||
// Convenience function for building expressions with a fixed number
|
||||
// of arguments.
|
||||
Expr* Build(Function fn, YYLTYPE loc, int count, ...);
|
||||
Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* LogicalAndFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* LogicalOrFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* LogicalNotFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* SubstringFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* InequalityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
|
||||
// Global builtins, registered by RegisterBuiltins().
|
||||
Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]);
|
||||
|
||||
|
||||
// For setting and getting the global error string (when returning
|
||||
// NULL from a function).
|
||||
void SetError(const char* message); // makes a copy
|
||||
const char* GetError(); // retains ownership
|
||||
void ClearError();
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
Function fn;
|
||||
} NamedFunction;
|
||||
Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
|
||||
// Register a new function. The same Function may be registered under
|
||||
// multiple names, but a given name should only be used once.
|
||||
void RegisterFunction(const char* name, Function fn);
|
||||
void RegisterFunction(const std::string& name, Function fn);
|
||||
|
||||
// Register all the builtins.
|
||||
void RegisterBuiltins();
|
||||
|
||||
// Call this after all calls to RegisterFunction() but before parsing
|
||||
// any scripts to finish building the function table.
|
||||
void FinishRegistration();
|
||||
|
||||
// Find the Function for a given name; return NULL if no such function
|
||||
// exists.
|
||||
Function FindFunction(const char* name);
|
||||
|
||||
Function FindFunction(const std::string& name);
|
||||
|
||||
// --- convenience functions for use in functions ---
|
||||
|
||||
// Evaluate the expressions in argv, giving 'count' char* (the ... is
|
||||
// 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, ...);
|
||||
// Evaluate the expressions in argv, and put the results of strings in args. If any expression
|
||||
// evaluates to nullptr, return false. Return true on success.
|
||||
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::string>* args);
|
||||
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::string>* args, size_t start, size_t len);
|
||||
|
||||
// Evaluate the expressions in argv, giving 'count' Value* (the ... is
|
||||
// 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, ...);
|
||||
|
||||
// Evaluate the expressions in argv, returning an array of char*
|
||||
// results. If any evaluate to NULL, free the rest and return NULL.
|
||||
// The caller is responsible for freeing the returned array and the
|
||||
// strings it contains.
|
||||
char** ReadVarArgs(State* state, int argc, Expr* argv[]);
|
||||
|
||||
// Evaluate the expressions in argv, returning an array of Value*
|
||||
// results. If any evaluate to NULL, free the rest and return NULL.
|
||||
// The caller is responsible for freeing the returned array and the
|
||||
// Values it contains.
|
||||
Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]);
|
||||
// Evaluate the expressions in argv, and put the results of Value* in args. If any
|
||||
// expression evaluate to nullptr, return false. Return true on success.
|
||||
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::unique_ptr<Value>>* args);
|
||||
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len);
|
||||
|
||||
// Use printf-style arguments to compose an error message to put into
|
||||
// *state. Returns NULL.
|
||||
@@ -172,12 +149,11 @@ Value* ErrorAbort(State* state, const char* format, ...)
|
||||
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);
|
||||
// Copying the string into a Value.
|
||||
Value* StringValue(const char* str);
|
||||
|
||||
// Free a Value object.
|
||||
void FreeValue(Value* v);
|
||||
Value* StringValue(const std::string& str);
|
||||
|
||||
int parse_string(const char* str, Expr** root, int* error_count);
|
||||
int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count);
|
||||
|
||||
#endif // _EXPRESSION_H
|
||||
|
||||
-219
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "expr.h"
|
||||
#include "parser.h"
|
||||
|
||||
extern int yyparse(Expr** root, int* error_count);
|
||||
|
||||
int expect(const char* expr_str, const char* expected, int* errors) {
|
||||
Expr* e;
|
||||
char* result;
|
||||
|
||||
printf(".");
|
||||
|
||||
int error_count = parse_string(expr_str, &e, &error_count);
|
||||
if (error_count > 0) {
|
||||
printf("error parsing \"%s\" (%d errors)\n",
|
||||
expr_str, error_count);
|
||||
++*errors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
State state;
|
||||
state.cookie = NULL;
|
||||
state.script = strdup(expr_str);
|
||||
state.errmsg = NULL;
|
||||
|
||||
result = Evaluate(&state, e);
|
||||
free(state.errmsg);
|
||||
free(state.script);
|
||||
if (result == NULL && expected != NULL) {
|
||||
printf("error evaluating \"%s\"\n", expr_str);
|
||||
++*errors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (result == NULL && expected == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(result, expected) != 0) {
|
||||
printf("evaluating \"%s\": expected \"%s\", got \"%s\"\n",
|
||||
expr_str, expected, result);
|
||||
++*errors;
|
||||
free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int test() {
|
||||
int errors = 0;
|
||||
|
||||
expect("a", "a", &errors);
|
||||
expect("\"a\"", "a", &errors);
|
||||
expect("\"\\x61\"", "a", &errors);
|
||||
expect("# this is a comment\n"
|
||||
" a\n"
|
||||
" \n",
|
||||
"a", &errors);
|
||||
|
||||
|
||||
// sequence operator
|
||||
expect("a; b; c", "c", &errors);
|
||||
|
||||
// string concat operator
|
||||
expect("a + b", "ab", &errors);
|
||||
expect("a + \n \"b\"", "ab", &errors);
|
||||
expect("a + b +\nc\n", "abc", &errors);
|
||||
|
||||
// string concat function
|
||||
expect("concat(a, b)", "ab", &errors);
|
||||
expect("concat(a,\n \"b\")", "ab", &errors);
|
||||
expect("concat(a + b,\nc,\"d\")", "abcd", &errors);
|
||||
expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors);
|
||||
|
||||
// logical and
|
||||
expect("a && b", "b", &errors);
|
||||
expect("a && \"\"", "", &errors);
|
||||
expect("\"\" && b", "", &errors);
|
||||
expect("\"\" && \"\"", "", &errors);
|
||||
expect("\"\" && abort()", "", &errors); // test short-circuiting
|
||||
expect("t && abort()", NULL, &errors);
|
||||
|
||||
// logical or
|
||||
expect("a || b", "a", &errors);
|
||||
expect("a || \"\"", "a", &errors);
|
||||
expect("\"\" || b", "b", &errors);
|
||||
expect("\"\" || \"\"", "", &errors);
|
||||
expect("a || abort()", "a", &errors); // test short-circuiting
|
||||
expect("\"\" || abort()", NULL, &errors);
|
||||
|
||||
// logical not
|
||||
expect("!a", "", &errors);
|
||||
expect("! \"\"", "t", &errors);
|
||||
expect("!!a", "t", &errors);
|
||||
|
||||
// precedence
|
||||
expect("\"\" == \"\" && b", "b", &errors);
|
||||
expect("a + b == ab", "t", &errors);
|
||||
expect("ab == a + b", "t", &errors);
|
||||
expect("a + (b == ab)", "a", &errors);
|
||||
expect("(ab == a) + b", "b", &errors);
|
||||
|
||||
// substring function
|
||||
expect("is_substring(cad, abracadabra)", "t", &errors);
|
||||
expect("is_substring(abrac, abracadabra)", "t", &errors);
|
||||
expect("is_substring(dabra, abracadabra)", "t", &errors);
|
||||
expect("is_substring(cad, abracxadabra)", "", &errors);
|
||||
expect("is_substring(abrac, axbracadabra)", "", &errors);
|
||||
expect("is_substring(dabra, abracadabrxa)", "", &errors);
|
||||
|
||||
// ifelse function
|
||||
expect("ifelse(t, yes, no)", "yes", &errors);
|
||||
expect("ifelse(!t, yes, no)", "no", &errors);
|
||||
expect("ifelse(t, yes, abort())", "yes", &errors);
|
||||
expect("ifelse(!t, abort(), no)", "no", &errors);
|
||||
|
||||
// if "statements"
|
||||
expect("if t then yes else no endif", "yes", &errors);
|
||||
expect("if \"\" then yes else no endif", "no", &errors);
|
||||
expect("if \"\" then yes endif", "", &errors);
|
||||
expect("if \"\"; t then yes endif", "yes", &errors);
|
||||
|
||||
// numeric comparisons
|
||||
expect("less_than_int(3, 14)", "t", &errors);
|
||||
expect("less_than_int(14, 3)", "", &errors);
|
||||
expect("less_than_int(x, 3)", "", &errors);
|
||||
expect("less_than_int(3, x)", "", &errors);
|
||||
expect("greater_than_int(3, 14)", "", &errors);
|
||||
expect("greater_than_int(14, 3)", "t", &errors);
|
||||
expect("greater_than_int(x, 3)", "", &errors);
|
||||
expect("greater_than_int(3, x)", "", &errors);
|
||||
|
||||
// big string
|
||||
expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str(), &errors);
|
||||
|
||||
printf("\n");
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
void ExprDump(int depth, Expr* n, char* script) {
|
||||
printf("%*s", depth*2, "");
|
||||
char temp = script[n->end];
|
||||
script[n->end] = '\0';
|
||||
printf("%s %p (%d-%d) \"%s\"\n",
|
||||
n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
|
||||
script+n->start);
|
||||
script[n->end] = temp;
|
||||
int i;
|
||||
for (i = 0; i < n->argc; ++i) {
|
||||
ExprDump(depth+1, n->argv[i], script);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
RegisterBuiltins();
|
||||
FinishRegistration();
|
||||
|
||||
if (argc == 1) {
|
||||
return test() != 0;
|
||||
}
|
||||
|
||||
FILE* f = fopen(argv[1], "r");
|
||||
if (f == NULL) {
|
||||
printf("%s: %s: No such file or directory\n", argv[0], argv[1]);
|
||||
return 1;
|
||||
}
|
||||
char buffer[8192];
|
||||
int size = fread(buffer, 1, 8191, f);
|
||||
fclose(f);
|
||||
buffer[size] = '\0';
|
||||
|
||||
Expr* root;
|
||||
int error_count = 0;
|
||||
int error = parse_string(buffer, &root, &error_count);
|
||||
printf("parse returned %d; %d errors encountered\n", error, error_count);
|
||||
if (error == 0 || error_count > 0) {
|
||||
|
||||
ExprDump(0, root, buffer);
|
||||
|
||||
State state;
|
||||
state.cookie = NULL;
|
||||
state.script = buffer;
|
||||
state.errmsg = NULL;
|
||||
|
||||
char* result = Evaluate(&state, root);
|
||||
if (result == NULL) {
|
||||
printf("result was NULL, message is: %s\n",
|
||||
(state.errmsg == NULL ? "(NULL)" : state.errmsg));
|
||||
free(state.errmsg);
|
||||
} else {
|
||||
printf("result is [%s]\n", result);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+38
-36
@@ -19,6 +19,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "expr.h"
|
||||
#include "yydefs.h"
|
||||
#include "parser.h"
|
||||
@@ -26,13 +30,26 @@
|
||||
extern int gLine;
|
||||
extern int gColumn;
|
||||
|
||||
void yyerror(Expr** root, int* error_count, const char* s);
|
||||
int yyparse(Expr** root, int* error_count);
|
||||
void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s);
|
||||
int yyparse(std::unique_ptr<Expr>* root, int* error_count);
|
||||
|
||||
struct yy_buffer_state;
|
||||
void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
|
||||
struct yy_buffer_state* yy_scan_string(const char* yystr);
|
||||
|
||||
// Convenience function for building expressions with a fixed number
|
||||
// of arguments.
|
||||
static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
|
||||
va_list v;
|
||||
va_start(v, count);
|
||||
Expr* e = new Expr(fn, "(operator)", loc.start, loc.end);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
e->argv.emplace_back(va_arg(v, Expr*));
|
||||
}
|
||||
va_end(v);
|
||||
return e;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%locations
|
||||
@@ -40,10 +57,7 @@ struct yy_buffer_state* yy_scan_string(const char* yystr);
|
||||
%union {
|
||||
char* str;
|
||||
Expr* expr;
|
||||
struct {
|
||||
int argc;
|
||||
Expr** argv;
|
||||
} args;
|
||||
std::vector<std::unique_ptr<Expr>>* args;
|
||||
}
|
||||
|
||||
%token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF
|
||||
@@ -51,7 +65,10 @@ struct yy_buffer_state* yy_scan_string(const char* yystr);
|
||||
%type <expr> expr
|
||||
%type <args> arglist
|
||||
|
||||
%parse-param {Expr** root}
|
||||
%destructor { delete $$; } expr
|
||||
%destructor { delete $$; } arglist
|
||||
|
||||
%parse-param {std::unique_ptr<Expr>* root}
|
||||
%parse-param {int* error_count}
|
||||
%error-verbose
|
||||
|
||||
@@ -66,17 +83,11 @@ struct yy_buffer_state* yy_scan_string(const char* yystr);
|
||||
|
||||
%%
|
||||
|
||||
input: expr { *root = $1; }
|
||||
input: expr { root->reset($1); }
|
||||
;
|
||||
|
||||
expr: STRING {
|
||||
$$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
|
||||
$$->fn = Literal;
|
||||
$$->name = $1;
|
||||
$$->argc = 0;
|
||||
$$->argv = NULL;
|
||||
$$->start = @$.start;
|
||||
$$->end = @$.end;
|
||||
$$ = new Expr(Literal, $1, @$.start, @$.end);
|
||||
}
|
||||
| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
|
||||
| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
|
||||
@@ -91,41 +102,32 @@ expr: STRING {
|
||||
| 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 ')' {
|
||||
$$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
|
||||
$$->fn = FindFunction($1);
|
||||
if ($$->fn == NULL) {
|
||||
char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
|
||||
yyerror(root, error_count, buffer);
|
||||
Function fn = FindFunction($1);
|
||||
if (fn == nullptr) {
|
||||
std::string msg = "unknown function \"" + std::string($1) + "\"";
|
||||
yyerror(root, error_count, msg.c_str());
|
||||
YYERROR;
|
||||
}
|
||||
$$->name = $1;
|
||||
$$->argc = $3.argc;
|
||||
$$->argv = $3.argv;
|
||||
$$->start = @$.start;
|
||||
$$->end = @$.end;
|
||||
$$ = new Expr(fn, $1, @$.start, @$.end);
|
||||
$$->argv = std::move(*$3);
|
||||
}
|
||||
;
|
||||
|
||||
arglist: /* empty */ {
|
||||
$$.argc = 0;
|
||||
$$.argv = NULL;
|
||||
$$ = new std::vector<std::unique_ptr<Expr>>;
|
||||
}
|
||||
| expr {
|
||||
$$.argc = 1;
|
||||
$$.argv = reinterpret_cast<Expr**>(malloc(sizeof(Expr*)));
|
||||
$$.argv[0] = $1;
|
||||
$$ = new std::vector<std::unique_ptr<Expr>>;
|
||||
$$->emplace_back($1);
|
||||
}
|
||||
| arglist ',' expr {
|
||||
$$.argc = $1.argc + 1;
|
||||
$$.argv = reinterpret_cast<Expr**>(realloc($$.argv, $$.argc * sizeof(Expr*)));
|
||||
$$.argv[$$.argc-1] = $3;
|
||||
$$->push_back(std::unique_ptr<Expr>($3));
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void yyerror(Expr** root, int* error_count, const char* s) {
|
||||
void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s) {
|
||||
if (strlen(s) == 0) {
|
||||
s = "syntax error";
|
||||
}
|
||||
@@ -133,7 +135,7 @@ void yyerror(Expr** root, int* error_count, const char* s) {
|
||||
++*error_count;
|
||||
}
|
||||
|
||||
int parse_string(const char* str, Expr** root, int* error_count) {
|
||||
int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count) {
|
||||
yy_switch_to_buffer(yy_scan_string(str));
|
||||
return yyparse(root, error_count);
|
||||
}
|
||||
|
||||
+43
-41
@@ -18,52 +18,54 @@
|
||||
#define _ERROR_CODE_H_
|
||||
|
||||
enum ErrorCode {
|
||||
kNoError = -1,
|
||||
kLowBattery = 20,
|
||||
kZipVerificationFailure,
|
||||
kZipOpenFailure,
|
||||
kBootreasonInBlacklist
|
||||
kNoError = -1,
|
||||
kLowBattery = 20,
|
||||
kZipVerificationFailure,
|
||||
kZipOpenFailure,
|
||||
kBootreasonInBlacklist,
|
||||
kPackageCompatibilityFailure,
|
||||
};
|
||||
|
||||
enum CauseCode {
|
||||
kNoCause = -1,
|
||||
kArgsParsingFailure = 100,
|
||||
kStashCreationFailure,
|
||||
kFileOpenFailure,
|
||||
kLseekFailure,
|
||||
kFreadFailure,
|
||||
kFwriteFailure,
|
||||
kFsyncFailure,
|
||||
kLibfecFailure,
|
||||
kFileGetPropFailure,
|
||||
kFileRenameFailure,
|
||||
kSymlinkFailure,
|
||||
kSetMetadataFailure,
|
||||
kTune2FsFailure,
|
||||
kRebootFailure,
|
||||
kVendorFailure = 200
|
||||
kNoCause = -1,
|
||||
kArgsParsingFailure = 100,
|
||||
kStashCreationFailure,
|
||||
kFileOpenFailure,
|
||||
kLseekFailure,
|
||||
kFreadFailure,
|
||||
kFwriteFailure,
|
||||
kFsyncFailure,
|
||||
kLibfecFailure,
|
||||
kFileGetPropFailure,
|
||||
kFileRenameFailure,
|
||||
kSymlinkFailure,
|
||||
kSetMetadataFailure,
|
||||
kTune2FsFailure,
|
||||
kRebootFailure,
|
||||
kPackageExtractFileFailure,
|
||||
kVendorFailure = 200
|
||||
};
|
||||
|
||||
enum UncryptErrorCode {
|
||||
kUncryptNoError = -1,
|
||||
kUncryptErrorPlaceholder = 50,
|
||||
kUncryptTimeoutError = 100,
|
||||
kUncryptFileRemoveError,
|
||||
kUncryptFileOpenError,
|
||||
kUncryptSocketOpenError,
|
||||
kUncryptSocketWriteError,
|
||||
kUncryptSocketListenError,
|
||||
kUncryptSocketAcceptError,
|
||||
kUncryptFstabReadError,
|
||||
kUncryptFileStatError,
|
||||
kUncryptBlockOpenError,
|
||||
kUncryptIoctlError,
|
||||
kUncryptReadError,
|
||||
kUncryptWriteError,
|
||||
kUncryptFileSyncError,
|
||||
kUncryptFileCloseError,
|
||||
kUncryptFileRenameError,
|
||||
kUncryptPackageMissingError,
|
||||
kUncryptNoError = -1,
|
||||
kUncryptErrorPlaceholder = 50,
|
||||
kUncryptTimeoutError = 100,
|
||||
kUncryptFileRemoveError,
|
||||
kUncryptFileOpenError,
|
||||
kUncryptSocketOpenError,
|
||||
kUncryptSocketWriteError,
|
||||
kUncryptSocketListenError,
|
||||
kUncryptSocketAcceptError,
|
||||
kUncryptFstabReadError,
|
||||
kUncryptFileStatError,
|
||||
kUncryptBlockOpenError,
|
||||
kUncryptIoctlError,
|
||||
kUncryptReadError,
|
||||
kUncryptWriteError,
|
||||
kUncryptFileSyncError,
|
||||
kUncryptFileCloseError,
|
||||
kUncryptFileRenameError,
|
||||
kUncryptPackageMissingError,
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // _ERROR_CODE_H_
|
||||
|
||||
@@ -51,6 +51,26 @@ else
|
||||
include $(BUILD_PREBUILT)
|
||||
endif
|
||||
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := init.recovery.hlthchrg.rc
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
|
||||
|
||||
LOCAL_SRC_FILES := init.recovery.hlthchrg26.rc
|
||||
include $(BUILD_PREBUILT)
|
||||
else
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := init.recovery.hlthchrg.rc
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
|
||||
|
||||
LOCAL_SRC_FILES := init.recovery.hlthchrg25.rc
|
||||
include $(BUILD_PREBUILT)
|
||||
endif
|
||||
|
||||
ifeq ($(TWRP_INCLUDE_LOGCAT), true)
|
||||
ifeq ($(TARGET_USES_LOGD), true)
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
assert compatible_with("0.1") == "true"
|
||||
assert file_contains("SYSTEM:build.prop", "ro.product.device=dream") == "true" || file_contains("SYSTEM:build.prop", "ro.build.product=dream") == "true"
|
||||
assert file_contains("RECOVERY:default.prop", "ro.product.device=dream") == "true" || file_contains("RECOVERY:default.prop", "ro.build.product=dream") == "true"
|
||||
assert getprop("ro.product.device") == "dream"
|
||||
format BOOT:
|
||||
format SYSTEM:
|
||||
copy_dir PACKAGE:system SYSTEM:
|
||||
write_raw_image PACKAGE:boot.img BOOT:
|
||||
@@ -16,7 +16,6 @@ on early-init
|
||||
restorecon /postinstall
|
||||
|
||||
start ueventd
|
||||
start healthd
|
||||
|
||||
service set_permissive /sbin/permissive.sh
|
||||
oneshot
|
||||
@@ -46,7 +45,6 @@ on init
|
||||
|
||||
on fs
|
||||
mount pstore pstore /sys/fs/pstore
|
||||
|
||||
mkdir /dev/usb-ffs 0770 shell shell
|
||||
mkdir /dev/usb-ffs/adb 0770 shell shell
|
||||
mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
|
||||
@@ -104,10 +102,6 @@ service ueventd /sbin/ueventd
|
||||
critical
|
||||
seclabel u:r:ueventd:s0
|
||||
|
||||
service healthd /sbin/healthd -r
|
||||
critical
|
||||
seclabel u:r:healthd:s0
|
||||
|
||||
service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
|
||||
disabled
|
||||
socket adbd stream 660 system system
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# healthd for pre Android 8.0
|
||||
|
||||
service healthd /sbin/healthd -r
|
||||
critical
|
||||
seclabel u:r:healthd:s0
|
||||
@@ -0,0 +1,5 @@
|
||||
# charger for Android 8.0 and up
|
||||
|
||||
service charger /charger -r
|
||||
critical
|
||||
seclabel u:r:charger:s0
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
#include "fuse_sideload.h"
|
||||
|
||||
struct file_data {
|
||||
@@ -41,14 +43,9 @@ static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
while (fetch_size > 0) {
|
||||
ssize_t r = TEMP_FAILURE_RETRY(read(fd->fd, buffer, fetch_size));
|
||||
if (r == -1) {
|
||||
fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
fetch_size -= r;
|
||||
buffer += r;
|
||||
if (!android::base::ReadFully(fd->fd, buffer, fetch_size)) {
|
||||
fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
+7
-1
@@ -38,7 +38,13 @@ else
|
||||
LOCAL_SRC_FILES += hardwarekeyboard.cpp
|
||||
endif
|
||||
|
||||
LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libminzip libaosprecovery libselinux
|
||||
LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libaosprecovery libselinux
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libziparchive
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libminzip
|
||||
LOCAL_CFLAGS += -DUSE_MINZIP
|
||||
endif
|
||||
LOCAL_MODULE := libguitwrp
|
||||
|
||||
#TWRP_EVENT_LOGGING := true
|
||||
|
||||
+1
-1
@@ -43,11 +43,11 @@
|
||||
#include "../adb_install.h"
|
||||
#include "../fuse_sideload.h"
|
||||
#include "blanktimer.hpp"
|
||||
#include "../twinstall.h"
|
||||
|
||||
extern "C" {
|
||||
#include "../twcommon.h"
|
||||
#include "../variables.h"
|
||||
#include "../twinstall.h"
|
||||
#include "cutils/properties.h"
|
||||
#include "../adb_install.h"
|
||||
};
|
||||
|
||||
+19
-16
@@ -39,12 +39,17 @@
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef USE_MINZIP
|
||||
#include "../minzip/SysUtil.h"
|
||||
#else
|
||||
#include "../otautil/SysUtil.h"
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include "../twcommon.h"
|
||||
#include "../minzip/SysUtil.h"
|
||||
#include "../minzip/Zip.h"
|
||||
#include "gui.h"
|
||||
}
|
||||
#include "../zipwrap.hpp"
|
||||
#include "../minuitwrp/minui.h"
|
||||
|
||||
#include "rapidxml.hpp"
|
||||
@@ -664,7 +669,7 @@ int Page::NotifyVarChange(std::string varName, std::string value)
|
||||
// transient data for loading themes
|
||||
struct LoadingContext
|
||||
{
|
||||
ZipArchive* zip; // zip to load theme from, or NULL for the stock theme
|
||||
ZipWrap* zip; // zip to load theme from, or NULL for the stock theme
|
||||
std::set<std::string> filenames; // to detect cyclic includes
|
||||
std::string basepath; // if zip is NULL, base path to load includes from with trailing slash, otherwise empty
|
||||
std::vector<xml_document<>*> xmldocs; // all loaded xml docs
|
||||
@@ -806,7 +811,7 @@ void PageSet::MakeEmergencyConsoleIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
int PageSet::LoadLanguage(char* languageFile, ZipArchive* package)
|
||||
int PageSet::LoadLanguage(char* languageFile, ZipWrap* package)
|
||||
{
|
||||
xml_document<> lang;
|
||||
xml_node<>* parent;
|
||||
@@ -1182,7 +1187,7 @@ void PageSet::AddStringResource(std::string resource_source, std::string resourc
|
||||
mResources->AddStringResource(resource_source, resource_name, value);
|
||||
}
|
||||
|
||||
char* PageManager::LoadFileToBuffer(std::string filename, ZipArchive* package) {
|
||||
char* PageManager::LoadFileToBuffer(std::string filename, ZipWrap* package) {
|
||||
size_t len;
|
||||
char* buffer = NULL;
|
||||
|
||||
@@ -1219,19 +1224,18 @@ char* PageManager::LoadFileToBuffer(std::string filename, ZipArchive* package) {
|
||||
close(fd);
|
||||
} else {
|
||||
LOGINFO("PageManager::LoadFileToBuffer loading filename: '%s' from zip\n", filename.c_str());
|
||||
const ZipEntry* zipentry = mzFindZipEntry(package, filename.c_str());
|
||||
if (zipentry == NULL) {
|
||||
if (!package->EntryExists(filename)) {
|
||||
LOGERR("Unable to locate '%s' in zip file\n", filename.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate the buffer for the file
|
||||
len = mzGetZipEntryUncompLen(zipentry);
|
||||
len = package->GetUncompressedSize(filename);
|
||||
buffer = (char*) malloc(len + 1);
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
if (!mzExtractZipEntryToBuffer(package, zipentry, (unsigned char*) buffer)) {
|
||||
if (!package->ExtractToBuffer(filename, (unsigned char*) buffer)) {
|
||||
LOGERR("Unable to extract '%s'\n", filename.c_str());
|
||||
free(buffer);
|
||||
return NULL;
|
||||
@@ -1295,14 +1299,13 @@ void PageManager::LoadLanguageListDir(string dir) {
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
void PageManager::LoadLanguageList(ZipArchive* package) {
|
||||
void PageManager::LoadLanguageList(ZipWrap* package) {
|
||||
Language_List.clear();
|
||||
if (TWFunc::Path_Exists(TWRES "customlanguages"))
|
||||
TWFunc::removeDir(TWRES "customlanguages", true);
|
||||
if (package) {
|
||||
TWFunc::Recursive_Mkdir(TWRES "customlanguages");
|
||||
struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
|
||||
mzExtractRecursive(package, "languages", TWRES "customlanguages/", ×tamp, NULL, NULL, NULL);
|
||||
package->ExtractRecursive("languages", TWRES "customlanguages/");
|
||||
LoadLanguageListDir(TWRES "customlanguages/");
|
||||
} else {
|
||||
LoadLanguageListDir(TWRES "languages/");
|
||||
@@ -1330,7 +1333,7 @@ void PageManager::LoadLanguage(string filename) {
|
||||
int PageManager::LoadPackage(std::string name, std::string package, std::string startpage)
|
||||
{
|
||||
std::string mainxmlfilename = package;
|
||||
ZipArchive zip;
|
||||
ZipWrap zip;
|
||||
char* languageFile = NULL;
|
||||
char* baseLanguageFile = NULL;
|
||||
PageSet* pageSet = NULL;
|
||||
@@ -1371,7 +1374,7 @@ int PageManager::LoadPackage(std::string name, std::string package, std::string
|
||||
LOGERR("Failed to map '%s'\n", package.c_str());
|
||||
goto error;
|
||||
}
|
||||
if (mzOpenZipArchive(map.addr, map.length, &zip)) {
|
||||
if (!zip.Open(package.c_str(), &map)) {
|
||||
LOGERR("Unable to open zip archive '%s'\n", package.c_str());
|
||||
sysReleaseMap(&map);
|
||||
goto error;
|
||||
@@ -1414,7 +1417,7 @@ int PageManager::LoadPackage(std::string name, std::string package, std::string
|
||||
mCurrentSet = pageSet;
|
||||
|
||||
if (ctx.zip) {
|
||||
mzCloseZipArchive(ctx.zip);
|
||||
ctx.zip->Close();
|
||||
sysReleaseMap(&map);
|
||||
}
|
||||
return ret;
|
||||
@@ -1422,7 +1425,7 @@ int PageManager::LoadPackage(std::string name, std::string package, std::string
|
||||
error:
|
||||
// Sometimes we get here without a real error
|
||||
if (ctx.zip) {
|
||||
mzCloseZipArchive(ctx.zip);
|
||||
ctx.zip->Close();
|
||||
sysReleaseMap(&map);
|
||||
}
|
||||
return -1;
|
||||
|
||||
+4
-4
@@ -21,7 +21,7 @@
|
||||
#ifndef _PAGES_HEADER_HPP
|
||||
#define _PAGES_HEADER_HPP
|
||||
|
||||
#include "../minzip/Zip.h"
|
||||
#include "../zipwrap.hpp"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
|
||||
public:
|
||||
int Load(LoadingContext& ctx, const std::string& filename);
|
||||
int LoadLanguage(char* languageFile, ZipArchive* package);
|
||||
int LoadLanguage(char* languageFile, ZipWrap* package);
|
||||
void MakeEmergencyConsoleIfNeeded();
|
||||
|
||||
Page* FindPage(std::string name);
|
||||
@@ -155,8 +155,8 @@ class PageManager
|
||||
{
|
||||
public:
|
||||
// Used by GUI
|
||||
static char* LoadFileToBuffer(std::string filename, ZipArchive* package);
|
||||
static void LoadLanguageList(ZipArchive* package);
|
||||
static char* LoadFileToBuffer(std::string filename, ZipWrap* package);
|
||||
static void LoadLanguageList(ZipWrap* package);
|
||||
static void LoadLanguage(std::string filename);
|
||||
static int LoadPackage(std::string name, std::string package, std::string startpage);
|
||||
static PageSet* SelectPackage(std::string name);
|
||||
|
||||
+12
-26
@@ -30,7 +30,7 @@
|
||||
#include <iomanip>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../minzip/Zip.h"
|
||||
#include "../zipwrap.hpp"
|
||||
extern "C" {
|
||||
#include "../twcommon.h"
|
||||
#include "gui.h"
|
||||
@@ -42,38 +42,24 @@ extern "C" {
|
||||
|
||||
#define TMP_RESOURCE_NAME "/tmp/extract.bin"
|
||||
|
||||
Resource::Resource(xml_node<>* node, ZipArchive* pZip __unused)
|
||||
Resource::Resource(xml_node<>* node, ZipWrap* pZip __unused)
|
||||
{
|
||||
if (node && node->first_attribute("name"))
|
||||
mName = node->first_attribute("name")->value();
|
||||
}
|
||||
|
||||
int Resource::ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile)
|
||||
int Resource::ExtractResource(ZipWrap* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile)
|
||||
{
|
||||
if (!pZip)
|
||||
return -1;
|
||||
|
||||
std::string src = folderName + "/" + fileName + fileExtn;
|
||||
|
||||
const ZipEntry* binary = mzFindZipEntry(pZip, src.c_str());
|
||||
if (binary == NULL) {
|
||||
if (!pZip->ExtractEntry(src, destFile, 0666))
|
||||
return -1;
|
||||
}
|
||||
|
||||
unlink(destFile.c_str());
|
||||
int fd = creat(destFile.c_str(), 0666);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
int ret = 0;
|
||||
if (!mzExtractZipEntryToFile(pZip, binary, fd))
|
||||
ret = -1;
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Resource::LoadImage(ZipArchive* pZip, std::string file, gr_surface* surface)
|
||||
void Resource::LoadImage(ZipWrap* pZip, std::string file, gr_surface* surface)
|
||||
{
|
||||
int rc = 0;
|
||||
if (ExtractResource(pZip, "images", file, ".png", TMP_RESOURCE_NAME) == 0)
|
||||
@@ -119,7 +105,7 @@ void Resource::CheckAndScaleImage(gr_surface source, gr_surface* destination, in
|
||||
}
|
||||
}
|
||||
|
||||
FontResource::FontResource(xml_node<>* node, ZipArchive* pZip)
|
||||
FontResource::FontResource(xml_node<>* node, ZipWrap* pZip)
|
||||
: Resource(node, pZip)
|
||||
{
|
||||
origFontSize = 0;
|
||||
@@ -127,7 +113,7 @@ FontResource::FontResource(xml_node<>* node, ZipArchive* pZip)
|
||||
LoadFont(node, pZip);
|
||||
}
|
||||
|
||||
void FontResource::LoadFont(xml_node<>* node, ZipArchive* pZip)
|
||||
void FontResource::LoadFont(xml_node<>* node, ZipWrap* pZip)
|
||||
{
|
||||
std::string file;
|
||||
xml_attribute<>* attr;
|
||||
@@ -192,7 +178,7 @@ void FontResource::DeleteFont() {
|
||||
origFont = NULL;
|
||||
}
|
||||
|
||||
void FontResource::Override(xml_node<>* node, ZipArchive* pZip) {
|
||||
void FontResource::Override(xml_node<>* node, ZipWrap* pZip) {
|
||||
if (!origFont) {
|
||||
origFont = mFont;
|
||||
} else if (mFont) {
|
||||
@@ -207,7 +193,7 @@ FontResource::~FontResource()
|
||||
DeleteFont();
|
||||
}
|
||||
|
||||
ImageResource::ImageResource(xml_node<>* node, ZipArchive* pZip)
|
||||
ImageResource::ImageResource(xml_node<>* node, ZipWrap* pZip)
|
||||
: Resource(node, pZip)
|
||||
{
|
||||
std::string file;
|
||||
@@ -238,7 +224,7 @@ ImageResource::~ImageResource()
|
||||
res_free_surface(mSurface);
|
||||
}
|
||||
|
||||
AnimationResource::AnimationResource(xml_node<>* node, ZipArchive* pZip)
|
||||
AnimationResource::AnimationResource(xml_node<>* node, ZipWrap* pZip)
|
||||
: Resource(node, pZip)
|
||||
{
|
||||
std::string file;
|
||||
@@ -359,7 +345,7 @@ void ResourceManager::AddStringResource(std::string resource_source, std::string
|
||||
mStrings[resource_name] = res;
|
||||
}
|
||||
|
||||
void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source)
|
||||
void ResourceManager::LoadResources(xml_node<>* resList, ZipWrap* pZip, std::string resource_source)
|
||||
{
|
||||
if (!resList)
|
||||
return;
|
||||
|
||||
+10
-11
@@ -25,8 +25,7 @@
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "rapidxml.hpp"
|
||||
|
||||
struct ZipArchive;
|
||||
#include "../zipwrap.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "../minuitwrp/minui.h"
|
||||
@@ -36,7 +35,7 @@ extern "C" {
|
||||
class Resource
|
||||
{
|
||||
public:
|
||||
Resource(xml_node<>* node, ZipArchive* pZip);
|
||||
Resource(xml_node<>* node, ZipWrap* pZip);
|
||||
virtual ~Resource() {}
|
||||
|
||||
public:
|
||||
@@ -46,27 +45,27 @@ private:
|
||||
std::string mName;
|
||||
|
||||
protected:
|
||||
static int ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile);
|
||||
static void LoadImage(ZipArchive* pZip, std::string file, gr_surface* surface);
|
||||
static int ExtractResource(ZipWrap* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile);
|
||||
static void LoadImage(ZipWrap* pZip, std::string file, gr_surface* surface);
|
||||
static void CheckAndScaleImage(gr_surface source, gr_surface* destination, int retain_aspect);
|
||||
};
|
||||
|
||||
class FontResource : public Resource
|
||||
{
|
||||
public:
|
||||
FontResource(xml_node<>* node, ZipArchive* pZip);
|
||||
FontResource(xml_node<>* node, ZipWrap* pZip);
|
||||
virtual ~FontResource();
|
||||
|
||||
public:
|
||||
void* GetResource() { return this ? mFont : NULL; }
|
||||
int GetHeight() { return gr_ttf_getMaxFontHeight(this ? mFont : NULL); }
|
||||
void Override(xml_node<>* node, ZipArchive* pZip);
|
||||
void Override(xml_node<>* node, ZipWrap* pZip);
|
||||
|
||||
protected:
|
||||
void* mFont;
|
||||
|
||||
private:
|
||||
void LoadFont(xml_node<>* node, ZipArchive* pZip);
|
||||
void LoadFont(xml_node<>* node, ZipWrap* pZip);
|
||||
void DeleteFont();
|
||||
|
||||
private:
|
||||
@@ -77,7 +76,7 @@ private:
|
||||
class ImageResource : public Resource
|
||||
{
|
||||
public:
|
||||
ImageResource(xml_node<>* node, ZipArchive* pZip);
|
||||
ImageResource(xml_node<>* node, ZipWrap* pZip);
|
||||
virtual ~ImageResource();
|
||||
|
||||
public:
|
||||
@@ -92,7 +91,7 @@ protected:
|
||||
class AnimationResource : public Resource
|
||||
{
|
||||
public:
|
||||
AnimationResource(xml_node<>* node, ZipArchive* pZip);
|
||||
AnimationResource(xml_node<>* node, ZipWrap* pZip);
|
||||
virtual ~AnimationResource();
|
||||
|
||||
public:
|
||||
@@ -112,7 +111,7 @@ public:
|
||||
ResourceManager();
|
||||
virtual ~ResourceManager();
|
||||
void AddStringResource(std::string resource_source, std::string resource_name, std::string value);
|
||||
void LoadResources(xml_node<>* resList, ZipArchive* pZip, std::string resource_source);
|
||||
void LoadResources(xml_node<>* resList, ZipWrap* pZip, std::string resource_source);
|
||||
|
||||
public:
|
||||
FontResource* FindFont(const std::string& name) const;
|
||||
|
||||
+488
-354
File diff suppressed because it is too large
Load Diff
@@ -18,12 +18,8 @@
|
||||
#define RECOVERY_INSTALL_H_
|
||||
|
||||
#include <string>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "minzip/Zip.h"
|
||||
|
||||
enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED,
|
||||
INSTALL_RETRY };
|
||||
// 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.
|
||||
@@ -36,6 +32,10 @@ bool verify_package(const unsigned char* package_data, size_t package_size);
|
||||
|
||||
// Read meta data file of the package, write its content in the string pointed by meta_data.
|
||||
// Return true if succeed, otherwise return false.
|
||||
bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data);
|
||||
bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data);
|
||||
|
||||
// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
|
||||
// entry doesn't exist.
|
||||
bool verify_package_compatibility(ZipArchiveHandle package_zip);
|
||||
|
||||
#endif // RECOVERY_INSTALL_H_
|
||||
|
||||
+99
-25
@@ -28,8 +28,11 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "installcommand.h"
|
||||
#include "minzip/SysUtil.h"
|
||||
#include "minzip/Zip.h"
|
||||
#include "zipwrap.hpp"
|
||||
#ifndef USE_MINZIP
|
||||
#include <ziparchive/zip_archive.h>
|
||||
#include <vintf/VintfObjectRecovery.h>
|
||||
#endif
|
||||
#ifdef USE_OLD_VERIFIER
|
||||
#include "verifier24/verifier.h"
|
||||
#else
|
||||
@@ -57,15 +60,13 @@ static int parse_build_number(std::string str) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data) {
|
||||
const ZipEntry* meta_entry = mzFindZipEntry(zip, METADATA_PATH);
|
||||
if (meta_entry == nullptr) {
|
||||
printf("Failed to find %s in update package.\n", METADATA_PATH);
|
||||
return false;
|
||||
}
|
||||
bool read_metadata_from_package(ZipWrap* zip, std::string* meta_data) {
|
||||
long size = zip->GetUncompressedSize(METADATA_PATH);
|
||||
if (size <= 0)
|
||||
return false;
|
||||
|
||||
meta_data->resize(meta_entry->uncompLen, '\0');
|
||||
if (!mzReadZipEntry(zip, meta_entry, &(*meta_data)[0], meta_entry->uncompLen)) {
|
||||
meta_data->resize(size, '\0');
|
||||
if (!zip->ExtractToBuffer(METADATA_PATH, reinterpret_cast<uint8_t*>(&(*meta_data)[0]))) {
|
||||
printf("Failed to read metadata in update package.\n");
|
||||
return false;
|
||||
}
|
||||
@@ -73,7 +74,7 @@ bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data) {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
static void read_source_target_build(ZipWrap* zip, std::vector<std::string>& log_buffer) {
|
||||
std::string meta_data;
|
||||
if (!read_metadata_from_package(zip, &meta_data)) {
|
||||
return;
|
||||
@@ -103,7 +104,7 @@ static void read_source_target_build(ZipArchive* zip, std::vector<std::string>&
|
||||
// Parses the metadata of the OTA package in |zip| and checks whether we are
|
||||
// allowed to accept this A/B package. Downgrading is not allowed unless
|
||||
// explicitly enabled in the package and only for incremental packages.
|
||||
static int check_newer_ab_build(ZipArchive* zip)
|
||||
static int check_newer_ab_build(ZipWrap* zip)
|
||||
{
|
||||
std::string metadata_str;
|
||||
if (!read_metadata_from_package(zip, &metadata_str)) {
|
||||
@@ -185,7 +186,7 @@ static int check_newer_ab_build(ZipArchive* zip)
|
||||
}
|
||||
|
||||
int
|
||||
abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
|
||||
abupdate_binary_command(const char* path, ZipWrap* zip, int retry_count,
|
||||
int status_fd, std::vector<std::string>* cmd)
|
||||
{
|
||||
int ret = check_newer_ab_build(zip);
|
||||
@@ -195,26 +196,22 @@ abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
|
||||
|
||||
// For A/B updates we extract the payload properties to a buffer and obtain
|
||||
// the RAW payload offset in the zip file.
|
||||
const ZipEntry* properties_entry =
|
||||
mzFindZipEntry(zip, AB_OTA_PAYLOAD_PROPERTIES);
|
||||
if (!properties_entry) {
|
||||
if (!zip->EntryExists(AB_OTA_PAYLOAD_PROPERTIES)) {
|
||||
printf("Can't find %s\n", AB_OTA_PAYLOAD_PROPERTIES);
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
std::vector<unsigned char> payload_properties(
|
||||
mzGetZipEntryUncompLen(properties_entry));
|
||||
if (!mzExtractZipEntryToBuffer(zip, properties_entry,
|
||||
payload_properties.data())) {
|
||||
zip->GetUncompressedSize(AB_OTA_PAYLOAD_PROPERTIES));
|
||||
if (!zip->ExtractToBuffer(AB_OTA_PAYLOAD_PROPERTIES, payload_properties.data())) {
|
||||
printf("Can't extract %s\n", AB_OTA_PAYLOAD_PROPERTIES);
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
|
||||
const ZipEntry* payload_entry = mzFindZipEntry(zip, AB_OTA_PAYLOAD);
|
||||
if (!payload_entry) {
|
||||
if (!zip->EntryExists(AB_OTA_PAYLOAD)) {
|
||||
printf("Can't find %s\n", AB_OTA_PAYLOAD);
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
long payload_offset = mzGetZipEntryOffset(payload_entry);
|
||||
long payload_offset = zip->GetEntryOffset(AB_OTA_PAYLOAD);
|
||||
*cmd = {
|
||||
"/sbin/update_engine_sideload",
|
||||
android::base::StringPrintf("--payload=file://%s", path),
|
||||
@@ -229,8 +226,8 @@ abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
|
||||
#else
|
||||
|
||||
int
|
||||
abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
|
||||
int status_fd, std::vector<std::string>* cmd)
|
||||
abupdate_binary_command(__unused const char* path, __unused ZipWrap* zip, __unused int retry_count,
|
||||
__unused int status_fd, __unused std::vector<std::string>* cmd)
|
||||
{
|
||||
printf("No support for AB OTA zips included\n");
|
||||
return INSTALL_CORRUPT;
|
||||
@@ -239,7 +236,7 @@ abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
|
||||
#endif
|
||||
|
||||
int
|
||||
update_binary_command(const char* path, ZipArchive* zip, int retry_count,
|
||||
update_binary_command(const char* path, int retry_count,
|
||||
int status_fd, std::vector<std::string>* cmd)
|
||||
{
|
||||
char charfd[16];
|
||||
@@ -258,3 +255,80 @@ update_binary_command(const char* path, ZipArchive* zip, int retry_count,
|
||||
cmd->push_back("retry");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef USE_MINZIP
|
||||
bool verify_package_compatibility(ZipWrap *package_zip) {
|
||||
if (package_zip->EntryExists("compatibility.zip"))
|
||||
printf("Cannot verify treble package compatibility, must build TWRP in Oreo tree or higher.\n");
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
|
||||
// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA
|
||||
// package.
|
||||
bool verify_package_compatibility(ZipWrap *zw) {
|
||||
ZipArchiveHandle package_zip = zw->GetZipArchiveHandle();
|
||||
printf("Verifying package compatibility...\n");
|
||||
|
||||
static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip";
|
||||
ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY);
|
||||
ZipEntry compatibility_entry;
|
||||
if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) {
|
||||
printf("Package doesn't contain %s entry\n", COMPATIBILITY_ZIP_ENTRY);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string zip_content(compatibility_entry.uncompressed_length, '\0');
|
||||
int32_t ret;
|
||||
if ((ret = ExtractToMemory(package_zip, &compatibility_entry,
|
||||
reinterpret_cast<uint8_t*>(&zip_content[0]),
|
||||
compatibility_entry.uncompressed_length)) != 0) {
|
||||
printf("Failed to read %s: %s\n", COMPATIBILITY_ZIP_ENTRY, ErrorCodeString(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ZipArchiveHandle zip_handle;
|
||||
ret = OpenArchiveFromMemory(static_cast<void*>(const_cast<char*>(zip_content.data())),
|
||||
zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle);
|
||||
if (ret != 0) {
|
||||
printf("Failed to OpenArchiveFromMemory: %s\n", ErrorCodeString(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
|
||||
void* cookie;
|
||||
ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
|
||||
if (ret != 0) {
|
||||
printf("Failed to start iterating zip entries: %s\n", ErrorCodeString(ret));
|
||||
CloseArchive(zip_handle);
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
|
||||
|
||||
std::vector<std::string> compatibility_info;
|
||||
ZipEntry info_entry;
|
||||
ZipString info_name;
|
||||
while (Next(cookie, &info_entry, &info_name) == 0) {
|
||||
std::string content(info_entry.uncompressed_length, '\0');
|
||||
int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]),
|
||||
info_entry.uncompressed_length);
|
||||
if (ret != 0) {
|
||||
printf("Failed to read %s: %s\n", info_name.name, ErrorCodeString(ret));
|
||||
CloseArchive(zip_handle);
|
||||
return false;
|
||||
}
|
||||
compatibility_info.emplace_back(std::move(content));
|
||||
}
|
||||
CloseArchive(zip_handle);
|
||||
|
||||
// VintfObjectRecovery::CheckCompatibility returns zero on success.
|
||||
std::string err;
|
||||
int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err);
|
||||
if (result == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
printf("Failed to verify package compatibility (result %i): %s\n", result, err.c_str());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
+6
-4
@@ -21,15 +21,17 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "minzip/Zip.h"
|
||||
#include "zipwrap.hpp"
|
||||
|
||||
bool read_metadata_from_package(ZipArchive* zip, std::string* meta_data);
|
||||
bool read_metadata_from_package(ZipWrap* zip, std::string* meta_data);
|
||||
|
||||
int
|
||||
abupdate_binary_command(const char* path, ZipArchive* zip, int retry_count,
|
||||
abupdate_binary_command(const char* path, ZipWrap* zip, int retry_count,
|
||||
int status_fd, std::vector<std::string>* cmd);
|
||||
int
|
||||
update_binary_command(const char* path, ZipArchive* zip, int retry_count,
|
||||
update_binary_command(const char* path, int retry_count,
|
||||
int status_fd, std::vector<std::string>* cmd);
|
||||
|
||||
bool verify_package_compatibility(ZipWrap *package_zip);
|
||||
|
||||
#endif // RECOVERY_INSTALL_COMMAND_H_
|
||||
|
||||
+3
-3
@@ -12,10 +12,10 @@ minadbd_cflags := \
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
adb_main.cpp \
|
||||
fuse_adb_provider.cpp \
|
||||
services.cpp \
|
||||
../fuse_sideload.cpp
|
||||
../fuse_sideload.cpp \
|
||||
minadbd.cpp \
|
||||
minadbd_services.cpp \
|
||||
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_MODULE := libminadbd
|
||||
|
||||
@@ -3,6 +3,6 @@ minadbd, and services.c has been modified as follows:
|
||||
|
||||
- all services removed
|
||||
- all host mode support removed
|
||||
- sideload_service() added; this is the only service supported. It
|
||||
- `sideload_service()` added; this is the only service supported. It
|
||||
receives a single blob of data, writes it to a fixed filename, and
|
||||
makes the process exit.
|
||||
@@ -19,8 +19,6 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "fuse_adb_provider.h"
|
||||
|
||||
@@ -14,18 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "minadbd.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_auth.h"
|
||||
#include "transport.h"
|
||||
|
||||
int adb_server_main(int is_daemon, int server_port, int /* reply_fd */) {
|
||||
int minadbd_main() {
|
||||
adb_device_banner = "sideload";
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 MINADBD_H__
|
||||
#define MINADBD_H__
|
||||
|
||||
int minadbd_main();
|
||||
|
||||
#endif
|
||||
@@ -21,11 +21,10 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include "adb.h"
|
||||
#include "fdevent.h"
|
||||
#include "fuse_adb_provider.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
typedef struct stinfo stinfo;
|
||||
|
||||
@@ -62,12 +61,12 @@ static void sideload_host_service(int sfd, void* data) {
|
||||
|
||||
static int create_service_thread(void (*func)(int, void *), void *cookie) {
|
||||
int s[2];
|
||||
if(adb_socketpair(s)) {
|
||||
if (adb_socketpair(s)) {
|
||||
printf("cannot create service socket pair\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
|
||||
stinfo* sti = static_cast<stinfo*>(malloc(sizeof(stinfo)));
|
||||
if(sti == 0) fatal("cannot allocate stinfo");
|
||||
sti->func = func;
|
||||
sti->cookie = cookie;
|
||||
+47
-4
@@ -1,3 +1,17 @@
|
||||
# Copyright (C) 2007 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
@@ -6,6 +20,7 @@ LOCAL_SRC_FILES := \
|
||||
graphics.cpp \
|
||||
graphics_drm.cpp \
|
||||
graphics_fbdev.cpp \
|
||||
graphics_overlay.cpp \
|
||||
resources.cpp
|
||||
|
||||
LOCAL_C_INCLUDES := external/libcxx/include external/libpng
|
||||
@@ -35,8 +50,21 @@ ifeq ($(TW_NEW_ION_HEAP), true)
|
||||
LOCAL_CFLAGS += -DNEW_ION_HEAP
|
||||
endif
|
||||
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES += libdrm
|
||||
LOCAL_STATIC_LIBRARIES += libpng
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES += \
|
||||
libdrm
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
|
||||
LOCAL_CFLAGS += -DHAS_LIBSYNC
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES += libsync_recovery
|
||||
endif
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libpng \
|
||||
libbase
|
||||
|
||||
LOCAL_CFLAGS := -Werror -std=c++14
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
|
||||
LOCAL_MODULE := libminui
|
||||
|
||||
@@ -90,7 +118,14 @@ include $(CLEAR_VARS)
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_MODULE := libminui
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES += libminui
|
||||
LOCAL_SHARED_LIBRARIES := libpng
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libpng \
|
||||
libbase
|
||||
|
||||
LOCAL_CFLAGS := -Werror
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
@@ -98,7 +133,15 @@ LOCAL_MODULE := minuitest
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
|
||||
LOCAL_SRC_FILES := main.cpp
|
||||
LOCAL_STATIC_LIBRARIES := libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
|
||||
LOCAL_SHARED_LIBRARIES := libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
|
||||
LOCAL_C_INCLUDES := external/libcxx/include external/libpng
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
ifneq ($(TARGET_ARCH), arm64)
|
||||
ifneq ($(TARGET_ARCH), x86_64)
|
||||
LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker
|
||||
else
|
||||
LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
|
||||
endif
|
||||
else
|
||||
LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
|
||||
endif
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
+135
-104
@@ -15,17 +15,19 @@
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <functional>
|
||||
|
||||
#include "minui.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
#define MAX_DEVICES 16
|
||||
#define MAX_MISC_FDS 16
|
||||
@@ -34,9 +36,11 @@
|
||||
#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
|
||||
|
||||
struct fd_info {
|
||||
int fd;
|
||||
ev_callback cb;
|
||||
void* data;
|
||||
int fd;
|
||||
ev_callback cb;
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
void* data;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int g_epoll_fd;
|
||||
@@ -49,92 +53,105 @@ static unsigned ev_count = 0;
|
||||
static unsigned ev_dev_count = 0;
|
||||
static unsigned ev_misc_count = 0;
|
||||
|
||||
static bool test_bit(size_t bit, unsigned long* array) {
|
||||
static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
|
||||
return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
|
||||
}
|
||||
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
int ev_init(ev_callback input_cb, void* data) {
|
||||
bool epollctlfail = false;
|
||||
#else
|
||||
int ev_init(ev_callback input_cb) {
|
||||
#endif
|
||||
bool epollctlfail = false;
|
||||
|
||||
g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
|
||||
if (g_epoll_fd == -1) {
|
||||
return -1;
|
||||
g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
|
||||
if (g_epoll_fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DIR* dir = opendir("/dev/input");
|
||||
if (dir != NULL) {
|
||||
dirent* de;
|
||||
while ((de = readdir(dir))) {
|
||||
// Use unsigned long to match ioctl's parameter type.
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
|
||||
|
||||
// fprintf(stderr,"/dev/input/%s\n", de->d_name);
|
||||
if (strncmp(de->d_name, "event", 5)) continue;
|
||||
int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
|
||||
if (fd == -1) continue;
|
||||
|
||||
// Read the evbits of the input device.
|
||||
if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
|
||||
if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
epoll_event ev;
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr = &ev_fdinfo[ev_count];
|
||||
if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
||||
close(fd);
|
||||
epollctlfail = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
ev_fdinfo[ev_count].fd = fd;
|
||||
ev_fdinfo[ev_count].cb = std::move(input_cb);
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
ev_fdinfo[ev_count].data = data;
|
||||
#endif
|
||||
ev_count++;
|
||||
ev_dev_count++;
|
||||
if (ev_dev_count == MAX_DEVICES) break;
|
||||
}
|
||||
|
||||
DIR* dir = opendir("/dev/input");
|
||||
if (dir != NULL) {
|
||||
dirent* de;
|
||||
while ((de = readdir(dir))) {
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
// fprintf(stderr,"/dev/input/%s\n", de->d_name);
|
||||
if (strncmp(de->d_name, "event", 5)) continue;
|
||||
int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
|
||||
if (fd == -1) continue;
|
||||
if (epollctlfail && !ev_count) {
|
||||
close(g_epoll_fd);
|
||||
g_epoll_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read the evbits of the input device.
|
||||
if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
|
||||
if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
epoll_event ev;
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr = &ev_fdinfo[ev_count];
|
||||
if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
||||
close(fd);
|
||||
epollctlfail = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
ev_fdinfo[ev_count].fd = fd;
|
||||
ev_fdinfo[ev_count].cb = input_cb;
|
||||
ev_fdinfo[ev_count].data = data;
|
||||
ev_count++;
|
||||
ev_dev_count++;
|
||||
if (ev_dev_count == MAX_DEVICES) break;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
if (epollctlfail && !ev_count) {
|
||||
close(g_epoll_fd);
|
||||
g_epoll_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ev_get_epollfd(void) {
|
||||
return g_epoll_fd;
|
||||
}
|
||||
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
int ev_add_fd(int fd, ev_callback cb, void* data) {
|
||||
if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
int ev_add_fd(int fd, ev_callback cb) {
|
||||
#endif
|
||||
if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
epoll_event ev;
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr = (void *)&ev_fdinfo[ev_count];
|
||||
int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
|
||||
if (!ret) {
|
||||
ev_fdinfo[ev_count].fd = fd;
|
||||
ev_fdinfo[ev_count].cb = cb;
|
||||
ev_fdinfo[ev_count].data = data;
|
||||
ev_count++;
|
||||
ev_misc_count++;
|
||||
}
|
||||
epoll_event ev;
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr = static_cast<void*>(&ev_fdinfo[ev_count]);
|
||||
int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
|
||||
if (!ret) {
|
||||
ev_fdinfo[ev_count].fd = fd;
|
||||
ev_fdinfo[ev_count].cb = std::move(cb);
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
ev_fdinfo[ev_count].data = data;
|
||||
#endif
|
||||
ev_count++;
|
||||
ev_misc_count++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ev_exit(void) {
|
||||
@@ -155,13 +172,17 @@ int ev_wait(int timeout) {
|
||||
}
|
||||
|
||||
void ev_dispatch(void) {
|
||||
for (int n = 0; n < npolledevents; n++) {
|
||||
fd_info* fdi = reinterpret_cast<fd_info*>(polledevents[n].data.ptr);
|
||||
ev_callback cb = fdi->cb;
|
||||
if (cb) {
|
||||
cb(fdi->fd, polledevents[n].events, fdi->data);
|
||||
}
|
||||
for (int n = 0; n < npolledevents; n++) {
|
||||
fd_info* fdi = static_cast<fd_info*>(polledevents[n].data.ptr);
|
||||
const ev_callback& cb = fdi->cb;
|
||||
if (cb) {
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
cb(fdi->fd, polledevents[n].events, fdi->data);
|
||||
#else
|
||||
cb(fdi->fd, polledevents[n].events);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
|
||||
@@ -174,37 +195,47 @@ int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data) {
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
|
||||
unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
|
||||
#else
|
||||
int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
|
||||
#endif
|
||||
// Use unsigned long to match ioctl's parameter type.
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
|
||||
unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
|
||||
|
||||
for (size_t i = 0; i < ev_dev_count; ++i) {
|
||||
memset(ev_bits, 0, sizeof(ev_bits));
|
||||
memset(key_bits, 0, sizeof(key_bits));
|
||||
for (size_t i = 0; i < ev_dev_count; ++i) {
|
||||
memset(ev_bits, 0, sizeof(ev_bits));
|
||||
memset(key_bits, 0, sizeof(key_bits));
|
||||
|
||||
if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
|
||||
continue;
|
||||
}
|
||||
if (!test_bit(EV_KEY, ev_bits)) {
|
||||
continue;
|
||||
}
|
||||
if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int code = 0; code <= KEY_MAX; code++) {
|
||||
if (test_bit(code, key_bits)) {
|
||||
set_key_cb(code, 1, data);
|
||||
}
|
||||
}
|
||||
if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
|
||||
continue;
|
||||
}
|
||||
if (!test_bit(EV_KEY, ev_bits)) {
|
||||
continue;
|
||||
}
|
||||
if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
for (int code = 0; code <= KEY_MAX; code++) {
|
||||
if (test_bit(code, key_bits)) {
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
set_key_cb(code, 1, data);
|
||||
#else
|
||||
set_key_cb(code, 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ev_iterate_available_keys(std::function<void(int)> f) {
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
|
||||
unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
|
||||
void ev_iterate_available_keys(const std::function<void(int)>& f) {
|
||||
// Use unsigned long to match ioctl's parameter type.
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
|
||||
unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
|
||||
|
||||
for (size_t i = 0; i < ev_dev_count; ++i) {
|
||||
memset(ev_bits, 0, sizeof(ev_bits));
|
||||
|
||||
+53
-115
@@ -14,22 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "graphics.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/kd.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <memory>
|
||||
|
||||
#ifdef BOARD_USE_CUSTOM_RECOVERY_FONT
|
||||
#include BOARD_USE_CUSTOM_RECOVERY_FONT
|
||||
@@ -37,11 +28,14 @@
|
||||
#include "font_10x18.h"
|
||||
#endif
|
||||
|
||||
#include "minui.h"
|
||||
#include "graphics.h"
|
||||
#include "graphics_adf.h"
|
||||
#include "graphics_drm.h"
|
||||
#include "graphics_fbdev.h"
|
||||
#include "graphics_overlay.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
static GRFont* gr_font = NULL;
|
||||
static minui_backend* gr_backend = NULL;
|
||||
static MinuiBackend* gr_backend = nullptr;
|
||||
|
||||
static int overscan_percent = OVERSCAN_PERCENT;
|
||||
static int overscan_offset_x = 0;
|
||||
@@ -470,7 +464,7 @@ void gr_set_font(__attribute__ ((unused))const char* name) {
|
||||
}
|
||||
#else // TW_NO_MINUI_CUSTOM_FONTS
|
||||
int gr_init_font(const char* name, GRFont** dest) {
|
||||
GRFont* font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
|
||||
GRFont* font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
|
||||
if (font == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
@@ -503,15 +497,15 @@ static void gr_init_font(void)
|
||||
|
||||
|
||||
// fall back to the compiled-in font.
|
||||
gr_font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
|
||||
gr_font->texture = reinterpret_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
|
||||
gr_font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
|
||||
gr_font->texture = static_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
|
||||
gr_font->texture->width = font.width;
|
||||
gr_font->texture->height = font.height;
|
||||
gr_font->texture->row_bytes = font.width;
|
||||
gr_font->texture->pixel_bytes = 1;
|
||||
|
||||
unsigned char* bits = reinterpret_cast<unsigned char*>(malloc(font.width * font.height));
|
||||
gr_font->texture->data = reinterpret_cast<unsigned char*>(bits);
|
||||
unsigned char* bits = static_cast<unsigned char*>(malloc(font.width * font.height));
|
||||
gr_font->texture->data = bits;
|
||||
|
||||
unsigned char data;
|
||||
unsigned char* in = font.rundata;
|
||||
@@ -525,131 +519,75 @@ static void gr_init_font(void)
|
||||
}
|
||||
#endif // TW_NO_MINUI_CUSTOM_FONTS
|
||||
|
||||
#if 0
|
||||
// Exercises many of the gr_*() functions; useful for testing.
|
||||
static void gr_test() {
|
||||
GRSurface** images;
|
||||
int frames;
|
||||
int result = res_create_multi_surface("icon_installing", &frames, &images);
|
||||
if (result < 0) {
|
||||
printf("create surface %d\n", result);
|
||||
gr_exit();
|
||||
return;
|
||||
}
|
||||
|
||||
time_t start = time(NULL);
|
||||
int x;
|
||||
for (x = 0; x <= 1200; ++x) {
|
||||
if (x < 400) {
|
||||
gr_color(0, 0, 0, 255);
|
||||
} else {
|
||||
gr_color(0, (x-400)%128, 0, 255);
|
||||
}
|
||||
gr_clear();
|
||||
|
||||
gr_color(255, 0, 0, 255);
|
||||
GRSurface* frame = images[x%frames];
|
||||
gr_blit(frame, 0, 0, frame->width, frame->height, x, 0);
|
||||
|
||||
gr_color(255, 0, 0, 128);
|
||||
gr_fill(400, 150, 600, 350);
|
||||
|
||||
gr_color(255, 255, 255, 255);
|
||||
gr_text(500, 225, "hello, world!", 0);
|
||||
gr_color(255, 255, 0, 128);
|
||||
gr_text(300+x, 275, "pack my box with five dozen liquor jugs", 1);
|
||||
|
||||
gr_color(0, 0, 255, 128);
|
||||
gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500);
|
||||
|
||||
gr_draw = gr_backend->flip(gr_backend);
|
||||
}
|
||||
printf("getting end time\n");
|
||||
time_t end = time(NULL);
|
||||
printf("got end time\n");
|
||||
printf("start %ld end %ld\n", (long)start, (long)end);
|
||||
if (end > start) {
|
||||
printf("%.2f fps\n", ((double)x) / (end-start));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void gr_flip() {
|
||||
gr_draw = gr_backend->flip(gr_backend);
|
||||
gr_draw = gr_backend->Flip();
|
||||
}
|
||||
|
||||
int gr_init(void)
|
||||
{
|
||||
gr_init_font();
|
||||
gr_draw = NULL;
|
||||
gr_init_font();
|
||||
|
||||
auto backend = std::unique_ptr<MinuiBackend>{ std::make_unique<MinuiBackendOverlay>() };
|
||||
gr_draw = backend->Init();
|
||||
|
||||
#ifdef MSM_BSP
|
||||
gr_backend = open_overlay();
|
||||
if (gr_backend) {
|
||||
gr_draw = gr_backend->init(gr_backend);
|
||||
if (!gr_draw) {
|
||||
gr_backend->exit(gr_backend);
|
||||
} else
|
||||
printf("Using overlay graphics.\n");
|
||||
if (gr_draw) {
|
||||
printf("Using overlay graphics.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MSM_BSP
|
||||
if (!gr_backend || !gr_draw) {
|
||||
gr_backend = open_adf();
|
||||
if (gr_backend) {
|
||||
gr_draw = gr_backend->init(gr_backend);
|
||||
if (!gr_draw) {
|
||||
gr_backend->exit(gr_backend);
|
||||
} else
|
||||
printf("Using adf graphics.\n");
|
||||
}
|
||||
if (!gr_draw) {
|
||||
backend = std::make_unique<MinuiBackendAdf>();
|
||||
gr_draw = backend->Init();
|
||||
if (gr_draw)
|
||||
printf("Using adf graphics.\n");
|
||||
}
|
||||
#else
|
||||
printf("Skipping adf graphics because TW_TARGET_USES_QCOM_BSP := true\n");
|
||||
#endif
|
||||
|
||||
if (!gr_backend || !gr_draw) {
|
||||
gr_backend = open_drm();
|
||||
gr_draw = gr_backend->init(gr_backend);
|
||||
if (!gr_draw) {
|
||||
backend = std::make_unique<MinuiBackendDrm>();
|
||||
gr_draw = backend->Init();
|
||||
if (gr_draw)
|
||||
printf("Using drm graphics.\n");
|
||||
}
|
||||
|
||||
if (!gr_backend || !gr_draw) {
|
||||
gr_backend = open_fbdev();
|
||||
gr_draw = gr_backend->init(gr_backend);
|
||||
if (gr_draw == NULL) {
|
||||
return -1;
|
||||
} else
|
||||
if (!gr_draw) {
|
||||
backend = std::make_unique<MinuiBackendFbdev>();
|
||||
gr_draw = backend->Init();
|
||||
if (gr_draw)
|
||||
printf("Using fbdev graphics.\n");
|
||||
}
|
||||
|
||||
overscan_offset_x = gr_draw->width * overscan_percent / 100;
|
||||
overscan_offset_y = gr_draw->height * overscan_percent / 100;
|
||||
if (!gr_draw) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
gr_flip();
|
||||
gr_flip();
|
||||
gr_backend = backend.release();
|
||||
|
||||
return 0;
|
||||
overscan_offset_x = gr_draw->width * overscan_percent / 100;
|
||||
overscan_offset_y = gr_draw->height * overscan_percent / 100;
|
||||
|
||||
gr_flip();
|
||||
gr_flip();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gr_exit(void)
|
||||
{
|
||||
gr_backend->exit(gr_backend);
|
||||
void gr_exit() {
|
||||
delete gr_backend;
|
||||
}
|
||||
|
||||
int gr_fb_width(void)
|
||||
{
|
||||
return gr_draw->width - 2*overscan_offset_x;
|
||||
int gr_fb_width() {
|
||||
return gr_draw->width - 2 * overscan_offset_x;
|
||||
}
|
||||
|
||||
int gr_fb_height(void)
|
||||
{
|
||||
return gr_draw->height - 2*overscan_offset_y;
|
||||
int gr_fb_height() {
|
||||
return gr_draw->height - 2 * overscan_offset_y;
|
||||
}
|
||||
|
||||
void gr_fb_blank(bool blank)
|
||||
{
|
||||
gr_backend->blank(gr_backend, blank);
|
||||
void gr_fb_blank(bool blank) {
|
||||
gr_backend->Blank(blank);
|
||||
}
|
||||
|
||||
+13
-19
@@ -17,28 +17,22 @@
|
||||
#ifndef _GRAPHICS_H_
|
||||
#define _GRAPHICS_H_
|
||||
|
||||
#include "minui.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
// TODO: lose the function pointers.
|
||||
struct minui_backend {
|
||||
// Initializes the backend and returns a GRSurface* to draw into.
|
||||
GRSurface* (*init)(minui_backend*);
|
||||
class MinuiBackend {
|
||||
public:
|
||||
// Initializes the backend and returns a GRSurface* to draw into.
|
||||
virtual GRSurface* Init() = 0;
|
||||
|
||||
// Causes the current drawing surface (returned by the most recent
|
||||
// call to flip() or init()) to be displayed, and returns a new
|
||||
// drawing surface.
|
||||
GRSurface* (*flip)(minui_backend*);
|
||||
// Causes the current drawing surface (returned by the most recent call to Flip() or Init()) to
|
||||
// be displayed, and returns a new drawing surface.
|
||||
virtual GRSurface* Flip() = 0;
|
||||
|
||||
// Blank (or unblank) the screen.
|
||||
void (*blank)(minui_backend*, bool);
|
||||
// Blank (or unblank) the screen.
|
||||
virtual void Blank(bool) = 0;
|
||||
|
||||
// Device cleanup when drawing is done.
|
||||
void (*exit)(minui_backend*);
|
||||
// Device cleanup when drawing is done.
|
||||
virtual ~MinuiBackend() {};
|
||||
};
|
||||
|
||||
minui_backend* open_fbdev();
|
||||
minui_backend* open_adf();
|
||||
minui_backend* open_drm();
|
||||
minui_backend* open_overlay();
|
||||
|
||||
#endif
|
||||
#endif // _GRAPHICS_H_
|
||||
|
||||
+165
-213
@@ -14,238 +14,190 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "graphics_adf.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <adf/adf.h>
|
||||
|
||||
#include "graphics.h"
|
||||
|
||||
struct adf_surface_pdata {
|
||||
GRSurface base;
|
||||
int fd;
|
||||
__u32 offset;
|
||||
__u32 pitch;
|
||||
};
|
||||
|
||||
struct adf_pdata {
|
||||
minui_backend base;
|
||||
int intf_fd;
|
||||
adf_id_t eng_id;
|
||||
__u32 format;
|
||||
|
||||
unsigned int current_surface;
|
||||
unsigned int n_surfaces;
|
||||
adf_surface_pdata surfaces[2];
|
||||
};
|
||||
|
||||
static GRSurface* adf_flip(minui_backend *backend);
|
||||
static void adf_blank(minui_backend *backend, bool blank);
|
||||
|
||||
static int adf_surface_init(adf_pdata *pdata, drm_mode_modeinfo *mode, adf_surface_pdata *surf) {
|
||||
memset(surf, 0, sizeof(*surf));
|
||||
|
||||
surf->fd = adf_interface_simple_buffer_alloc(pdata->intf_fd, mode->hdisplay,
|
||||
mode->vdisplay, pdata->format, &surf->offset, &surf->pitch);
|
||||
if (surf->fd < 0)
|
||||
return surf->fd;
|
||||
|
||||
surf->base.width = mode->hdisplay;
|
||||
surf->base.height = mode->vdisplay;
|
||||
surf->base.row_bytes = surf->pitch;
|
||||
surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4;
|
||||
|
||||
surf->base.data = reinterpret_cast<uint8_t*>(mmap(NULL,
|
||||
surf->pitch * surf->base.height, PROT_WRITE,
|
||||
MAP_SHARED, surf->fd, surf->offset));
|
||||
if (surf->base.data == MAP_FAILED) {
|
||||
close(surf->fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adf_interface_init(adf_pdata *pdata)
|
||||
{
|
||||
adf_interface_data intf_data;
|
||||
int ret = 0;
|
||||
int err;
|
||||
|
||||
err = adf_get_interface_data(pdata->intf_fd, &intf_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = adf_surface_init(pdata, &intf_data.current_mode, &pdata->surfaces[0]);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err));
|
||||
ret = err;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = adf_surface_init(pdata, &intf_data.current_mode,
|
||||
&pdata->surfaces[1]);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err));
|
||||
memset(&pdata->surfaces[1], 0, sizeof(pdata->surfaces[1]));
|
||||
pdata->n_surfaces = 1;
|
||||
} else {
|
||||
pdata->n_surfaces = 2;
|
||||
}
|
||||
|
||||
done:
|
||||
adf_free_interface_data(&intf_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adf_device_init(adf_pdata *pdata, adf_device *dev)
|
||||
{
|
||||
adf_id_t intf_id;
|
||||
int intf_fd;
|
||||
int err;
|
||||
|
||||
err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id,
|
||||
&pdata->eng_id);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = adf_device_attach(dev, pdata->eng_id, intf_id);
|
||||
if (err < 0 && err != -EALREADY)
|
||||
return err;
|
||||
|
||||
pdata->intf_fd = adf_interface_open(dev, intf_id, O_RDWR);
|
||||
if (pdata->intf_fd < 0)
|
||||
return pdata->intf_fd;
|
||||
|
||||
err = adf_interface_init(pdata);
|
||||
if (err < 0) {
|
||||
close(pdata->intf_fd);
|
||||
pdata->intf_fd = -1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static GRSurface* adf_init(minui_backend *backend)
|
||||
{
|
||||
adf_pdata *pdata = (adf_pdata *)backend;
|
||||
adf_id_t *dev_ids = NULL;
|
||||
ssize_t n_dev_ids, i;
|
||||
GRSurface* ret;
|
||||
|
||||
#if defined(RECOVERY_ABGR)
|
||||
pdata->format = DRM_FORMAT_ABGR8888;
|
||||
#elif defined(RECOVERY_BGRA)
|
||||
pdata->format = DRM_FORMAT_BGRA8888;
|
||||
#elif defined(RECOVERY_RGBA)
|
||||
pdata->format = DRM_FORMAT_RGBA8888;
|
||||
#elif defined(RECOVERY_RGBX)
|
||||
pdata->format = DRM_FORMAT_RGBX8888;
|
||||
#else
|
||||
pdata->format = DRM_FORMAT_RGB565;
|
||||
#ifdef HAS_LIBSYNC
|
||||
#include <sync/sync.h>
|
||||
#endif
|
||||
|
||||
n_dev_ids = adf_devices(&dev_ids);
|
||||
if (n_dev_ids == 0) {
|
||||
return NULL;
|
||||
} else if (n_dev_ids < 0) {
|
||||
fprintf(stderr, "enumerating adf devices failed: %s\n",
|
||||
strerror(-n_dev_ids));
|
||||
return NULL;
|
||||
}
|
||||
#include "minui/minui.h"
|
||||
|
||||
pdata->intf_fd = -1;
|
||||
MinuiBackendAdf::MinuiBackendAdf() : intf_fd(-1), dev(), n_surfaces(0), surfaces() {}
|
||||
|
||||
for (i = 0; i < n_dev_ids && pdata->intf_fd < 0; i++) {
|
||||
adf_device dev;
|
||||
int MinuiBackendAdf::SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf) {
|
||||
*surf = {};
|
||||
surf->fence_fd = -1;
|
||||
surf->fd = adf_interface_simple_buffer_alloc(intf_fd, mode->hdisplay, mode->vdisplay, format,
|
||||
&surf->offset, &surf->pitch);
|
||||
if (surf->fd < 0) {
|
||||
return surf->fd;
|
||||
}
|
||||
|
||||
int err = adf_device_open(dev_ids[i], O_RDWR, &dev);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i],
|
||||
strerror(-err));
|
||||
continue;
|
||||
}
|
||||
surf->width = mode->hdisplay;
|
||||
surf->height = mode->vdisplay;
|
||||
surf->row_bytes = surf->pitch;
|
||||
surf->pixel_bytes = (format == DRM_FORMAT_RGB565) ? 2 : 4;
|
||||
|
||||
err = adf_device_init(pdata, &dev);
|
||||
if (err < 0)
|
||||
fprintf(stderr, "initializing adf device %u failed: %s\n",
|
||||
dev_ids[i], strerror(-err));
|
||||
|
||||
adf_device_close(&dev);
|
||||
}
|
||||
|
||||
free(dev_ids);
|
||||
|
||||
if (pdata->intf_fd < 0)
|
||||
return NULL;
|
||||
|
||||
ret = adf_flip(backend);
|
||||
|
||||
adf_blank(backend, true);
|
||||
adf_blank(backend, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GRSurface* adf_flip(minui_backend *backend)
|
||||
{
|
||||
adf_pdata *pdata = (adf_pdata *)backend;
|
||||
adf_surface_pdata *surf = &pdata->surfaces[pdata->current_surface];
|
||||
|
||||
int fence_fd = adf_interface_simple_post(pdata->intf_fd, pdata->eng_id,
|
||||
surf->base.width, surf->base.height, pdata->format, surf->fd,
|
||||
surf->offset, surf->pitch, -1);
|
||||
if (fence_fd >= 0)
|
||||
close(fence_fd);
|
||||
|
||||
pdata->current_surface = (pdata->current_surface + 1) % pdata->n_surfaces;
|
||||
return &pdata->surfaces[pdata->current_surface].base;
|
||||
}
|
||||
|
||||
static void adf_blank(minui_backend *backend, bool blank)
|
||||
{
|
||||
adf_pdata *pdata = (adf_pdata *)backend;
|
||||
adf_interface_blank(pdata->intf_fd,
|
||||
blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
static void adf_surface_destroy(adf_surface_pdata *surf)
|
||||
{
|
||||
munmap(surf->base.data, surf->pitch * surf->base.height);
|
||||
surf->data = static_cast<uint8_t*>(
|
||||
mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset));
|
||||
if (surf->data == MAP_FAILED) {
|
||||
int saved_errno = errno;
|
||||
close(surf->fd);
|
||||
return -saved_errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adf_exit(minui_backend *backend)
|
||||
{
|
||||
adf_pdata *pdata = (adf_pdata *)backend;
|
||||
unsigned int i;
|
||||
int MinuiBackendAdf::InterfaceInit() {
|
||||
adf_interface_data intf_data;
|
||||
int err = adf_get_interface_data(intf_fd, &intf_data);
|
||||
if (err < 0) return err;
|
||||
|
||||
for (i = 0; i < pdata->n_surfaces; i++)
|
||||
adf_surface_destroy(&pdata->surfaces[i]);
|
||||
if (pdata->intf_fd >= 0)
|
||||
close(pdata->intf_fd);
|
||||
free(pdata);
|
||||
int ret = 0;
|
||||
err = SurfaceInit(&intf_data.current_mode, &surfaces[0]);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err));
|
||||
ret = err;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = SurfaceInit(&intf_data.current_mode, &surfaces[1]);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err));
|
||||
surfaces[1] = {};
|
||||
n_surfaces = 1;
|
||||
} else {
|
||||
n_surfaces = 2;
|
||||
}
|
||||
|
||||
done:
|
||||
adf_free_interface_data(&intf_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
minui_backend *open_adf()
|
||||
{
|
||||
adf_pdata* pdata = reinterpret_cast<adf_pdata*>(calloc(1, sizeof(*pdata)));
|
||||
if (!pdata) {
|
||||
perror("allocating adf backend failed");
|
||||
return NULL;
|
||||
int MinuiBackendAdf::DeviceInit(adf_device* dev) {
|
||||
adf_id_t intf_id;
|
||||
int err = adf_find_simple_post_configuration(dev, &format, 1, &intf_id, &eng_id);
|
||||
if (err < 0) return err;
|
||||
|
||||
err = adf_device_attach(dev, eng_id, intf_id);
|
||||
if (err < 0 && err != -EALREADY) return err;
|
||||
|
||||
intf_fd = adf_interface_open(dev, intf_id, O_RDWR);
|
||||
if (intf_fd < 0) return intf_fd;
|
||||
|
||||
err = InterfaceInit();
|
||||
if (err < 0) {
|
||||
close(intf_fd);
|
||||
intf_fd = -1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
GRSurface* MinuiBackendAdf::Init() {
|
||||
#if defined(RECOVERY_ABGR)
|
||||
format = DRM_FORMAT_ABGR8888;
|
||||
#elif defined(RECOVERY_BGRA)
|
||||
format = DRM_FORMAT_BGRA8888;
|
||||
#elif defined(RECOVERY_RGBA)
|
||||
format = DRM_FORMAT_RGBA8888;
|
||||
#elif defined(RECOVERY_RGBX)
|
||||
format = DRM_FORMAT_RGBX8888;
|
||||
#else
|
||||
format = DRM_FORMAT_RGB565;
|
||||
#endif
|
||||
|
||||
adf_id_t* dev_ids = nullptr;
|
||||
ssize_t n_dev_ids = adf_devices(&dev_ids);
|
||||
if (n_dev_ids == 0) {
|
||||
return nullptr;
|
||||
} else if (n_dev_ids < 0) {
|
||||
fprintf(stderr, "enumerating adf devices failed: %s\n", strerror(-n_dev_ids));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
intf_fd = -1;
|
||||
|
||||
for (ssize_t i = 0; i < n_dev_ids && intf_fd < 0; i++) {
|
||||
int err = adf_device_open(dev_ids[i], O_RDWR, &dev);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], strerror(-err));
|
||||
continue;
|
||||
}
|
||||
|
||||
pdata->base.init = adf_init;
|
||||
pdata->base.flip = adf_flip;
|
||||
pdata->base.blank = adf_blank;
|
||||
pdata->base.exit = adf_exit;
|
||||
return &pdata->base;
|
||||
err = DeviceInit(&dev);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "initializing adf device %u failed: %s\n", dev_ids[i], strerror(-err));
|
||||
adf_device_close(&dev);
|
||||
}
|
||||
}
|
||||
|
||||
free(dev_ids);
|
||||
|
||||
if (intf_fd < 0) return nullptr;
|
||||
|
||||
GRSurface* ret = Flip();
|
||||
|
||||
Blank(true);
|
||||
Blank(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MinuiBackendAdf::Sync(__unused GRSurfaceAdf* surf) {
|
||||
#ifdef HAS_LIBSYNC
|
||||
static constexpr unsigned int warningTimeout = 3000;
|
||||
|
||||
if (surf == nullptr) return;
|
||||
|
||||
if (surf->fence_fd >= 0) {
|
||||
int err = sync_wait(surf->fence_fd, warningTimeout);
|
||||
if (err < 0) {
|
||||
perror("adf sync fence wait error\n");
|
||||
}
|
||||
|
||||
close(surf->fence_fd);
|
||||
surf->fence_fd = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
GRSurface* MinuiBackendAdf::Flip() {
|
||||
GRSurfaceAdf* surf = &surfaces[current_surface];
|
||||
|
||||
int fence_fd = adf_interface_simple_post(intf_fd, eng_id, surf->width, surf->height, format,
|
||||
surf->fd, surf->offset, surf->pitch, -1);
|
||||
if (fence_fd >= 0) surf->fence_fd = fence_fd;
|
||||
|
||||
current_surface = (current_surface + 1) % n_surfaces;
|
||||
Sync(&surfaces[current_surface]);
|
||||
return &surfaces[current_surface];
|
||||
}
|
||||
|
||||
void MinuiBackendAdf::Blank(bool blank) {
|
||||
adf_interface_blank(intf_fd, blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
void MinuiBackendAdf::SurfaceDestroy(GRSurfaceAdf* surf) {
|
||||
munmap(surf->data, surf->pitch * surf->height);
|
||||
close(surf->fence_fd);
|
||||
close(surf->fd);
|
||||
}
|
||||
|
||||
MinuiBackendAdf::~MinuiBackendAdf() {
|
||||
adf_device_close(&dev);
|
||||
for (unsigned int i = 0; i < n_surfaces; i++) {
|
||||
SurfaceDestroy(&surfaces[i]);
|
||||
}
|
||||
if (intf_fd >= 0) close(intf_fd);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 _GRAPHICS_ADF_H_
|
||||
#define _GRAPHICS_ADF_H_
|
||||
|
||||
#include <adf/adf.h>
|
||||
|
||||
#include "graphics.h"
|
||||
|
||||
class GRSurfaceAdf : public GRSurface {
|
||||
private:
|
||||
int fence_fd;
|
||||
int fd;
|
||||
__u32 offset;
|
||||
__u32 pitch;
|
||||
|
||||
friend class MinuiBackendAdf;
|
||||
};
|
||||
|
||||
class MinuiBackendAdf : public MinuiBackend {
|
||||
public:
|
||||
GRSurface* Init() override;
|
||||
GRSurface* Flip() override;
|
||||
void Blank(bool) override;
|
||||
~MinuiBackendAdf() override;
|
||||
MinuiBackendAdf();
|
||||
|
||||
private:
|
||||
int SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf);
|
||||
int InterfaceInit();
|
||||
int DeviceInit(adf_device* dev);
|
||||
void SurfaceDestroy(GRSurfaceAdf* surf);
|
||||
void Sync(GRSurfaceAdf* surf);
|
||||
|
||||
int intf_fd;
|
||||
adf_id_t eng_id;
|
||||
__u32 format;
|
||||
adf_device dev;
|
||||
unsigned int current_surface;
|
||||
unsigned int n_surfaces;
|
||||
GRSurfaceAdf surfaces[2];
|
||||
};
|
||||
|
||||
#endif // _GRAPHICS_ADF_H_
|
||||
+291
-373
@@ -14,466 +14,384 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
#include "graphics_drm.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
#include "minui.h"
|
||||
#include "graphics.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
|
||||
|
||||
struct drm_surface {
|
||||
GRSurface base;
|
||||
uint32_t fb_id;
|
||||
uint32_t handle;
|
||||
};
|
||||
MinuiBackendDrm::MinuiBackendDrm()
|
||||
: GRSurfaceDrms(), main_monitor_crtc(nullptr), main_monitor_connector(nullptr), drm_fd(-1) {}
|
||||
|
||||
static drm_surface *drm_surfaces[2];
|
||||
static int current_buffer;
|
||||
|
||||
static drmModeCrtc *main_monitor_crtc;
|
||||
static drmModeConnector *main_monitor_connector;
|
||||
|
||||
static int drm_fd = -1;
|
||||
|
||||
static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) {
|
||||
if (crtc) {
|
||||
drmModeSetCrtc(drm_fd, crtc->crtc_id,
|
||||
0, // fb_id
|
||||
0, 0, // x,y
|
||||
NULL, // connectors
|
||||
0, // connector_count
|
||||
NULL); // mode
|
||||
}
|
||||
void MinuiBackendDrm::DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc) {
|
||||
if (crtc) {
|
||||
drmModeSetCrtc(drm_fd, crtc->crtc_id,
|
||||
0, // fb_id
|
||||
0, 0, // x,y
|
||||
nullptr, // connectors
|
||||
0, // connector_count
|
||||
nullptr); // mode
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc,
|
||||
struct drm_surface *surface) {
|
||||
int32_t ret;
|
||||
void MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, GRSurfaceDrm* surface) {
|
||||
int32_t ret = drmModeSetCrtc(drm_fd, crtc->crtc_id, surface->fb_id, 0, 0, // x,y
|
||||
&main_monitor_connector->connector_id,
|
||||
1, // connector_count
|
||||
&main_monitor_crtc->mode);
|
||||
|
||||
ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
|
||||
surface->fb_id,
|
||||
0, 0, // x,y
|
||||
&main_monitor_connector->connector_id,
|
||||
1, // connector_count
|
||||
&main_monitor_crtc->mode);
|
||||
|
||||
if (ret)
|
||||
printf("drmModeSetCrtc failed ret=%d\n", ret);
|
||||
if (ret) {
|
||||
printf("drmModeSetCrtc failed ret=%d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_blank(minui_backend* backend __unused, bool blank) {
|
||||
if (blank)
|
||||
drm_disable_crtc(drm_fd, main_monitor_crtc);
|
||||
else
|
||||
drm_enable_crtc(drm_fd, main_monitor_crtc,
|
||||
drm_surfaces[current_buffer]);
|
||||
void MinuiBackendDrm::Blank(bool blank) {
|
||||
if (blank) {
|
||||
DrmDisableCrtc(drm_fd, main_monitor_crtc);
|
||||
} else {
|
||||
DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]);
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_destroy_surface(struct drm_surface *surface) {
|
||||
struct drm_gem_close gem_close;
|
||||
int ret;
|
||||
void MinuiBackendDrm::DrmDestroySurface(GRSurfaceDrm* surface) {
|
||||
if (!surface) return;
|
||||
|
||||
if(!surface)
|
||||
return;
|
||||
if (surface->data) {
|
||||
munmap(surface->data, surface->row_bytes * surface->height);
|
||||
}
|
||||
|
||||
if (surface->base.data)
|
||||
munmap(surface->base.data,
|
||||
surface->base.row_bytes * surface->base.height);
|
||||
|
||||
if (surface->fb_id) {
|
||||
ret = drmModeRmFB(drm_fd, surface->fb_id);
|
||||
if (ret)
|
||||
printf("drmModeRmFB failed ret=%d\n", ret);
|
||||
if (surface->fb_id) {
|
||||
int ret = drmModeRmFB(drm_fd, surface->fb_id);
|
||||
if (ret) {
|
||||
printf("drmModeRmFB failed ret=%d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (surface->handle) {
|
||||
memset(&gem_close, 0, sizeof(gem_close));
|
||||
gem_close.handle = surface->handle;
|
||||
if (surface->handle) {
|
||||
drm_gem_close gem_close = {};
|
||||
gem_close.handle = surface->handle;
|
||||
|
||||
ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
|
||||
if (ret)
|
||||
printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
|
||||
int ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
|
||||
if (ret) {
|
||||
printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
free(surface);
|
||||
delete surface;
|
||||
}
|
||||
|
||||
static int drm_format_to_bpp(uint32_t format) {
|
||||
switch(format) {
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
case DRM_FORMAT_RGBX8888:
|
||||
case DRM_FORMAT_BGRX8888:
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
return 32;
|
||||
case DRM_FORMAT_RGB565:
|
||||
return 16;
|
||||
default:
|
||||
printf("Unknown format %d\n", format);
|
||||
return 32;
|
||||
}
|
||||
switch (format) {
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
case DRM_FORMAT_RGBX8888:
|
||||
case DRM_FORMAT_BGRX8888:
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
return 32;
|
||||
case DRM_FORMAT_RGB565:
|
||||
return 16;
|
||||
default:
|
||||
printf("Unknown format %d\n", format);
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
|
||||
static drm_surface *drm_create_surface(int width, int height) {
|
||||
struct drm_surface *surface;
|
||||
struct drm_mode_create_dumb create_dumb;
|
||||
uint32_t format;
|
||||
int ret;
|
||||
|
||||
surface = (struct drm_surface*)calloc(1, sizeof(*surface));
|
||||
if (!surface) {
|
||||
printf("Can't allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
GRSurfaceDrm* MinuiBackendDrm::DrmCreateSurface(int width, int height) {
|
||||
GRSurfaceDrm* surface = new GRSurfaceDrm;
|
||||
*surface = {};
|
||||
|
||||
uint32_t format;
|
||||
#if defined(RECOVERY_ABGR)
|
||||
format = DRM_FORMAT_RGBA8888;
|
||||
format = DRM_FORMAT_RGBA8888;
|
||||
#elif defined(RECOVERY_BGRA)
|
||||
format = DRM_FORMAT_ARGB8888;
|
||||
format = DRM_FORMAT_ARGB8888;
|
||||
#elif defined(RECOVERY_RGBA)
|
||||
format = DRM_FORMAT_ABGR8888;
|
||||
format = DRM_FORMAT_ARGB8888;
|
||||
#elif defined(RECOVERY_RGBX)
|
||||
format = DRM_FORMAT_XBGR8888;
|
||||
format = DRM_FORMAT_XBGR8888;
|
||||
#else
|
||||
format = DRM_FORMAT_RGB565;
|
||||
format = DRM_FORMAT_RGB565;
|
||||
#endif
|
||||
|
||||
memset(&create_dumb, 0, sizeof(create_dumb));
|
||||
create_dumb.height = height;
|
||||
create_dumb.width = width;
|
||||
create_dumb.bpp = drm_format_to_bpp(format);
|
||||
create_dumb.flags = 0;
|
||||
drm_mode_create_dumb create_dumb = {};
|
||||
create_dumb.height = height;
|
||||
create_dumb.width = width;
|
||||
create_dumb.bpp = drm_format_to_bpp(format);
|
||||
create_dumb.flags = 0;
|
||||
|
||||
ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
|
||||
if (ret) {
|
||||
printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret);
|
||||
drm_destroy_surface(surface);
|
||||
return NULL;
|
||||
}
|
||||
surface->handle = create_dumb.handle;
|
||||
int ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
|
||||
if (ret) {
|
||||
printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n", ret);
|
||||
DrmDestroySurface(surface);
|
||||
return nullptr;
|
||||
}
|
||||
surface->handle = create_dumb.handle;
|
||||
|
||||
uint32_t handles[4], pitches[4], offsets[4];
|
||||
uint32_t handles[4], pitches[4], offsets[4];
|
||||
|
||||
handles[0] = surface->handle;
|
||||
pitches[0] = create_dumb.pitch;
|
||||
offsets[0] = 0;
|
||||
handles[0] = surface->handle;
|
||||
pitches[0] = create_dumb.pitch;
|
||||
offsets[0] = 0;
|
||||
|
||||
ret = drmModeAddFB2(drm_fd, width, height,
|
||||
format, handles, pitches, offsets,
|
||||
&(surface->fb_id), 0);
|
||||
if (ret) {
|
||||
printf("drmModeAddFB2 failed ret=%d\n", ret);
|
||||
drm_destroy_surface(surface);
|
||||
return NULL;
|
||||
}
|
||||
ret =
|
||||
drmModeAddFB2(drm_fd, width, height, format, handles, pitches, offsets, &(surface->fb_id), 0);
|
||||
if (ret) {
|
||||
printf("drmModeAddFB2 failed ret=%d\n", ret);
|
||||
DrmDestroySurface(surface);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct drm_mode_map_dumb map_dumb;
|
||||
memset(&map_dumb, 0, sizeof(map_dumb));
|
||||
map_dumb.handle = create_dumb.handle;
|
||||
ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
|
||||
if (ret) {
|
||||
printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret);
|
||||
drm_destroy_surface(surface);
|
||||
return NULL;;
|
||||
}
|
||||
drm_mode_map_dumb map_dumb = {};
|
||||
map_dumb.handle = create_dumb.handle;
|
||||
ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
|
||||
if (ret) {
|
||||
printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n", ret);
|
||||
DrmDestroySurface(surface);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
surface->base.height = height;
|
||||
surface->base.width = width;
|
||||
surface->base.row_bytes = create_dumb.pitch;
|
||||
surface->base.pixel_bytes = create_dumb.bpp / 8;
|
||||
surface->base.data = (unsigned char*)
|
||||
mmap(NULL,
|
||||
surface->base.height * surface->base.row_bytes,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
drm_fd, map_dumb.offset);
|
||||
if (surface->base.data == MAP_FAILED) {
|
||||
perror("mmap() failed");
|
||||
drm_destroy_surface(surface);
|
||||
return NULL;
|
||||
}
|
||||
surface->height = height;
|
||||
surface->width = width;
|
||||
surface->row_bytes = create_dumb.pitch;
|
||||
surface->pixel_bytes = create_dumb.bpp / 8;
|
||||
surface->data = static_cast<unsigned char*>(mmap(nullptr, surface->height * surface->row_bytes,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd,
|
||||
map_dumb.offset));
|
||||
if (surface->data == MAP_FAILED) {
|
||||
perror("mmap() failed");
|
||||
DrmDestroySurface(surface);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return surface;
|
||||
return surface;
|
||||
}
|
||||
|
||||
static drmModeCrtc *find_crtc_for_connector(int fd,
|
||||
drmModeRes *resources,
|
||||
drmModeConnector *connector) {
|
||||
int i, j;
|
||||
drmModeEncoder *encoder;
|
||||
int32_t crtc;
|
||||
static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources,
|
||||
drmModeConnector* connector) {
|
||||
// Find the encoder. If we already have one, just use it.
|
||||
drmModeEncoder* encoder;
|
||||
if (connector->encoder_id) {
|
||||
encoder = drmModeGetEncoder(fd, connector->encoder_id);
|
||||
} else {
|
||||
encoder = nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the encoder. If we already have one, just use it.
|
||||
*/
|
||||
if (connector->encoder_id)
|
||||
encoder = drmModeGetEncoder(fd, connector->encoder_id);
|
||||
else
|
||||
encoder = NULL;
|
||||
int32_t crtc;
|
||||
if (encoder && encoder->crtc_id) {
|
||||
crtc = encoder->crtc_id;
|
||||
drmModeFreeEncoder(encoder);
|
||||
return drmModeGetCrtc(fd, crtc);
|
||||
}
|
||||
|
||||
if (encoder && encoder->crtc_id) {
|
||||
crtc = encoder->crtc_id;
|
||||
// Didn't find anything, try to find a crtc and encoder combo.
|
||||
crtc = -1;
|
||||
for (int i = 0; i < connector->count_encoders; i++) {
|
||||
encoder = drmModeGetEncoder(fd, connector->encoders[i]);
|
||||
|
||||
if (encoder) {
|
||||
for (int j = 0; j < resources->count_crtcs; j++) {
|
||||
if (!(encoder->possible_crtcs & (1 << j))) continue;
|
||||
crtc = resources->crtcs[j];
|
||||
break;
|
||||
}
|
||||
if (crtc >= 0) {
|
||||
drmModeFreeEncoder(encoder);
|
||||
return drmModeGetCrtc(fd, crtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Didn't find anything, try to find a crtc and encoder combo.
|
||||
*/
|
||||
crtc = -1;
|
||||
for (i = 0; i < connector->count_encoders; i++) {
|
||||
encoder = drmModeGetEncoder(fd, connector->encoders[i]);
|
||||
|
||||
if (encoder) {
|
||||
for (j = 0; j < resources->count_crtcs; j++) {
|
||||
if (!(encoder->possible_crtcs & (1 << j)))
|
||||
continue;
|
||||
crtc = resources->crtcs[j];
|
||||
break;
|
||||
}
|
||||
if (crtc >= 0) {
|
||||
drmModeFreeEncoder(encoder);
|
||||
return drmModeGetCrtc(fd, crtc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static drmModeConnector *find_used_connector_by_type(int fd,
|
||||
drmModeRes *resources,
|
||||
unsigned type) {
|
||||
int i;
|
||||
for (i = 0; i < resources->count_connectors; i++) {
|
||||
drmModeConnector *connector;
|
||||
|
||||
connector = drmModeGetConnector(fd, resources->connectors[i]);
|
||||
if (connector) {
|
||||
if ((connector->connector_type == type) &&
|
||||
(connector->connection == DRM_MODE_CONNECTED) &&
|
||||
(connector->count_modes > 0))
|
||||
return connector;
|
||||
|
||||
drmModeFreeConnector(connector);
|
||||
}
|
||||
static drmModeConnector* find_used_connector_by_type(int fd, drmModeRes* resources, unsigned type) {
|
||||
for (int i = 0; i < resources->count_connectors; i++) {
|
||||
drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
|
||||
if (connector) {
|
||||
if ((connector->connector_type == type) && (connector->connection == DRM_MODE_CONNECTED) &&
|
||||
(connector->count_modes > 0)) {
|
||||
return connector;
|
||||
}
|
||||
drmModeFreeConnector(connector);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static drmModeConnector *find_first_connected_connector(int fd,
|
||||
drmModeRes *resources) {
|
||||
int i;
|
||||
for (i = 0; i < resources->count_connectors; i++) {
|
||||
drmModeConnector *connector;
|
||||
static drmModeConnector* find_first_connected_connector(int fd, drmModeRes* resources) {
|
||||
for (int i = 0; i < resources->count_connectors; i++) {
|
||||
drmModeConnector* connector;
|
||||
|
||||
connector = drmModeGetConnector(fd, resources->connectors[i]);
|
||||
if (connector) {
|
||||
if ((connector->count_modes > 0) &&
|
||||
(connector->connection == DRM_MODE_CONNECTED))
|
||||
return connector;
|
||||
connector = drmModeGetConnector(fd, resources->connectors[i]);
|
||||
if (connector) {
|
||||
if ((connector->count_modes > 0) && (connector->connection == DRM_MODE_CONNECTED))
|
||||
return connector;
|
||||
|
||||
drmModeFreeConnector(connector);
|
||||
}
|
||||
drmModeFreeConnector(connector);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources,
|
||||
uint32_t *mode_index) {
|
||||
unsigned i = 0;
|
||||
int modes;
|
||||
/* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
|
||||
unsigned kConnectorPriority[] = {
|
||||
DRM_MODE_CONNECTOR_LVDS,
|
||||
DRM_MODE_CONNECTOR_eDP,
|
||||
DRM_MODE_CONNECTOR_DSI,
|
||||
};
|
||||
drmModeConnector* MinuiBackendDrm::FindMainMonitor(int fd, drmModeRes* resources,
|
||||
uint32_t* mode_index) {
|
||||
/* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
|
||||
static constexpr unsigned kConnectorPriority[] = {
|
||||
DRM_MODE_CONNECTOR_LVDS,
|
||||
DRM_MODE_CONNECTOR_eDP,
|
||||
DRM_MODE_CONNECTOR_DSI,
|
||||
};
|
||||
|
||||
drmModeConnector *main_monitor_connector = NULL;
|
||||
do {
|
||||
main_monitor_connector = find_used_connector_by_type(fd,
|
||||
resources,
|
||||
kConnectorPriority[i]);
|
||||
i++;
|
||||
} while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
|
||||
drmModeConnector* main_monitor_connector = nullptr;
|
||||
unsigned i = 0;
|
||||
do {
|
||||
main_monitor_connector = find_used_connector_by_type(fd, resources, kConnectorPriority[i]);
|
||||
i++;
|
||||
} while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
|
||||
|
||||
/* If we didn't find a connector, grab the first one that is connected. */
|
||||
if (!main_monitor_connector)
|
||||
main_monitor_connector =
|
||||
find_first_connected_connector(fd, resources);
|
||||
/* If we didn't find a connector, grab the first one that is connected. */
|
||||
if (!main_monitor_connector) {
|
||||
main_monitor_connector = find_first_connected_connector(fd, resources);
|
||||
}
|
||||
|
||||
/* If we still didn't find a connector, give up and return. */
|
||||
if (!main_monitor_connector)
|
||||
return NULL;
|
||||
/* If we still didn't find a connector, give up and return. */
|
||||
if (!main_monitor_connector) return nullptr;
|
||||
|
||||
*mode_index = 0;
|
||||
for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
|
||||
if (main_monitor_connector->modes[modes].type &
|
||||
DRM_MODE_TYPE_PREFERRED) {
|
||||
*mode_index = modes;
|
||||
break;
|
||||
}
|
||||
*mode_index = 0;
|
||||
for (int modes = 0; modes < main_monitor_connector->count_modes; modes++) {
|
||||
if (main_monitor_connector->modes[modes].type & DRM_MODE_TYPE_PREFERRED) {
|
||||
*mode_index = modes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return main_monitor_connector;
|
||||
return main_monitor_connector;
|
||||
}
|
||||
|
||||
static void disable_non_main_crtcs(int fd,
|
||||
drmModeRes *resources,
|
||||
drmModeCrtc* main_crtc) {
|
||||
int i;
|
||||
drmModeCrtc* crtc;
|
||||
|
||||
for (i = 0; i < resources->count_connectors; i++) {
|
||||
drmModeConnector *connector;
|
||||
|
||||
connector = drmModeGetConnector(fd, resources->connectors[i]);
|
||||
crtc = find_crtc_for_connector(fd, resources, connector);
|
||||
if (crtc->crtc_id != main_crtc->crtc_id)
|
||||
drm_disable_crtc(fd, crtc);
|
||||
drmModeFreeCrtc(crtc);
|
||||
void MinuiBackendDrm::DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc) {
|
||||
for (int i = 0; i < resources->count_connectors; i++) {
|
||||
drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
|
||||
drmModeCrtc* crtc = find_crtc_for_connector(fd, resources, connector);
|
||||
if (crtc->crtc_id != main_crtc->crtc_id) {
|
||||
DrmDisableCrtc(fd, crtc);
|
||||
}
|
||||
drmModeFreeCrtc(crtc);
|
||||
}
|
||||
}
|
||||
|
||||
static GRSurface* drm_init(minui_backend* backend __unused) {
|
||||
drmModeRes *res = NULL;
|
||||
uint32_t selected_mode;
|
||||
char *dev_name;
|
||||
int width, height;
|
||||
int ret, i;
|
||||
GRSurface* MinuiBackendDrm::Init() {
|
||||
drmModeRes* res = nullptr;
|
||||
|
||||
/* Consider DRM devices in order. */
|
||||
for (i = 0; i < DRM_MAX_MINOR; i++) {
|
||||
uint64_t cap = 0;
|
||||
/* Consider DRM devices in order. */
|
||||
for (int i = 0; i < DRM_MAX_MINOR; i++) {
|
||||
char* dev_name;
|
||||
int ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
|
||||
if (ret < 0) continue;
|
||||
|
||||
ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
drm_fd = open(dev_name, O_RDWR, 0);
|
||||
free(dev_name);
|
||||
if (drm_fd < 0) continue;
|
||||
|
||||
drm_fd = open(dev_name, O_RDWR, 0);
|
||||
free(dev_name);
|
||||
if (drm_fd < 0)
|
||||
continue;
|
||||
|
||||
/* We need dumb buffers. */
|
||||
ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
|
||||
if (ret || cap == 0) {
|
||||
close(drm_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
res = drmModeGetResources(drm_fd);
|
||||
if (!res) {
|
||||
close(drm_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Use this device if it has at least one connected monitor. */
|
||||
if (res->count_crtcs > 0 && res->count_connectors > 0)
|
||||
if (find_first_connected_connector(drm_fd, res))
|
||||
break;
|
||||
|
||||
drmModeFreeResources(res);
|
||||
close(drm_fd);
|
||||
res = NULL;
|
||||
uint64_t cap = 0;
|
||||
/* We need dumb buffers. */
|
||||
ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
|
||||
if (ret || cap == 0) {
|
||||
close(drm_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (drm_fd < 0 || res == NULL) {
|
||||
perror("cannot find/open a drm device");
|
||||
return NULL;
|
||||
res = drmModeGetResources(drm_fd);
|
||||
if (!res) {
|
||||
close(drm_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
main_monitor_connector = find_main_monitor(drm_fd,
|
||||
res, &selected_mode);
|
||||
|
||||
if (!main_monitor_connector) {
|
||||
printf("main_monitor_connector not found\n");
|
||||
drmModeFreeResources(res);
|
||||
close(drm_fd);
|
||||
return NULL;
|
||||
/* Use this device if it has at least one connected monitor. */
|
||||
if (res->count_crtcs > 0 && res->count_connectors > 0) {
|
||||
if (find_first_connected_connector(drm_fd, res)) break;
|
||||
}
|
||||
|
||||
main_monitor_crtc = find_crtc_for_connector(drm_fd, res,
|
||||
main_monitor_connector);
|
||||
|
||||
if (!main_monitor_crtc) {
|
||||
printf("main_monitor_crtc not found\n");
|
||||
drmModeFreeResources(res);
|
||||
close(drm_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
disable_non_main_crtcs(drm_fd,
|
||||
res, main_monitor_crtc);
|
||||
|
||||
main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
|
||||
|
||||
width = main_monitor_crtc->mode.hdisplay;
|
||||
height = main_monitor_crtc->mode.vdisplay;
|
||||
|
||||
drmModeFreeResources(res);
|
||||
|
||||
drm_surfaces[0] = drm_create_surface(width, height);
|
||||
drm_surfaces[1] = drm_create_surface(width, height);
|
||||
if (!drm_surfaces[0] || !drm_surfaces[1]) {
|
||||
drm_destroy_surface(drm_surfaces[0]);
|
||||
drm_destroy_surface(drm_surfaces[1]);
|
||||
drmModeFreeResources(res);
|
||||
close(drm_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
current_buffer = 0;
|
||||
|
||||
drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]);
|
||||
|
||||
return &(drm_surfaces[0]->base);
|
||||
}
|
||||
|
||||
static GRSurface* drm_flip(minui_backend* backend __unused) {
|
||||
int ret;
|
||||
|
||||
ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
|
||||
drm_surfaces[current_buffer]->fb_id, 0, NULL);
|
||||
if (ret < 0) {
|
||||
printf("drmModePageFlip failed ret=%d\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
current_buffer = 1 - current_buffer;
|
||||
return &(drm_surfaces[current_buffer]->base);
|
||||
}
|
||||
|
||||
static void drm_exit(minui_backend* backend __unused) {
|
||||
drm_disable_crtc(drm_fd, main_monitor_crtc);
|
||||
drm_destroy_surface(drm_surfaces[0]);
|
||||
drm_destroy_surface(drm_surfaces[1]);
|
||||
drmModeFreeCrtc(main_monitor_crtc);
|
||||
drmModeFreeConnector(main_monitor_connector);
|
||||
close(drm_fd);
|
||||
drm_fd = -1;
|
||||
res = nullptr;
|
||||
}
|
||||
|
||||
if (drm_fd < 0 || res == nullptr) {
|
||||
perror("cannot find/open a drm device");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t selected_mode;
|
||||
main_monitor_connector = FindMainMonitor(drm_fd, res, &selected_mode);
|
||||
|
||||
if (!main_monitor_connector) {
|
||||
printf("main_monitor_connector not found\n");
|
||||
drmModeFreeResources(res);
|
||||
close(drm_fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
main_monitor_crtc = find_crtc_for_connector(drm_fd, res, main_monitor_connector);
|
||||
|
||||
if (!main_monitor_crtc) {
|
||||
printf("main_monitor_crtc not found\n");
|
||||
drmModeFreeResources(res);
|
||||
close(drm_fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DisableNonMainCrtcs(drm_fd, res, main_monitor_crtc);
|
||||
|
||||
main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
|
||||
|
||||
int width = main_monitor_crtc->mode.hdisplay;
|
||||
int height = main_monitor_crtc->mode.vdisplay;
|
||||
|
||||
drmModeFreeResources(res);
|
||||
|
||||
GRSurfaceDrms[0] = DrmCreateSurface(width, height);
|
||||
GRSurfaceDrms[1] = DrmCreateSurface(width, height);
|
||||
if (!GRSurfaceDrms[0] || !GRSurfaceDrms[1]) {
|
||||
// GRSurfaceDrms and drm_fd should be freed in d'tor.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
current_buffer = 0;
|
||||
|
||||
DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[1]);
|
||||
|
||||
return GRSurfaceDrms[0];
|
||||
}
|
||||
|
||||
static minui_backend drm_backend = {
|
||||
.init = drm_init,
|
||||
.flip = drm_flip,
|
||||
.blank = drm_blank,
|
||||
.exit = drm_exit,
|
||||
};
|
||||
|
||||
minui_backend* open_drm() {
|
||||
return &drm_backend;
|
||||
GRSurface* MinuiBackendDrm::Flip() {
|
||||
int ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
|
||||
GRSurfaceDrms[current_buffer]->fb_id, 0, nullptr);
|
||||
if (ret < 0) {
|
||||
printf("drmModePageFlip failed ret=%d\n", ret);
|
||||
return nullptr;
|
||||
}
|
||||
current_buffer = 1 - current_buffer;
|
||||
return GRSurfaceDrms[current_buffer];
|
||||
}
|
||||
|
||||
MinuiBackendDrm::~MinuiBackendDrm() {
|
||||
DrmDisableCrtc(drm_fd, main_monitor_crtc);
|
||||
DrmDestroySurface(GRSurfaceDrms[0]);
|
||||
DrmDestroySurface(GRSurfaceDrms[1]);
|
||||
drmModeFreeCrtc(main_monitor_crtc);
|
||||
drmModeFreeConnector(main_monitor_connector);
|
||||
close(drm_fd);
|
||||
drm_fd = -1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 _GRAPHICS_DRM_H_
|
||||
#define _GRAPHICS_DRM_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
#include "graphics.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
class GRSurfaceDrm : public GRSurface {
|
||||
private:
|
||||
uint32_t fb_id;
|
||||
uint32_t handle;
|
||||
|
||||
friend class MinuiBackendDrm;
|
||||
};
|
||||
|
||||
class MinuiBackendDrm : public MinuiBackend {
|
||||
public:
|
||||
GRSurface* Init() override;
|
||||
GRSurface* Flip() override;
|
||||
void Blank(bool) override;
|
||||
~MinuiBackendDrm() override;
|
||||
MinuiBackendDrm();
|
||||
|
||||
private:
|
||||
void DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc);
|
||||
void DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, GRSurfaceDrm* surface);
|
||||
GRSurfaceDrm* DrmCreateSurface(int width, int height);
|
||||
void DrmDestroySurface(GRSurfaceDrm* surface);
|
||||
void DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc);
|
||||
drmModeConnector* FindMainMonitor(int fd, drmModeRes* resources, uint32_t* mode_index);
|
||||
|
||||
GRSurfaceDrm* GRSurfaceDrms[2];
|
||||
int current_buffer;
|
||||
drmModeCrtc* main_monitor_crtc;
|
||||
drmModeConnector* main_monitor_connector;
|
||||
int drm_fd;
|
||||
};
|
||||
|
||||
#endif // _GRAPHICS_DRM_H_
|
||||
+124
-155
@@ -14,51 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "graphics_fbdev.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/fb.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/kd.h>
|
||||
#include "minui/minui.h"
|
||||
|
||||
#include "minui.h"
|
||||
#include "graphics.h"
|
||||
MinuiBackendFbdev::MinuiBackendFbdev() : gr_draw(nullptr), fb_fd(-1) {}
|
||||
|
||||
static GRSurface* fbdev_init(minui_backend*);
|
||||
static GRSurface* fbdev_flip(minui_backend*);
|
||||
static void fbdev_blank(minui_backend*, bool);
|
||||
static void fbdev_exit(minui_backend*);
|
||||
|
||||
static GRSurface gr_framebuffer[2];
|
||||
static bool double_buffered;
|
||||
static GRSurface* gr_draw = NULL;
|
||||
static int displayed_buffer;
|
||||
|
||||
static fb_var_screeninfo vi;
|
||||
static int fb_fd = -1;
|
||||
|
||||
static minui_backend my_backend = {
|
||||
.init = fbdev_init,
|
||||
.flip = fbdev_flip,
|
||||
.blank = fbdev_blank,
|
||||
.exit = fbdev_exit,
|
||||
};
|
||||
|
||||
minui_backend* open_fbdev() {
|
||||
return &my_backend;
|
||||
}
|
||||
|
||||
static void fbdev_blank(minui_backend* backend __unused, bool blank)
|
||||
{
|
||||
void MinuiBackendFbdev::Blank(bool blank) {
|
||||
#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
|
||||
int fd;
|
||||
char brightness[4];
|
||||
@@ -80,136 +52,133 @@ static void fbdev_blank(minui_backend* backend __unused, bool blank)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void set_displayed_framebuffer(unsigned n)
|
||||
{
|
||||
if (n > 1 || !double_buffered) return;
|
||||
void MinuiBackendFbdev::SetDisplayedFramebuffer(unsigned n) {
|
||||
if (n > 1 || !double_buffered) return;
|
||||
|
||||
vi.yres_virtual = gr_framebuffer[0].height * 2;
|
||||
vi.yoffset = n * gr_framebuffer[0].height;
|
||||
vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
|
||||
if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
|
||||
perror("active fb swap failed");
|
||||
}
|
||||
displayed_buffer = n;
|
||||
vi.yres_virtual = gr_framebuffer[0].height * 2;
|
||||
vi.yoffset = n * gr_framebuffer[0].height;
|
||||
vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
|
||||
if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
|
||||
perror("active fb swap failed");
|
||||
}
|
||||
displayed_buffer = n;
|
||||
}
|
||||
|
||||
static GRSurface* fbdev_init(minui_backend* backend) {
|
||||
int fd = open("/dev/graphics/fb0", O_RDWR);
|
||||
if (fd == -1) {
|
||||
perror("cannot open fb0");
|
||||
return NULL;
|
||||
GRSurface* MinuiBackendFbdev::Init() {
|
||||
int fd = open("/dev/graphics/fb0", O_RDWR);
|
||||
if (fd == -1) {
|
||||
perror("cannot open fb0");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fb_fix_screeninfo fi;
|
||||
if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
|
||||
perror("failed to get fb0 info");
|
||||
close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
|
||||
perror("failed to get fb0 info");
|
||||
close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We print this out for informational purposes only, but
|
||||
// throughout we assume that the framebuffer device uses an RGBX
|
||||
// pixel format. This is the case for every development device I
|
||||
// have access to. For some of those devices (eg, hammerhead aka
|
||||
// Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
|
||||
// different format (XBGR) but actually produces the correct
|
||||
// results on the display when you write RGBX.
|
||||
//
|
||||
// If you have a device that actually *needs* another pixel format
|
||||
// (ie, BGRX, or 565), patches welcome...
|
||||
|
||||
printf(
|
||||
"fb0 reports (possibly inaccurate):\n"
|
||||
" vi.bits_per_pixel = %d\n"
|
||||
" vi.red.offset = %3d .length = %3d\n"
|
||||
" vi.green.offset = %3d .length = %3d\n"
|
||||
" vi.blue.offset = %3d .length = %3d\n",
|
||||
vi.bits_per_pixel, vi.red.offset, vi.red.length, vi.green.offset, vi.green.length,
|
||||
vi.blue.offset, vi.blue.length);
|
||||
|
||||
void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (bits == MAP_FAILED) {
|
||||
perror("failed to mmap framebuffer");
|
||||
close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memset(bits, 0, fi.smem_len);
|
||||
|
||||
gr_framebuffer[0].width = vi.xres;
|
||||
gr_framebuffer[0].height = vi.yres;
|
||||
gr_framebuffer[0].row_bytes = fi.line_length;
|
||||
gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
|
||||
gr_framebuffer[0].data = static_cast<uint8_t*>(bits);
|
||||
memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
|
||||
|
||||
/* check if we can use double buffering */
|
||||
if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
|
||||
double_buffered = true;
|
||||
|
||||
memcpy(gr_framebuffer + 1, gr_framebuffer, sizeof(GRSurface));
|
||||
gr_framebuffer[1].data =
|
||||
gr_framebuffer[0].data + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;
|
||||
|
||||
gr_draw = gr_framebuffer + 1;
|
||||
|
||||
} else {
|
||||
double_buffered = false;
|
||||
|
||||
// Without double-buffering, we allocate RAM for a buffer to
|
||||
// draw in, and then "flipping" the buffer consists of a
|
||||
// memcpy from the buffer we allocated to the framebuffer.
|
||||
|
||||
gr_draw = static_cast<GRSurface*>(malloc(sizeof(GRSurface)));
|
||||
memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
|
||||
gr_draw->data = static_cast<unsigned char*>(malloc(gr_draw->height * gr_draw->row_bytes));
|
||||
if (!gr_draw->data) {
|
||||
perror("failed to allocate in-memory surface");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
fb_fix_screeninfo fi;
|
||||
if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
|
||||
perror("failed to get fb0 info");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
|
||||
fb_fd = fd;
|
||||
SetDisplayedFramebuffer(0);
|
||||
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
|
||||
perror("failed to get fb0 info");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
|
||||
|
||||
// We print this out for informational purposes only, but
|
||||
// throughout we assume that the framebuffer device uses an RGBX
|
||||
// pixel format. This is the case for every development device I
|
||||
// have access to. For some of those devices (eg, hammerhead aka
|
||||
// Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a
|
||||
// different format (XBGR) but actually produces the correct
|
||||
// results on the display when you write RGBX.
|
||||
//
|
||||
// If you have a device that actually *needs* another pixel format
|
||||
// (ie, BGRX, or 565), patches welcome...
|
||||
Blank(true);
|
||||
Blank(false);
|
||||
|
||||
printf("fb0 reports (possibly inaccurate):\n"
|
||||
" vi.bits_per_pixel = %d\n"
|
||||
" vi.red.offset = %3d .length = %3d\n"
|
||||
" vi.green.offset = %3d .length = %3d\n"
|
||||
" vi.blue.offset = %3d .length = %3d\n",
|
||||
vi.bits_per_pixel,
|
||||
vi.red.offset, vi.red.length,
|
||||
vi.green.offset, vi.green.length,
|
||||
vi.blue.offset, vi.blue.length);
|
||||
|
||||
void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (bits == MAP_FAILED) {
|
||||
perror("failed to mmap framebuffer");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(bits, 0, fi.smem_len);
|
||||
|
||||
gr_framebuffer[0].width = vi.xres;
|
||||
gr_framebuffer[0].height = vi.yres;
|
||||
gr_framebuffer[0].row_bytes = fi.line_length;
|
||||
gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
|
||||
gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits);
|
||||
memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
|
||||
|
||||
/* check if we can use double buffering */
|
||||
if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
|
||||
double_buffered = true;
|
||||
|
||||
memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface));
|
||||
gr_framebuffer[1].data = gr_framebuffer[0].data +
|
||||
gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;
|
||||
|
||||
gr_draw = gr_framebuffer+1;
|
||||
|
||||
} else {
|
||||
double_buffered = false;
|
||||
|
||||
// Without double-buffering, we allocate RAM for a buffer to
|
||||
// draw in, and then "flipping" the buffer consists of a
|
||||
// memcpy from the buffer we allocated to the framebuffer.
|
||||
|
||||
gr_draw = (GRSurface*) malloc(sizeof(GRSurface));
|
||||
memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface));
|
||||
gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes);
|
||||
if (!gr_draw->data) {
|
||||
perror("failed to allocate in-memory surface");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
|
||||
fb_fd = fd;
|
||||
set_displayed_framebuffer(0);
|
||||
|
||||
printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
|
||||
|
||||
fbdev_blank(backend, true);
|
||||
fbdev_blank(backend, false);
|
||||
|
||||
return gr_draw;
|
||||
return gr_draw;
|
||||
}
|
||||
|
||||
static GRSurface* fbdev_flip(minui_backend* backend __unused) {
|
||||
if (double_buffered) {
|
||||
// Change gr_draw to point to the buffer currently displayed,
|
||||
// then flip the driver so we're displaying the other buffer
|
||||
// instead.
|
||||
gr_draw = gr_framebuffer + displayed_buffer;
|
||||
set_displayed_framebuffer(1-displayed_buffer);
|
||||
} else {
|
||||
// Copy from the in-memory surface to the framebuffer.
|
||||
memcpy(gr_framebuffer[0].data, gr_draw->data,
|
||||
gr_draw->height * gr_draw->row_bytes);
|
||||
}
|
||||
return gr_draw;
|
||||
GRSurface* MinuiBackendFbdev::Flip() {
|
||||
if (double_buffered) {
|
||||
// Change gr_draw to point to the buffer currently displayed,
|
||||
// then flip the driver so we're displaying the other buffer
|
||||
// instead.
|
||||
gr_draw = gr_framebuffer + displayed_buffer;
|
||||
SetDisplayedFramebuffer(1 - displayed_buffer);
|
||||
} else {
|
||||
// Copy from the in-memory surface to the framebuffer.
|
||||
memcpy(gr_framebuffer[0].data, gr_draw->data, gr_draw->height * gr_draw->row_bytes);
|
||||
}
|
||||
return gr_draw;
|
||||
}
|
||||
|
||||
static void fbdev_exit(minui_backend* backend __unused) {
|
||||
close(fb_fd);
|
||||
fb_fd = -1;
|
||||
MinuiBackendFbdev::~MinuiBackendFbdev() {
|
||||
close(fb_fd);
|
||||
fb_fd = -1;
|
||||
|
||||
if (!double_buffered && gr_draw) {
|
||||
free(gr_draw->data);
|
||||
free(gr_draw);
|
||||
}
|
||||
gr_draw = NULL;
|
||||
if (!double_buffered && gr_draw) {
|
||||
free(gr_draw->data);
|
||||
free(gr_draw);
|
||||
}
|
||||
gr_draw = nullptr;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 _GRAPHICS_FBDEV_H_
|
||||
#define _GRAPHICS_FBDEV_H_
|
||||
|
||||
#include <linux/fb.h>
|
||||
|
||||
#include "graphics.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
class MinuiBackendFbdev : public MinuiBackend {
|
||||
public:
|
||||
GRSurface* Init() override;
|
||||
GRSurface* Flip() override;
|
||||
void Blank(bool) override;
|
||||
~MinuiBackendFbdev() override;
|
||||
MinuiBackendFbdev();
|
||||
|
||||
private:
|
||||
void SetDisplayedFramebuffer(unsigned n);
|
||||
|
||||
GRSurface gr_framebuffer[2];
|
||||
bool double_buffered;
|
||||
GRSurface* gr_draw;
|
||||
int displayed_buffer;
|
||||
fb_var_screeninfo vi;
|
||||
int fb_fd;
|
||||
};
|
||||
|
||||
#endif // _GRAPHICS_FBDEV_H_
|
||||
+60
-92
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "graphics_overlay.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -34,50 +36,28 @@
|
||||
#ifdef MSM_BSP
|
||||
#include <linux/msm_mdp.h>
|
||||
#include <linux/msm_ion.h>
|
||||
#else
|
||||
#define MSMFB_NEW_REQUEST 0
|
||||
#endif
|
||||
|
||||
#include "minui.h"
|
||||
#include "graphics.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
#define MDP_V4_0 400
|
||||
#define MAX_DISPLAY_DIM 2048
|
||||
|
||||
static GRSurface* overlay_init(minui_backend*);
|
||||
static GRSurface* overlay_flip(minui_backend*);
|
||||
static void overlay_blank(minui_backend*, bool);
|
||||
static void overlay_exit(minui_backend*);
|
||||
|
||||
static GRSurface gr_framebuffer[2];
|
||||
static bool double_buffered;
|
||||
static GRSurface* gr_draw = NULL;
|
||||
static int displayed_buffer;
|
||||
|
||||
static fb_var_screeninfo vi;
|
||||
static int fb_fd = -1;
|
||||
static bool isMDP5 = false;
|
||||
static int leftSplit = 0;
|
||||
static int rightSplit = 0;
|
||||
#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1))
|
||||
|
||||
static size_t frame_size = 0;
|
||||
MinuiBackendOverlay::MinuiBackendOverlay() :
|
||||
gr_draw(nullptr),
|
||||
fb_fd(-1),
|
||||
isMDP5(false),
|
||||
leftSplit(0),
|
||||
rightSplit(0),
|
||||
frame_size(0),
|
||||
overlayL_id(MSMFB_NEW_REQUEST),
|
||||
overlayR_id(MSMFB_NEW_REQUEST) {}
|
||||
|
||||
#ifdef MSM_BSP
|
||||
typedef struct {
|
||||
unsigned char *mem_buf;
|
||||
int size;
|
||||
int ion_fd;
|
||||
int mem_fd;
|
||||
struct ion_handle_data handle_data;
|
||||
} memInfo;
|
||||
|
||||
//Left and right overlay id
|
||||
static int overlayL_id = MSMFB_NEW_REQUEST;
|
||||
static int overlayR_id = MSMFB_NEW_REQUEST;
|
||||
|
||||
static memInfo mem_info;
|
||||
#
|
||||
|
||||
static int map_mdp_pixel_format()
|
||||
int MinuiBackendOverlay::map_mdp_pixel_format()
|
||||
{
|
||||
int format = MDP_RGB_565;
|
||||
#if defined(RECOVERY_BGRA)
|
||||
@@ -91,66 +71,49 @@ static int map_mdp_pixel_format()
|
||||
}
|
||||
#endif // MSM_BSP
|
||||
|
||||
static minui_backend my_backend = {
|
||||
.init = overlay_init,
|
||||
.flip = overlay_flip,
|
||||
.blank = overlay_blank,
|
||||
.exit = overlay_exit,
|
||||
};
|
||||
|
||||
bool target_has_overlay(char *version)
|
||||
bool MinuiBackendOverlay::target_has_overlay()
|
||||
{
|
||||
int ret;
|
||||
int mdp_version;
|
||||
bool overlay_supported = false;
|
||||
|
||||
if (strlen(version) >= 8) {
|
||||
if(!strncmp(version, "msmfb", strlen("msmfb"))) {
|
||||
char str_ver[4];
|
||||
memcpy(str_ver, version + strlen("msmfb"), 3);
|
||||
str_ver[3] = '\0';
|
||||
mdp_version = atoi(str_ver);
|
||||
if (mdp_version >= MDP_V4_0) {
|
||||
overlay_supported = true;
|
||||
}
|
||||
} else if (!strncmp(version, "mdssfb", strlen("mdssfb"))) {
|
||||
overlay_supported = true;
|
||||
isMDP5 = true;
|
||||
}
|
||||
}
|
||||
|
||||
return overlay_supported;
|
||||
}
|
||||
|
||||
minui_backend* open_overlay() {
|
||||
fb_fix_screeninfo fi;
|
||||
int fd;
|
||||
|
||||
fd = open("/dev/graphics/fb0", O_RDWR);
|
||||
if (fd < 0) {
|
||||
perror("open_overlay cannot open fb0");
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
|
||||
perror("failed to get fb0 info");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (target_has_overlay(fi.id)) {
|
||||
#ifdef MSM_BSP
|
||||
close(fd);
|
||||
return &my_backend;
|
||||
#else
|
||||
printf("Overlay graphics may work (%s), but not enabled. Use TW_TARGET_USES_QCOM_BSP := true to enable.\n", fi.id);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return NULL;
|
||||
|
||||
if (strlen(fi.id) >= 8) {
|
||||
if(!strncmp(fi.id, "msmfb", strlen("msmfb"))) {
|
||||
char str_ver[4];
|
||||
memcpy(str_ver, fi.id + strlen("msmfb"), 3);
|
||||
str_ver[3] = '\0';
|
||||
mdp_version = atoi(str_ver);
|
||||
if (mdp_version >= MDP_V4_0) {
|
||||
overlay_supported = true;
|
||||
}
|
||||
} else if (!strncmp(fi.id, "mdssfb", strlen("mdssfb"))) {
|
||||
overlay_supported = true;
|
||||
isMDP5 = true;
|
||||
}
|
||||
}
|
||||
#ifndef MSM_BSP
|
||||
if (overlay_supported)
|
||||
printf("Overlay graphics may work (%s), but not enabled. Use TW_TARGET_USES_QCOM_BSP := true to enable.\n", fi.id);
|
||||
#endif
|
||||
return overlay_supported;
|
||||
}
|
||||
|
||||
static void overlay_blank(minui_backend* backend __unused, bool blank)
|
||||
void MinuiBackendOverlay::Blank(bool blank)
|
||||
{
|
||||
#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
|
||||
int fd;
|
||||
@@ -173,7 +136,7 @@ static void overlay_blank(minui_backend* backend __unused, bool blank)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void set_displayed_framebuffer(unsigned n)
|
||||
void MinuiBackendOverlay::SetDisplayedFramebuffer(unsigned n)
|
||||
{
|
||||
if (n > 1 || !double_buffered) return;
|
||||
|
||||
@@ -187,7 +150,7 @@ static void set_displayed_framebuffer(unsigned n)
|
||||
}
|
||||
|
||||
#ifdef MSM_BSP
|
||||
void setDisplaySplit(void) {
|
||||
void MinuiBackendOverlay::setDisplaySplit(void) {
|
||||
char split[64] = {0};
|
||||
if (!isMDP5)
|
||||
return;
|
||||
@@ -209,7 +172,7 @@ void setDisplaySplit(void) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
int getLeftSplit(void) {
|
||||
int MinuiBackendOverlay::getLeftSplit(void) {
|
||||
//Default even split for all displays with high res
|
||||
int lSplit = vi.xres / 2;
|
||||
|
||||
@@ -220,11 +183,11 @@ int getLeftSplit(void) {
|
||||
return lSplit;
|
||||
}
|
||||
|
||||
int getRightSplit(void) {
|
||||
int MinuiBackendOverlay::getRightSplit(void) {
|
||||
return rightSplit;
|
||||
}
|
||||
|
||||
int free_ion_mem(void) {
|
||||
int MinuiBackendOverlay::free_ion_mem(void) {
|
||||
int ret = 0;
|
||||
|
||||
if (mem_info.mem_buf)
|
||||
@@ -247,7 +210,7 @@ int free_ion_mem(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int alloc_ion_mem(unsigned int size)
|
||||
int MinuiBackendOverlay::alloc_ion_mem(unsigned int size)
|
||||
{
|
||||
int result;
|
||||
struct ion_fd_data fd_data;
|
||||
@@ -298,7 +261,7 @@ int alloc_ion_mem(unsigned int size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isDisplaySplit(void) {
|
||||
bool MinuiBackendOverlay::isDisplaySplit(void) {
|
||||
if (vi.xres > MAX_DISPLAY_DIM)
|
||||
return true;
|
||||
//check if right split is set by driver
|
||||
@@ -308,7 +271,7 @@ bool isDisplaySplit(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int allocate_overlay(int fd, GRSurface gr_fb[])
|
||||
int MinuiBackendOverlay::allocate_overlay(int fd, GRSurface gr_fb[])
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@@ -407,7 +370,7 @@ int allocate_overlay(int fd, GRSurface gr_fb[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
int overlay_display_frame(int fd, void* data, size_t size)
|
||||
int MinuiBackendOverlay::overlay_display_frame(int fd, void* data, size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
struct msmfb_overlay_data ovdataL, ovdataR;
|
||||
@@ -480,7 +443,7 @@ int overlay_display_frame(int fd, void* data, size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GRSurface* overlay_flip(minui_backend* backend __unused) {
|
||||
GRSurface* MinuiBackendOverlay::Flip() {
|
||||
if (double_buffered) {
|
||||
#if defined(RECOVERY_BGRA)
|
||||
// In case of BGRA, do some byte swapping
|
||||
@@ -507,7 +470,7 @@ static GRSurface* overlay_flip(minui_backend* backend __unused) {
|
||||
return gr_draw;
|
||||
}
|
||||
|
||||
int free_overlay(int fd)
|
||||
int MinuiBackendOverlay::free_overlay(int fd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mdp_display_commit ext_commit;
|
||||
@@ -557,7 +520,10 @@ int free_overlay(int fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static GRSurface* overlay_init(minui_backend* backend) {
|
||||
GRSurface* MinuiBackendOverlay::Init() {
|
||||
if (!target_has_overlay())
|
||||
return NULL;
|
||||
|
||||
int fd = open("/dev/graphics/fb0", O_RDWR);
|
||||
if (fd == -1) {
|
||||
perror("cannot open fb0");
|
||||
@@ -662,7 +628,7 @@ static GRSurface* overlay_init(minui_backend* backend) {
|
||||
return gr_draw;
|
||||
}
|
||||
|
||||
static void overlay_exit(minui_backend* backend __unused) {
|
||||
MinuiBackendOverlay::~MinuiBackendOverlay() {
|
||||
free_overlay(fb_fd);
|
||||
free_ion_mem();
|
||||
|
||||
@@ -680,15 +646,17 @@ static void overlay_exit(minui_backend* backend __unused) {
|
||||
}
|
||||
}
|
||||
#else // MSM_BSP
|
||||
static GRSurface* overlay_flip(minui_backend* backend __unused) {
|
||||
|
||||
GRSurface* MinuiBackendOverlay::Flip() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GRSurface* overlay_init(minui_backend* backend __unused) {
|
||||
GRSurface* MinuiBackendOverlay::Init() {
|
||||
target_has_overlay(); // Don't care about return value, just for logging
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void overlay_exit(minui_backend* backend __unused) {
|
||||
MinuiBackendOverlay::~MinuiBackendOverlay() {
|
||||
return;
|
||||
}
|
||||
#endif // MSM_BSP
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 _GRAPHICS_OVERLAY_H_
|
||||
#define _GRAPHICS_OVERLAY_H_
|
||||
|
||||
#include <linux/fb.h>
|
||||
|
||||
#include "graphics.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
#ifdef MSM_BSP
|
||||
typedef struct {
|
||||
unsigned char *mem_buf;
|
||||
int size;
|
||||
int ion_fd;
|
||||
int mem_fd;
|
||||
struct ion_handle_data handle_data;
|
||||
} memInfo;
|
||||
#endif
|
||||
|
||||
class MinuiBackendOverlay : public MinuiBackend {
|
||||
public:
|
||||
GRSurface* Init() override;
|
||||
GRSurface* Flip() override;
|
||||
void Blank(bool) override;
|
||||
~MinuiBackendOverlay() override;
|
||||
MinuiBackendOverlay();
|
||||
|
||||
private:
|
||||
void SetDisplayedFramebuffer(unsigned n);
|
||||
bool target_has_overlay();
|
||||
|
||||
#ifdef MSM_BSP
|
||||
int map_mdp_pixel_format();
|
||||
void setDisplaySplit(void);
|
||||
int getLeftSplit(void);
|
||||
int getRightSplit(void);
|
||||
int free_ion_mem(void);
|
||||
int alloc_ion_mem(unsigned int size);
|
||||
bool isDisplaySplit(void);
|
||||
int allocate_overlay(int fd, GRSurface gr_fb[]);
|
||||
int overlay_display_frame(int fd, void* data, size_t size);
|
||||
int free_overlay(int fd);
|
||||
#endif
|
||||
|
||||
GRSurface gr_framebuffer[2];
|
||||
bool double_buffered;
|
||||
GRSurface* gr_draw;
|
||||
int displayed_buffer;
|
||||
fb_var_screeninfo vi;
|
||||
int fb_fd;
|
||||
bool isMDP5;
|
||||
int leftSplit;
|
||||
int rightSplit;
|
||||
size_t frame_size;
|
||||
int overlayL_id;
|
||||
int overlayR_id;
|
||||
};
|
||||
|
||||
#endif // _GRAPHICS_OVERLAY_H_
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 _MINUI_H_
|
||||
#define _MINUI_H_
|
||||
|
||||
#ifndef TW_USE_MINUI_21
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
//
|
||||
// Graphics.
|
||||
//
|
||||
|
||||
struct GRSurface {
|
||||
int width;
|
||||
int height;
|
||||
int row_bytes;
|
||||
int pixel_bytes;
|
||||
unsigned char* data;
|
||||
};
|
||||
|
||||
struct GRFont {
|
||||
GRSurface* texture;
|
||||
int char_width;
|
||||
int char_height;
|
||||
};
|
||||
|
||||
int gr_init();
|
||||
void gr_exit();
|
||||
|
||||
int gr_fb_width();
|
||||
int gr_fb_height();
|
||||
|
||||
void gr_flip();
|
||||
void gr_fb_blank(bool blank);
|
||||
|
||||
void gr_clear(); // clear entire surface to current color
|
||||
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||
void gr_fill(int x1, int y1, int x2, int y2);
|
||||
|
||||
void gr_texticon(int x, int y, GRSurface* icon);
|
||||
#ifdef TW_NO_MINUI_CUSTOM_FONTS
|
||||
void gr_text(int x, int y, const char *s, bool bold);
|
||||
int gr_measure(const char *s);
|
||||
void gr_font_size(int *x, int *y);
|
||||
void gr_set_font(__attribute__ ((unused))const char* name);
|
||||
#else
|
||||
|
||||
const GRFont* gr_sys_font();
|
||||
int gr_init_font(const char* name, GRFont** dest);
|
||||
void gr_text(const GRFont* font, int x, int y, const char *s, bool bold);
|
||||
int gr_measure(const GRFont* font, const char *s);
|
||||
void gr_font_size(const GRFont* font, int *x, int *y);
|
||||
#endif
|
||||
|
||||
void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy);
|
||||
unsigned int gr_get_width(GRSurface* surface);
|
||||
unsigned int gr_get_height(GRSurface* surface);
|
||||
|
||||
//
|
||||
// Input events.
|
||||
//
|
||||
|
||||
struct input_event;
|
||||
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
typedef int (*ev_callback)(int fd, uint32_t epevents, void* data);
|
||||
typedef int (*ev_set_key_callback)(int code, int value, void* data);
|
||||
|
||||
int ev_init(ev_callback input_cb, void* data);
|
||||
int ev_add_fd(int fd, ev_callback cb, void* data);
|
||||
int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
|
||||
#else
|
||||
using ev_callback = std::function<int(int fd, uint32_t epevents)>;
|
||||
using ev_set_key_callback = std::function<int(int code, int value)>;
|
||||
|
||||
int ev_init(ev_callback input_cb);
|
||||
int ev_add_fd(int fd, ev_callback cb);
|
||||
int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
|
||||
#endif
|
||||
void ev_exit();
|
||||
void ev_iterate_available_keys(const std::function<void(int)>& f);
|
||||
|
||||
// 'timeout' has the same semantics as poll(2).
|
||||
// 0 : don't block
|
||||
// < 0 : block forever
|
||||
// > 0 : block for 'timeout' milliseconds
|
||||
int ev_wait(int timeout);
|
||||
|
||||
int ev_get_input(int fd, uint32_t epevents, input_event* ev);
|
||||
void ev_dispatch();
|
||||
int ev_get_epollfd();
|
||||
|
||||
//
|
||||
// Resources
|
||||
//
|
||||
|
||||
bool matches_locale(const std::string& prefix, const std::string& locale);
|
||||
|
||||
// res_create_*_surface() functions return 0 if no error, else
|
||||
// negative.
|
||||
//
|
||||
// A "display" surface is one that is intended to be drawn to the
|
||||
// screen with gr_blit(). An "alpha" surface is a grayscale image
|
||||
// interpreted as an alpha mask used to render text in the current
|
||||
// color (with gr_text() or gr_texticon()).
|
||||
//
|
||||
// All these functions load PNG images from "/res/images/${name}.png".
|
||||
|
||||
// Load a single display surface from a PNG image.
|
||||
int res_create_display_surface(const char* name, GRSurface** pSurface);
|
||||
|
||||
// Load an array of display surfaces from a single PNG image. The PNG
|
||||
// should have a 'Frames' text chunk whose value is the number of
|
||||
// frames this image represents. The pixel data itself is interlaced
|
||||
// by row.
|
||||
int res_create_multi_display_surface(const char* name, int* frames,
|
||||
int* fps, GRSurface*** pSurface);
|
||||
int res_create_multi_display_surface(const char* name, int* frames,
|
||||
GRSurface*** pSurface);
|
||||
|
||||
// Load a single alpha surface from a grayscale PNG image.
|
||||
int res_create_alpha_surface(const char* name, GRSurface** pSurface);
|
||||
|
||||
// Load part of a grayscale PNG image that is the first match for the
|
||||
// given locale. The image is expected to be a composite of multiple
|
||||
// translations of the same text, with special added rows that encode
|
||||
// the subimages' size and intended locale in the pixel data. See
|
||||
// bootable/recovery/tools/recovery_l10n for an app that will generate
|
||||
// these specialized images from Android resources.
|
||||
int res_create_localized_alpha_surface(const char* name, const char* locale,
|
||||
GRSurface** pSurface);
|
||||
|
||||
// Free a surface allocated by any of the res_create_*_surface()
|
||||
// functions.
|
||||
void res_free_surface(GRSurface* surface);
|
||||
|
||||
#else //ifndef TW_USE_MINUI_21
|
||||
|
||||
// This the old minui21/minui.h for compatibility with building TWRP
|
||||
// in pre 6.0 trees.
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void* gr_surface;
|
||||
typedef unsigned short gr_pixel;
|
||||
|
||||
int gr_init(void);
|
||||
void gr_exit(void);
|
||||
|
||||
int gr_fb_width(void);
|
||||
int gr_fb_height(void);
|
||||
gr_pixel *gr_fb_data(void);
|
||||
void gr_flip(void);
|
||||
void gr_fb_blank(bool blank);
|
||||
|
||||
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||
void gr_fill(int x1, int y1, int x2, int y2);
|
||||
|
||||
// system/core/charger uses different gr_print signatures in diferent
|
||||
// Android versions, either with or without int bold.
|
||||
int gr_text(int x, int y, const char *s, ...);
|
||||
int gr_text_impl(int x, int y, const char *s, int bold);
|
||||
|
||||
void gr_texticon(int x, int y, gr_surface icon);
|
||||
int gr_measure(const char *s);
|
||||
void gr_font_size(int *x, int *y);
|
||||
void gr_get_memory_surface(gr_surface);
|
||||
|
||||
void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);
|
||||
unsigned int gr_get_width(gr_surface surface);
|
||||
unsigned int gr_get_height(gr_surface surface);
|
||||
|
||||
// input event structure, include <linux/input.h> for the definition.
|
||||
// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
|
||||
struct input_event;
|
||||
|
||||
typedef int (*ev_callback)(int fd, uint32_t epevents, void *data);
|
||||
typedef int (*ev_set_key_callback)(int code, int value, void *data);
|
||||
|
||||
int ev_init(ev_callback input_cb, void *data);
|
||||
void ev_exit(void);
|
||||
int ev_add_fd(int fd, ev_callback cb, void *data);
|
||||
int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data);
|
||||
|
||||
/* timeout has the same semantics as for poll
|
||||
* 0 : don't block
|
||||
* < 0 : block forever
|
||||
* > 0 : block for 'timeout' milliseconds
|
||||
*/
|
||||
int ev_wait(int timeout);
|
||||
|
||||
int ev_get_input(int fd, uint32_t epevents, struct input_event *ev);
|
||||
void ev_dispatch(void);
|
||||
int ev_get_epollfd(void);
|
||||
|
||||
// Resources
|
||||
|
||||
// Returns 0 if no error, else negative.
|
||||
int res_create_surface(const char* name, gr_surface* pSurface);
|
||||
|
||||
// Load an array of display surfaces from a single PNG image. The PNG
|
||||
// should have a 'Frames' text chunk whose value is the number of
|
||||
// frames this image represents. The pixel data itself is interlaced
|
||||
// by row.
|
||||
int res_create_multi_display_surface(const char* name,
|
||||
int* frames, gr_surface** pSurface);
|
||||
|
||||
int res_create_localized_surface(const char* name, gr_surface* pSurface);
|
||||
void res_free_surface(gr_surface surface);
|
||||
static inline int res_create_display_surface(const char* name, gr_surface* pSurface) {
|
||||
return res_create_surface(name, pSurface);
|
||||
}
|
||||
|
||||
// These are new graphics functions from 5.0 that were not available in
|
||||
// 4.4 that are required by charger and healthd
|
||||
void gr_clear();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ifndef TW_USE_MINUI_21
|
||||
#endif // ifndef _MINUI_H_
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "minui.h"
|
||||
#include <minui/minui.h>
|
||||
#include "graphics.h"
|
||||
|
||||
int main() {
|
||||
|
||||
+15
-6
@@ -22,6 +22,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
//
|
||||
// Graphics.
|
||||
@@ -79,15 +80,23 @@ unsigned int gr_get_height(GRSurface* surface);
|
||||
|
||||
struct input_event;
|
||||
|
||||
// TODO: move these over to std::function.
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
typedef int (*ev_callback)(int fd, uint32_t epevents, void* data);
|
||||
typedef int (*ev_set_key_callback)(int code, int value, void* data);
|
||||
|
||||
int ev_init(ev_callback input_cb, void* data);
|
||||
void ev_exit();
|
||||
int ev_add_fd(int fd, ev_callback cb, void* data);
|
||||
void ev_iterate_available_keys(std::function<void(int)> f);
|
||||
int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
|
||||
#else
|
||||
using ev_callback = std::function<int(int fd, uint32_t epevents)>;
|
||||
using ev_set_key_callback = std::function<int(int code, int value)>;
|
||||
|
||||
int ev_init(ev_callback input_cb);
|
||||
int ev_add_fd(int fd, ev_callback cb);
|
||||
int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
|
||||
#endif
|
||||
void ev_exit();
|
||||
void ev_iterate_available_keys(const std::function<void(int)>& f);
|
||||
|
||||
// 'timeout' has the same semantics as poll(2).
|
||||
// 0 : don't block
|
||||
@@ -103,7 +112,7 @@ int ev_get_epollfd();
|
||||
// Resources
|
||||
//
|
||||
|
||||
bool matches_locale(const char* prefix, const char* locale);
|
||||
bool matches_locale(const std::string& prefix, const std::string& locale);
|
||||
|
||||
// res_create_*_surface() functions return 0 if no error, else
|
||||
// negative.
|
||||
@@ -134,8 +143,8 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface);
|
||||
// given locale. The image is expected to be a composite of multiple
|
||||
// translations of the same text, with special added rows that encode
|
||||
// the subimages' size and intended locale in the pixel data. See
|
||||
// development/tools/recovery_l10n for an app that will generate these
|
||||
// specialized images from Android resources.
|
||||
// bootable/recovery/tools/recovery_l10n for an app that will generate
|
||||
// these specialized images from Android resources.
|
||||
int res_create_localized_alpha_surface(const char* name, const char* locale,
|
||||
GRSurface** pSurface);
|
||||
|
||||
|
||||
+45
-35
@@ -14,29 +14,31 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/kd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/kd.h>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
//#include <android-base/strings.h> // does not exist in 6.0
|
||||
#include <png.h>
|
||||
|
||||
#include "minui.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
#define SURFACE_DATA_ALIGNMENT 8
|
||||
|
||||
static GRSurface* malloc_surface(size_t data_size) {
|
||||
size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT;
|
||||
unsigned char* temp = reinterpret_cast<unsigned char*>(malloc(size));
|
||||
unsigned char* temp = static_cast<unsigned char*>(malloc(size));
|
||||
if (temp == NULL) return NULL;
|
||||
GRSurface* surface = reinterpret_cast<GRSurface*>(temp);
|
||||
surface->data = temp + sizeof(GRSurface) +
|
||||
@@ -220,7 +222,7 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) {
|
||||
png_set_bgr(png_ptr);
|
||||
#endif
|
||||
|
||||
p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
|
||||
p_row = static_cast<unsigned char*>(malloc(width * 4));
|
||||
for (y = 0; y < height; ++y) {
|
||||
png_read_row(png_ptr, p_row, NULL);
|
||||
transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width);
|
||||
@@ -280,7 +282,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
surface = reinterpret_cast<GRSurface**>(malloc(*frames * sizeof(GRSurface*)));
|
||||
surface = static_cast<GRSurface**>(calloc(*frames, sizeof(GRSurface*)));
|
||||
if (surface == NULL) {
|
||||
result = -8;
|
||||
goto exit;
|
||||
@@ -297,7 +299,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps,
|
||||
png_set_bgr(png_ptr);
|
||||
#endif
|
||||
|
||||
p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
|
||||
p_row = static_cast<unsigned char*>(malloc(width * 4));
|
||||
for (y = 0; y < height; ++y) {
|
||||
png_read_row(png_ptr, p_row, NULL);
|
||||
int frame = y % *frames;
|
||||
@@ -307,7 +309,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps,
|
||||
}
|
||||
free(p_row);
|
||||
|
||||
*pSurface = reinterpret_cast<GRSurface**>(surface);
|
||||
*pSurface = surface;
|
||||
|
||||
exit:
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
@@ -315,7 +317,7 @@ exit:
|
||||
if (result < 0) {
|
||||
if (surface) {
|
||||
for (int i = 0; i < *frames; ++i) {
|
||||
if (surface[i]) free(surface[i]);
|
||||
free(surface[i]);
|
||||
}
|
||||
free(surface);
|
||||
}
|
||||
@@ -378,14 +380,27 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) {
|
||||
|
||||
// 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;
|
||||
bool matches_locale(const std::string& prefix, const std::string& locale) {
|
||||
// According to the BCP 47 format, A locale string may consists of:
|
||||
// language-{extlang}-{script}-{region}-{variant}
|
||||
// The locale headers in PNG mostly consist of language-{region} except for sr-Latn, and some
|
||||
// android's system locale can have the format language-{script}-{region}.
|
||||
|
||||
// Return true if the whole string of prefix matches the top part of
|
||||
// locale. For instance, prefix == "en" matches locale == "en_US";
|
||||
// and prefix == "zh_CN" matches locale == "zh_CN_#Hans".
|
||||
// Return true if the whole string of prefix matches the top part of locale. Otherwise try to
|
||||
// match the locale string without the {script} section.
|
||||
// For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale
|
||||
// == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN".
|
||||
//if (android::base::StartsWith(locale, prefix.c_str())) { // does not exist in 6.0
|
||||
if (strncmp(prefix.c_str(), locale.c_str(), prefix.length()) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (strncmp(prefix, locale, strlen(prefix)) == 0);
|
||||
size_t separator = prefix.find('-');
|
||||
if (separator == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
std::regex loc_regex(prefix.substr(0, separator) + "-[A-Za-z]*" + prefix.substr(separator));
|
||||
return std::regex_match(locale, loc_regex);
|
||||
}
|
||||
|
||||
int res_create_localized_alpha_surface(const char* name,
|
||||
@@ -397,18 +412,13 @@ int res_create_localized_alpha_surface(const char* name,
|
||||
png_infop info_ptr = NULL;
|
||||
png_uint_32 width, height;
|
||||
png_byte channels;
|
||||
unsigned char* row;
|
||||
png_uint_32 y;
|
||||
std::vector<unsigned char> row;
|
||||
|
||||
*pSurface = NULL;
|
||||
|
||||
if (locale == NULL) {
|
||||
surface = malloc_surface(0);
|
||||
surface->width = 0;
|
||||
surface->height = 0;
|
||||
surface->row_bytes = 0;
|
||||
surface->pixel_bytes = 1;
|
||||
goto exit;
|
||||
return result;
|
||||
}
|
||||
|
||||
result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
|
||||
@@ -419,13 +429,13 @@ int res_create_localized_alpha_surface(const char* name,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
row = reinterpret_cast<unsigned char*>(malloc(width));
|
||||
row.resize(width);
|
||||
for (y = 0; y < height; ++y) {
|
||||
png_read_row(png_ptr, row, NULL);
|
||||
png_read_row(png_ptr, row.data(), NULL);
|
||||
int w = (row[1] << 8) | row[0];
|
||||
int h = (row[3] << 8) | row[2];
|
||||
int len = row[4];
|
||||
char* loc = (char*)row+5;
|
||||
__unused int len = row[4];
|
||||
char* loc = reinterpret_cast<char*>(&row[5]);
|
||||
|
||||
if (y+1+h >= height || matches_locale(loc, locale)) {
|
||||
printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
|
||||
@@ -442,16 +452,16 @@ int res_create_localized_alpha_surface(const char* name,
|
||||
|
||||
int i;
|
||||
for (i = 0; i < h; ++i, ++y) {
|
||||
png_read_row(png_ptr, row, NULL);
|
||||
memcpy(surface->data + i*w, row, w);
|
||||
png_read_row(png_ptr, row.data(), NULL);
|
||||
memcpy(surface->data + i*w, row.data(), w);
|
||||
}
|
||||
|
||||
*pSurface = reinterpret_cast<GRSurface*>(surface);
|
||||
*pSurface = surface;
|
||||
break;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < h; ++i, ++y) {
|
||||
png_read_row(png_ptr, row, NULL);
|
||||
png_read_row(png_ptr, row.data(), NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +180,9 @@ ifneq ($(TW_INCLUDE_JPEG),)
|
||||
LOCAL_SHARED_LIBRARIES += libjpeg
|
||||
endif
|
||||
LOCAL_STATIC_LIBRARIES += libpixelflinger_twrp
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 25; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libcutils liblog libutils
|
||||
endif
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE := libminuitwrp
|
||||
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 "mounts.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <mntent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct MountedVolume {
|
||||
std::string device;
|
||||
std::string mount_point;
|
||||
std::string filesystem;
|
||||
std::string flags;
|
||||
};
|
||||
|
||||
std::vector<MountedVolume*> g_mounts_state;
|
||||
|
||||
bool scan_mounted_volumes() {
|
||||
for (size_t i = 0; i < g_mounts_state.size(); ++i) {
|
||||
delete g_mounts_state[i];
|
||||
}
|
||||
g_mounts_state.clear();
|
||||
|
||||
// Open and read mount table entries.
|
||||
FILE* fp = setmntent("/proc/mounts", "re");
|
||||
if (fp == NULL) {
|
||||
return false;
|
||||
}
|
||||
mntent* e;
|
||||
while ((e = getmntent(fp)) != NULL) {
|
||||
MountedVolume* v = new MountedVolume;
|
||||
v->device = e->mnt_fsname;
|
||||
v->mount_point = e->mnt_dir;
|
||||
v->filesystem = e->mnt_type;
|
||||
v->flags = e->mnt_opts;
|
||||
g_mounts_state.push_back(v);
|
||||
}
|
||||
endmntent(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
MountedVolume* find_mounted_volume_by_device(const char* device) {
|
||||
for (size_t i = 0; i < g_mounts_state.size(); ++i) {
|
||||
if (g_mounts_state[i]->device == device) return g_mounts_state[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) {
|
||||
for (size_t i = 0; i < g_mounts_state.size(); ++i) {
|
||||
if (g_mounts_state[i]->mount_point == mount_point) return g_mounts_state[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int unmount_mounted_volume(MountedVolume* volume) {
|
||||
// Intentionally pass the empty string to umount if the caller tries
|
||||
// to unmount a volume they already unmounted using this
|
||||
// function.
|
||||
std::string mount_point = volume->mount_point;
|
||||
volume->mount_point.clear();
|
||||
return umount(mount_point.c_str());
|
||||
}
|
||||
|
||||
int remount_read_only(MountedVolume* volume) {
|
||||
return mount(volume->device.c_str(), volume->mount_point.c_str(), volume->filesystem.c_str(),
|
||||
MS_NOATIME | MS_NODEV | MS_NODIRATIME | MS_RDONLY | MS_REMOUNT, 0);
|
||||
}
|
||||
@@ -14,25 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef MTDUTILS_MOUNTS_H_
|
||||
#define MTDUTILS_MOUNTS_H_
|
||||
#ifndef MOUNTS_H_
|
||||
#define MOUNTS_H_
|
||||
|
||||
typedef struct {
|
||||
const char *device;
|
||||
const char *mount_point;
|
||||
const char *filesystem;
|
||||
const char *flags;
|
||||
} MountedVolume;
|
||||
struct MountedVolume;
|
||||
|
||||
int scan_mounted_volumes(void);
|
||||
bool scan_mounted_volumes();
|
||||
|
||||
const MountedVolume *find_mounted_volume_by_device(const char *device);
|
||||
MountedVolume* find_mounted_volume_by_device(const char* device);
|
||||
|
||||
const MountedVolume *
|
||||
find_mounted_volume_by_mount_point(const char *mount_point);
|
||||
MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point);
|
||||
|
||||
int unmount_mounted_volume(const MountedVolume *volume);
|
||||
int unmount_mounted_volume(MountedVolume* volume);
|
||||
|
||||
int remount_read_only(const MountedVolume* volume);
|
||||
int remount_read_only(MountedVolume* volume);
|
||||
|
||||
#endif // MTDUTILS_MOUNTS_H_
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 MTDUTILS_MOUNTS_H_
|
||||
#define MTDUTILS_MOUNTS_H_
|
||||
|
||||
typedef struct {
|
||||
const char *device;
|
||||
const char *mount_point;
|
||||
const char *filesystem;
|
||||
const char *flags;
|
||||
} MountedVolume;
|
||||
|
||||
int scan_mounted_volumes(void);
|
||||
|
||||
const MountedVolume *find_mounted_volume_by_device(const char *device);
|
||||
|
||||
const MountedVolume *
|
||||
find_mounted_volume_by_mount_point(const char *mount_point);
|
||||
|
||||
int unmount_mounted_volume(const MountedVolume *volume);
|
||||
|
||||
int remount_read_only(const MountedVolume* volume);
|
||||
|
||||
#endif // MTDUTILS_MOUNTS_H_
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cutils/log.h"
|
||||
#include "mtdutils.h"
|
||||
|
||||
#ifdef LOG_TAG
|
||||
#undef LOG_TAG
|
||||
#endif
|
||||
#define LOG_TAG "flash_image"
|
||||
|
||||
#define HEADER_SIZE 2048 // size of header to compare for equality
|
||||
|
||||
void die(const char *msg, ...) {
|
||||
int err = errno;
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
char buf[1024];
|
||||
vsnprintf(buf, sizeof(buf), msg, args);
|
||||
va_end(args);
|
||||
|
||||
if (err != 0) {
|
||||
strlcat(buf, ": ", sizeof(buf));
|
||||
strlcat(buf, strerror(err), sizeof(buf));
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
ALOGE("%s\n", buf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Read an image file and write it to a flash partition. */
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const MtdPartition *ptn;
|
||||
MtdWriteContext *write;
|
||||
void *data;
|
||||
unsigned sz;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (mtd_scan_partitions() <= 0) die("error scanning partitions");
|
||||
const MtdPartition *partition = mtd_find_partition_by_name(argv[1]);
|
||||
if (partition == NULL) die("can't find %s partition", argv[1]);
|
||||
|
||||
// If the first part of the file matches the partition, skip writing
|
||||
|
||||
int fd = open(argv[2], O_RDONLY);
|
||||
if (fd < 0) die("error opening %s", argv[2]);
|
||||
|
||||
char header[HEADER_SIZE];
|
||||
int headerlen = TEMP_FAILURE_RETRY(read(fd, header, sizeof(header)));
|
||||
if (headerlen <= 0) die("error reading %s header", argv[2]);
|
||||
|
||||
MtdReadContext *in = mtd_read_partition(partition);
|
||||
if (in == NULL) {
|
||||
ALOGW("error opening %s: %s\n", argv[1], strerror(errno));
|
||||
// just assume it needs re-writing
|
||||
} else {
|
||||
char check[HEADER_SIZE];
|
||||
int checklen = mtd_read_data(in, check, sizeof(check));
|
||||
if (checklen <= 0) {
|
||||
ALOGW("error reading %s: %s\n", argv[1], strerror(errno));
|
||||
// just assume it needs re-writing
|
||||
} else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
|
||||
ALOGI("header is the same, not flashing %s\n", argv[1]);
|
||||
return 0;
|
||||
}
|
||||
mtd_read_close(in);
|
||||
}
|
||||
|
||||
// Skip the header (we'll come back to it), write everything else
|
||||
ALOGI("flashing %s from %s\n", argv[1], argv[2]);
|
||||
|
||||
MtdWriteContext *out = mtd_write_partition(partition);
|
||||
if (out == NULL) die("error writing %s", argv[1]);
|
||||
|
||||
char buf[HEADER_SIZE];
|
||||
memset(buf, 0, headerlen);
|
||||
int wrote = mtd_write_data(out, buf, headerlen);
|
||||
if (wrote != headerlen) die("error writing %s", argv[1]);
|
||||
|
||||
int len;
|
||||
while ((len = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)))) > 0) {
|
||||
wrote = mtd_write_data(out, buf, len);
|
||||
if (wrote != len) die("error writing %s", argv[1]);
|
||||
}
|
||||
if (len < 0) die("error reading %s", argv[2]);
|
||||
|
||||
if (mtd_write_close(out)) die("error closing %s", argv[1]);
|
||||
|
||||
// Now come back and write the header last
|
||||
|
||||
out = mtd_write_partition(partition);
|
||||
if (out == NULL) die("error re-opening %s", argv[1]);
|
||||
|
||||
wrote = mtd_write_data(out, header, headerlen);
|
||||
if (wrote != headerlen) die("error re-writing %s", argv[1]);
|
||||
|
||||
// Need to write a complete block, so write the rest of the first block
|
||||
size_t block_size;
|
||||
if (mtd_partition_info(partition, NULL, &block_size, NULL))
|
||||
die("error getting %s block size", argv[1]);
|
||||
|
||||
if (TEMP_FAILURE_RETRY(lseek(fd, headerlen, SEEK_SET)) != headerlen)
|
||||
die("error rewinding %s", argv[2]);
|
||||
|
||||
int left = block_size - headerlen;
|
||||
while (left < 0) left += block_size;
|
||||
while (left > 0) {
|
||||
len = TEMP_FAILURE_RETRY(read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left));
|
||||
if (len <= 0) die("error reading %s", argv[2]);
|
||||
if (mtd_write_data(out, buf, len) != len)
|
||||
die("error writing %s", argv[1]);
|
||||
left -= len;
|
||||
}
|
||||
|
||||
if (mtd_write_close(out)) die("error closing %s", argv[1]);
|
||||
return 0;
|
||||
}
|
||||
+6
-9
@@ -17,11 +17,12 @@
|
||||
#ifndef MTDUTILS_MOUNTS_H_
|
||||
#define MTDUTILS_MOUNTS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct MountedVolume MountedVolume;
|
||||
typedef struct {
|
||||
const char *device;
|
||||
const char *mount_point;
|
||||
const char *filesystem;
|
||||
const char *flags;
|
||||
} MountedVolume;
|
||||
|
||||
int scan_mounted_volumes(void);
|
||||
|
||||
@@ -34,8 +35,4 @@ int unmount_mounted_volume(const MountedVolume *volume);
|
||||
|
||||
int remount_read_only(const MountedVolume* volume);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MTDUTILS_MOUNTS_H_
|
||||
|
||||
@@ -52,11 +52,10 @@
|
||||
#include "gui/pages.hpp"
|
||||
#include "orscmd/orscmd.h"
|
||||
#include "adbbu/libtwadbbu.hpp"
|
||||
#include "twinstall.h"
|
||||
extern "C" {
|
||||
#include "twinstall.h"
|
||||
#include "gui/gui.h"
|
||||
#include "cutils/properties.h"
|
||||
int TWinstall_zip(const char* path, int* wipe_cache);
|
||||
}
|
||||
|
||||
OpenRecoveryScript::VoidFunction OpenRecoveryScript::call_after_cli_command;
|
||||
|
||||
+8
-3
@@ -23,11 +23,13 @@ endif
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
otafault_static_libs := \
|
||||
libbase \
|
||||
libminzip \
|
||||
libziparchive \
|
||||
libz \
|
||||
libselinux
|
||||
libselinux \
|
||||
libbase \
|
||||
liblog
|
||||
|
||||
LOCAL_CFLAGS := -Werror
|
||||
LOCAL_SRC_FILES := config.cpp ota_io.cpp
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
LOCAL_MODULE := libotafault
|
||||
@@ -38,12 +40,15 @@ LOCAL_WHOLE_STATIC_LIBRARIES := $(otafault_static_libs)
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# otafault_test (static executable)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := config.cpp ota_io.cpp test.cpp
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_MODULE := otafault_test
|
||||
LOCAL_STATIC_LIBRARIES := $(otafault_static_libs)
|
||||
LOCAL_CFLAGS := -Werror
|
||||
LOCAL_C_INCLUDES := $(RECOVERY_PATH)
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
|
||||
|
||||
+15
-9
@@ -21,38 +21,42 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#include "minzip/Zip.h"
|
||||
#include "config.h"
|
||||
#include "ota_io.h"
|
||||
|
||||
#define OTAIO_MAX_FNAME_SIZE 128
|
||||
|
||||
static ZipArchive* archive;
|
||||
static ZipArchiveHandle archive;
|
||||
static bool is_retry = false;
|
||||
static std::map<std::string, bool> should_inject_cache;
|
||||
|
||||
static std::string get_type_path(const char* io_type) {
|
||||
return android::base::StringPrintf("%s/%s", OTAIO_BASE_DIR, io_type);
|
||||
}
|
||||
|
||||
void ota_io_init(ZipArchive* za) {
|
||||
void ota_io_init(ZipArchiveHandle za, bool retry) {
|
||||
archive = za;
|
||||
is_retry = retry;
|
||||
ota_set_fault_files();
|
||||
}
|
||||
|
||||
bool should_fault_inject(const char* io_type) {
|
||||
// archive will be NULL if we used an entry point other
|
||||
// than updater/updater.cpp:main
|
||||
if (archive == NULL) {
|
||||
if (archive == nullptr || is_retry) {
|
||||
return false;
|
||||
}
|
||||
const std::string type_path = get_type_path(io_type);
|
||||
if (should_inject_cache.find(type_path) != should_inject_cache.end()) {
|
||||
return should_inject_cache[type_path];
|
||||
}
|
||||
const ZipEntry* entry = mzFindZipEntry(archive, type_path.c_str());
|
||||
should_inject_cache[type_path] = entry != nullptr;
|
||||
return entry != NULL;
|
||||
ZipString zip_type_path(type_path.c_str());
|
||||
ZipEntry entry;
|
||||
int status = FindEntry(archive, zip_type_path, &entry);
|
||||
should_inject_cache[type_path] = (status == 0);
|
||||
return (status == 0);
|
||||
}
|
||||
|
||||
bool should_hit_cache() {
|
||||
@@ -63,7 +67,9 @@ std::string fault_fname(const char* io_type) {
|
||||
std::string type_path = get_type_path(io_type);
|
||||
std::string fname;
|
||||
fname.resize(OTAIO_MAX_FNAME_SIZE);
|
||||
const ZipEntry* entry = mzFindZipEntry(archive, type_path.c_str());
|
||||
mzReadZipEntry(archive, entry, &fname[0], OTAIO_MAX_FNAME_SIZE);
|
||||
ZipString zip_type_path(type_path.c_str());
|
||||
ZipEntry entry;
|
||||
int status = FindEntry(archive, zip_type_path, &entry);
|
||||
ExtractToMemory(archive, &entry, reinterpret_cast<uint8_t*>(&fname[0]), OTAIO_MAX_FNAME_SIZE);
|
||||
return fname;
|
||||
}
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "minzip/Zip.h"
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#define OTAIO_BASE_DIR ".libotafault"
|
||||
#define OTAIO_READ "READ"
|
||||
@@ -52,7 +52,7 @@
|
||||
/*
|
||||
* Initialize libotafault by providing a reference to the OTA package.
|
||||
*/
|
||||
void ota_io_init(ZipArchive* za);
|
||||
void ota_io_init(ZipArchiveHandle zip, bool retry);
|
||||
|
||||
/*
|
||||
* Return true if a config file is present for the given IO type.
|
||||
|
||||
+25
-6
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include "ota_io.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -22,15 +22,17 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "config.h"
|
||||
#include "ota_io.h"
|
||||
|
||||
static std::map<intptr_t, const char*> filename_cache;
|
||||
static std::string read_fault_file_name = "";
|
||||
static std::string write_fault_file_name = "";
|
||||
static std::string fsync_fault_file_name = "";
|
||||
|
||||
static bool get_hit_file(const char* cached_path, std::string ffn) {
|
||||
static bool get_hit_file(const char* cached_path, const std::string& ffn) {
|
||||
return should_hit_cache()
|
||||
? !strncmp(cached_path, OTAIO_CACHE_FNAME, strlen(cached_path))
|
||||
: !strncmp(cached_path, ffn.c_str(), strlen(cached_path));
|
||||
@@ -68,17 +70,33 @@ FILE* ota_fopen(const char* path, const char* mode) {
|
||||
return fh;
|
||||
}
|
||||
|
||||
int ota_close(int fd) {
|
||||
static int __ota_close(int fd) {
|
||||
// descriptors can be reused, so make sure not to leave them in the cache
|
||||
filename_cache.erase(fd);
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
int ota_fclose(FILE* fh) {
|
||||
filename_cache.erase((intptr_t)fh);
|
||||
void OtaCloser::Close(int fd) {
|
||||
__ota_close(fd);
|
||||
}
|
||||
|
||||
int ota_close(unique_fd& fd) {
|
||||
return __ota_close(fd.release());
|
||||
}
|
||||
|
||||
static int __ota_fclose(FILE* fh) {
|
||||
filename_cache.erase(reinterpret_cast<intptr_t>(fh));
|
||||
return fclose(fh);
|
||||
}
|
||||
|
||||
void OtaFcloser::operator()(FILE* f) const {
|
||||
__ota_fclose(f);
|
||||
};
|
||||
|
||||
int ota_fclose(unique_file& fh) {
|
||||
return __ota_fclose(fh.release());
|
||||
}
|
||||
|
||||
size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) {
|
||||
if (should_fault_inject(OTAIO_READ)) {
|
||||
auto cached = filename_cache.find((intptr_t)stream);
|
||||
@@ -92,6 +110,7 @@ size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) {
|
||||
}
|
||||
}
|
||||
size_t status = fread(ptr, size, nitems, stream);
|
||||
// If I/O error occurs, set the retry-update flag.
|
||||
if (status != nitems && errno == EIO) {
|
||||
have_eio_error = true;
|
||||
}
|
||||
|
||||
+20
-4
@@ -26,6 +26,10 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#define OTAIO_CACHE_FNAME "/cache/saved.file"
|
||||
|
||||
void ota_set_fault_files();
|
||||
@@ -36,10 +40,6 @@ int ota_open(const char* path, int oflags, mode_t mode);
|
||||
|
||||
FILE* ota_fopen(const char* filename, const char* mode);
|
||||
|
||||
int ota_close(int fd);
|
||||
|
||||
int ota_fclose(FILE* fh);
|
||||
|
||||
size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream);
|
||||
|
||||
ssize_t ota_read(int fd, void* buf, size_t nbyte);
|
||||
@@ -50,4 +50,20 @@ ssize_t ota_write(int fd, const void* buf, size_t nbyte);
|
||||
|
||||
int ota_fsync(int fd);
|
||||
|
||||
struct OtaCloser {
|
||||
static void Close(int);
|
||||
};
|
||||
|
||||
using unique_fd = android::base::unique_fd_impl<OtaCloser>;
|
||||
|
||||
int ota_close(unique_fd& fd);
|
||||
|
||||
struct OtaFcloser {
|
||||
void operator()(FILE*) const;
|
||||
};
|
||||
|
||||
using unique_file = std::unique_ptr<FILE, OtaFcloser>;
|
||||
|
||||
int ota_fclose(unique_file& fh);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
SysUtil.cpp \
|
||||
DirUtil.cpp \
|
||||
ZipUtil.cpp \
|
||||
ThermalUtil.cpp
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libselinux \
|
||||
libbase
|
||||
|
||||
LOCAL_MODULE := libotautil
|
||||
LOCAL_CFLAGS := \
|
||||
-Werror \
|
||||
-Wall
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 "DirUtil.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <selinux/label.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
|
||||
|
||||
static DirStatus
|
||||
getPathDirStatus(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
int err;
|
||||
|
||||
err = stat(path, &st);
|
||||
if (err == 0) {
|
||||
/* Something's there; make sure it's a directory.
|
||||
*/
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
return DDIR;
|
||||
}
|
||||
errno = ENOTDIR;
|
||||
return DILLEGAL;
|
||||
} else if (errno != ENOENT) {
|
||||
/* Something went wrong, or something in the path
|
||||
* is bad. Can't do anything in this situation.
|
||||
*/
|
||||
return DILLEGAL;
|
||||
}
|
||||
return DMISSING;
|
||||
}
|
||||
|
||||
int
|
||||
dirCreateHierarchy(const char *path, int mode,
|
||||
const struct utimbuf *timestamp, bool stripFileName,
|
||||
struct selabel_handle *sehnd)
|
||||
{
|
||||
DirStatus ds;
|
||||
|
||||
/* Check for an empty string before we bother
|
||||
* making any syscalls.
|
||||
*/
|
||||
if (path[0] == '\0') {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
// Allocate a path that we can modify; stick a slash on
|
||||
// the end to make things easier.
|
||||
std::string cpath = path;
|
||||
if (stripFileName) {
|
||||
// Strip everything after the last slash.
|
||||
size_t pos = cpath.rfind('/');
|
||||
if (pos == std::string::npos) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
cpath.resize(pos + 1);
|
||||
} else {
|
||||
// Make sure that the path ends in a slash.
|
||||
cpath.push_back('/');
|
||||
}
|
||||
|
||||
/* See if it already exists.
|
||||
*/
|
||||
ds = getPathDirStatus(cpath.c_str());
|
||||
if (ds == DDIR) {
|
||||
return 0;
|
||||
} else if (ds == DILLEGAL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Walk up the path from the root and make each level.
|
||||
* If a directory already exists, no big deal.
|
||||
*/
|
||||
const char *path_start = &cpath[0];
|
||||
char *p = &cpath[0];
|
||||
while (*p != '\0') {
|
||||
/* Skip any slashes, watching out for the end of the string.
|
||||
*/
|
||||
while (*p != '\0' && *p == '/') {
|
||||
p++;
|
||||
}
|
||||
if (*p == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Find the end of the next path component.
|
||||
* We know that we'll see a slash before the NUL,
|
||||
* because we added it, above.
|
||||
*/
|
||||
while (*p != '/') {
|
||||
p++;
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
/* Check this part of the path and make a new directory
|
||||
* if necessary.
|
||||
*/
|
||||
ds = getPathDirStatus(path_start);
|
||||
if (ds == DILLEGAL) {
|
||||
/* Could happen if some other process/thread is
|
||||
* messing with the filesystem.
|
||||
*/
|
||||
return -1;
|
||||
} else if (ds == DMISSING) {
|
||||
int err;
|
||||
|
||||
char *secontext = NULL;
|
||||
|
||||
if (sehnd) {
|
||||
selabel_lookup(sehnd, &secontext, path_start, mode);
|
||||
setfscreatecon(secontext);
|
||||
}
|
||||
|
||||
err = mkdir(path_start, mode);
|
||||
|
||||
if (secontext) {
|
||||
freecon(secontext);
|
||||
setfscreatecon(NULL);
|
||||
}
|
||||
|
||||
if (err != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (timestamp != NULL && utime(path_start, timestamp)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// else, this directory already exists.
|
||||
|
||||
// Repair the path and continue.
|
||||
*p = '/';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dirUnlinkHierarchy(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
int fail = 0;
|
||||
|
||||
/* is it a file or directory? */
|
||||
if (lstat(path, &st) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* a file, so unlink it */
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
return unlink(path);
|
||||
}
|
||||
|
||||
/* a directory, so open handle */
|
||||
dir = opendir(path);
|
||||
if (dir == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* recurse over components */
|
||||
errno = 0;
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
//TODO: don't blow the stack
|
||||
char dn[PATH_MAX];
|
||||
if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
|
||||
continue;
|
||||
}
|
||||
snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
|
||||
if (dirUnlinkHierarchy(dn) < 0) {
|
||||
fail = 1;
|
||||
break;
|
||||
}
|
||||
errno = 0;
|
||||
}
|
||||
/* in case readdir or unlink_recursive failed */
|
||||
if (fail || errno < 0) {
|
||||
int save = errno;
|
||||
closedir(dir);
|
||||
errno = save;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* close directory handle */
|
||||
if (closedir(dir) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* delete target directory */
|
||||
return rmdir(path);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 MINZIP_DIRUTIL_H_
|
||||
#define MINZIP_DIRUTIL_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <utime.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct selabel_handle;
|
||||
|
||||
/* Like "mkdir -p", try to guarantee that all directories
|
||||
* specified in path are present, creating as many directories
|
||||
* as necessary. The specified mode is passed to all mkdir
|
||||
* calls; no modifications are made to umask.
|
||||
*
|
||||
* If stripFileName is set, everything after the final '/'
|
||||
* is stripped before creating the directory hierarchy.
|
||||
*
|
||||
* If timestamp is non-NULL, new directories will be timestamped accordingly.
|
||||
*
|
||||
* Returns 0 on success; returns -1 (and sets errno) on failure
|
||||
* (usually if some element of path is not a directory).
|
||||
*/
|
||||
int dirCreateHierarchy(const char *path, int mode,
|
||||
const struct utimbuf *timestamp, bool stripFileName,
|
||||
struct selabel_handle* sehnd);
|
||||
|
||||
/* rm -rf <path>
|
||||
*/
|
||||
int dirUnlinkHierarchy(const char *path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MINZIP_DIRUTIL_H_
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2006 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 "SysUtil.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
static bool sysMapFD(int fd, MemMapping* pMap) {
|
||||
CHECK(pMap != nullptr);
|
||||
|
||||
struct stat sb;
|
||||
if (fstat(fd, &sb) == -1) {
|
||||
PLOG(ERROR) << "fstat(" << fd << ") failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
void* memPtr = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (memPtr == MAP_FAILED) {
|
||||
PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
pMap->addr = static_cast<unsigned char*>(memPtr);
|
||||
pMap->length = sb.st_size;
|
||||
pMap->ranges.push_back({ memPtr, static_cast<size_t>(sb.st_size) });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// A "block map" which looks like this (from uncrypt/uncrypt.cpp):
|
||||
//
|
||||
// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device
|
||||
// 49652 4096 # file size in bytes, block size
|
||||
// 3 # count of block ranges
|
||||
// 1000 1008 # block range 0
|
||||
// 2100 2102 # ... block range 1
|
||||
// 30 33 # ... block range 2
|
||||
//
|
||||
// Each block range represents a half-open interval; the line "30 33"
|
||||
// reprents the blocks [30, 31, 32].
|
||||
static int sysMapBlockFile(const char* filename, MemMapping* pMap) {
|
||||
CHECK(pMap != nullptr);
|
||||
|
||||
std::string content;
|
||||
if (!android::base::ReadFileToString(filename, &content)) {
|
||||
PLOG(ERROR) << "Failed to read " << filename;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
|
||||
if (lines.size() < 4) {
|
||||
LOG(ERROR) << "Block map file is too short: " << lines.size();
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t size;
|
||||
unsigned int blksize;
|
||||
if (sscanf(lines[1].c_str(), "%zu %u", &size, &blksize) != 2) {
|
||||
LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t range_count;
|
||||
if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
|
||||
LOG(ERROR) << "Failed to parse block map header: " << lines[2];
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t blocks;
|
||||
if (blksize != 0) {
|
||||
blocks = ((size - 1) / blksize) + 1;
|
||||
}
|
||||
if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 ||
|
||||
lines.size() != 3 + range_count) {
|
||||
LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize
|
||||
<< ", range_count " << range_count << ", lines " << lines.size();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Reserve enough contiguous address space for the whole file.
|
||||
void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
if (reserve == MAP_FAILED) {
|
||||
PLOG(ERROR) << "failed to reserve address space";
|
||||
return -1;
|
||||
}
|
||||
|
||||
const std::string& block_dev = lines[0];
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY)));
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "failed to open block device " << block_dev;
|
||||
munmap(reserve, blocks * blksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pMap->ranges.resize(range_count);
|
||||
|
||||
unsigned char* next = static_cast<unsigned char*>(reserve);
|
||||
size_t remaining_size = blocks * blksize;
|
||||
bool success = true;
|
||||
for (size_t i = 0; i < range_count; ++i) {
|
||||
const std::string& line = lines[i + 3];
|
||||
|
||||
size_t start, end;
|
||||
if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) {
|
||||
LOG(ERROR) << "failed to parse range " << i << " in block map: " << line;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
size_t length = (end - start) * blksize;
|
||||
if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) {
|
||||
LOG(ERROR) << "unexpected range in block map: " << start << " " << end;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd,
|
||||
static_cast<off64_t>(start) * blksize);
|
||||
if (addr == MAP_FAILED) {
|
||||
PLOG(ERROR) << "failed to map block " << i;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
pMap->ranges[i].addr = addr;
|
||||
pMap->ranges[i].length = length;
|
||||
|
||||
next += length;
|
||||
remaining_size -= length;
|
||||
}
|
||||
if (success && remaining_size != 0) {
|
||||
LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size;
|
||||
success = false;
|
||||
}
|
||||
if (!success) {
|
||||
munmap(reserve, blocks * blksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pMap->addr = static_cast<unsigned char*>(reserve);
|
||||
pMap->length = size;
|
||||
|
||||
LOG(INFO) << "mmapped " << range_count << " ranges";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysMapFile(const char* fn, MemMapping* pMap) {
|
||||
if (fn == nullptr || pMap == nullptr) {
|
||||
LOG(ERROR) << "Invalid argument(s)";
|
||||
return -1;
|
||||
}
|
||||
|
||||
*pMap = {};
|
||||
|
||||
if (fn[0] == '@') {
|
||||
if (sysMapBlockFile(fn + 1, pMap) != 0) {
|
||||
LOG(ERROR) << "Map of '" << fn << "' failed";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// This is a regular file.
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn, O_RDONLY)));
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Unable to open '" << fn << "'";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!sysMapFD(fd, pMap)) {
|
||||
LOG(ERROR) << "Map of '" << fn << "' failed";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release a memory mapping.
|
||||
*/
|
||||
void sysReleaseMap(MemMapping* pMap) {
|
||||
std::for_each(pMap->ranges.cbegin(), pMap->ranges.cend(), [](const MappedRange& range) {
|
||||
if (munmap(range.addr, range.length) == -1) {
|
||||
PLOG(ERROR) << "munmap(" << range.addr << ", " << range.length << ") failed";
|
||||
}
|
||||
});
|
||||
pMap->ranges.clear();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2006 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 _OTAUTIL_SYSUTIL
|
||||
#define _OTAUTIL_SYSUTIL
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct MappedRange {
|
||||
void* addr;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
/*
|
||||
* Use this to keep track of mapped segments.
|
||||
*/
|
||||
struct MemMapping {
|
||||
unsigned char* addr; /* start of data */
|
||||
size_t length; /* length of data */
|
||||
|
||||
std::vector<MappedRange> ranges;
|
||||
};
|
||||
|
||||
/*
|
||||
* Map a file into a private, read-only memory segment. If 'fn'
|
||||
* begins with an '@' character, it is a map of blocks to be mapped,
|
||||
* otherwise it is treated as an ordinary file.
|
||||
*
|
||||
* On success, "pMap" is filled in, and zero is returned.
|
||||
*/
|
||||
int sysMapFile(const char* fn, MemMapping* pMap);
|
||||
|
||||
/*
|
||||
* Release the pages associated with a shared memory segment.
|
||||
*
|
||||
* This does not free "pMap"; it just releases the memory.
|
||||
*/
|
||||
void sysReleaseMap(MemMapping* pMap);
|
||||
|
||||
#endif // _OTAUTIL_SYSUTIL
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 "ThermalUtil.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
static constexpr auto THERMAL_PREFIX = "/sys/class/thermal/";
|
||||
|
||||
static int thermal_filter(const dirent* de) {
|
||||
if (android::base::StartsWith(de->d_name, "thermal_zone")) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::vector<std::string> InitThermalPaths() {
|
||||
dirent** namelist;
|
||||
int n = scandir(THERMAL_PREFIX, &namelist, thermal_filter, alphasort);
|
||||
if (n == -1) {
|
||||
PLOG(ERROR) << "Failed to scandir " << THERMAL_PREFIX;
|
||||
return {};
|
||||
}
|
||||
if (n == 0) {
|
||||
LOG(ERROR) << "Failed to find CPU thermal info in " << THERMAL_PREFIX;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> thermal_paths;
|
||||
while (n--) {
|
||||
thermal_paths.push_back(THERMAL_PREFIX + std::string(namelist[n]->d_name) + "/temp");
|
||||
free(namelist[n]);
|
||||
}
|
||||
free(namelist);
|
||||
return thermal_paths;
|
||||
}
|
||||
|
||||
int GetMaxValueFromThermalZone() {
|
||||
static std::vector<std::string> thermal_paths = InitThermalPaths();
|
||||
int max_temperature = -1;
|
||||
for (const auto& path : thermal_paths) {
|
||||
std::string content;
|
||||
if (!android::base::ReadFileToString(path, &content)) {
|
||||
PLOG(WARNING) << "Failed to read " << path;
|
||||
continue;
|
||||
}
|
||||
|
||||
int temperature;
|
||||
if (!android::base::ParseInt(android::base::Trim(content), &temperature)) {
|
||||
LOG(WARNING) << "Failed to parse integer in " << content;
|
||||
continue;
|
||||
}
|
||||
max_temperature = std::max(temperature, max_temperature);
|
||||
}
|
||||
LOG(INFO) << "current maximum temperature: " << max_temperature;
|
||||
return max_temperature;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 OTAUTIL_THERMALUTIL_H
|
||||
#define OTAUTIL_THERMALUTIL_H
|
||||
|
||||
// We can find the temperature reported by all sensors in /sys/class/thermal/thermal_zone*/temp.
|
||||
// Their values are in millidegree Celsius; and we will log the maximum one.
|
||||
int GetMaxValueFromThermalZone();
|
||||
|
||||
#endif // OTAUTIL_THERMALUTIL_H
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 "ZipUtil.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <selinux/label.h>
|
||||
#include <selinux/selinux.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#include "DirUtil.h"
|
||||
|
||||
static constexpr mode_t UNZIP_DIRMODE = 0755;
|
||||
static constexpr mode_t UNZIP_FILEMODE = 0644;
|
||||
|
||||
bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path,
|
||||
const std::string& dest_path, const struct utimbuf* timestamp,
|
||||
struct selabel_handle* sehnd) {
|
||||
if (!zip_path.empty() && zip_path[0] == '/') {
|
||||
LOG(ERROR) << "ExtractPackageRecursive(): zip_path must be a relative path " << zip_path;
|
||||
return false;
|
||||
}
|
||||
if (dest_path.empty() || dest_path[0] != '/') {
|
||||
LOG(ERROR) << "ExtractPackageRecursive(): dest_path must be an absolute path " << dest_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
void* cookie;
|
||||
std::string target_dir(dest_path);
|
||||
if (dest_path.back() != '/') {
|
||||
target_dir += '/';
|
||||
}
|
||||
std::string prefix_path(zip_path);
|
||||
if (!zip_path.empty() && zip_path.back() != '/') {
|
||||
prefix_path += '/';
|
||||
}
|
||||
const ZipString zip_prefix(prefix_path.c_str());
|
||||
|
||||
int ret = StartIteration(zip, &cookie, &zip_prefix, nullptr);
|
||||
if (ret != 0) {
|
||||
LOG(ERROR) << "failed to start iterating zip entries.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
|
||||
ZipEntry entry;
|
||||
ZipString name;
|
||||
int extractCount = 0;
|
||||
while (Next(cookie, &entry, &name) == 0) {
|
||||
std::string entry_name(name.name, name.name + name.name_length);
|
||||
CHECK_LE(prefix_path.size(), entry_name.size());
|
||||
std::string path = target_dir + entry_name.substr(prefix_path.size());
|
||||
// Skip dir.
|
||||
if (path.back() == '/') {
|
||||
continue;
|
||||
}
|
||||
//TODO(b/31917448) handle the symlink.
|
||||
|
||||
if (dirCreateHierarchy(path.c_str(), UNZIP_DIRMODE, timestamp, true, sehnd) != 0) {
|
||||
LOG(ERROR) << "failed to create dir for " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
char *secontext = NULL;
|
||||
if (sehnd) {
|
||||
selabel_lookup(sehnd, &secontext, path.c_str(), UNZIP_FILEMODE);
|
||||
setfscreatecon(secontext);
|
||||
}
|
||||
android::base::unique_fd fd(open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, UNZIP_FILEMODE));
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Can't create target file \"" << path << "\"";
|
||||
return false;
|
||||
}
|
||||
if (secontext) {
|
||||
freecon(secontext);
|
||||
setfscreatecon(NULL);
|
||||
}
|
||||
|
||||
int err = ExtractEntryToFile(zip, &entry, fd);
|
||||
if (err != 0) {
|
||||
LOG(ERROR) << "Error extracting \"" << path << "\" : " << ErrorCodeString(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fsync(fd) != 0) {
|
||||
PLOG(ERROR) << "Error syncing file descriptor when extracting \"" << path << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (timestamp != nullptr && utime(path.c_str(), timestamp)) {
|
||||
PLOG(ERROR) << "Error touching \"" << path << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Extracted file \"" << path << "\"";
|
||||
++extractCount;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Extracted " << extractCount << " file(s)";
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 _OTAUTIL_ZIPUTIL_H
|
||||
#define _OTAUTIL_ZIPUTIL_H
|
||||
|
||||
#include <utime.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <selinux/label.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
/*
|
||||
* Inflate all files under zip_path to the directory specified by
|
||||
* dest_path, which must exist and be a writable directory. The zip_path
|
||||
* is allowed to be an empty string, in which case the whole package
|
||||
* will be extracted.
|
||||
*
|
||||
* Directory entries are not extracted.
|
||||
*
|
||||
* The immediate children of zip_path will become the immediate
|
||||
* children of dest_path; e.g., if the archive contains the entries
|
||||
*
|
||||
* a/b/c/one
|
||||
* a/b/c/two
|
||||
* a/b/c/d/three
|
||||
*
|
||||
* and ExtractPackageRecursive(a, "a/b/c", "/tmp", ...) is called, the resulting
|
||||
* files will be
|
||||
*
|
||||
* /tmp/one
|
||||
* /tmp/two
|
||||
* /tmp/d/three
|
||||
*
|
||||
* If timestamp is non-NULL, file timestamps will be set accordingly.
|
||||
*
|
||||
* Returns true on success, false on failure.
|
||||
*/
|
||||
bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path,
|
||||
const std::string& dest_path, const struct utimbuf* timestamp,
|
||||
struct selabel_handle* sehnd);
|
||||
|
||||
#endif // _OTAUTIL_ZIPUTIL_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user