Merge tag 'android-10.0.0_r25' into aosp10-4
Android 10.0.0 release 25
This commit is contained in:
@@ -1,3 +1,33 @@
|
||||
# bootable/recovery project uses repohook to apply `clang-format` to the changed lines, with the
|
||||
# local style file in `.clang-format`. This will be triggered automatically with `repo upload`.
|
||||
# Alternatively, one can stage and format a change with `git clang-format` directly.
|
||||
#
|
||||
# $ git add <files>
|
||||
# $ git clang-format --style file
|
||||
#
|
||||
# Or to format a committed change.
|
||||
#
|
||||
# $ git clang-format --style file HEAD~1
|
||||
#
|
||||
# `--style file` will pick up the local style file in `.clang-format`. This can be configured as the
|
||||
# default behavior for bootable/recovery project.
|
||||
#
|
||||
# $ git config --local clangFormat.style file
|
||||
#
|
||||
# Note that `repo upload` calls the `clang-format` binary in Android repo (i.e.
|
||||
# `$ANDROID_BUILD_TOP/prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format`), which might
|
||||
# give slightly different results from the one installed in host machine (e.g.
|
||||
# `/usr/bin/clang-format`). Specifying the file with `--binary` will ensure consistent results.
|
||||
#
|
||||
# $ git clang-format --binary \
|
||||
# /path/to/aosp-master/prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format
|
||||
#
|
||||
# Or to do one-time setup to make it default.
|
||||
#
|
||||
# $ git config --local clangFormat.binary \
|
||||
# /path/to/aosp-master/prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format
|
||||
#
|
||||
|
||||
BasedOnStyle: Google
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
|
||||
+197
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
subdirs = [
|
||||
// "applypatch",
|
||||
"bootloader_message",
|
||||
@@ -6,3 +7,199 @@ subdirs = [
|
||||
// "otautil",
|
||||
// "uncrypt",
|
||||
]
|
||||
=======
|
||||
// Copyright (C) 2018 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.
|
||||
|
||||
cc_defaults {
|
||||
name: "recovery_defaults",
|
||||
|
||||
cflags: [
|
||||
"-D_FILE_OFFSET_BITS=64",
|
||||
|
||||
// Must be the same as RECOVERY_API_VERSION.
|
||||
"-DRECOVERY_API_VERSION=3",
|
||||
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "librecovery_fastboot",
|
||||
recovery_available: true,
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"fastboot/fastboot.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbootloader_message",
|
||||
"libcutils",
|
||||
"liblog",
|
||||
"librecovery_ui",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"librecovery_ui_default",
|
||||
],
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "librecovery_defaults",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"android.hardware.health@2.0",
|
||||
"libbase",
|
||||
"libbootloader_message",
|
||||
"libcrypto",
|
||||
"libcutils",
|
||||
"libfs_mgr",
|
||||
"liblog",
|
||||
"libziparchive",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libinstall",
|
||||
"librecovery_fastboot",
|
||||
"libminui",
|
||||
"libotautil",
|
||||
|
||||
// external dependencies
|
||||
"libhealthhalutils",
|
||||
"libfstab",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "librecovery",
|
||||
recovery_available: true,
|
||||
|
||||
defaults: [
|
||||
"librecovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"fsck_unshare_blocks.cpp",
|
||||
"recovery.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"librecovery_ui",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "recovery",
|
||||
recovery: true,
|
||||
|
||||
defaults: [
|
||||
"libinstall_defaults",
|
||||
"librecovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"recovery_main.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"librecovery_ui",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"librecovery",
|
||||
"librecovery_ui_default",
|
||||
],
|
||||
|
||||
required: [
|
||||
"e2fsdroid.recovery",
|
||||
"librecovery_ui_ext",
|
||||
"minadbd",
|
||||
"mke2fs.conf.recovery",
|
||||
"mke2fs.recovery",
|
||||
"recovery_deps",
|
||||
],
|
||||
}
|
||||
|
||||
// The dynamic executable that runs after /data mounts.
|
||||
cc_binary {
|
||||
name: "recovery-persist",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"recovery-persist.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
"libmetricslogger",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libotautil",
|
||||
"libfstab",
|
||||
],
|
||||
|
||||
init_rc: [
|
||||
"recovery-persist.rc",
|
||||
],
|
||||
}
|
||||
|
||||
// The dynamic executable that runs at init.
|
||||
cc_binary {
|
||||
name: "recovery-refresh",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"recovery-refresh.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libotautil",
|
||||
"libfstab",
|
||||
],
|
||||
|
||||
init_rc: [
|
||||
"recovery-refresh.rc",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "res-testdata",
|
||||
|
||||
srcs: [
|
||||
"res-*/images/*_text.png",
|
||||
],
|
||||
}
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
+69
@@ -15,6 +15,7 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
commands_TWRP_local_path := $(LOCAL_PATH)
|
||||
|
||||
<<<<<<< HEAD
|
||||
ifneq ($(project-path-for),)
|
||||
ifeq ($(LOCAL_PATH),$(call project-path-for,recovery))
|
||||
PROJECT_PATH_AGREES := true
|
||||
@@ -211,9 +212,34 @@ LOCAL_C_INCLUDES += system/extras/ext4_utils
|
||||
tw_git_revision := $(shell git -C $(LOCAL_PATH) rev-parse --short=8 HEAD 2>/dev/null)
|
||||
ifeq ($(shell git -C $(LOCAL_PATH) diff --quiet; echo $$?),1)
|
||||
tw_git_revision := $(tw_git_revision)-dirty
|
||||
=======
|
||||
# Needed by build/make/core/Makefile. Must be consistent with the value in Android.bp.
|
||||
RECOVERY_API_VERSION := 3
|
||||
RECOVERY_FSTAB_VERSION := 2
|
||||
|
||||
# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr} or a device-specific
|
||||
# module that defines make_device() and the exact RecoveryUI class for the target. It defaults to
|
||||
# librecovery_ui_default, which uses ScreenRecoveryUI.
|
||||
TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default
|
||||
|
||||
# librecovery_ui_ext (shared library)
|
||||
# ===================================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := librecovery_ui_ext
|
||||
|
||||
# LOCAL_MODULE_PATH for shared libraries is unsupported in multiarch builds.
|
||||
LOCAL_MULTILIB := first
|
||||
|
||||
ifeq ($(TARGET_IS_64_BIT),true)
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib64
|
||||
else
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib
|
||||
>>>>>>> android-10.0.0_r25
|
||||
endif
|
||||
LOCAL_CFLAGS += -DTW_GIT_REVISION='"$(tw_git_revision)"'
|
||||
|
||||
<<<<<<< HEAD
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
|
||||
ifeq ($(TW_EXCLUDE_MTP),)
|
||||
LOCAL_SHARED_LIBRARIES += libtwrpmtp-ffs
|
||||
@@ -648,6 +674,31 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
|
||||
LOCAL_CFLAGS += -DUSE_FUSE_SIDELOAD22
|
||||
else
|
||||
LOCAL_SRC_FILES := fuse_sideload.cpp
|
||||
=======
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := \
|
||||
$(TARGET_RECOVERY_UI_LIB)
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libbase \
|
||||
liblog \
|
||||
librecovery_ui.recovery
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
# recovery_deps: A phony target that's depended on by `recovery`, which
|
||||
# builds additional modules conditionally based on Makefile variables.
|
||||
# ======================================================================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := recovery_deps
|
||||
|
||||
ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
|
||||
ifeq ($(HOST_OS),linux)
|
||||
LOCAL_REQUIRED_MODULES += \
|
||||
make_f2fs.recovery \
|
||||
sload_f2fs.recovery
|
||||
endif
|
||||
>>>>>>> android-10.0.0_r25
|
||||
endif
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -658,6 +709,7 @@ LOCAL_CLANG := true
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
|
||||
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
|
||||
|
||||
<<<<<<< HEAD
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE := libfusesideload
|
||||
LOCAL_SHARED_LIBRARIES := libcutils libc
|
||||
@@ -667,6 +719,15 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0)
|
||||
LOCAL_CFLAGS += -DUSE_MINCRYPT
|
||||
else
|
||||
LOCAL_STATIC_LIBRARIES += libcrypto_static
|
||||
=======
|
||||
# On A/B devices recovery-persist reads the recovery related file from the persist storage and
|
||||
# copies them into /data/misc/recovery. Then, for both A/B and non-A/B devices, recovery-persist
|
||||
# parses the last_install file and reports the embedded update metrics. Also, the last_install file
|
||||
# will be deteleted after the report.
|
||||
LOCAL_REQUIRED_MODULES += recovery-persist
|
||||
ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),)
|
||||
LOCAL_REQUIRED_MODULES += recovery-refresh
|
||||
>>>>>>> android-10.0.0_r25
|
||||
endif
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
|
||||
LOCAL_SRC_FILES := fuse_sideload22.cpp
|
||||
@@ -676,6 +737,7 @@ else
|
||||
endif
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
<<<<<<< HEAD
|
||||
# libmounts (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
@@ -797,6 +859,9 @@ LOCAL_CFLAGS := -Wall -Werror
|
||||
LOCAL_MODULE := librecovery_ui_vr
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
=======
|
||||
include $(BUILD_PHONY_PACKAGE)
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
commands_recovery_local_path := $(LOCAL_PATH)
|
||||
|
||||
@@ -804,6 +869,7 @@ commands_recovery_local_path := $(LOCAL_PATH)
|
||||
# $(LOCAL_PATH)/otafault/Android.mk
|
||||
# $(LOCAL_PATH)/bootloader_message/Android.mk
|
||||
include \
|
||||
<<<<<<< HEAD
|
||||
$(commands_TWRP_local_path)/boot_control/Android.mk \
|
||||
$(commands_TWRP_local_path)/tests/Android.mk \
|
||||
$(commands_TWRP_local_path)/tools/Android.mk \
|
||||
@@ -907,3 +973,6 @@ endif
|
||||
endif
|
||||
|
||||
commands_TWRP_local_path :=
|
||||
=======
|
||||
$(LOCAL_PATH)/updater/Android.mk \
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
+7
-2
@@ -44,8 +44,13 @@
|
||||
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
|
||||
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
|
||||
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin)
|
||||
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinstall.recovery_intermediates)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/system/lib64/libinstall.so)
|
||||
|
||||
# ************************************************
|
||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
|
||||
# ************************************************
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes)
|
||||
|
||||
@@ -4,3 +4,7 @@ clang_format = true
|
||||
[Builtin Hooks Options]
|
||||
# Handle native codes only.
|
||||
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
|
||||
|
||||
[Hook Scripts]
|
||||
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
|
||||
--file_whitelist tools/ updater_sample/
|
||||
|
||||
@@ -1,3 +1,136 @@
|
||||
**Team Win Recovery Project (TWRP)**
|
||||
|
||||
<<<<<<< HEAD
|
||||
You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide").
|
||||
=======
|
||||
Quick turn-around testing
|
||||
-------------------------
|
||||
|
||||
mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
|
||||
|
||||
# To boot into the new recovery image
|
||||
# without flashing the recovery partition:
|
||||
adb reboot bootloader
|
||||
fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
|
||||
|
||||
Running the tests
|
||||
-----------------
|
||||
# After setting up environment and lunch.
|
||||
mmma -j bootable/recovery
|
||||
|
||||
# Running the tests on device.
|
||||
adb root
|
||||
adb sync data
|
||||
|
||||
# 32-bit device
|
||||
adb shell /data/nativetest/recovery_unit_test/recovery_unit_test
|
||||
adb shell /data/nativetest/recovery_component_test/recovery_component_test
|
||||
|
||||
# Or 64-bit device
|
||||
adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
|
||||
adb shell /data/nativetest64/recovery_component_test/recovery_component_test
|
||||
|
||||
Running the manual tests
|
||||
------------------------
|
||||
|
||||
`recovery-refresh` and `recovery-persist` executables exist only on systems without
|
||||
/cache partition. And we need to follow special steps to run tests for them.
|
||||
|
||||
- Execute the test on an A/B device first. The test should fail but it will log
|
||||
some contents to pmsg.
|
||||
|
||||
- Reboot the device immediately and run the test again. The test should save the
|
||||
contents of pmsg buffer into /data/misc/recovery/inject.txt. Test will pass if
|
||||
this file has expected contents.
|
||||
|
||||
Using `adb` under recovery
|
||||
--------------------------
|
||||
|
||||
When running recovery image from debuggable builds (i.e. `-eng` or `-userdebug` build variants, or
|
||||
`ro.debuggable=1` in `/prop.default`), `adbd` service is enabled and started by default, which
|
||||
allows `adb` communication. A device should be listed under `adb devices`, either in `recovery` or
|
||||
`sideload` state.
|
||||
|
||||
$ adb devices
|
||||
List of devices attached
|
||||
1234567890abcdef recovery
|
||||
|
||||
Although `/system/bin/adbd` is built from the same code base as the one in the normal boot, only a
|
||||
subset of `adb` commands are meaningful under recovery, such as `adb root`, `adb shell`, `adb push`,
|
||||
`adb pull` etc. Since Android Q, `adb shell` no longer requires manually mounting `/system` from
|
||||
recovery menu.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `adb devices` doesn't show the device.
|
||||
|
||||
$ adb devices
|
||||
List of devices attached
|
||||
|
||||
* Ensure `adbd` is built and running.
|
||||
|
||||
By default, `adbd` is always included into recovery image, as `/system/bin/adbd`. `init` starts
|
||||
`adbd` service automatically only in debuggable builds. This behavior is controlled by the recovery
|
||||
specific `/init.rc`, whose source code is at `bootable/recovery/etc/init.rc`.
|
||||
|
||||
The best way to confirm a running `adbd` is by checking the serial output, which shows a service
|
||||
start log as below.
|
||||
|
||||
[ 18.961986] c1 1 init: starting service 'adbd'...
|
||||
|
||||
* Ensure USB gadget has been enabled.
|
||||
|
||||
If `adbd` service has been started but device not shown under `adb devices`, use `lsusb(8)` (on
|
||||
host) to check if the device is visible to the host.
|
||||
|
||||
`bootable/recovery/etc/init.rc` disables Android USB gadget (via sysfs) as part of the `fs` action
|
||||
trigger, and will only re-enable it in debuggable builds (the `on property` rule will always run
|
||||
_after_ `on fs`).
|
||||
|
||||
on fs
|
||||
write /sys/class/android_usb/android0/enable 0
|
||||
|
||||
# Always start adbd on userdebug and eng builds
|
||||
on property:ro.debuggable=1
|
||||
write /sys/class/android_usb/android0/enable 1
|
||||
start adbd
|
||||
|
||||
If device is using [configfs](https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt),
|
||||
check if configfs has been properly set up in init rc scripts. See the [example
|
||||
configuration](https://android.googlesource.com/device/google/wahoo/+/master/init.recovery.hardware.rc)
|
||||
for Pixel 2 devices. Note that the flag set via sysfs (i.e. the one above) is no-op when using
|
||||
configfs.
|
||||
|
||||
### `adb devices` shows the device, but in `unauthorized` state.
|
||||
|
||||
$ adb devices
|
||||
List of devices attached
|
||||
1234567890abcdef unauthorized
|
||||
|
||||
recovery image doesn't honor the USB debugging toggle and the authorizations added under normal boot
|
||||
(because such authorization data stays in /data, which recovery doesn't mount), nor does it support
|
||||
authorizing a host device under recovery. We can use one of the following options instead.
|
||||
|
||||
* **Option 1 (Recommended):** Authorize a host device with adb vendor keys.
|
||||
|
||||
For debuggable builds, an RSA keypair can be used to authorize a host device that has the private
|
||||
key. The public key, defined via `PRODUCT_ADB_KEYS`, will be copied to `/adb_keys`. When starting
|
||||
the host-side `adbd`, make sure the filename (or the directory) of the matching private key has been
|
||||
added to `$ADB_VENDOR_KEYS`.
|
||||
|
||||
$ export ADB_VENDOR_KEYS=/path/to/adb/private/key
|
||||
$ adb kill-server
|
||||
$ adb devices
|
||||
|
||||
`-user` builds filter out `PRODUCT_ADB_KEYS`, so no `/adb_keys` will be included there.
|
||||
|
||||
Note that this mechanism applies to both of normal boot and recovery modes.
|
||||
|
||||
* **Option 2:** Allow `adbd` to connect without authentication.
|
||||
* `adbd` is compiled with `ALLOW_ADBD_NO_AUTH` (only on debuggable builds).
|
||||
* `ro.adb.secure` has a value of `0`.
|
||||
|
||||
Both of the two conditions need to be satisfied. Although `ro.adb.secure` is a runtime property, its
|
||||
value is set at build time (written into `/prop.default`). It defaults to `1` on `-user` builds, and
|
||||
`0` for other build variants. The value is overridable via `PRODUCT_DEFAULT_PROPERTY_OVERRIDES`.
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
@@ -53,7 +53,6 @@ cc_library_static {
|
||||
"libbz",
|
||||
"libcrypto",
|
||||
"libedify",
|
||||
"libotafault",
|
||||
"libotautil",
|
||||
"libz",
|
||||
],
|
||||
@@ -100,7 +99,6 @@ cc_binary {
|
||||
"libapplypatch_modes",
|
||||
"libapplypatch",
|
||||
"libedify",
|
||||
"libotafault",
|
||||
"libotautil",
|
||||
"libbspatch",
|
||||
],
|
||||
|
||||
+347
-314
@@ -23,35 +23,33 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "bmlutils/bmlutils.h"
|
||||
#include "mtdutils/mtdutils.h"
|
||||
|
||||
#include "edify/expr.h"
|
||||
#include "otafault/ota_io.h"
|
||||
#include "otautil/cache_location.h"
|
||||
#include "otautil/paths.h"
|
||||
#include "otautil/print_sha1.h"
|
||||
|
||||
static int LoadPartitionContents(const std::string& filename, FileContents* file);
|
||||
static size_t FileSink(const unsigned char* data, size_t len, int fd);
|
||||
static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch,
|
||||
const std::string& target_filename,
|
||||
const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data);
|
||||
using namespace std::string_literals;
|
||||
|
||||
<<<<<<< HEAD
|
||||
static bool mtd_partitions_scanned = false;
|
||||
|
||||
// Read a file into memory; store the file contents and associated metadata in *file.
|
||||
@@ -64,30 +62,29 @@ int LoadFileContents(const char* filename, FileContents* file) {
|
||||
strncmp(filename, "BML:", 4) == 0) {
|
||||
return LoadPartitionContents(filename, file);
|
||||
}
|
||||
=======
|
||||
static bool GenerateTarget(const Partition& target, const FileContents& source_file,
|
||||
const Value& patch, const Value* bonus_data);
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
struct stat sb;
|
||||
if (stat(filename, &sb) == -1) {
|
||||
printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
bool LoadFileContents(const std::string& filename, FileContents* file) {
|
||||
// No longer allow loading contents from eMMC partitions.
|
||||
if (android::base::StartsWith(filename, "EMMC:")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> data(sb.st_size);
|
||||
unique_file f(ota_fopen(filename, "rb"));
|
||||
if (!f) {
|
||||
printf("failed to open \"%s\": %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
std::string data;
|
||||
if (!android::base::ReadFileToString(filename, &data)) {
|
||||
PLOG(ERROR) << "Failed to read \"" << filename << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t bytes_read = ota_fread(data.data(), 1, data.size(), f.get());
|
||||
if (bytes_read != data.size()) {
|
||||
printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size());
|
||||
return -1;
|
||||
}
|
||||
file->data = std::move(data);
|
||||
file->data = std::vector<unsigned char>(data.begin(), data.end());
|
||||
SHA1(file->data.data(), file->data.size(), file->sha1);
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Load the contents of an EMMC partition into the provided
|
||||
// FileContents. filename should be a string of the form
|
||||
// "EMMC:<partition_device>:...". The smallest size_n bytes for
|
||||
@@ -142,30 +139,32 @@ static int LoadPartitionContents(const std::string& filename, FileContents* file
|
||||
if (!dev) {
|
||||
printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
|
||||
return -1;
|
||||
=======
|
||||
// Reads the contents of a Partition to the given FileContents buffer.
|
||||
static bool ReadPartitionToBuffer(const Partition& partition, FileContents* out,
|
||||
bool check_backup) {
|
||||
uint8_t expected_sha1[SHA_DIGEST_LENGTH];
|
||||
if (ParseSha1(partition.hash, expected_sha1) != 0) {
|
||||
LOG(ERROR) << "Failed to parse target hash \"" << partition.hash << "\"";
|
||||
return false;
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
|
||||
SHA_CTX sha_ctx;
|
||||
SHA1_Init(&sha_ctx);
|
||||
|
||||
// Allocate enough memory to hold the largest size.
|
||||
std::vector<unsigned char> buffer(pairs[pair_count - 1].first);
|
||||
unsigned char* buffer_ptr = buffer.data();
|
||||
size_t buffer_size = 0; // # bytes read so far
|
||||
bool found = false;
|
||||
|
||||
for (const auto& pair : pairs) {
|
||||
size_t current_size = pair.first;
|
||||
const std::string& current_sha1 = pair.second;
|
||||
|
||||
// Read enough additional bytes to get us up to the next size. (Again,
|
||||
// we're trying the possibilities in order of increasing size).
|
||||
size_t next = current_size - buffer_size;
|
||||
if (next > 0) {
|
||||
size_t read = ota_fread(buffer_ptr, 1, next, dev.get());
|
||||
if (next != read) {
|
||||
printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition);
|
||||
return -1;
|
||||
android::base::unique_fd dev(open(partition.name.c_str(), O_RDONLY));
|
||||
if (dev == -1) {
|
||||
PLOG(ERROR) << "Failed to open eMMC partition \"" << partition << "\"";
|
||||
} else {
|
||||
std::vector<unsigned char> buffer(partition.size);
|
||||
if (!android::base::ReadFully(dev, buffer.data(), buffer.size())) {
|
||||
PLOG(ERROR) << "Failed to read " << buffer.size() << " bytes of data for partition "
|
||||
<< partition;
|
||||
} else {
|
||||
SHA1(buffer.data(), buffer.size(), out->sha1);
|
||||
if (memcmp(out->sha1, expected_sha1, SHA_DIGEST_LENGTH) == 0) {
|
||||
out->data = std::move(buffer);
|
||||
return true;
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
SHA1_Update(&sha_ctx, buffer_ptr, read);
|
||||
buffer_size += read;
|
||||
buffer_ptr += read;
|
||||
@@ -197,47 +196,39 @@ static int LoadPartitionContents(const std::string& filename, FileContents* file
|
||||
printf("partition read matched size %zu SHA-1 %s\n", current_size, current_sha1.c_str());
|
||||
found = true;
|
||||
break;
|
||||
=======
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// Ran off the end of the list of (size, sha1) pairs without finding a match.
|
||||
printf("contents of partition \"%s\" didn't match %s\n", partition, filename.c_str());
|
||||
return -1;
|
||||
if (!check_backup) {
|
||||
LOG(ERROR) << "Partition contents don't have the expected checksum";
|
||||
return false;
|
||||
}
|
||||
|
||||
SHA1_Final(file->sha1, &sha_ctx);
|
||||
if (LoadFileContents(Paths::Get().cache_temp_source(), out) &&
|
||||
memcmp(out->sha1, expected_sha1, SHA_DIGEST_LENGTH) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
buffer.resize(buffer_size);
|
||||
file->data = std::move(buffer);
|
||||
|
||||
return 0;
|
||||
LOG(ERROR) << "Both of partition contents and backup don't have the expected checksum";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the contents of the given FileContents object under the given
|
||||
// filename. Return 0 on success.
|
||||
int SaveFileContents(const char* filename, const FileContents* file) {
|
||||
unique_fd fd(ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR));
|
||||
bool SaveFileContents(const std::string& filename, const FileContents* file) {
|
||||
android::base::unique_fd fd(
|
||||
open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR));
|
||||
if (fd == -1) {
|
||||
printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
PLOG(ERROR) << "Failed to open \"" << filename << "\" for write";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t bytes_written = FileSink(file->data.data(), file->data.size(), fd);
|
||||
if (bytes_written != file->data.size()) {
|
||||
printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written,
|
||||
file->data.size(), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (ota_fsync(fd) != 0) {
|
||||
printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (ota_close(fd) != 0) {
|
||||
printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
if (!android::base::WriteFully(fd, file->data.data(), file->data.size())) {
|
||||
PLOG(ERROR) << "Failed to write " << file->data.size() << " bytes of data to " << filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -264,10 +255,78 @@ int WriteToPartition(const unsigned char* data, size_t len, const std::string& t
|
||||
} else {
|
||||
printf("WriteToPartition called with bad target (%s)\n", target.c_str());
|
||||
return -1;
|
||||
=======
|
||||
if (fsync(fd) != 0) {
|
||||
PLOG(ERROR) << "Failed to fsync \"" << filename << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (close(fd.release()) != 0) {
|
||||
PLOG(ERROR) << "Failed to close \"" << filename << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Writes a memory buffer to 'target' Partition.
|
||||
static bool WriteBufferToPartition(const FileContents& file_contents, const Partition& partition) {
|
||||
const unsigned char* data = file_contents.data.data();
|
||||
size_t len = file_contents.data.size();
|
||||
size_t start = 0;
|
||||
bool success = false;
|
||||
for (size_t attempt = 0; attempt < 2; ++attempt) {
|
||||
android::base::unique_fd fd(open(partition.name.c_str(), O_RDWR));
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Failed to open \"" << partition << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) {
|
||||
PLOG(ERROR) << "Failed to seek to " << start << " on \"" << partition << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!android::base::WriteFully(fd, data + start, len - start)) {
|
||||
PLOG(ERROR) << "Failed to write " << len - start << " bytes to \"" << partition << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fsync(fd) != 0) {
|
||||
PLOG(ERROR) << "Failed to sync \"" << partition << "\"";
|
||||
return false;
|
||||
}
|
||||
if (close(fd.release()) != 0) {
|
||||
PLOG(ERROR) << "Failed to close \"" << partition << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
fd.reset(open(partition.name.c_str(), O_RDONLY));
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Failed to reopen \"" << partition << "\" for verification";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drop caches so our subsequent verification read won't just be reading the cache.
|
||||
sync();
|
||||
std::string drop_cache = "/proc/sys/vm/drop_caches";
|
||||
if (!android::base::WriteStringToFile("3\n", drop_cache)) {
|
||||
PLOG(ERROR) << "Failed to write to " << drop_cache;
|
||||
} else {
|
||||
LOG(INFO) << " caches dropped";
|
||||
}
|
||||
sleep(1);
|
||||
|
||||
// Verify.
|
||||
if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
|
||||
PLOG(ERROR) << "Failed to seek to 0 on " << partition;
|
||||
return false;
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
|
||||
const char* partition = pieces[1].c_str();
|
||||
|
||||
<<<<<<< HEAD
|
||||
if (pieces[0] == "BML") {
|
||||
if (strcmp(partition, "boot") == 0) {
|
||||
partition = BOARD_BML_BOOT;
|
||||
@@ -482,290 +541,264 @@ static int FindMatchingPatch(uint8_t* sha1, const std::vector<std::string>& patc
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
=======
|
||||
if (!android::base::ReadFully(fd, buffer, to_read)) {
|
||||
PLOG(ERROR) << "Failed to verify-read " << partition << " at " << p;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns 0 if the contents of the file (argv[2]) or the cached file
|
||||
// match any of the sha1's on the command line (argv[3:]). Returns
|
||||
// nonzero otherwise.
|
||||
int applypatch_check(const char* filename, const std::vector<std::string>& patch_sha1_str) {
|
||||
FileContents file;
|
||||
|
||||
// It's okay to specify no sha1s; the check will pass if the
|
||||
// LoadFileContents is successful. (Useful for reading
|
||||
// partitions, where the filename encodes the sha1s; no need to
|
||||
// check them twice.)
|
||||
if (LoadFileContents(filename, &file) != 0 ||
|
||||
(!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) {
|
||||
printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename);
|
||||
|
||||
// If the source file is missing or corrupted, it might be because we were killed in the middle
|
||||
// of patching it. A copy of it should have been made in cache_temp_source. If that file
|
||||
// exists and matches the sha1 we're looking for, the check still passes.
|
||||
if (LoadFileContents(CacheLocation::location().cache_temp_source().c_str(), &file) != 0) {
|
||||
printf("failed to load cache file\n");
|
||||
return 1;
|
||||
if (memcmp(buffer, data + p, to_read) != 0) {
|
||||
LOG(ERROR) << "Verification failed starting at " << p;
|
||||
start = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (FindMatchingPatch(file.sha1, patch_sha1_str) < 0) {
|
||||
printf("cache bits don't match any sha1 for \"%s\"\n", filename);
|
||||
return 1;
|
||||
if (start == len) {
|
||||
LOG(INFO) << "Verification read succeeded (attempt " << attempt + 1 << ")";
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (close(fd.release()) != 0) {
|
||||
PLOG(ERROR) << "Failed to close " << partition;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
LOG(ERROR) << "Failed to verify after all attempts";
|
||||
return false;
|
||||
}
|
||||
|
||||
sync();
|
||||
|
||||
return true;
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
|
||||
int ParseSha1(const std::string& str, uint8_t* digest) {
|
||||
const char* ps = str.c_str();
|
||||
uint8_t* pd = digest;
|
||||
for (int i = 0; i < SHA_DIGEST_LENGTH * 2; ++i, ++ps) {
|
||||
int digit;
|
||||
if (*ps >= '0' && *ps <= '9') {
|
||||
digit = *ps - '0';
|
||||
} else if (*ps >= 'a' && *ps <= 'f') {
|
||||
digit = *ps - 'a' + 10;
|
||||
} else if (*ps >= 'A' && *ps <= 'F') {
|
||||
digit = *ps - 'A' + 10;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
if (i % 2 == 0) {
|
||||
*pd = digit << 4;
|
||||
} else {
|
||||
*pd |= digit;
|
||||
++pd;
|
||||
}
|
||||
}
|
||||
if (*ps != '\0') return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PatchPartitionCheck(const Partition& target, const Partition& source) {
|
||||
FileContents target_file;
|
||||
FileContents source_file;
|
||||
return (ReadPartitionToBuffer(target, &target_file, false) ||
|
||||
ReadPartitionToBuffer(source, &source_file, true));
|
||||
}
|
||||
|
||||
int ShowLicenses() {
|
||||
ShowBSDiffLicense();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t FileSink(const unsigned char* data, size_t len, int fd) {
|
||||
size_t done = 0;
|
||||
while (done < len) {
|
||||
ssize_t wrote = TEMP_FAILURE_RETRY(ota_write(fd, data + done, len - done));
|
||||
if (wrote == -1) {
|
||||
printf("error writing %zd bytes: %s\n", (len - done), strerror(errno));
|
||||
return done;
|
||||
}
|
||||
done += wrote;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
// Return the amount of free space (in bytes) on the filesystem
|
||||
// containing filename. filename must exist. Return -1 on error.
|
||||
size_t FreeSpaceForFile(const char* filename) {
|
||||
struct statfs sf;
|
||||
if (statfs(filename, &sf) != 0) {
|
||||
printf("failed to statfs %s: %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return sf.f_bsize * sf.f_bavail;
|
||||
}
|
||||
|
||||
int CacheSizeCheck(size_t bytes) {
|
||||
if (MakeFreeSpaceOnCache(bytes) < 0) {
|
||||
printf("unable to make %zu bytes available on /cache\n", bytes);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This function applies binary patches to EMMC target 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).
|
||||
//
|
||||
// - If the SHA-1 hash of <target_filename> is <target_sha1_string>, does nothing and exits
|
||||
// successfully.
|
||||
//
|
||||
// - Otherwise, if the SHA-1 hash of <source_filename> is one of the entries in <patch_sha1_str>,
|
||||
// the corresponding patch from <patch_data> (which must be a VAL_BLOB) is applied to produce a
|
||||
// new file (the type of patch is automatically detected from the blob data). If that new file
|
||||
// has SHA-1 hash <target_sha1_str>, moves it to replace <target_filename>, and exits
|
||||
// successfully. Note that if <source_filename> and <target_filename> are not the same,
|
||||
// <source_filename> is NOT deleted on success. <target_filename> may be the string "-" to mean
|
||||
// "the same as <source_filename>".
|
||||
//
|
||||
// - Otherwise, or if any error is encountered, exits with non-zero status.
|
||||
//
|
||||
// <source_filename> must refer to an EMMC partition to read the source data. See the comments for
|
||||
// the LoadPartitionContents() function above for the format of such a filename. <target_size> has
|
||||
// become obsolete since we have dropped the support for patching non-EMMC targets (EMMC targets
|
||||
// have the size embedded in the filename).
|
||||
int applypatch(const char* source_filename, const char* target_filename,
|
||||
const char* target_sha1_str, size_t /* target_size */,
|
||||
const std::vector<std::string>& patch_sha1_str,
|
||||
const std::vector<std::unique_ptr<Value>>& patch_data, const Value* bonus_data) {
|
||||
printf("patch %s: ", source_filename);
|
||||
|
||||
if (target_filename[0] == '-' && target_filename[1] == '\0') {
|
||||
target_filename = source_filename;
|
||||
}
|
||||
|
||||
if (strncmp(target_filename, "EMMC:", 5) != 0) {
|
||||
printf("Supporting patching EMMC targets only.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t target_sha1[SHA_DIGEST_LENGTH];
|
||||
if (ParseSha1(target_sha1_str, target_sha1) != 0) {
|
||||
printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We try to load the target file into the source_file object.
|
||||
FileContents source_file;
|
||||
if (LoadFileContents(target_filename, &source_file) == 0) {
|
||||
if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
|
||||
// The early-exit case: the patch was already applied, this file has the desired hash, nothing
|
||||
// for us to do.
|
||||
printf("already %s\n", short_sha1(target_sha1).c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (source_file.data.empty() ||
|
||||
(target_filename != source_filename && strcmp(target_filename, source_filename) != 0)) {
|
||||
// Need to load the source file: either we failed to load the target file, or we did but it's
|
||||
// different from the expected.
|
||||
source_file.data.clear();
|
||||
LoadFileContents(source_filename, &source_file);
|
||||
}
|
||||
|
||||
if (!source_file.data.empty()) {
|
||||
int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str);
|
||||
if (to_use != -1) {
|
||||
return GenerateTarget(source_file, patch_data[to_use], target_filename, target_sha1,
|
||||
bonus_data);
|
||||
}
|
||||
}
|
||||
|
||||
printf("source file is bad; trying copy\n");
|
||||
|
||||
FileContents copy_file;
|
||||
if (LoadFileContents(CacheLocation::location().cache_temp_source().c_str(), ©_file) < 0) {
|
||||
printf("failed to read copy file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int to_use = FindMatchingPatch(copy_file.sha1, patch_sha1_str);
|
||||
if (to_use == -1) {
|
||||
printf("copy file doesn't match source SHA-1s either\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return GenerateTarget(copy_file, patch_data[to_use], target_filename, target_sha1, bonus_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function flashes a given image to the target partition. It verifies
|
||||
* the target cheksum first, and will return if target has the desired hash.
|
||||
* It checks the checksum of the given source image before flashing, and
|
||||
* verifies the target partition afterwards. The function is idempotent.
|
||||
* Returns zero on success.
|
||||
*/
|
||||
int applypatch_flash(const char* source_filename, const char* target_filename,
|
||||
const char* target_sha1_str, size_t target_size) {
|
||||
printf("flash %s: ", target_filename);
|
||||
|
||||
uint8_t target_sha1[SHA_DIGEST_LENGTH];
|
||||
if (ParseSha1(target_sha1_str, target_sha1) != 0) {
|
||||
printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string target_str(target_filename);
|
||||
std::vector<std::string> pieces = android::base::Split(target_str, ":");
|
||||
if (pieces.size() != 2 || pieces[0] != "EMMC") {
|
||||
printf("invalid target name \"%s\"", target_filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load the target into the source_file object to see if already applied.
|
||||
pieces.push_back(std::to_string(target_size));
|
||||
pieces.push_back(target_sha1_str);
|
||||
std::string fullname = android::base::Join(pieces, ':');
|
||||
FileContents source_file;
|
||||
if (LoadPartitionContents(fullname, &source_file) == 0 &&
|
||||
memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
|
||||
// The early-exit case: the image was already applied, this partition
|
||||
// has the desired hash, nothing for us to do.
|
||||
printf("already %s\n", short_sha1(target_sha1).c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LoadFileContents(source_filename, &source_file) == 0) {
|
||||
if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
|
||||
// The source doesn't have desired checksum.
|
||||
printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename);
|
||||
printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(),
|
||||
short_sha1(source_file.sha1).c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) {
|
||||
printf("write of copied data to %s failed\n", target_filename);
|
||||
return 1;
|
||||
}
|
||||
ShowBSDiffLicense();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch,
|
||||
const std::string& target_filename,
|
||||
const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data) {
|
||||
if (patch->type != VAL_BLOB) {
|
||||
printf("patch is not a blob\n");
|
||||
return 1;
|
||||
bool PatchPartition(const Partition& target, const Partition& source, const Value& patch,
|
||||
const Value* bonus) {
|
||||
LOG(INFO) << "Patching " << target.name;
|
||||
|
||||
// We try to load and check against the target hash first.
|
||||
FileContents target_file;
|
||||
if (ReadPartitionToBuffer(target, &target_file, false)) {
|
||||
// The early-exit case: the patch was already applied, this file has the desired hash, nothing
|
||||
// for us to do.
|
||||
LOG(INFO) << " already " << target.hash.substr(0, 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* header = &patch->data[0];
|
||||
size_t header_bytes_read = patch->data.size();
|
||||
FileContents source_file;
|
||||
if (ReadPartitionToBuffer(source, &source_file, true)) {
|
||||
return GenerateTarget(target, source_file, patch, bonus);
|
||||
}
|
||||
|
||||
LOG(ERROR) << "Failed to find any match";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FlashPartition(const Partition& partition, const std::string& source_filename) {
|
||||
LOG(INFO) << "Flashing " << partition;
|
||||
|
||||
// We try to load and check against the target hash first.
|
||||
FileContents target_file;
|
||||
if (ReadPartitionToBuffer(partition, &target_file, false)) {
|
||||
// The early-exit case: the patch was already applied, this file has the desired hash, nothing
|
||||
// for us to do.
|
||||
LOG(INFO) << " already " << partition.hash.substr(0, 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
FileContents source_file;
|
||||
if (!LoadFileContents(source_filename, &source_file)) {
|
||||
LOG(ERROR) << "Failed to load source file";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t expected_sha1[SHA_DIGEST_LENGTH];
|
||||
if (ParseSha1(partition.hash, expected_sha1) != 0) {
|
||||
LOG(ERROR) << "Failed to parse source hash \"" << partition.hash << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(source_file.sha1, expected_sha1, SHA_DIGEST_LENGTH) != 0) {
|
||||
// The source doesn't have desired checksum.
|
||||
LOG(ERROR) << "source \"" << source_filename << "\" doesn't have expected SHA-1 sum";
|
||||
LOG(ERROR) << "expected: " << partition.hash.substr(0, 8)
|
||||
<< ", found: " << short_sha1(source_file.sha1);
|
||||
return false;
|
||||
}
|
||||
if (!WriteBufferToPartition(source_file, partition)) {
|
||||
LOG(ERROR) << "Failed to write to " << partition;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GenerateTarget(const Partition& target, const FileContents& source_file,
|
||||
const Value& patch, const Value* bonus_data) {
|
||||
uint8_t expected_sha1[SHA_DIGEST_LENGTH];
|
||||
if (ParseSha1(target.hash, expected_sha1) != 0) {
|
||||
LOG(ERROR) << "Failed to parse target hash \"" << target.hash << "\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (patch.type != Value::Type::BLOB) {
|
||||
LOG(ERROR) << "patch is not a blob";
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* header = patch.data.data();
|
||||
size_t header_bytes_read = patch.data.size();
|
||||
bool use_bsdiff = false;
|
||||
if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) {
|
||||
use_bsdiff = true;
|
||||
} else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) {
|
||||
use_bsdiff = false;
|
||||
} else {
|
||||
printf("Unknown patch file format\n");
|
||||
return 1;
|
||||
LOG(ERROR) << "Unknown patch file format";
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK(android::base::StartsWith(target_filename, "EMMC:"));
|
||||
|
||||
// We still write the original source to cache, in case the partition write is interrupted.
|
||||
if (MakeFreeSpaceOnCache(source_file.data.size()) < 0) {
|
||||
printf("not enough free space on /cache\n");
|
||||
return 1;
|
||||
// We write the original source to cache, in case the partition write is interrupted.
|
||||
if (!CheckAndFreeSpaceOnCache(source_file.data.size())) {
|
||||
LOG(ERROR) << "Not enough free space on /cache";
|
||||
return false;
|
||||
}
|
||||
if (SaveFileContents(CacheLocation::location().cache_temp_source().c_str(), &source_file) < 0) {
|
||||
printf("failed to back up source file\n");
|
||||
return 1;
|
||||
if (!SaveFileContents(Paths::Get().cache_temp_source(), &source_file)) {
|
||||
LOG(ERROR) << "Failed to back up source file";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We store the decoded output in memory.
|
||||
std::string memory_sink_str; // Don't need to reserve space.
|
||||
SinkFn sink = [&memory_sink_str](const unsigned char* data, size_t len) {
|
||||
memory_sink_str.append(reinterpret_cast<const char*>(data), len);
|
||||
FileContents patched;
|
||||
SHA_CTX ctx;
|
||||
SHA1_Init(&ctx);
|
||||
SinkFn sink = [&patched, &ctx](const unsigned char* data, size_t len) {
|
||||
SHA1_Update(&ctx, data, len);
|
||||
patched.data.insert(patched.data.end(), data, data + len);
|
||||
return len;
|
||||
};
|
||||
|
||||
SHA_CTX ctx;
|
||||
SHA1_Init(&ctx);
|
||||
|
||||
int result;
|
||||
if (use_bsdiff) {
|
||||
result =
|
||||
ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), *patch, 0, sink, &ctx);
|
||||
result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch, 0, sink);
|
||||
} else {
|
||||
result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), *patch, sink, &ctx,
|
||||
bonus_data);
|
||||
result =
|
||||
ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch, sink, bonus_data);
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
printf("applying patch failed\n");
|
||||
return 1;
|
||||
LOG(ERROR) << "Failed to apply the patch: " << result;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t current_target_sha1[SHA_DIGEST_LENGTH];
|
||||
SHA1_Final(current_target_sha1, &ctx);
|
||||
if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
|
||||
printf("patch did not produce expected sha1\n");
|
||||
return 1;
|
||||
} else {
|
||||
printf("now %s\n", short_sha1(target_sha1).c_str());
|
||||
SHA1_Final(patched.sha1, &ctx);
|
||||
if (memcmp(patched.sha1, expected_sha1, SHA_DIGEST_LENGTH) != 0) {
|
||||
LOG(ERROR) << "Patching did not produce the expected SHA-1 of " << short_sha1(expected_sha1);
|
||||
|
||||
LOG(ERROR) << "target size " << patched.data.size() << " SHA-1 " << short_sha1(patched.sha1);
|
||||
LOG(ERROR) << "source size " << source_file.data.size() << " SHA-1 "
|
||||
<< short_sha1(source_file.sha1);
|
||||
|
||||
uint8_t patch_digest[SHA_DIGEST_LENGTH];
|
||||
SHA1(reinterpret_cast<const uint8_t*>(patch.data.data()), patch.data.size(), patch_digest);
|
||||
LOG(ERROR) << "patch size " << patch.data.size() << " SHA-1 " << short_sha1(patch_digest);
|
||||
|
||||
if (bonus_data != nullptr) {
|
||||
uint8_t bonus_digest[SHA_DIGEST_LENGTH];
|
||||
SHA1(reinterpret_cast<const uint8_t*>(bonus_data->data.data()), bonus_data->data.size(),
|
||||
bonus_digest);
|
||||
LOG(ERROR) << "bonus size " << bonus_data->data.size() << " SHA-1 "
|
||||
<< short_sha1(bonus_digest);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << " now " << short_sha1(expected_sha1);
|
||||
|
||||
// Write back the temp file to the partition.
|
||||
if (WriteToPartition(reinterpret_cast<const unsigned char*>(memory_sink_str.c_str()),
|
||||
memory_sink_str.size(), target_filename) != 0) {
|
||||
printf("write of patched data to %s failed\n", target_filename.c_str());
|
||||
return 1;
|
||||
if (!WriteBufferToPartition(patched, target)) {
|
||||
LOG(ERROR) << "Failed to write patched data to " << target.name;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete the backup copy of the source.
|
||||
unlink(CacheLocation::location().cache_temp_source().c_str());
|
||||
unlink(Paths::Get().cache_temp_source().c_str());
|
||||
|
||||
// Success!
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckPartition(const Partition& partition) {
|
||||
FileContents target_file;
|
||||
return ReadPartitionToBuffer(partition, &target_file, false);
|
||||
}
|
||||
|
||||
Partition Partition::Parse(const std::string& input_str, std::string* err) {
|
||||
std::vector<std::string> pieces = android::base::Split(input_str, ":");
|
||||
if (pieces.size() != 4 || pieces[0] != "EMMC") {
|
||||
*err = "Invalid number of tokens or non-eMMC target";
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t size;
|
||||
if (!android::base::ParseUint(pieces[2], &size) || size == 0) {
|
||||
*err = "Failed to parse \"" + pieces[2] + "\" as byte count";
|
||||
return {};
|
||||
}
|
||||
|
||||
return Partition(pieces[1], size, pieces[3]);
|
||||
}
|
||||
|
||||
std::string Partition::ToString() const {
|
||||
if (*this) {
|
||||
return "EMMC:"s + name + ":" + std::to_string(size) + ":" + hash;
|
||||
}
|
||||
return "<invalid-partition>";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Partition& partition) {
|
||||
os << partition.ToString();
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
|
||||
#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.
|
||||
#include <android-base/logging.h>
|
||||
|
||||
// See the comments for applypatch() function.
|
||||
int main(int argc, char** argv) {
|
||||
return applypatch_modes(argc, const_cast<const char**>(argv));
|
||||
android::base::InitLogging(argv);
|
||||
return applypatch_modes(argc, argv);
|
||||
}
|
||||
|
||||
+149
-150
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "applypatch_modes.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -25,6 +26,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <openssl/sha.h>
|
||||
@@ -32,157 +35,153 @@
|
||||
#include "applypatch/applypatch.h"
|
||||
#include "edify/expr.h"
|
||||
|
||||
static int CheckMode(int argc, const char** argv) {
|
||||
if (argc < 3) {
|
||||
static int CheckMode(const std::string& target_emmc) {
|
||||
std::string err;
|
||||
auto target = Partition::Parse(target_emmc, &err);
|
||||
if (!target) {
|
||||
LOG(ERROR) << "Failed to parse target \"" << target_emmc << "\": " << err;
|
||||
return 2;
|
||||
}
|
||||
return CheckPartition(target) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int FlashMode(const std::string& target_emmc, const std::string& source_file) {
|
||||
std::string err;
|
||||
auto target = Partition::Parse(target_emmc, &err);
|
||||
if (!target) {
|
||||
LOG(ERROR) << "Failed to parse target \"" << target_emmc << "\": " << err;
|
||||
return 2;
|
||||
}
|
||||
return FlashPartition(target, source_file) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int PatchMode(const std::string& target_emmc, const std::string& source_emmc,
|
||||
const std::string& patch_file, const std::string& bonus_file) {
|
||||
std::string err;
|
||||
auto target = Partition::Parse(target_emmc, &err);
|
||||
if (!target) {
|
||||
LOG(ERROR) << "Failed to parse target \"" << target_emmc << "\": " << err;
|
||||
return 2;
|
||||
}
|
||||
|
||||
auto source = Partition::Parse(source_emmc, &err);
|
||||
if (!source) {
|
||||
LOG(ERROR) << "Failed to parse source \"" << source_emmc << "\": " << err;
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::string patch_contents;
|
||||
if (!android::base::ReadFileToString(patch_file, &patch_contents)) {
|
||||
PLOG(ERROR) << "Failed to read patch file \"" << patch_file << "\"";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Value patch(Value::Type::BLOB, std::move(patch_contents));
|
||||
std::unique_ptr<Value> bonus;
|
||||
if (!bonus_file.empty()) {
|
||||
std::string bonus_contents;
|
||||
if (!android::base::ReadFileToString(bonus_file, &bonus_contents)) {
|
||||
PLOG(ERROR) << "Failed to read bonus file \"" << bonus_file << "\"";
|
||||
return 1;
|
||||
}
|
||||
bonus = std::make_unique<Value>(Value::Type::BLOB, std::move(bonus_contents));
|
||||
}
|
||||
|
||||
return PatchPartition(target, source, patch, bonus.get()) ? 0 : 1;
|
||||
}
|
||||
|
||||
static void Usage() {
|
||||
printf(
|
||||
"Usage: \n"
|
||||
"check mode\n"
|
||||
" applypatch --check EMMC:<target-file>:<target-size>:<target-sha1>\n\n"
|
||||
"flash mode\n"
|
||||
" applypatch --flash <source-file>\n"
|
||||
" --target EMMC:<target-file>:<target-size>:<target-sha1>\n\n"
|
||||
"patch mode\n"
|
||||
" applypatch [--bonus <bonus-file>]\n"
|
||||
" --patch <patch-file>\n"
|
||||
" --target EMMC:<target-file>:<target-size>:<target-sha1>\n"
|
||||
" --source EMMC:<source-file>:<source-size>:<source-sha1>\n\n"
|
||||
"show license\n"
|
||||
" applypatch --license\n"
|
||||
"\n\n");
|
||||
}
|
||||
|
||||
int applypatch_modes(int argc, char* argv[]) {
|
||||
static constexpr struct option OPTIONS[]{
|
||||
// clang-format off
|
||||
{ "bonus", required_argument, nullptr, 0 },
|
||||
{ "check", required_argument, nullptr, 0 },
|
||||
{ "flash", required_argument, nullptr, 0 },
|
||||
{ "license", no_argument, nullptr, 0 },
|
||||
{ "patch", required_argument, nullptr, 0 },
|
||||
{ "source", required_argument, nullptr, 0 },
|
||||
{ "target", required_argument, nullptr, 0 },
|
||||
{ nullptr, 0, nullptr, 0 },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
std::string check_target;
|
||||
std::string source;
|
||||
std::string target;
|
||||
std::string patch;
|
||||
std::string bonus;
|
||||
|
||||
bool check_mode = false;
|
||||
bool flash_mode = false;
|
||||
bool patch_mode = false;
|
||||
|
||||
optind = 1;
|
||||
|
||||
int arg;
|
||||
int option_index;
|
||||
while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
|
||||
switch (arg) {
|
||||
case 0: {
|
||||
std::string option = OPTIONS[option_index].name;
|
||||
if (option == "bonus") {
|
||||
bonus = optarg;
|
||||
} else if (option == "check") {
|
||||
check_target = optarg;
|
||||
check_mode = true;
|
||||
} else if (option == "flash") {
|
||||
source = optarg;
|
||||
flash_mode = true;
|
||||
} else if (option == "license") {
|
||||
return ShowLicenses();
|
||||
} else if (option == "patch") {
|
||||
patch = optarg;
|
||||
patch_mode = true;
|
||||
} else if (option == "source") {
|
||||
source = optarg;
|
||||
} else if (option == "target") {
|
||||
target = optarg;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
default:
|
||||
LOG(ERROR) << "Invalid argument";
|
||||
Usage();
|
||||
return 2;
|
||||
}
|
||||
std::vector<std::string> sha1;
|
||||
for (int i = 3; i < argc; i++) {
|
||||
sha1.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return applypatch_check(argv[2], sha1);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (sha1s == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < argc; ++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;
|
||||
}
|
||||
|
||||
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(pieces[0]);
|
||||
FileContents fc;
|
||||
if (LoadFileContents(pieces[1].c_str(), &fc) != 0) {
|
||||
return false;
|
||||
}
|
||||
files->push_back(std::move(fc));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int FlashMode(const char* src_filename, const char* tgt_filename,
|
||||
const char* tgt_sha1, size_t tgt_size) {
|
||||
return applypatch_flash(src_filename, tgt_filename, tgt_sha1, tgt_size);
|
||||
}
|
||||
|
||||
static int PatchMode(int argc, const char** argv) {
|
||||
FileContents bonusFc;
|
||||
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.type = VAL_BLOB;
|
||||
bonus.data = std::string(bonusFc.data.cbegin(), bonusFc.data.cend());
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
}
|
||||
|
||||
if (argc < 4) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
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.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<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<std::unique_ptr<Value>> patches;
|
||||
for (size_t i = 0; i < files.size(); ++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, sha1s, patches, &bonus);
|
||||
}
|
||||
|
||||
// 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).
|
||||
//
|
||||
// - if the sha1 hash of <tgt-file> is <tgt-sha1>, does nothing and exits
|
||||
// successfully.
|
||||
//
|
||||
// - otherwise, if no <src-sha1>:<patch> is provided, flashes <tgt-file> with
|
||||
// <src-file>. <tgt-file> must be a partition name, while <src-file> must
|
||||
// be a regular image file. <src-file> will not be deleted on success.
|
||||
//
|
||||
// - otherwise, if the sha1 hash of <src-file> is <src-sha1>, applies the
|
||||
// bsdiff <patch> to <src-file> to produce a new file (the type of patch
|
||||
// is automatically detected from the file header). If that new
|
||||
// file has sha1 hash <tgt-sha1>, moves it to replace <tgt-file>, and
|
||||
// exits successfully. Note that if <src-file> and <tgt-file> are
|
||||
// not the same, <src-file> is NOT deleted on success. <tgt-file>
|
||||
// may be the string "-" to mean "the same as src-file".
|
||||
//
|
||||
// - otherwise, or if any error is encountered, exits with non-zero
|
||||
// status.
|
||||
//
|
||||
// <src-file> (or <file> in check mode) may refer to an EMMC partition
|
||||
// to read the source data. See the comments for the
|
||||
// LoadPartitionContents() function for the format of such a filename.
|
||||
|
||||
int applypatch_modes(int argc, const char** argv) {
|
||||
if (argc < 2) {
|
||||
usage:
|
||||
printf(
|
||||
"usage: %s [-b <bonus-file>] <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
|
||||
"[<src-sha1>:<patch> ...]\n"
|
||||
" or %s -c <file> [<sha1> ...]\n"
|
||||
" or %s -l\n"
|
||||
"\n"
|
||||
"Filenames may be of the form\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]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int result;
|
||||
|
||||
if (strncmp(argv[1], "-l", 3) == 0) {
|
||||
result = ShowLicenses();
|
||||
} else if (strncmp(argv[1], "-c", 3) == 0) {
|
||||
result = CheckMode(argc, argv);
|
||||
} else {
|
||||
result = PatchMode(argc, argv);
|
||||
}
|
||||
|
||||
if (result == 2) {
|
||||
goto usage;
|
||||
}
|
||||
return result;
|
||||
if (check_mode) {
|
||||
return CheckMode(check_target);
|
||||
}
|
||||
if (flash_mode) {
|
||||
if (!bonus.empty()) {
|
||||
LOG(ERROR) << "bonus file not supported in flash mode";
|
||||
return 1;
|
||||
}
|
||||
return FlashMode(target, source);
|
||||
}
|
||||
if (patch_mode) {
|
||||
return PatchMode(target, source, patch, bonus);
|
||||
}
|
||||
|
||||
Usage();
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
#ifndef _APPLYPATCH_MODES_H
|
||||
#define _APPLYPATCH_MODES_H
|
||||
|
||||
int applypatch_modes(int argc, const char** argv);
|
||||
int applypatch_modes(int argc, char* argv[]);
|
||||
|
||||
#endif // _APPLYPATCH_MODES_H
|
||||
|
||||
@@ -66,18 +66,12 @@ void ShowBSDiffLicense() {
|
||||
}
|
||||
|
||||
int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch,
|
||||
size_t patch_offset, SinkFn sink, SHA_CTX* ctx) {
|
||||
auto sha_sink = [&sink, &ctx](const uint8_t* data, size_t len) {
|
||||
len = sink(data, len);
|
||||
if (ctx) SHA1_Update(ctx, data, len);
|
||||
return len;
|
||||
};
|
||||
|
||||
size_t patch_offset, SinkFn sink) {
|
||||
CHECK_LE(patch_offset, patch.data.size());
|
||||
|
||||
int result = bsdiff::bspatch(old_data, old_size,
|
||||
reinterpret_cast<const uint8_t*>(&patch.data[patch_offset]),
|
||||
patch.data.size() - patch_offset, sha_sink);
|
||||
patch.data.size() - patch_offset, sink);
|
||||
if (result != 0) {
|
||||
LOG(ERROR) << "bspatch failed, result: " << result;
|
||||
// print SHA1 of the patch in the case of a data error.
|
||||
|
||||
+161
-63
@@ -14,31 +14,35 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "applypatch/applypatch.h"
|
||||
#include "otautil/cache_location.h"
|
||||
#include "otautil/paths.h"
|
||||
|
||||
static int EliminateOpenFiles(std::set<std::string>* files) {
|
||||
static int EliminateOpenFiles(const std::string& dirname, std::set<std::string>* files) {
|
||||
std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir);
|
||||
if (!d) {
|
||||
printf("error opening /proc: %s\n", strerror(errno));
|
||||
PLOG(ERROR) << "Failed to open /proc";
|
||||
return -1;
|
||||
}
|
||||
struct dirent* de;
|
||||
@@ -52,7 +56,7 @@ static int EliminateOpenFiles(std::set<std::string>* files) {
|
||||
struct dirent* fdde;
|
||||
std::unique_ptr<DIR, decltype(&closedir)> fdd(opendir(path.c_str()), closedir);
|
||||
if (!fdd) {
|
||||
printf("error opening %s: %s\n", path.c_str(), strerror(errno));
|
||||
PLOG(ERROR) << "Failed to open " << path;
|
||||
continue;
|
||||
}
|
||||
while ((fdde = readdir(fdd.get())) != 0) {
|
||||
@@ -62,9 +66,9 @@ static int EliminateOpenFiles(std::set<std::string>* files) {
|
||||
int count = readlink(fd_path.c_str(), link, sizeof(link)-1);
|
||||
if (count >= 0) {
|
||||
link[count] = '\0';
|
||||
if (strncmp(link, "/cache/", 7) == 0) {
|
||||
if (android::base::StartsWith(link, dirname)) {
|
||||
if (files->erase(link) > 0) {
|
||||
printf("%s is open by %s\n", link, de->d_name);
|
||||
LOG(INFO) << link << " is open by " << de->d_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,77 +77,171 @@ static int EliminateOpenFiles(std::set<std::string>* files) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::set<std::string> FindExpendableFiles() {
|
||||
std::set<std::string> files;
|
||||
// We're allowed to delete unopened regular files in any of these
|
||||
// directories.
|
||||
const char* dirs[2] = {"/cache", "/cache/recovery/otatest"};
|
||||
static std::vector<std::string> FindExpendableFiles(
|
||||
const std::string& dirname, const std::function<bool(const std::string&)>& name_filter) {
|
||||
std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dirname.c_str()), closedir);
|
||||
if (!d) {
|
||||
PLOG(ERROR) << "Failed to open " << dirname;
|
||||
return {};
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) {
|
||||
std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dirs[i]), closedir);
|
||||
if (!d) {
|
||||
printf("error opening %s: %s\n", dirs[i], strerror(errno));
|
||||
// Look for regular files in the directory (not in any subdirectories).
|
||||
std::set<std::string> files;
|
||||
struct dirent* de;
|
||||
while ((de = readdir(d.get())) != 0) {
|
||||
std::string path = dirname + "/" + de->d_name;
|
||||
|
||||
// We can't delete cache_temp_source; if it's there we might have restarted during
|
||||
// installation and could be depending on it to be there.
|
||||
if (path == Paths::Get().cache_temp_source()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for regular files in the directory (not in any subdirectories).
|
||||
struct dirent* de;
|
||||
while ((de = readdir(d.get())) != 0) {
|
||||
std::string path = std::string(dirs[i]) + "/" + de->d_name;
|
||||
// Do not delete the file if it doesn't have the expected format.
|
||||
if (name_filter != nullptr && !name_filter(de->d_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We can't delete cache_temp_source; if it's there we might have restarted during
|
||||
// installation and could be depending on it to be there.
|
||||
if (path == CacheLocation::location().cache_temp_source()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
|
||||
files.insert(path);
|
||||
}
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
|
||||
files.insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
printf("%zu regular files in deletable directories\n", files.size());
|
||||
if (EliminateOpenFiles(&files) < 0) {
|
||||
return std::set<std::string>();
|
||||
LOG(INFO) << files.size() << " regular files in deletable directory";
|
||||
if (EliminateOpenFiles(dirname, &files) < 0) {
|
||||
return {};
|
||||
}
|
||||
return files;
|
||||
|
||||
return std::vector<std::string>(files.begin(), files.end());
|
||||
}
|
||||
|
||||
int MakeFreeSpaceOnCache(size_t bytes_needed) {
|
||||
#ifndef __ANDROID__
|
||||
// TODO (xunchang) implement a heuristic cache size check during host simulation.
|
||||
printf("Skip making (%zu) bytes free space on cache; program is running on host\n", bytes_needed);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
size_t free_now = FreeSpaceForFile("/cache");
|
||||
printf("%zu bytes free on /cache (%zu needed)\n", free_now, bytes_needed);
|
||||
|
||||
if (free_now >= bytes_needed) {
|
||||
// Parses the index of given log file, e.g. 3 for last_log.3; returns max number if the log name
|
||||
// doesn't have the expected format so that we'll delete these ones first.
|
||||
static unsigned int GetLogIndex(const std::string& log_name) {
|
||||
if (log_name == "last_log" || log_name == "last_kmsg") {
|
||||
return 0;
|
||||
}
|
||||
std::set<std::string> files = FindExpendableFiles();
|
||||
if (files.empty()) {
|
||||
// nothing we can delete to free up space!
|
||||
printf("no files can be deleted to free space on /cache\n");
|
||||
|
||||
unsigned int index;
|
||||
if (sscanf(log_name.c_str(), "last_log.%u", &index) == 1 ||
|
||||
sscanf(log_name.c_str(), "last_kmsg.%u", &index) == 1) {
|
||||
return index;
|
||||
}
|
||||
|
||||
return std::numeric_limits<unsigned int>::max();
|
||||
}
|
||||
|
||||
// Returns the amount of free space (in bytes) on the filesystem containing filename, or -1 on
|
||||
// error.
|
||||
static int64_t FreeSpaceForFile(const std::string& filename) {
|
||||
struct statfs sf;
|
||||
if (statfs(filename.c_str(), &sf) == -1) {
|
||||
PLOG(ERROR) << "Failed to statfs " << filename;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We could try to be smarter about which files to delete: the
|
||||
// biggest ones? the smallest ones that will free up enough space?
|
||||
// the oldest? the newest?
|
||||
//
|
||||
// Instead, we'll be dumb.
|
||||
auto f_bsize = static_cast<int64_t>(sf.f_bsize);
|
||||
auto free_space = sf.f_bsize * sf.f_bavail;
|
||||
if (f_bsize == 0 || free_space / f_bsize != static_cast<int64_t>(sf.f_bavail)) {
|
||||
LOG(ERROR) << "Invalid block size or overflow (sf.f_bsize " << sf.f_bsize << ", sf.f_bavail "
|
||||
<< sf.f_bavail << ")";
|
||||
return -1;
|
||||
}
|
||||
return free_space;
|
||||
}
|
||||
|
||||
for (const auto& file : files) {
|
||||
unlink(file.c_str());
|
||||
free_now = FreeSpaceForFile("/cache");
|
||||
printf("deleted %s; now %zu bytes free\n", file.c_str(), free_now);
|
||||
if (free_now < bytes_needed) {
|
||||
break;
|
||||
bool CheckAndFreeSpaceOnCache(size_t bytes) {
|
||||
#ifndef __ANDROID__
|
||||
// TODO(xunchang): Implement a heuristic cache size check during host simulation.
|
||||
LOG(WARNING) << "Skipped making (" << bytes
|
||||
<< ") bytes free space on /cache; program is running on host";
|
||||
return true;
|
||||
#endif
|
||||
|
||||
std::vector<std::string> dirs{ "/cache", Paths::Get().cache_log_directory() };
|
||||
for (const auto& dirname : dirs) {
|
||||
if (RemoveFilesInDirectory(bytes, dirname, FreeSpaceForFile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return (free_now >= bytes_needed) ? 0 : -1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RemoveFilesInDirectory(size_t bytes_needed, const std::string& dirname,
|
||||
const std::function<int64_t(const std::string&)>& space_checker) {
|
||||
// The requested size cannot exceed max int64_t.
|
||||
if (static_cast<uint64_t>(bytes_needed) >
|
||||
static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
|
||||
LOG(ERROR) << "Invalid arg of bytes_needed: " << bytes_needed;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(dirname.c_str(), &st) == -1) {
|
||||
PLOG(ERROR) << "Failed to stat " << dirname;
|
||||
return false;
|
||||
}
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
LOG(ERROR) << dirname << " is not a directory";
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t free_now = space_checker(dirname);
|
||||
if (free_now == -1) {
|
||||
LOG(ERROR) << "Failed to check free space for " << dirname;
|
||||
return false;
|
||||
}
|
||||
LOG(INFO) << free_now << " bytes free on " << dirname << " (" << bytes_needed << " needed)";
|
||||
|
||||
if (free_now >= static_cast<int64_t>(bytes_needed)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> files;
|
||||
if (dirname == Paths::Get().cache_log_directory()) {
|
||||
// Deletes the log files only.
|
||||
auto log_filter = [](const std::string& file_name) {
|
||||
return android::base::StartsWith(file_name, "last_log") ||
|
||||
android::base::StartsWith(file_name, "last_kmsg");
|
||||
};
|
||||
|
||||
files = FindExpendableFiles(dirname, log_filter);
|
||||
|
||||
// Older logs will come to the top of the queue.
|
||||
auto comparator = [](const std::string& name1, const std::string& name2) -> bool {
|
||||
unsigned int index1 = GetLogIndex(android::base::Basename(name1));
|
||||
unsigned int index2 = GetLogIndex(android::base::Basename(name2));
|
||||
if (index1 == index2) {
|
||||
return name1 < name2;
|
||||
}
|
||||
|
||||
return index1 > index2;
|
||||
};
|
||||
|
||||
std::sort(files.begin(), files.end(), comparator);
|
||||
} else {
|
||||
// We're allowed to delete unopened regular files in the directory.
|
||||
files = FindExpendableFiles(dirname, nullptr);
|
||||
}
|
||||
|
||||
for (const auto& file : files) {
|
||||
if (unlink(file.c_str()) == -1) {
|
||||
PLOG(ERROR) << "Failed to delete " << file;
|
||||
continue;
|
||||
}
|
||||
|
||||
free_now = space_checker(dirname);
|
||||
if (free_now == -1) {
|
||||
LOG(ERROR) << "Failed to check free space for " << dirname;
|
||||
return false;
|
||||
}
|
||||
LOG(INFO) << "Deleted " << file << "; now " << free_now << " bytes free";
|
||||
if (free_now >= static_cast<int64_t>(bytes_needed)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -462,12 +462,12 @@ PatchChunk::PatchChunk(const ImageChunk& tgt)
|
||||
target_len_(tgt.GetRawDataLength()),
|
||||
target_uncompressed_len_(tgt.DataLengthForPatch()),
|
||||
target_compress_level_(tgt.GetCompressLevel()),
|
||||
data_(tgt.DataForPatch(), tgt.DataForPatch() + tgt.DataLengthForPatch()) {}
|
||||
data_(tgt.GetRawData(), tgt.GetRawData() + tgt.GetRawDataLength()) {}
|
||||
|
||||
// Return true if raw data is smaller than the patch size.
|
||||
bool PatchChunk::RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size) {
|
||||
size_t target_len = tgt.GetRawDataLength();
|
||||
return (tgt.GetType() == CHUNK_NORMAL && (target_len <= 160 || target_len < patch_size));
|
||||
return target_len < patch_size || (tgt.GetType() == CHUNK_NORMAL && target_len <= 160);
|
||||
}
|
||||
|
||||
void PatchChunk::UpdateSourceOffset(const SortedRangeSet& src_range) {
|
||||
|
||||
+22
-20
@@ -38,6 +38,7 @@
|
||||
#include <zlib.h>
|
||||
|
||||
#include "edify/expr.h"
|
||||
#include "otautil/print_sha1.h"
|
||||
|
||||
static inline int64_t Read8(const void *address) {
|
||||
return android::base::get_unaligned<int64_t>(address);
|
||||
@@ -51,8 +52,9 @@ static inline int32_t Read4(const void *address) {
|
||||
// patched data and stream the deflated data to output.
|
||||
static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len,
|
||||
const Value& patch, size_t patch_offset,
|
||||
const char* deflate_header, SinkFn sink, SHA_CTX* ctx) {
|
||||
const char* deflate_header, SinkFn sink) {
|
||||
size_t expected_target_length = static_cast<size_t>(Read8(deflate_header + 32));
|
||||
CHECK_GT(expected_target_length, static_cast<size_t>(0));
|
||||
int level = Read4(deflate_header + 40);
|
||||
int method = Read4(deflate_header + 44);
|
||||
int window_bits = Read4(deflate_header + 48);
|
||||
@@ -77,7 +79,7 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_
|
||||
size_t total_written = 0;
|
||||
static constexpr size_t buffer_size = 32768;
|
||||
auto compression_sink = [&strm, &actual_target_length, &expected_target_length, &total_written,
|
||||
&ret, &ctx, &sink](const uint8_t* data, size_t len) -> size_t {
|
||||
&ret, &sink](const uint8_t* data, size_t len) -> size_t {
|
||||
// The input patch length for an update never exceeds INT_MAX.
|
||||
strm.avail_in = len;
|
||||
strm.next_in = data;
|
||||
@@ -102,15 +104,13 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_
|
||||
LOG(ERROR) << "Failed to write " << have << " compressed bytes to output.";
|
||||
return 0;
|
||||
}
|
||||
if (ctx) SHA1_Update(ctx, buffer.data(), have);
|
||||
} while ((strm.avail_in != 0 || strm.avail_out == 0) && ret != Z_STREAM_END);
|
||||
|
||||
actual_target_length += len;
|
||||
return len;
|
||||
};
|
||||
|
||||
int bspatch_result =
|
||||
ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink, nullptr);
|
||||
int bspatch_result = ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink);
|
||||
deflateEnd(&strm);
|
||||
|
||||
if (bspatch_result != 0) {
|
||||
@@ -127,19 +127,20 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_
|
||||
<< actual_target_length;
|
||||
return false;
|
||||
}
|
||||
LOG(DEBUG) << "bspatch writes " << total_written << " bytes in total to streaming output.";
|
||||
LOG(DEBUG) << "bspatch wrote " << total_written << " bytes in total to streaming output.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data,
|
||||
size_t patch_size, SinkFn sink) {
|
||||
Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size));
|
||||
return ApplyImagePatch(old_data, old_size, patch, sink, nullptr, nullptr);
|
||||
Value patch(Value::Type::BLOB,
|
||||
std::string(reinterpret_cast<const char*>(patch_data), patch_size));
|
||||
return ApplyImagePatch(old_data, old_size, patch, sink, nullptr);
|
||||
}
|
||||
|
||||
int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink,
|
||||
SHA_CTX* ctx, const Value* bonus_data) {
|
||||
const Value* bonus_data) {
|
||||
if (patch.data.size() < 12) {
|
||||
printf("patch too short to contain header\n");
|
||||
return -1;
|
||||
@@ -180,10 +181,12 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value&
|
||||
printf("source data too short\n");
|
||||
return -1;
|
||||
}
|
||||
if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx) != 0) {
|
||||
if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink) != 0) {
|
||||
printf("Failed to apply bsdiff patch.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Processed chunk type normal";
|
||||
} else if (type == CHUNK_RAW) {
|
||||
const char* raw_header = patch_header + pos;
|
||||
pos += 4;
|
||||
@@ -198,14 +201,13 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value&
|
||||
printf("failed to read chunk %d raw data\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ctx) {
|
||||
SHA1_Update(ctx, patch_header + pos, data_len);
|
||||
}
|
||||
if (sink(reinterpret_cast<const unsigned char*>(patch_header + pos), data_len) != data_len) {
|
||||
printf("failed to write chunk %d raw data\n", i);
|
||||
return -1;
|
||||
}
|
||||
pos += data_len;
|
||||
|
||||
LOG(DEBUG) << "Processed chunk type raw";
|
||||
} else if (type == CHUNK_DEFLATE) {
|
||||
// deflate chunks have an additional 60 bytes in their chunk header.
|
||||
const char* deflate_header = patch_header + pos;
|
||||
@@ -228,11 +230,10 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value&
|
||||
// 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;
|
||||
// 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 != nullptr) ? bonus_data->data.size() : 0;
|
||||
|
||||
std::vector<unsigned char> expanded_source(expanded_len);
|
||||
|
||||
@@ -270,17 +271,18 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value&
|
||||
inflateEnd(&strm);
|
||||
|
||||
if (bonus_size) {
|
||||
memcpy(expanded_source.data() + (expanded_len - bonus_size), &bonus_data->data[0],
|
||||
memcpy(expanded_source.data() + (expanded_len - bonus_size), bonus_data->data.data(),
|
||||
bonus_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ApplyBSDiffPatchAndStreamOutput(expanded_source.data(), expanded_len, patch,
|
||||
patch_offset, deflate_header, sink, ctx)) {
|
||||
patch_offset, deflate_header, sink)) {
|
||||
LOG(ERROR) << "Fail to apply streaming bspatch.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Processed chunk type deflate";
|
||||
} else {
|
||||
printf("patch chunk %d is unknown type %d\n", i, type);
|
||||
return -1;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -39,45 +40,91 @@ using SinkFn = std::function<size_t(const unsigned char*, size_t)>;
|
||||
// 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(const char* source_filename,
|
||||
const char* target_filename,
|
||||
const char* target_sha1_str,
|
||||
size_t target_size,
|
||||
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,
|
||||
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);
|
||||
// Parses a given string of 40 hex digits into 20-byte array 'digest'. 'str' may contain only the
|
||||
// digest or be of the form "<digest>:<anything>". Returns 0 on success, or -1 on any error.
|
||||
int ParseSha1(const std::string& str, uint8_t* digest);
|
||||
|
||||
int LoadFileContents(const char* filename, FileContents* file);
|
||||
int SaveFileContents(const char* filename, const FileContents* file);
|
||||
struct Partition {
|
||||
Partition() = default;
|
||||
|
||||
Partition(const std::string& name, size_t size, const std::string& hash)
|
||||
: name(name), size(size), hash(hash) {}
|
||||
|
||||
// Parses and returns the given string into a Partition object. The input string is of the form
|
||||
// "EMMC:<device>:<size>:<hash>". Returns the parsed Partition, or an empty object on error.
|
||||
static Partition Parse(const std::string& partition, std::string* err);
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
// Returns whether the current Partition object is valid.
|
||||
explicit operator bool() const {
|
||||
return !name.empty();
|
||||
}
|
||||
|
||||
std::string name;
|
||||
size_t size;
|
||||
std::string hash;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Partition& partition);
|
||||
|
||||
// Applies the given 'patch' to the 'source' Partition, verifies then writes the patching result to
|
||||
// the 'target' Partition. While patching, it will backup the data on the source partition to
|
||||
// /cache, so that the patching could be resumed on interruption even if both of the source and
|
||||
// target partitions refer to the same device. The function is idempotent if called multiple times.
|
||||
// An optional arg 'bonus' can be provided, if the patch was generated with a bonus output.
|
||||
// Returns the patching result.
|
||||
bool PatchPartition(const Partition& target, const Partition& source, const Value& patch,
|
||||
const Value* bonus);
|
||||
|
||||
// Returns whether the contents of the eMMC target or the cached file match the embedded hash.
|
||||
// It will look for the backup on /cache if the given partition doesn't match the checksum.
|
||||
bool PatchPartitionCheck(const Partition& target, const Partition& source);
|
||||
|
||||
// Checks whether the contents of the given partition has the desired hash. It will NOT look for
|
||||
// the backup on /cache if the given partition doesn't have the expected checksum.
|
||||
bool CheckPartition(const Partition& target);
|
||||
|
||||
// Flashes a given image in 'source_filename' to the eMMC target partition. It verifies the target
|
||||
// checksum first, and will return if target already has the desired hash. Otherwise it checks the
|
||||
// checksum of the given source image, flashes, and verifies the target partition afterwards. The
|
||||
// function is idempotent. Returns the flashing result.
|
||||
bool FlashPartition(const Partition& target, const std::string& source_filename);
|
||||
|
||||
// Reads a file into memory; stores the file contents and associated metadata in *file.
|
||||
bool LoadFileContents(const std::string& filename, FileContents* file);
|
||||
|
||||
// Saves the given FileContents object to the given filename.
|
||||
bool SaveFileContents(const std::string& filename, const FileContents* file);
|
||||
|
||||
// bspatch.cpp
|
||||
|
||||
void ShowBSDiffLicense();
|
||||
|
||||
// Applies the bsdiff-patch given in 'patch' (from offset 'patch_offset' to the end) to the source
|
||||
// data given by (old_data, old_size). Writes the patched output through the given 'sink', and
|
||||
// updates the SHA-1 context with the output data. Returns 0 on success.
|
||||
// data given by (old_data, old_size). Writes the patched output through the given 'sink'. Returns
|
||||
// 0 on success.
|
||||
int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch,
|
||||
size_t patch_offset, SinkFn sink, SHA_CTX* ctx);
|
||||
size_t patch_offset, SinkFn sink);
|
||||
|
||||
// imgpatch.cpp
|
||||
|
||||
// Applies the imgdiff-patch given in 'patch' to the source data given by (old_data, old_size), with
|
||||
// the optional bonus data. Writes the patched output through the given 'sink', and updates the
|
||||
// SHA-1 context with the output data. Returns 0 on success.
|
||||
// the optional bonus data. Writes the patched output through the given 'sink'. Returns 0 on
|
||||
// success.
|
||||
int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink,
|
||||
SHA_CTX* ctx, const Value* bonus_data);
|
||||
const Value* bonus_data);
|
||||
|
||||
// freecache.cpp
|
||||
|
||||
int MakeFreeSpaceOnCache(size_t bytes_needed);
|
||||
// Checks whether /cache partition has at least 'bytes'-byte free space. Returns true immediately
|
||||
// if so. Otherwise, it will try to free some space by removing older logs, checks again and
|
||||
// returns the checking result.
|
||||
bool CheckAndFreeSpaceOnCache(size_t bytes);
|
||||
|
||||
// Removes the files in |dirname| until we have at least |bytes_needed| bytes of free space on the
|
||||
// partition. |space_checker| should return the size of the free space, or -1 on error.
|
||||
bool RemoveFilesInDirectory(size_t bytes_needed, const std::string& dirname,
|
||||
const std::function<int64_t(const std::string&)>& space_checker);
|
||||
#endif
|
||||
|
||||
@@ -44,6 +44,8 @@ class ImageChunk {
|
||||
int GetType() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
const uint8_t* GetRawData() const;
|
||||
size_t GetRawDataLength() const {
|
||||
return raw_data_len_;
|
||||
}
|
||||
@@ -99,7 +101,6 @@ class ImageChunk {
|
||||
bsdiff::SuffixArrayIndexInterface** bsdiff_cache);
|
||||
|
||||
private:
|
||||
const uint8_t* GetRawData() const;
|
||||
bool TryReconstruction(int level);
|
||||
|
||||
int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright (C) 2018 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.
|
||||
//
|
||||
|
||||
cc_library_shared {
|
||||
name: "bootctrl.default",
|
||||
recovery_available: true,
|
||||
relative_install_path: "hw",
|
||||
|
||||
srcs: ["boot_control.cpp"],
|
||||
|
||||
cflags: [
|
||||
"-D_FILE_OFFSET_BITS=64",
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbootloader_message",
|
||||
"libfs_mgr",
|
||||
"liblog",
|
||||
],
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := bootctrl.bcb
|
||||
LOCAL_MODULE_RELATIVE_PATH := hw
|
||||
LOCAL_SRC_FILES := boot_control.cpp
|
||||
LOCAL_CFLAGS := \
|
||||
-D_FILE_OFFSET_BITS=64 \
|
||||
-Werror \
|
||||
-Wall \
|
||||
-Wextra
|
||||
LOCAL_SHARED_LIBRARIES := liblog
|
||||
LOCAL_STATIC_LIBRARIES := libbootloader_message libfs_mgr libbase
|
||||
LOCAL_POST_INSTALL_CMD := \
|
||||
$(hide) mkdir -p $(TARGET_OUT_SHARED_LIBRARIES)/hw && \
|
||||
ln -sf bootctrl.bcb.so $(TARGET_OUT_SHARED_LIBRARIES)/hw/bootctrl.default.so
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -14,16 +14,34 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_library_static {
|
||||
name: "libbootloader_message",
|
||||
cc_defaults {
|
||||
name: "libbootloader_message_defaults",
|
||||
srcs: ["bootloader_message.cpp"],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
static_libs: [
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libfs_mgr",
|
||||
],
|
||||
static_libs: [
|
||||
"libfstab",
|
||||
],
|
||||
export_include_dirs: ["include"],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libbootloader_message",
|
||||
defaults: [
|
||||
"libbootloader_message_defaults",
|
||||
],
|
||||
recovery_available: true,
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libbootloader_message_vendor",
|
||||
defaults: [
|
||||
"libbootloader_message_defaults",
|
||||
],
|
||||
vendor: true,
|
||||
}
|
||||
|
||||
@@ -21,12 +21,23 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <fs_mgr.h>
|
||||
#include <fstab/fstab.h>
|
||||
|
||||
using android::fs_mgr::Fstab;
|
||||
using android::fs_mgr::ReadDefaultFstab;
|
||||
|
||||
static std::string g_misc_device_for_test;
|
||||
|
||||
// Exposed for test purpose.
|
||||
void SetMiscBlockDeviceForTest(std::string_view misc_device) {
|
||||
g_misc_device_for_test = misc_device;
|
||||
}
|
||||
|
||||
#ifdef USE_OLD_BOOTLOADER_MESSAGE
|
||||
#include <sys/system_properties.h>
|
||||
@@ -49,6 +60,7 @@ static struct fstab* read_fstab(std::string* err) {
|
||||
#endif
|
||||
|
||||
static std::string get_misc_blk_device(std::string* err) {
|
||||
<<<<<<< HEAD
|
||||
#ifdef USE_OLD_BOOTLOADER_MESSAGE
|
||||
struct fstab* fstab = read_fstab(err);
|
||||
#else
|
||||
@@ -67,8 +79,24 @@ static std::string get_misc_blk_device(std::string* err) {
|
||||
if (record == nullptr) {
|
||||
*err = "failed to find /misc partition";
|
||||
return "";
|
||||
=======
|
||||
if (!g_misc_device_for_test.empty()) {
|
||||
return g_misc_device_for_test;
|
||||
}
|
||||
return record->blk_device;
|
||||
Fstab fstab;
|
||||
if (!ReadDefaultFstab(&fstab)) {
|
||||
*err = "failed to read default fstab";
|
||||
return "";
|
||||
}
|
||||
for (const auto& entry : fstab) {
|
||||
if (entry.mount_point == "/misc") {
|
||||
return entry.blk_device;
|
||||
}
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
|
||||
*err = "failed to find /misc partition";
|
||||
return "";
|
||||
}
|
||||
|
||||
// In recovery mode, recovery can get started and try to access the misc
|
||||
@@ -251,6 +279,37 @@ bool write_wipe_package(const std::string& package_data, std::string* err) {
|
||||
WIPE_PACKAGE_OFFSET_IN_MISC, err);
|
||||
}
|
||||
|
||||
static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size) {
|
||||
auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC;
|
||||
return size <= total_size && offset <= total_size - size;
|
||||
}
|
||||
|
||||
bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err) {
|
||||
if (!OffsetAndSizeInVendorSpace(offset, size)) {
|
||||
*err = android::base::StringPrintf("Out of bound read (offset %zu size %zu)", offset, size);
|
||||
return false;
|
||||
}
|
||||
auto misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
return read_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset,
|
||||
err);
|
||||
}
|
||||
|
||||
bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err) {
|
||||
if (!OffsetAndSizeInVendorSpace(offset, size)) {
|
||||
*err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size);
|
||||
return false;
|
||||
}
|
||||
auto misc_blk_device = get_misc_blk_device(err);
|
||||
if (misc_blk_device.empty()) {
|
||||
return false;
|
||||
}
|
||||
return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset,
|
||||
err);
|
||||
}
|
||||
|
||||
extern "C" bool write_reboot_bootloader(void) {
|
||||
std::string err;
|
||||
return write_reboot_bootloader(&err);
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
// 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.
|
||||
static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
|
||||
static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
|
||||
constexpr size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
|
||||
constexpr size_t VENDOR_SPACE_OFFSET_IN_MISC = 2 * 1024;
|
||||
constexpr size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
|
||||
|
||||
/* Bootloader Message (2-KiB)
|
||||
*
|
||||
@@ -239,6 +240,14 @@ 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);
|
||||
|
||||
// Reads data from the vendor space in /misc partition, with the given offset and size. Note that
|
||||
// offset is in relative to the start of vendor space.
|
||||
bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err);
|
||||
|
||||
// Writes the given data to the vendor space in /misc partition, at the given offset. Note that
|
||||
// offset is in relative to the start of the vendor space.
|
||||
bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err);
|
||||
|
||||
#else
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -14,11 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifndef RECOVERY_COMMON_H
|
||||
#define RECOVERY_COMMON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
=======
|
||||
#pragma once
|
||||
|
||||
>>>>>>> android-10.0.0_r25
|
||||
#include <string>
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -45,10 +50,16 @@ extern "C" {
|
||||
//static constexpr int kRecoveryApiVersion = 3;
|
||||
|
||||
class RecoveryUI;
|
||||
struct selabel_handle;
|
||||
|
||||
extern struct selabel_handle* sehandle;
|
||||
extern RecoveryUI* ui;
|
||||
<<<<<<< HEAD
|
||||
extern bool modified_flash;
|
||||
//typedef struct fstab_rec Volume;
|
||||
=======
|
||||
extern bool has_cache;
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
// The current stage, e.g. "1/2".
|
||||
extern std::string stage;
|
||||
@@ -56,6 +67,7 @@ extern std::string stage;
|
||||
// The reason argument provided in "--reason=".
|
||||
extern const char* reason;
|
||||
|
||||
<<<<<<< HEAD
|
||||
// fopen a file, mounting volumes and making parent dirs as necessary.
|
||||
FILE* fopen_path(const char *path, const char *mode);
|
||||
|
||||
@@ -70,3 +82,6 @@ void ui_print(const char* format, ...);
|
||||
bool reboot(const std::string& command);
|
||||
|
||||
#endif // RECOVERY_COMMON_H
|
||||
=======
|
||||
bool is_ro_debuggable();
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
|
||||
static const char* MENU_ITEMS[] = {
|
||||
"Reboot system now",
|
||||
"Reboot to bootloader",
|
||||
"Apply update from ADB",
|
||||
"Apply update from SD card",
|
||||
"Wipe data/factory reset",
|
||||
#ifndef AB_OTA_UPDATER
|
||||
"Wipe cache partition",
|
||||
#endif // !AB_OTA_UPDATER
|
||||
"Mount /system",
|
||||
"View recovery logs",
|
||||
"Run graphics test",
|
||||
"Run locale test",
|
||||
"Power off",
|
||||
nullptr,
|
||||
};
|
||||
|
||||
static const Device::BuiltinAction MENU_ACTIONS[] = {
|
||||
Device::REBOOT,
|
||||
Device::REBOOT_BOOTLOADER,
|
||||
Device::APPLY_ADB_SIDELOAD,
|
||||
Device::APPLY_SDCARD,
|
||||
Device::WIPE_DATA,
|
||||
#ifndef AB_OTA_UPDATER
|
||||
Device::WIPE_CACHE,
|
||||
#endif // !AB_OTA_UPDATER
|
||||
Device::MOUNT_SYSTEM,
|
||||
Device::VIEW_RECOVERY_LOGS,
|
||||
Device::RUN_GRAPHICS_TEST,
|
||||
Device::RUN_LOCALE_TEST,
|
||||
Device::SHUTDOWN,
|
||||
};
|
||||
|
||||
static_assert(sizeof(MENU_ITEMS) / sizeof(MENU_ITEMS[0]) ==
|
||||
sizeof(MENU_ACTIONS) / sizeof(MENU_ACTIONS[0]) + 1,
|
||||
"MENU_ITEMS and MENU_ACTIONS should have the same length, "
|
||||
"except for the extra NULL entry in MENU_ITEMS.");
|
||||
|
||||
const char* const* Device::GetMenuItems() {
|
||||
return MENU_ITEMS;
|
||||
}
|
||||
|
||||
Device::BuiltinAction Device::InvokeMenuItem(int menu_position) {
|
||||
return menu_position < 0 ? NO_ACTION : MENU_ACTIONS[menu_position];
|
||||
}
|
||||
|
||||
int Device::HandleMenuKey(int key, bool visible) {
|
||||
if (!visible) {
|
||||
return kNoAction;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case KEY_DOWN:
|
||||
case KEY_VOLUMEDOWN:
|
||||
return kHighlightDown;
|
||||
|
||||
case KEY_UP:
|
||||
case KEY_VOLUMEUP:
|
||||
return kHighlightUp;
|
||||
|
||||
case KEY_ENTER:
|
||||
case KEY_POWER:
|
||||
return kInvokeItem;
|
||||
|
||||
default:
|
||||
// If you have all of the above buttons, any other buttons
|
||||
// are ignored. Otherwise, any button cycles the highlight.
|
||||
return ui_->HasThreeButtons() ? kNoAction : kHighlightDown;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _RECOVERY_DEVICE_H
|
||||
#define _RECOVERY_DEVICE_H
|
||||
|
||||
#include "ui.h"
|
||||
|
||||
class 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 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.
|
||||
//
|
||||
// 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,
|
||||
RUN_LOCALE_TEST = 12,
|
||||
};
|
||||
|
||||
// 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 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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.
|
||||
Device* make_device();
|
||||
|
||||
#endif // _DEVICE_H
|
||||
+4
-4
@@ -51,9 +51,9 @@ bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* resu
|
||||
if (!v) {
|
||||
return false;
|
||||
}
|
||||
if (v->type != VAL_STRING) {
|
||||
ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type);
|
||||
return false;
|
||||
if (v->type != Value::Type::STRING) {
|
||||
ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type);
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = v->data;
|
||||
@@ -68,7 +68,7 @@ Value* StringValue(const char* str) {
|
||||
if (str == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new Value(VAL_STRING, str);
|
||||
return new Value(Value::Type::STRING, str);
|
||||
}
|
||||
|
||||
Value* StringValue(const std::string& str) {
|
||||
|
||||
@@ -53,19 +53,16 @@ struct State {
|
||||
bool is_retry = false;
|
||||
};
|
||||
|
||||
enum ValueType {
|
||||
VAL_INVALID = -1,
|
||||
VAL_STRING = 1,
|
||||
VAL_BLOB = 2,
|
||||
};
|
||||
|
||||
struct Value {
|
||||
ValueType type;
|
||||
std::string data;
|
||||
enum class Type {
|
||||
STRING = 1,
|
||||
BLOB = 2,
|
||||
};
|
||||
|
||||
Value(ValueType type, const std::string& str) :
|
||||
type(type),
|
||||
data(str) {}
|
||||
Value(Type type, const std::string& str) : type(type), data(str) {}
|
||||
|
||||
Type type;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
struct Expr;
|
||||
@@ -156,6 +153,6 @@ Value* StringValue(const char* str);
|
||||
|
||||
Value* StringValue(const std::string& str);
|
||||
|
||||
int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count);
|
||||
int ParseString(const std::string& str, std::unique_ptr<Expr>* root, int* error_count);
|
||||
|
||||
#endif // _EXPRESSION_H
|
||||
|
||||
+3
-3
@@ -138,7 +138,7 @@ void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s) {
|
||||
++*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);
|
||||
int ParseString(const std::string& str, std::unique_ptr<Expr>* root, int* error_count) {
|
||||
yy_switch_to_buffer(yy_scan_string(str.c_str()));
|
||||
return yyparse(root, error_count);
|
||||
}
|
||||
|
||||
+131
-1
@@ -19,9 +19,13 @@ on early-init
|
||||
|
||||
start ueventd
|
||||
|
||||
<<<<<<< HEAD
|
||||
service set_permissive /sbin/permissive.sh
|
||||
oneshot
|
||||
seclabel u:r:recovery:s0
|
||||
=======
|
||||
setprop sys.usb.configfs 0
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
on init
|
||||
export PATH /sbin:/system/bin
|
||||
@@ -31,6 +35,16 @@ on init
|
||||
export ANDROID_DATA /data
|
||||
export EXTERNAL_STORAGE /sdcard
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
symlink /proc/self/fd/0 /dev/stdin
|
||||
symlink /proc/self/fd/1 /dev/stdout
|
||||
symlink /proc/self/fd/2 /dev/stderr
|
||||
|
||||
symlink /system/bin /bin
|
||||
symlink /system/etc /etc
|
||||
|
||||
>>>>>>> android-10.0.0_r25
|
||||
mount cgroup none /acct cpuacct
|
||||
mkdir /acct/uid
|
||||
|
||||
@@ -38,6 +52,7 @@ on init
|
||||
mkdir /data
|
||||
mkdir /cache
|
||||
mkdir /sideload
|
||||
mkdir /mnt/system
|
||||
mount tmpfs tmpfs /tmp
|
||||
|
||||
chown root shell /tmp
|
||||
@@ -46,12 +61,15 @@ on init
|
||||
write /proc/sys/kernel/panic_on_oops 1
|
||||
write /proc/sys/vm/max_map_count 1000000
|
||||
|
||||
<<<<<<< HEAD
|
||||
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
|
||||
|
||||
=======
|
||||
>>>>>>> android-10.0.0_r25
|
||||
on boot
|
||||
ifup lo
|
||||
hostname localhost
|
||||
@@ -98,6 +116,7 @@ on late-init
|
||||
trigger early-boot
|
||||
trigger boot
|
||||
|
||||
<<<<<<< HEAD
|
||||
on property:sys.powerctl=*
|
||||
powerctl ${sys.powerctl}
|
||||
|
||||
@@ -106,18 +125,129 @@ service ueventd /sbin/ueventd
|
||||
seclabel u:r:ueventd:s0
|
||||
|
||||
service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
|
||||
=======
|
||||
service ueventd /system/bin/ueventd
|
||||
critical
|
||||
seclabel u:r:ueventd:s0
|
||||
|
||||
service charger /system/bin/charger
|
||||
critical
|
||||
seclabel u:r:charger:s0
|
||||
|
||||
service recovery /system/bin/recovery
|
||||
socket recovery stream 422 system system
|
||||
seclabel u:r:recovery:s0
|
||||
|
||||
service adbd /system/bin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
|
||||
>>>>>>> android-10.0.0_r25
|
||||
disabled
|
||||
socket adbd stream 660 system system
|
||||
seclabel u:r:adbd:s0
|
||||
|
||||
<<<<<<< HEAD
|
||||
# Always start adbd on userdebug and eng builds
|
||||
on property:ro.debuggable=1
|
||||
#write /sys/class/android_usb/android0/enable 1
|
||||
#start adbd
|
||||
setprop service.adb.root 1
|
||||
=======
|
||||
service fastbootd /system/bin/fastbootd
|
||||
disabled
|
||||
group system
|
||||
seclabel u:r:fastbootd:s0
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
# Restart adbd so it can run as root
|
||||
on property:service.adb.root=1
|
||||
write /sys/class/android_usb/android0/enable 0
|
||||
restart adbd
|
||||
|
||||
# Always start adbd on userdebug and eng builds
|
||||
on fs && property:ro.debuggable=1
|
||||
setprop sys.usb.config adb
|
||||
|
||||
on fs && property:sys.usb.configfs=1
|
||||
mount configfs none /config
|
||||
mkdir /config/usb_gadget/g1 0770 shell shell
|
||||
write /config/usb_gadget/g1/idVendor 0x18D1
|
||||
mkdir /config/usb_gadget/g1/strings/0x409 0770
|
||||
write /config/usb_gadget/g1/strings/0x409/serialnumber ${ro.serialno}
|
||||
write /config/usb_gadget/g1/strings/0x409/manufacturer ${ro.product.manufacturer}
|
||||
write /config/usb_gadget/g1/strings/0x409/product ${ro.product.model}
|
||||
mkdir /config/usb_gadget/g1/functions/ffs.adb
|
||||
mkdir /config/usb_gadget/g1/functions/ffs.fastboot
|
||||
mkdir /config/usb_gadget/g1/configs/b.1 0777 shell shell
|
||||
mkdir /config/usb_gadget/g1/configs/b.1/strings/0x409 0770 shell shell
|
||||
|
||||
on fs && property:sys.usb.configfs=0
|
||||
write /sys/class/android_usb/android0/f_ffs/aliases adb,fastboot
|
||||
write /sys/class/android_usb/android0/idVendor 18D1
|
||||
write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer}
|
||||
write /sys/class/android_usb/android0/iProduct ${ro.product.model}
|
||||
write /sys/class/android_usb/android0/iSerial ${ro.serialno}
|
||||
|
||||
on fs
|
||||
mkdir /dev/usb-ffs 0775 shell shell
|
||||
mkdir /dev/usb-ffs/adb 0770 shell shell
|
||||
mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
|
||||
mkdir /dev/usb-ffs/fastboot 0770 system system
|
||||
mount functionfs fastboot /dev/usb-ffs/fastboot rmode=0770,fmode=0660,uid=1000,gid=1000
|
||||
|
||||
on property:sys.usb.config=adb
|
||||
start adbd
|
||||
|
||||
on property:sys.usb.config=fastboot
|
||||
start fastbootd
|
||||
|
||||
on property:sys.usb.config=none && property:sys.usb.configfs=0
|
||||
stop adbd
|
||||
stop fastboot
|
||||
write /sys/class/android_usb/android0/enable 0
|
||||
setprop sys.usb.state ${sys.usb.config}
|
||||
|
||||
on property:sys.usb.config=adb && property:sys.usb.configfs=0
|
||||
write /sys/class/android_usb/android0/idProduct D001
|
||||
write /sys/class/android_usb/android0/functions adb
|
||||
write /sys/class/android_usb/android0/enable 1
|
||||
setprop sys.usb.state ${sys.usb.config}
|
||||
|
||||
on property:sys.usb.config=sideload && property:sys.usb.configfs=0
|
||||
write /sys/class/android_usb/android0/idProduct D001
|
||||
write /sys/class/android_usb/android0/functions adb
|
||||
write /sys/class/android_usb/android0/enable 1
|
||||
setprop sys.usb.state ${sys.usb.config}
|
||||
|
||||
on property:sys.usb.config=fastboot && property:sys.usb.configfs=0
|
||||
write /sys/class/android_usb/android0/idProduct 4EE0
|
||||
write /sys/class/android_usb/android0/functions fastboot
|
||||
write /sys/class/android_usb/android0/enable 1
|
||||
setprop sys.usb.state ${sys.usb.config}
|
||||
|
||||
# Configfs triggers
|
||||
on property:sys.usb.config=none && property:sys.usb.configfs=1
|
||||
write /config/usb_gadget/g1/UDC "none"
|
||||
stop adbd
|
||||
stop fastbootd
|
||||
setprop sys.usb.ffs.ready 0
|
||||
rm /config/usb_gadget/g1/configs/b.1/f1
|
||||
setprop sys.usb.state ${sys.usb.config}
|
||||
|
||||
on property:sys.usb.config=sideload && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1
|
||||
write /config/usb_gadget/g1/idProduct 0xD001
|
||||
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
|
||||
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
|
||||
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
|
||||
setprop sys.usb.state ${sys.usb.config}
|
||||
|
||||
on property:sys.usb.config=adb && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1
|
||||
write /config/usb_gadget/g1/idProduct 0xD001
|
||||
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
|
||||
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
|
||||
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
|
||||
setprop sys.usb.state ${sys.usb.config}
|
||||
|
||||
on property:sys.usb.config=fastboot && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1
|
||||
write /config/usb_gadget/g1/idProduct 0x4EE0
|
||||
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "fastboot"
|
||||
symlink /config/usb_gadget/g1/functions/ffs.fastboot /config/usb_gadget/g1/configs/b.1/f1
|
||||
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
|
||||
setprop sys.usb.state ${sys.usb.config}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 "fastboot.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <bootloader_message/bootloader_message.h>
|
||||
|
||||
#include "recovery_ui/ui.h"
|
||||
|
||||
static const std::vector<std::pair<std::string, Device::BuiltinAction>> kFastbootMenuActions{
|
||||
{ "Reboot system now", Device::REBOOT },
|
||||
{ "Enter recovery", Device::ENTER_RECOVERY },
|
||||
{ "Reboot to bootloader", Device::REBOOT_BOOTLOADER },
|
||||
{ "Power off", Device::SHUTDOWN },
|
||||
};
|
||||
|
||||
Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& /* args */) {
|
||||
RecoveryUI* ui = device->GetUI();
|
||||
|
||||
std::vector<std::string> title_lines = { "Android Fastboot" };
|
||||
title_lines.push_back("Product name - " + android::base::GetProperty("ro.product.device", ""));
|
||||
title_lines.push_back("Bootloader version - " + android::base::GetProperty("ro.bootloader", ""));
|
||||
title_lines.push_back("Baseband version - " +
|
||||
android::base::GetProperty("ro.build.expect.baseband", ""));
|
||||
title_lines.push_back("Serial number - " + android::base::GetProperty("ro.serialno", ""));
|
||||
title_lines.push_back(std::string("Secure boot - ") +
|
||||
((android::base::GetProperty("ro.secure", "") == "1") ? "yes" : "no"));
|
||||
title_lines.push_back("HW version - " + android::base::GetProperty("ro.revision", ""));
|
||||
|
||||
ui->ResetKeyInterruptStatus();
|
||||
ui->SetTitle(title_lines);
|
||||
ui->ShowText(true);
|
||||
|
||||
// Reset to normal system boot so recovery won't cycle indefinitely.
|
||||
// TODO(b/112277594) Clear only if 'recovery' field of BCB is empty. If not,
|
||||
// set the 'command' field of BCB to 'boot-recovery' so the next boot is into recovery
|
||||
// to finish any interrupted tasks.
|
||||
std::string err;
|
||||
if (!clear_bootloader_message(&err)) {
|
||||
LOG(ERROR) << "Failed to clear BCB message: " << err;
|
||||
}
|
||||
|
||||
std::vector<std::string> fastboot_menu_items;
|
||||
std::transform(kFastbootMenuActions.cbegin(), kFastbootMenuActions.cend(),
|
||||
std::back_inserter(fastboot_menu_items),
|
||||
[](const auto& entry) { return entry.first; });
|
||||
|
||||
auto chosen_item = ui->ShowMenu(
|
||||
{}, fastboot_menu_items, 0, false,
|
||||
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
|
||||
return Device::KEY_INTERRUPTED;
|
||||
}
|
||||
if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::TIMED_OUT)) {
|
||||
return Device::BuiltinAction::NO_ACTION;
|
||||
}
|
||||
return kFastbootMenuActions[chosen_item].second;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "recovery_ui/device.h"
|
||||
|
||||
Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& args);
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 "fsck_unshare_blocks.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <spawn.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <fstab/fstab.h>
|
||||
|
||||
#include "otautil/roots.h"
|
||||
|
||||
static constexpr const char* SYSTEM_E2FSCK_BIN = "/system/bin/e2fsck_static";
|
||||
static constexpr const char* TMP_E2FSCK_BIN = "/tmp/e2fsck.bin";
|
||||
|
||||
static bool copy_file(const char* source, const char* dest) {
|
||||
android::base::unique_fd source_fd(open(source, O_RDONLY));
|
||||
if (source_fd < 0) {
|
||||
PLOG(ERROR) << "open %s failed" << source;
|
||||
return false;
|
||||
}
|
||||
|
||||
android::base::unique_fd dest_fd(open(dest, O_CREAT | O_WRONLY, S_IRWXU));
|
||||
if (dest_fd < 0) {
|
||||
PLOG(ERROR) << "open %s failed" << dest;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
char buf[4096];
|
||||
ssize_t rv = read(source_fd, buf, sizeof(buf));
|
||||
if (rv < 0) {
|
||||
PLOG(ERROR) << "read failed";
|
||||
return false;
|
||||
}
|
||||
if (rv == 0) {
|
||||
break;
|
||||
}
|
||||
if (write(dest_fd, buf, rv) != rv) {
|
||||
PLOG(ERROR) << "write failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool run_e2fsck(const std::string& partition) {
|
||||
Volume* volume = volume_for_mount_point(partition);
|
||||
if (!volume) {
|
||||
LOG(INFO) << "No fstab entry for " << partition << ", skipping.";
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Running e2fsck on device " << volume->blk_device;
|
||||
|
||||
std::vector<std::string> args = { TMP_E2FSCK_BIN, "-p", "-E", "unshare_blocks",
|
||||
volume->blk_device };
|
||||
std::vector<char*> argv(args.size());
|
||||
std::transform(args.cbegin(), args.cend(), argv.begin(),
|
||||
[](const std::string& arg) { return const_cast<char*>(arg.c_str()); });
|
||||
argv.push_back(nullptr);
|
||||
|
||||
pid_t child;
|
||||
char* env[] = { nullptr };
|
||||
if (posix_spawn(&child, argv[0], nullptr, nullptr, argv.data(), env)) {
|
||||
PLOG(ERROR) << "posix_spawn failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
|
||||
if (ret < 0) {
|
||||
PLOG(ERROR) << "waitpid failed";
|
||||
return false;
|
||||
}
|
||||
if (!WIFEXITED(status)) {
|
||||
LOG(ERROR) << "e2fsck exited abnormally: " << status;
|
||||
return false;
|
||||
}
|
||||
int return_code = WEXITSTATUS(status);
|
||||
if (return_code >= 8) {
|
||||
LOG(ERROR) << "e2fsck could not unshare blocks: " << return_code;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Successfully unshared blocks on " << partition;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool do_fsck_unshare_blocks() {
|
||||
// List of partitions we will try to e2fsck -E unshare_blocks.
|
||||
std::vector<std::string> partitions = { "/odm", "/oem", "/product", "/vendor" };
|
||||
|
||||
// Temporarily mount system so we can copy e2fsck_static.
|
||||
std::string system_root = get_system_root();
|
||||
bool mounted = ensure_path_mounted_at(system_root, "/mnt/system") != -1;
|
||||
partitions.push_back(system_root);
|
||||
|
||||
if (!mounted) {
|
||||
LOG(ERROR) << "Failed to mount system image.";
|
||||
return false;
|
||||
}
|
||||
if (!copy_file(SYSTEM_E2FSCK_BIN, TMP_E2FSCK_BIN)) {
|
||||
LOG(ERROR) << "Could not copy e2fsck to /tmp.";
|
||||
return false;
|
||||
}
|
||||
if (umount("/mnt/system") < 0) {
|
||||
PLOG(ERROR) << "umount failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
for (const auto& partition : partitions) {
|
||||
ok &= run_e2fsck(partition);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
LOG(INFO) << "Finished running e2fsck.";
|
||||
} else {
|
||||
LOG(ERROR) << "Finished running e2fsck, but not all partitions succceeded.";
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright (C) 2018 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.
|
||||
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __FUSE_SDCARD_PROVIDER_H
|
||||
#define __FUSE_SDCARD_PROVIDER_H
|
||||
#ifndef _FILESYSTEM_CMDS_H
|
||||
#define _FILESYSTEM_CMDS_H
|
||||
|
||||
bool start_sdcard_fuse(const char* path);
|
||||
bool do_fsck_unshare_blocks();
|
||||
|
||||
#endif
|
||||
#endif // _FILESYSTEM_CMDS_H
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "fuse_sdcard_provider.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
#include "fuse_sideload.h"
|
||||
|
||||
struct file_data {
|
||||
int fd; // the underlying sdcard file
|
||||
|
||||
uint64_t file_size;
|
||||
uint32_t block_size;
|
||||
};
|
||||
|
||||
static int read_block_file(const file_data& fd, uint32_t block, uint8_t* buffer,
|
||||
uint32_t fetch_size) {
|
||||
off64_t offset = static_cast<off64_t>(block) * fd.block_size;
|
||||
if (TEMP_FAILURE_RETRY(lseek64(fd.fd, offset, SEEK_SET)) == -1) {
|
||||
fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!android::base::ReadFully(fd.fd, buffer, fetch_size)) {
|
||||
fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool start_sdcard_fuse(const char* path) {
|
||||
struct stat sb;
|
||||
if (stat(path, &sb) == -1) {
|
||||
fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
file_data fd;
|
||||
fd.fd = open(path, O_RDONLY);
|
||||
if (fd.fd == -1) {
|
||||
fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
fd.file_size = sb.st_size;
|
||||
fd.block_size = 65536;
|
||||
|
||||
provider_vtab vtab;
|
||||
vtab.read_block = std::bind(&read_block_file, fd, std::placeholders::_1, std::placeholders::_2,
|
||||
std::placeholders::_3);
|
||||
vtab.close = [&fd]() { close(fd.fd); };
|
||||
|
||||
// The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
|
||||
// that our open file continues to work but new references see it as unmounted.
|
||||
umount2("/sdcard", MNT_DETACH);
|
||||
|
||||
return run_fuse_sideload(vtab, fd.file_size, fd.block_size) == 0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2018 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.
|
||||
|
||||
cc_library {
|
||||
name: "libfusesideload",
|
||||
recovery_available: true,
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-D_XOPEN_SOURCE",
|
||||
"-D_GNU_SOURCE",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"fuse_provider.cpp",
|
||||
"fuse_sideload.cpp",
|
||||
],
|
||||
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcrypto",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 "fuse_provider.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
#include "fuse_sideload.h"
|
||||
|
||||
FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t block_size) {
|
||||
struct stat sb;
|
||||
if (stat(path.c_str(), &sb) == -1) {
|
||||
fprintf(stderr, "failed to stat %s: %s\n", path.c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
fd_.reset(open(path.c_str(), O_RDONLY));
|
||||
if (fd_ == -1) {
|
||||
fprintf(stderr, "failed to open %s: %s\n", path.c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
file_size_ = sb.st_size;
|
||||
fuse_block_size_ = block_size;
|
||||
}
|
||||
|
||||
bool FuseFileDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
|
||||
uint32_t start_block) const {
|
||||
uint64_t offset = static_cast<uint64_t>(start_block) * fuse_block_size_;
|
||||
if (fetch_size > file_size_ || offset > file_size_ - fetch_size) {
|
||||
fprintf(stderr,
|
||||
"Out of bound read, start block: %" PRIu32 ", fetch size: %" PRIu32
|
||||
", file size %" PRIu64 "\n",
|
||||
start_block, fetch_size, file_size_);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!android::base::ReadFullyAtOffset(fd_, buffer, fetch_size, offset)) {
|
||||
fprintf(stderr, "Failed to read fetch size: %" PRIu32 " bytes data at offset %" PRIu64 ": %s\n",
|
||||
fetch_size, offset, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FuseFileDataProvider::Close() {
|
||||
fd_.reset();
|
||||
}
|
||||
@@ -87,7 +87,7 @@ using SHA256Digest = std::array<uint8_t, SHA256_DIGEST_LENGTH>;
|
||||
struct fuse_data {
|
||||
int ffd; // file descriptor for the fuse socket
|
||||
|
||||
provider_vtab vtab;
|
||||
FuseDataProvider* provider; // Provider of the source data.
|
||||
|
||||
uint64_t file_size; // bytes
|
||||
|
||||
@@ -250,7 +250,7 @@ static int fetch_block(fuse_data* fd, uint32_t block) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t fetch_size = fd->block_size;
|
||||
uint32_t fetch_size = fd->block_size;
|
||||
if (block * fd->block_size + fetch_size > fd->file_size) {
|
||||
// If we're reading the last (partial) block of the file, expect a shorter response from the
|
||||
// host, and pad the rest of the block with zeroes.
|
||||
@@ -258,8 +258,9 @@ static int fetch_block(fuse_data* fd, uint32_t block) {
|
||||
memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size);
|
||||
}
|
||||
|
||||
int result = fd->vtab.read_block(block, fd->block_data, fetch_size);
|
||||
if (result < 0) return result;
|
||||
if (!fd->provider->ReadBlockAlignedData(fd->block_data, fetch_size, block)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fd->curr_block = block;
|
||||
|
||||
@@ -358,12 +359,14 @@ static int handle_read(void* data, fuse_data* fd, const fuse_in_header* hdr) {
|
||||
return NO_STATUS;
|
||||
}
|
||||
|
||||
int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
|
||||
const char* mount_point) {
|
||||
int run_fuse_sideload(std::unique_ptr<FuseDataProvider>&& provider, const char* mount_point) {
|
||||
// If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a
|
||||
// previous abnormal exit.)
|
||||
umount2(mount_point, MNT_FORCE);
|
||||
|
||||
uint64_t file_size = provider->file_size();
|
||||
uint32_t block_size = provider->fuse_block_size();
|
||||
|
||||
// fs/fuse/inode.c in kernel code uses the greater of 4096 and the passed-in max_read.
|
||||
if (block_size < 4096) {
|
||||
fprintf(stderr, "block size (%u) is too small\n", block_size);
|
||||
@@ -374,9 +377,14 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl
|
||||
return -1;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD:fuse_sideload.cpp
|
||||
fuse_data fd;
|
||||
memset(&fd, 0, sizeof(fd));
|
||||
fd.vtab = vtab;
|
||||
=======
|
||||
fuse_data fd = {};
|
||||
fd.provider = provider.get();
|
||||
>>>>>>> android-10.0.0_r25:fuse_sideload/fuse_sideload.cpp
|
||||
fd.file_size = file_size;
|
||||
fd.block_size = block_size;
|
||||
fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1);
|
||||
@@ -413,8 +421,13 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl
|
||||
goto done;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD:fuse_sideload.cpp
|
||||
fd.ffd = open("/dev/fuse", O_RDWR);
|
||||
if (!fd.ffd) {
|
||||
=======
|
||||
fd.ffd.reset(open("/dev/fuse", O_RDWR));
|
||||
if (fd.ffd == -1) {
|
||||
>>>>>>> android-10.0.0_r25:fuse_sideload/fuse_sideload.cpp
|
||||
perror("open /dev/fuse");
|
||||
result = -1;
|
||||
goto done;
|
||||
@@ -506,7 +519,7 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl
|
||||
}
|
||||
|
||||
done:
|
||||
fd.vtab.close();
|
||||
provider->Close();
|
||||
|
||||
if (umount2(mount_point, MNT_DETACH) == -1) {
|
||||
fprintf(stderr, "fuse_sideload umount failed: %s\n", strerror(errno));
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
// This is the base class to read data from source and provide the data to FUSE.
|
||||
class FuseDataProvider {
|
||||
public:
|
||||
FuseDataProvider(uint64_t file_size, uint32_t block_size)
|
||||
: file_size_(file_size), fuse_block_size_(block_size) {}
|
||||
|
||||
virtual ~FuseDataProvider() = default;
|
||||
|
||||
uint64_t file_size() const {
|
||||
return file_size_;
|
||||
}
|
||||
uint32_t fuse_block_size() const {
|
||||
return fuse_block_size_;
|
||||
}
|
||||
|
||||
// Reads |fetch_size| bytes data starting from |start_block|. Puts the result in |buffer|.
|
||||
virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
|
||||
uint32_t start_block) const = 0;
|
||||
|
||||
virtual void Close() {}
|
||||
|
||||
protected:
|
||||
FuseDataProvider() = default;
|
||||
|
||||
// Size in bytes of the file to read.
|
||||
uint64_t file_size_ = 0;
|
||||
// Block size passed to the fuse, this is different from the block size of the block device.
|
||||
uint32_t fuse_block_size_ = 0;
|
||||
};
|
||||
|
||||
// This class reads data from a file.
|
||||
class FuseFileDataProvider : public FuseDataProvider {
|
||||
public:
|
||||
FuseFileDataProvider(const std::string& path, uint32_t block_size);
|
||||
|
||||
bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
|
||||
uint32_t start_block) const override;
|
||||
|
||||
bool Valid() const {
|
||||
return fd_ != -1;
|
||||
}
|
||||
|
||||
void Close() override;
|
||||
|
||||
private:
|
||||
// The underlying source to read data from.
|
||||
android::base::unique_fd fd_;
|
||||
};
|
||||
@@ -17,11 +17,17 @@
|
||||
#ifndef __FUSE_SIDELOAD_H
|
||||
#define __FUSE_SIDELOAD_H
|
||||
|
||||
<<<<<<< HEAD:fuse_sideload.h
|
||||
#ifdef USE_FUSE_SIDELOAD22
|
||||
#include "fuse_sideload22.h"
|
||||
#else
|
||||
|
||||
#include <functional>
|
||||
=======
|
||||
#include <memory>
|
||||
|
||||
#include "fuse_provider.h"
|
||||
>>>>>>> android-10.0.0_r25:fuse_sideload/include/fuse_sideload.h
|
||||
|
||||
// Define the filenames created by the sideload FUSE filesystem.
|
||||
static constexpr const char* FUSE_SIDELOAD_HOST_MOUNTPOINT = "/sideload";
|
||||
@@ -30,15 +36,7 @@ static constexpr const char* FUSE_SIDELOAD_HOST_PATHNAME = "/sideload/package.zi
|
||||
static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_FLAG = "exit";
|
||||
static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_PATHNAME = "/sideload/exit";
|
||||
|
||||
struct provider_vtab {
|
||||
// read a block
|
||||
std::function<int(uint32_t block, uint8_t* buffer, uint32_t fetch_size)> read_block;
|
||||
|
||||
// close down
|
||||
std::function<void(void)> close;
|
||||
};
|
||||
|
||||
int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
|
||||
int run_fuse_sideload(std::unique_ptr<FuseDataProvider>&& provider,
|
||||
const char* mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT);
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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 RECOVERY_INSTALL_H_
|
||||
#define RECOVERY_INSTALL_H_
|
||||
|
||||
#include <string>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED,
|
||||
INSTALL_RETRY };
|
||||
|
||||
// Installs the given update package. If INSTALL_SUCCESS is returned and *wipe_cache is true on
|
||||
// exit, caller should wipe the cache partition.
|
||||
int install_package(const std::string& package, bool* wipe_cache, const std::string& install_file,
|
||||
bool needs_mount, int retry_count);
|
||||
|
||||
// Verify the package by ota keys. Return true if the package is verified successfully,
|
||||
// otherwise return false.
|
||||
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(ZipArchiveHandle zip, std::string* metadata);
|
||||
|
||||
// Verifies 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_
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright (C) 2019 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.
|
||||
|
||||
cc_defaults {
|
||||
name: "libinstall_defaults",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
header_libs: [
|
||||
"libminadbd_headers",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbootloader_message",
|
||||
"libcrypto",
|
||||
"libext4_utils",
|
||||
"libfs_mgr",
|
||||
"libfusesideload",
|
||||
"libhidl-gen-utils",
|
||||
"libhidlbase",
|
||||
"libhidltransport",
|
||||
"liblog",
|
||||
"libselinux",
|
||||
"libtinyxml2",
|
||||
"libutils",
|
||||
"libz",
|
||||
"libziparchive",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libotautil",
|
||||
|
||||
// external dependencies
|
||||
"libvintf_recovery",
|
||||
"libvintf",
|
||||
"libfstab",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libinstall",
|
||||
recovery_available: true,
|
||||
|
||||
defaults: [
|
||||
"libinstall_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"adb_install.cpp",
|
||||
"asn1_decoder.cpp",
|
||||
"fuse_sdcard_install.cpp",
|
||||
"install.cpp",
|
||||
"package.cpp",
|
||||
"verifier.cpp",
|
||||
"wipe_data.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"librecovery_ui",
|
||||
],
|
||||
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
|
||||
export_shared_lib_headers: [
|
||||
"librecovery_ui",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 "install/adb_install.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/memory.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "fuse_sideload.h"
|
||||
#include "install/install.h"
|
||||
#include "install/wipe_data.h"
|
||||
#include "minadbd_types.h"
|
||||
#include "otautil/sysutil.h"
|
||||
#include "recovery_ui/device.h"
|
||||
#include "recovery_ui/ui.h"
|
||||
|
||||
// A CommandFunction returns a pair of (result, should_continue), which indicates the command
|
||||
// execution result and whether it should proceed to the next iteration. The execution result will
|
||||
// always be sent to the minadbd side.
|
||||
using CommandFunction = std::function<std::pair<bool, bool>()>;
|
||||
|
||||
static bool SetUsbConfig(const std::string& state) {
|
||||
android::base::SetProperty("sys.usb.config", state);
|
||||
return android::base::WaitForProperty("sys.usb.state", state);
|
||||
}
|
||||
|
||||
// Parses the minadbd command in |message|; returns MinadbdCommand::kError upon errors.
|
||||
static MinadbdCommand ParseMinadbdCommand(const std::string& message) {
|
||||
if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) {
|
||||
LOG(ERROR) << "Failed to parse command in message " << message;
|
||||
return MinadbdCommand::kError;
|
||||
}
|
||||
|
||||
auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix));
|
||||
auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str());
|
||||
if (cmd_code >= static_cast<uint32_t>(MinadbdCommand::kError)) {
|
||||
LOG(ERROR) << "Unsupported command code: " << cmd_code;
|
||||
return MinadbdCommand::kError;
|
||||
}
|
||||
|
||||
return static_cast<MinadbdCommand>(cmd_code);
|
||||
}
|
||||
|
||||
static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) {
|
||||
char message[kMinadbdMessageSize];
|
||||
memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
|
||||
android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status);
|
||||
|
||||
if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
|
||||
PLOG(ERROR) << "Failed to write message " << message;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Installs the package from FUSE. Returns the installation result and whether it should continue
|
||||
// waiting for new commands.
|
||||
static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) {
|
||||
// How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long
|
||||
// because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME
|
||||
// will start to exist once the host connects and starts serving a package. Poll for its
|
||||
// appearance. (Note that inotify doesn't work with FUSE.)
|
||||
constexpr int ADB_INSTALL_TIMEOUT = 15;
|
||||
bool should_continue = true;
|
||||
*result = INSTALL_ERROR;
|
||||
for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
|
||||
struct stat st;
|
||||
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
|
||||
if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
|
||||
sleep(1);
|
||||
continue;
|
||||
} else {
|
||||
should_continue = false;
|
||||
ui->Print("\nTimed out waiting for fuse to be ready.\n\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
*result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
|
||||
break;
|
||||
}
|
||||
|
||||
// Calling stat() on this magic filename signals the FUSE to exit.
|
||||
struct stat st;
|
||||
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
|
||||
return std::make_pair(*result == INSTALL_SUCCESS, should_continue);
|
||||
}
|
||||
|
||||
static auto AdbRebootHandler(MinadbdCommand command, int* result,
|
||||
Device::BuiltinAction* reboot_action) {
|
||||
// Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows
|
||||
// rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly
|
||||
// installed bootloader/recovery image.
|
||||
switch (command) {
|
||||
case MinadbdCommand::kRebootBootloader:
|
||||
*reboot_action = Device::REBOOT_BOOTLOADER;
|
||||
break;
|
||||
case MinadbdCommand::kRebootFastboot:
|
||||
*reboot_action = Device::REBOOT_FASTBOOT;
|
||||
break;
|
||||
case MinadbdCommand::kRebootRecovery:
|
||||
*reboot_action = Device::REBOOT_RECOVERY;
|
||||
break;
|
||||
case MinadbdCommand::kRebootRescue:
|
||||
*reboot_action = Device::REBOOT_RESCUE;
|
||||
break;
|
||||
case MinadbdCommand::kRebootAndroid:
|
||||
default:
|
||||
*reboot_action = Device::REBOOT;
|
||||
break;
|
||||
}
|
||||
*result = INSTALL_REBOOT;
|
||||
return std::make_pair(true, false);
|
||||
}
|
||||
|
||||
// Parses and executes the command from minadbd. Returns whether the caller should keep waiting for
|
||||
// next command.
|
||||
static bool HandleMessageFromMinadbd(int socket_fd,
|
||||
const std::map<MinadbdCommand, CommandFunction>& command_map) {
|
||||
char buffer[kMinadbdMessageSize];
|
||||
if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) {
|
||||
PLOG(ERROR) << "Failed to read message from minadbd";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string message(buffer, buffer + kMinadbdMessageSize);
|
||||
auto command_type = ParseMinadbdCommand(message);
|
||||
if (command_type == MinadbdCommand::kError) {
|
||||
return false;
|
||||
}
|
||||
if (command_map.find(command_type) == command_map.end()) {
|
||||
LOG(ERROR) << "Unsupported command: "
|
||||
<< android::base::get_unaligned<unsigned int>(
|
||||
message.substr(strlen(kMinadbdCommandPrefix)).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have received a valid command, execute the corresponding function.
|
||||
const auto& command_func = command_map.at(command_type);
|
||||
const auto [result, should_continue] = command_func();
|
||||
LOG(INFO) << "Command " << static_cast<uint32_t>(command_type) << " finished with " << result;
|
||||
if (!WriteStatusToFd(result ? MinadbdCommandStatus::kSuccess : MinadbdCommandStatus::kFailure,
|
||||
socket_fd)) {
|
||||
return false;
|
||||
}
|
||||
return should_continue;
|
||||
}
|
||||
|
||||
// TODO(xunchang) add a wrapper function and kill the minadbd service there.
|
||||
static void ListenAndExecuteMinadbdCommands(
|
||||
RecoveryUI* ui, pid_t minadbd_pid, android::base::unique_fd&& socket_fd,
|
||||
const std::map<MinadbdCommand, CommandFunction>& command_map) {
|
||||
android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC));
|
||||
if (epoll_fd == -1) {
|
||||
PLOG(ERROR) << "Failed to create epoll";
|
||||
kill(minadbd_pid, SIGKILL);
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int EPOLL_MAX_EVENTS = 10;
|
||||
struct epoll_event ev = {};
|
||||
ev.events = EPOLLIN | EPOLLHUP;
|
||||
ev.data.fd = socket_fd.get();
|
||||
struct epoll_event events[EPOLL_MAX_EVENTS];
|
||||
if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) {
|
||||
PLOG(ERROR) << "Failed to add socket fd to epoll";
|
||||
kill(minadbd_pid, SIGKILL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the timeout to be 300s when waiting for minadbd commands.
|
||||
constexpr int TIMEOUT_MILLIS = 300 * 1000;
|
||||
while (true) {
|
||||
// Reset the progress bar and the background image before each command.
|
||||
ui->SetProgressType(RecoveryUI::EMPTY);
|
||||
ui->SetBackground(RecoveryUI::NO_COMMAND);
|
||||
|
||||
// Poll for the status change of the socket_fd, and handle the message if the fd is ready to
|
||||
// read.
|
||||
int event_count =
|
||||
TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS));
|
||||
if (event_count == -1) {
|
||||
PLOG(ERROR) << "Failed to wait for epoll events";
|
||||
kill(minadbd_pid, SIGKILL);
|
||||
return;
|
||||
}
|
||||
if (event_count == 0) {
|
||||
LOG(ERROR) << "Timeout waiting for messages from minadbd";
|
||||
kill(minadbd_pid, SIGKILL);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int n = 0; n < event_count; n++) {
|
||||
if (events[n].events & EPOLLHUP) {
|
||||
LOG(INFO) << "Socket has been closed";
|
||||
kill(minadbd_pid, SIGKILL);
|
||||
return;
|
||||
}
|
||||
if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) {
|
||||
kill(minadbd_pid, SIGKILL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recovery starts minadbd service as a child process, and spawns another thread to listen for the
|
||||
// message from minadbd through a socket pair. Here is an example to execute one command from adb
|
||||
// host.
|
||||
// a. recovery b. listener thread c. minadbd service
|
||||
//
|
||||
// a1. create socket pair
|
||||
// a2. fork minadbd service
|
||||
// c3. wait for the adb commands
|
||||
// from host
|
||||
// c4. after receiving host commands:
|
||||
// 1) set up pre-condition (i.e.
|
||||
// start fuse for adb sideload)
|
||||
// 2) issue command through
|
||||
// socket.
|
||||
// 3) wait for result
|
||||
// a5. start listener thread
|
||||
// b6. listen for message from
|
||||
// minadbd in a loop.
|
||||
// b7. After receiving a minadbd
|
||||
// command from socket
|
||||
// 1) execute the command function
|
||||
// 2) send the result back to
|
||||
// minadbd
|
||||
// ......
|
||||
// c8. exit upon receiving the
|
||||
// result
|
||||
// a9. wait for listener thread
|
||||
// to exit.
|
||||
//
|
||||
// a10. wait for minadbd to
|
||||
// exit
|
||||
// b11. exit the listening loop
|
||||
//
|
||||
static void CreateMinadbdServiceAndExecuteCommands(
|
||||
RecoveryUI* ui, const std::map<MinadbdCommand, CommandFunction>& command_map,
|
||||
bool rescue_mode) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
android::base::unique_fd recovery_socket;
|
||||
android::base::unique_fd minadbd_socket;
|
||||
if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) {
|
||||
PLOG(ERROR) << "Failed to create socket";
|
||||
return;
|
||||
}
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == -1) {
|
||||
PLOG(ERROR) << "Failed to fork child process";
|
||||
return;
|
||||
}
|
||||
if (child == 0) {
|
||||
recovery_socket.reset();
|
||||
std::vector<std::string> minadbd_commands = {
|
||||
"/system/bin/minadbd",
|
||||
"--socket_fd",
|
||||
std::to_string(minadbd_socket.release()),
|
||||
};
|
||||
if (rescue_mode) {
|
||||
minadbd_commands.push_back("--rescue");
|
||||
}
|
||||
auto exec_args = StringVectorToNullTerminatedArray(minadbd_commands);
|
||||
execv(exec_args[0], exec_args.data());
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
minadbd_socket.reset();
|
||||
|
||||
// We need to call SetUsbConfig() after forking minadbd service. Because the function waits for
|
||||
// the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon.
|
||||
if (!SetUsbConfig("sideload")) {
|
||||
LOG(ERROR) << "Failed to set usb config to sideload";
|
||||
return;
|
||||
}
|
||||
|
||||
std::thread listener_thread(ListenAndExecuteMinadbdCommands, ui, child,
|
||||
std::move(recovery_socket), std::ref(command_map));
|
||||
if (listener_thread.joinable()) {
|
||||
listener_thread.join();
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(child, &status, 0);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) {
|
||||
LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n";
|
||||
} else if (!WIFSIGNALED(status)) {
|
||||
LOG(ERROR) << "\n(adbd status " << WEXITSTATUS(status) << ")";
|
||||
}
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
}
|
||||
|
||||
int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) {
|
||||
// Save the usb state to restore after the sideload operation.
|
||||
std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
|
||||
// Clean up state and stop adbd.
|
||||
if (usb_state != "none" && !SetUsbConfig("none")) {
|
||||
LOG(ERROR) << "Failed to clear USB config";
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
RecoveryUI* ui = device->GetUI();
|
||||
|
||||
int install_result = INSTALL_ERROR;
|
||||
std::map<MinadbdCommand, CommandFunction> command_map{
|
||||
{ MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) },
|
||||
{ MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid,
|
||||
&install_result, reboot_action) },
|
||||
{ MinadbdCommand::kRebootBootloader,
|
||||
std::bind(&AdbRebootHandler, MinadbdCommand::kRebootBootloader, &install_result,
|
||||
reboot_action) },
|
||||
{ MinadbdCommand::kRebootFastboot, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootFastboot,
|
||||
&install_result, reboot_action) },
|
||||
{ MinadbdCommand::kRebootRecovery, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRecovery,
|
||||
&install_result, reboot_action) },
|
||||
{ MinadbdCommand::kRebootRescue,
|
||||
std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) },
|
||||
};
|
||||
|
||||
if (!rescue_mode) {
|
||||
ui->Print(
|
||||
"\n\nNow send the package you want to apply\n"
|
||||
"to the device with \"adb sideload <filename>\"...\n");
|
||||
} else {
|
||||
command_map.emplace(MinadbdCommand::kWipeData, [&device]() {
|
||||
bool result = WipeData(device, false);
|
||||
return std::make_pair(result, true);
|
||||
});
|
||||
command_map.emplace(MinadbdCommand::kNoOp, []() { return std::make_pair(true, true); });
|
||||
|
||||
ui->Print("\n\nWaiting for rescue commands...\n");
|
||||
}
|
||||
|
||||
CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode);
|
||||
|
||||
// Clean up before switching to the older state, for example setting the state
|
||||
// to none sets sys/class/android_usb/android0/enable to 0.
|
||||
if (!SetUsbConfig("none")) {
|
||||
LOG(ERROR) << "Failed to clear USB config";
|
||||
}
|
||||
|
||||
if (usb_state != "none") {
|
||||
if (!SetUsbConfig(usb_state)) {
|
||||
LOG(ERROR) << "Failed to set USB config to " << usb_state;
|
||||
}
|
||||
}
|
||||
|
||||
return install_result;
|
||||
}
|
||||
@@ -14,9 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "asn1_decoder.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "private/asn1_decoder.h"
|
||||
|
||||
int asn1_context::peek_byte() const {
|
||||
if (length_ == 0) {
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 "install/fuse_sdcard_install.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "bootloader_message/bootloader_message.h"
|
||||
#include "fuse_provider.h"
|
||||
#include "fuse_sideload.h"
|
||||
#include "install/install.h"
|
||||
#include "otautil/roots.h"
|
||||
|
||||
static constexpr const char* SDCARD_ROOT = "/sdcard";
|
||||
// How long (in seconds) we wait for the fuse-provided package file to
|
||||
// appear, before timing out.
|
||||
static constexpr int SDCARD_INSTALL_TIMEOUT = 10;
|
||||
|
||||
// Set the BCB to reboot back into recovery (it won't resume the install from
|
||||
// sdcard though).
|
||||
static void SetSdcardUpdateBootloaderMessage() {
|
||||
std::vector<std::string> options;
|
||||
std::string err;
|
||||
if (!update_bootloader_message(options, &err)) {
|
||||
LOG(ERROR) << "Failed to set BCB message: " << err;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the selected filename, or an empty string.
|
||||
static std::string BrowseDirectory(const std::string& path, Device* device, RecoveryUI* ui) {
|
||||
ensure_path_mounted(path);
|
||||
|
||||
std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
|
||||
if (!d) {
|
||||
PLOG(ERROR) << "error opening " << path;
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<std::string> dirs;
|
||||
std::vector<std::string> entries{ "../" }; // "../" is always the first entry.
|
||||
|
||||
dirent* de;
|
||||
while ((de = readdir(d.get())) != nullptr) {
|
||||
std::string name(de->d_name);
|
||||
|
||||
if (de->d_type == DT_DIR) {
|
||||
// Skip "." and ".." entries.
|
||||
if (name == "." || name == "..") continue;
|
||||
dirs.push_back(name + "/");
|
||||
} else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
|
||||
entries.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(dirs.begin(), dirs.end());
|
||||
std::sort(entries.begin(), entries.end());
|
||||
|
||||
// Append dirs to the entries list.
|
||||
entries.insert(entries.end(), dirs.begin(), dirs.end());
|
||||
|
||||
std::vector<std::string> headers{ "Choose a package to install:", path };
|
||||
|
||||
size_t chosen_item = 0;
|
||||
while (true) {
|
||||
chosen_item = ui->ShowMenu(
|
||||
headers, entries, chosen_item, true,
|
||||
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
// Return if WaitKey() was interrupted.
|
||||
if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const std::string& item = entries[chosen_item];
|
||||
if (chosen_item == 0) {
|
||||
// Go up but continue browsing (if the caller is BrowseDirectory).
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string new_path = path + "/" + item;
|
||||
if (new_path.back() == '/') {
|
||||
// Recurse down into a subdirectory.
|
||||
new_path.pop_back();
|
||||
std::string result = BrowseDirectory(new_path, device, ui);
|
||||
if (!result.empty()) return result;
|
||||
} else {
|
||||
// Selected a zip file: return the path to the caller.
|
||||
return new_path;
|
||||
}
|
||||
}
|
||||
|
||||
// Unreachable.
|
||||
}
|
||||
|
||||
static bool StartSdcardFuse(const std::string& path) {
|
||||
auto file_data_reader = std::make_unique<FuseFileDataProvider>(path, 65536);
|
||||
|
||||
if (!file_data_reader->Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
|
||||
// that our open file continues to work but new references see it as unmounted.
|
||||
umount2("/sdcard", MNT_DETACH);
|
||||
|
||||
return run_fuse_sideload(std::move(file_data_reader)) == 0;
|
||||
}
|
||||
|
||||
int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
|
||||
if (ensure_path_mounted(SDCARD_ROOT) != 0) {
|
||||
LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
std::string path = BrowseDirectory(SDCARD_ROOT, device, ui);
|
||||
if (path.empty()) {
|
||||
LOG(ERROR) << "\n-- No package file selected.\n";
|
||||
ensure_path_unmounted(SDCARD_ROOT);
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
ui->Print("\n-- Install %s ...\n", path.c_str());
|
||||
SetSdcardUpdateBootloaderMessage();
|
||||
|
||||
// We used to use fuse in a thread as opposed to a process. Since accessing
|
||||
// through fuse involves going from kernel to userspace to kernel, it leads
|
||||
// to deadlock when a page fault occurs. (Bug: 26313124)
|
||||
pid_t child;
|
||||
if ((child = fork()) == 0) {
|
||||
bool status = StartSdcardFuse(path);
|
||||
|
||||
_exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child
|
||||
// process is ready.
|
||||
int result = INSTALL_ERROR;
|
||||
int status;
|
||||
bool waited = false;
|
||||
for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
|
||||
if (waitpid(child, &status, WNOHANG) == -1) {
|
||||
result = INSTALL_ERROR;
|
||||
waited = true;
|
||||
break;
|
||||
}
|
||||
|
||||
struct stat sb;
|
||||
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) {
|
||||
if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT - 1) {
|
||||
sleep(1);
|
||||
continue;
|
||||
} else {
|
||||
LOG(ERROR) << "Timed out waiting for the fuse-provided package.";
|
||||
result = INSTALL_ERROR;
|
||||
kill(child, SIGKILL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!waited) {
|
||||
// Calling stat() on this magic filename signals the fuse
|
||||
// filesystem to shut down.
|
||||
struct stat sb;
|
||||
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &sb);
|
||||
|
||||
waitpid(child, &status, 0);
|
||||
}
|
||||
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
ensure_path_unmounted(SDCARD_ROOT);
|
||||
return result;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
* Copyright (C) 2012 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.
|
||||
@@ -14,22 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#pragma once
|
||||
|
||||
#include "otafault/ota_io.h"
|
||||
#include <recovery_ui/device.h>
|
||||
|
||||
int main(int /* argc */, char** /* argv */) {
|
||||
int fd = open("testdata/test.file", O_RDWR);
|
||||
char buf[8];
|
||||
const char* out = "321";
|
||||
int readv = ota_read(fd, buf, 4);
|
||||
printf("Read returned %d\n", readv);
|
||||
int writev = ota_write(fd, out, 4);
|
||||
printf("Write returned %d\n", writev);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
// Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum
|
||||
// InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with
|
||||
// the reboot target set in reboot_action.
|
||||
int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action);
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* Copyright (C) 2019 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.
|
||||
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _ADB_INSTALL_H
|
||||
#define _ADB_INSTALL_H
|
||||
#pragma once
|
||||
|
||||
<<<<<<< HEAD:adb_install.h
|
||||
#include <sys/types.h>
|
||||
|
||||
//class RecoveryUI;
|
||||
@@ -24,5 +24,9 @@
|
||||
//static void set_usb_driver(bool enabled);
|
||||
//static void maybe_restart_adbd();
|
||||
int apply_from_adb(const char* install_file, pid_t* child_pid);
|
||||
=======
|
||||
#include "recovery_ui/device.h"
|
||||
#include "recovery_ui/ui.h"
|
||||
>>>>>>> android-10.0.0_r25:install/include/install/fuse_sdcard_install.h
|
||||
|
||||
#endif
|
||||
int ApplyFromSdcard(Device* device, RecoveryUI* ui);
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#include "package.h"
|
||||
#include "recovery_ui/ui.h"
|
||||
|
||||
enum InstallResult {
|
||||
INSTALL_SUCCESS,
|
||||
INSTALL_ERROR,
|
||||
INSTALL_CORRUPT,
|
||||
INSTALL_NONE,
|
||||
INSTALL_SKIPPED,
|
||||
INSTALL_RETRY,
|
||||
INSTALL_KEY_INTERRUPTED,
|
||||
INSTALL_REBOOT,
|
||||
};
|
||||
|
||||
enum class OtaType {
|
||||
AB,
|
||||
BLOCK,
|
||||
BRICK,
|
||||
};
|
||||
|
||||
// Installs the given update package. This function should also wipe the cache partition after a
|
||||
// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the
|
||||
// cache.
|
||||
int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount,
|
||||
int retry_count, RecoveryUI* ui);
|
||||
|
||||
// Verifies the package by ota keys. Returns true if the package is verified successfully,
|
||||
// otherwise returns false.
|
||||
bool verify_package(Package* package, RecoveryUI* ui);
|
||||
|
||||
// Reads meta data file of the package; parses each line in the format "key=value"; and writes the
|
||||
// result to |metadata|. Return true if succeed, otherwise return false.
|
||||
bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata);
|
||||
|
||||
// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe.
|
||||
std::vector<std::string> GetWipePartitionList(Package* wipe_package);
|
||||
|
||||
// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the
|
||||
// entry doesn't exist.
|
||||
bool verify_package_compatibility(ZipArchiveHandle package_zip);
|
||||
|
||||
// Checks if the the metadata in the OTA package has expected values. Returns 0 on success.
|
||||
// Mandatory checks: ota-type, pre-device and serial number(if presents)
|
||||
// AB OTA specific checks: pre-build version, fingerprint, timestamp.
|
||||
int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type);
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#include "verifier.h"
|
||||
|
||||
// This class serves as a wrapper for an OTA update package. It aims to provide the common
|
||||
// interface for both packages loaded in memory and packages read from fd.
|
||||
class Package : public VerifierInterface {
|
||||
public:
|
||||
static std::unique_ptr<Package> CreateMemoryPackage(
|
||||
const std::string& path, const std::function<void(float)>& set_progress);
|
||||
static std::unique_ptr<Package> CreateMemoryPackage(
|
||||
std::vector<uint8_t> content, const std::function<void(float)>& set_progress);
|
||||
static std::unique_ptr<Package> CreateFilePackage(const std::string& path,
|
||||
const std::function<void(float)>& set_progress);
|
||||
|
||||
virtual ~Package() = default;
|
||||
|
||||
// Opens the package as a zip file and returns the ZipArchiveHandle.
|
||||
virtual ZipArchiveHandle GetZipArchiveHandle() = 0;
|
||||
|
||||
// Updates the progress in fraction during package verification.
|
||||
void SetProgress(float progress) override;
|
||||
|
||||
protected:
|
||||
// An optional function to update the progress.
|
||||
std::function<void(float)> set_progress_;
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <openssl/ec_key.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
constexpr size_t MiB = 1024 * 1024;
|
||||
|
||||
using HasherUpdateCallback = std::function<void(const uint8_t* addr, uint64_t size)>;
|
||||
|
||||
struct RSADeleter {
|
||||
void operator()(RSA* rsa) const {
|
||||
RSA_free(rsa);
|
||||
}
|
||||
};
|
||||
|
||||
struct ECKEYDeleter {
|
||||
void operator()(EC_KEY* ec_key) const {
|
||||
EC_KEY_free(ec_key);
|
||||
}
|
||||
};
|
||||
|
||||
struct Certificate {
|
||||
typedef enum {
|
||||
KEY_TYPE_RSA,
|
||||
KEY_TYPE_EC,
|
||||
} KeyType;
|
||||
|
||||
Certificate(int hash_len_, KeyType key_type_, std::unique_ptr<RSA, RSADeleter>&& rsa_,
|
||||
std::unique_ptr<EC_KEY, ECKEYDeleter>&& ec_)
|
||||
: hash_len(hash_len_), key_type(key_type_), rsa(std::move(rsa_)), ec(std::move(ec_)) {}
|
||||
|
||||
// SHA_DIGEST_LENGTH (SHA-1) or SHA256_DIGEST_LENGTH (SHA-256)
|
||||
int hash_len;
|
||||
KeyType key_type;
|
||||
std::unique_ptr<RSA, RSADeleter> rsa;
|
||||
std::unique_ptr<EC_KEY, ECKEYDeleter> ec;
|
||||
};
|
||||
|
||||
class VerifierInterface {
|
||||
public:
|
||||
virtual ~VerifierInterface() = default;
|
||||
|
||||
// Returns the package size in bytes.
|
||||
virtual uint64_t GetPackageSize() const = 0;
|
||||
|
||||
// Reads |byte_count| data starting from |offset|, and puts the result in |buffer|.
|
||||
virtual bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) = 0;
|
||||
|
||||
// Updates the hash contexts for |length| bytes data starting from |start|.
|
||||
virtual bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
|
||||
uint64_t length) = 0;
|
||||
|
||||
// Updates the progress in fraction during package verification.
|
||||
virtual void SetProgress(float progress) = 0;
|
||||
};
|
||||
|
||||
// Looks for an RSA signature embedded in the .ZIP file comment given the path to the zip.
|
||||
// Verifies that it matches one of the given public keys. Returns VERIFY_SUCCESS or
|
||||
// VERIFY_FAILURE (if any error is encountered or no key matches the signature).
|
||||
int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys);
|
||||
|
||||
// Checks that the RSA key has a modulus of 2048 or 4096 bits long, and public exponent is 3 or
|
||||
// 65537.
|
||||
bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa);
|
||||
|
||||
// Checks that the field size of the curve for the EC key is 256 bits.
|
||||
bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key);
|
||||
|
||||
// Parses a PEM-encoded x509 certificate from the given buffer and saves it into |cert|. Returns
|
||||
// false if there is a parsing failure or the signature's encryption algorithm is not supported.
|
||||
bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert);
|
||||
|
||||
// Iterates over the zip entries with the suffix "x509.pem" and returns a list of recognized
|
||||
// certificates. Returns an empty list if we fail to parse any of the entries.
|
||||
std::vector<Certificate> LoadKeysFromZipfile(const std::string& zip_name);
|
||||
|
||||
#define VERIFY_SUCCESS 0
|
||||
#define VERIFY_FAILURE 1
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "recovery_ui/device.h"
|
||||
#include "recovery_ui/ui.h"
|
||||
|
||||
struct selabel_handle;
|
||||
|
||||
// Returns true on success.
|
||||
bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm);
|
||||
|
||||
// Returns true on success.
|
||||
bool WipeData(Device* device, bool convert_fbe);
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef ASN1_DECODER_H_
|
||||
#define ASN1_DECODER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class asn1_context {
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Private headers exposed for testing purpose only.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
// Sets up the commands for a non-A/B update. Extracts the updater binary from the open zip archive
|
||||
// |zip| located at |package|. Stores the command line that should be called into |cmd|. The
|
||||
// |status_fd| is the file descriptor the child process should use to report back the progress of
|
||||
// the update.
|
||||
int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
|
||||
int status_fd, std::vector<std::string>* cmd);
|
||||
|
||||
// Sets up the commands for an A/B update. Extracts the needed entries from the open zip archive
|
||||
// |zip| located at |package|. Stores the command line that should be called into |cmd|. The
|
||||
// |status_fd| is the file descriptor the child process should use to report back the progress of
|
||||
// the update. Note that since this applies to the sideloading flow only, it takes one less
|
||||
// parameter |retry_count| than the non-A/B version.
|
||||
int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
|
||||
std::vector<std::string>* cmd);
|
||||
+251
-216
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "install.h"
|
||||
#include "install/install.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
@@ -32,9 +32,7 @@
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
@@ -45,38 +43,33 @@
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <vintf/VintfObjectRecovery.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "otautil/SysUtil.h"
|
||||
#include "otautil/ThermalUtil.h"
|
||||
#include "install/package.h"
|
||||
#include "install/verifier.h"
|
||||
#include "install/wipe_data.h"
|
||||
#include "otautil/error_code.h"
|
||||
#include "private/install.h"
|
||||
#include "roots.h"
|
||||
#include "ui.h"
|
||||
#include "verifier.h"
|
||||
#include "otautil/paths.h"
|
||||
#include "otautil/roots.h"
|
||||
#include "otautil/sysutil.h"
|
||||
#include "otautil/thermalutil.h"
|
||||
#include "private/setup_commands.h"
|
||||
#include "recovery_ui/ui.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static constexpr int kRecoveryApiVersion = 3;
|
||||
// Assert the version defined in code and in Android.mk are consistent.
|
||||
static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions.");
|
||||
|
||||
// Default allocation of progress bar segments to operations
|
||||
static constexpr int VERIFICATION_PROGRESS_TIME = 60;
|
||||
static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
|
||||
|
||||
static std::condition_variable finish_log_temperature;
|
||||
|
||||
// This function parses and returns the build.version.incremental
|
||||
static std::string parse_build_number(const std::string& str) {
|
||||
size_t pos = str.find('=');
|
||||
if (pos != std::string::npos) {
|
||||
return android::base::Trim(str.substr(pos+1));
|
||||
}
|
||||
|
||||
LOG(ERROR) << "Failed to parse build number in " << str;
|
||||
return "";
|
||||
}
|
||||
|
||||
bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) {
|
||||
bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata) {
|
||||
CHECK(metadata != nullptr);
|
||||
|
||||
static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
|
||||
@@ -88,117 +81,160 @@ bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) {
|
||||
}
|
||||
|
||||
uint32_t length = entry.uncompressed_length;
|
||||
metadata->resize(length, '\0');
|
||||
int32_t err = ExtractToMemory(zip, &entry, reinterpret_cast<uint8_t*>(&(*metadata)[0]), length);
|
||||
std::string metadata_string(length, '\0');
|
||||
int32_t err =
|
||||
ExtractToMemory(zip, &entry, reinterpret_cast<uint8_t*>(&metadata_string[0]), length);
|
||||
if (err != 0) {
|
||||
LOG(ERROR) << "Failed to extract " << METADATA_PATH << ": " << ErrorCodeString(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const std::string& line : android::base::Split(metadata_string, "\n")) {
|
||||
size_t eq = line.find('=');
|
||||
if (eq != std::string::npos) {
|
||||
metadata->emplace(android::base::Trim(line.substr(0, eq)),
|
||||
android::base::Trim(line.substr(eq + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read the build.version.incremental of src/tgt from the metadata and log it to last_install.
|
||||
static void read_source_target_build(ZipArchiveHandle zip, std::vector<std::string>* log_buffer) {
|
||||
std::string metadata;
|
||||
if (!read_metadata_from_package(zip, &metadata)) {
|
||||
return;
|
||||
}
|
||||
// Examples of the pre-build and post-build strings in metadata:
|
||||
// pre-build-incremental=2943039
|
||||
// post-build-incremental=2951741
|
||||
std::vector<std::string> lines = android::base::Split(metadata, "\n");
|
||||
for (const std::string& line : lines) {
|
||||
std::string str = android::base::Trim(line);
|
||||
if (android::base::StartsWith(str, "pre-build-incremental")) {
|
||||
std::string source_build = parse_build_number(str);
|
||||
if (!source_build.empty()) {
|
||||
log_buffer->push_back("source_build: " + source_build);
|
||||
}
|
||||
} else if (android::base::StartsWith(str, "post-build-incremental")) {
|
||||
std::string target_build = parse_build_number(str);
|
||||
if (!target_build.empty()) {
|
||||
log_buffer->push_back("target_build: " + target_build);
|
||||
}
|
||||
}
|
||||
// Gets the value for the given key in |metadata|. Returns an emtpy string if the key isn't
|
||||
// present.
|
||||
static std::string get_value(const std::map<std::string, std::string>& metadata,
|
||||
const std::string& key) {
|
||||
const auto& it = metadata.find(key);
|
||||
return (it == metadata.end()) ? "" : it->second;
|
||||
}
|
||||
|
||||
static std::string OtaTypeToString(OtaType type) {
|
||||
switch (type) {
|
||||
case OtaType::AB:
|
||||
return "AB";
|
||||
case OtaType::BLOCK:
|
||||
return "BLOCK";
|
||||
case OtaType::BRICK:
|
||||
return "BRICK";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AB_OTA_UPDATER
|
||||
|
||||
// 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(ZipArchiveHandle zip) {
|
||||
std::string metadata_str;
|
||||
if (!read_metadata_from_package(zip, &metadata_str)) {
|
||||
return INSTALL_CORRUPT;
|
||||
// Read the build.version.incremental of src/tgt from the metadata and log it to last_install.
|
||||
static void ReadSourceTargetBuild(const std::map<std::string, std::string>& metadata,
|
||||
std::vector<std::string>* log_buffer) {
|
||||
// Examples of the pre-build and post-build strings in metadata:
|
||||
// pre-build-incremental=2943039
|
||||
// post-build-incremental=2951741
|
||||
auto source_build = get_value(metadata, "pre-build-incremental");
|
||||
if (!source_build.empty()) {
|
||||
log_buffer->push_back("source_build: " + source_build);
|
||||
}
|
||||
std::map<std::string, std::string> metadata;
|
||||
for (const std::string& line : android::base::Split(metadata_str, "\n")) {
|
||||
size_t eq = line.find('=');
|
||||
if (eq != std::string::npos) {
|
||||
metadata[line.substr(0, eq)] = line.substr(eq + 1);
|
||||
|
||||
auto target_build = get_value(metadata, "post-build-incremental");
|
||||
if (!target_build.empty()) {
|
||||
log_buffer->push_back("target_build: " + target_build);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the build version, fingerprint and timestamp in the metadata of the A/B package.
|
||||
// Downgrading is not allowed unless explicitly enabled in the package and only for
|
||||
// incremental packages.
|
||||
static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) {
|
||||
// Incremental updates should match the current build.
|
||||
auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", "");
|
||||
auto pkg_pre_build = get_value(metadata, "pre-build-incremental");
|
||||
if (!pkg_pre_build.empty() && pkg_pre_build != device_pre_build) {
|
||||
LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected "
|
||||
<< device_pre_build;
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
auto device_fingerprint = android::base::GetProperty("ro.build.fingerprint", "");
|
||||
auto pkg_pre_build_fingerprint = get_value(metadata, "pre-build");
|
||||
if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != device_fingerprint) {
|
||||
LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected "
|
||||
<< device_fingerprint;
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD:install.cpp
|
||||
=======
|
||||
// Check for downgrade version.
|
||||
int64_t build_timestamp =
|
||||
android::base::GetIntProperty("ro.build.date.utc", std::numeric_limits<int64_t>::max());
|
||||
int64_t pkg_post_timestamp = 0;
|
||||
// We allow to full update to the same version we are running, in case there
|
||||
// is a problem with the current copy of that version.
|
||||
auto pkg_post_timestamp_string = get_value(metadata, "post-timestamp");
|
||||
if (pkg_post_timestamp_string.empty() ||
|
||||
!android::base::ParseInt(pkg_post_timestamp_string, &pkg_post_timestamp) ||
|
||||
pkg_post_timestamp < build_timestamp) {
|
||||
if (get_value(metadata, "ota-downgrade") != "yes") {
|
||||
LOG(ERROR) << "Update package is older than the current build, expected a build "
|
||||
"newer than timestamp "
|
||||
<< build_timestamp << " but package has timestamp " << pkg_post_timestamp
|
||||
<< " and downgrade not allowed.";
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
if (pkg_pre_build_fingerprint.empty()) {
|
||||
LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
std::string value = android::base::GetProperty("ro.product.device", "");
|
||||
const std::string& pkg_device = metadata["pre-device"];
|
||||
if (pkg_device != value || pkg_device.empty()) {
|
||||
LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << value;
|
||||
>>>>>>> android-10.0.0_r25:install/install.cpp
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) {
|
||||
auto package_ota_type = get_value(metadata, "ota-type");
|
||||
auto expected_ota_type = OtaTypeToString(ota_type);
|
||||
if (ota_type != OtaType::AB && ota_type != OtaType::BRICK) {
|
||||
LOG(INFO) << "Skip package metadata check for ota type " << expected_ota_type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (package_ota_type != expected_ota_type) {
|
||||
LOG(ERROR) << "Unexpected ota package type, expects " << expected_ota_type << ", actual "
|
||||
<< package_ota_type;
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
auto device = android::base::GetProperty("ro.product.device", "");
|
||||
auto pkg_device = get_value(metadata, "pre-device");
|
||||
if (pkg_device != device || pkg_device.empty()) {
|
||||
LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << device;
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
// We allow the package to not have any serialno; and we also allow it to carry multiple serial
|
||||
// numbers split by "|"; e.g. serialno=serialno1|serialno2|serialno3 ... We will fail the
|
||||
// verification if the device's serialno doesn't match any of these carried numbers.
|
||||
value = android::base::GetProperty("ro.serialno", "");
|
||||
const std::string& pkg_serial_no = metadata["serialno"];
|
||||
auto pkg_serial_no = get_value(metadata, "serialno");
|
||||
if (!pkg_serial_no.empty()) {
|
||||
bool match = false;
|
||||
for (const std::string& number : android::base::Split(pkg_serial_no, "|")) {
|
||||
if (value == android::base::Trim(number)) {
|
||||
match = true;
|
||||
break;
|
||||
auto device_serial_no = android::base::GetProperty("ro.serialno", "");
|
||||
bool serial_number_match = false;
|
||||
for (const auto& number : android::base::Split(pkg_serial_no, "|")) {
|
||||
if (device_serial_no == android::base::Trim(number)) {
|
||||
serial_number_match = true;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
if (!serial_number_match) {
|
||||
LOG(ERROR) << "Package is for serial " << pkg_serial_no;
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata["ota-type"] != "AB") {
|
||||
LOG(ERROR) << "Package is not A/B";
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
// Incremental updates should match the current build.
|
||||
value = android::base::GetProperty("ro.build.version.incremental", "");
|
||||
const std::string& pkg_pre_build = metadata["pre-build-incremental"];
|
||||
if (!pkg_pre_build.empty() && pkg_pre_build != value) {
|
||||
LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected " << value;
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
value = android::base::GetProperty("ro.build.fingerprint", "");
|
||||
const std::string& pkg_pre_build_fingerprint = metadata["pre-build"];
|
||||
if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != value) {
|
||||
LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected "
|
||||
<< value;
|
||||
return INSTALL_ERROR;
|
||||
if (ota_type == OtaType::AB) {
|
||||
return CheckAbSpecificMetadata(metadata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_binary_command(const std::string& package, ZipArchiveHandle zip,
|
||||
const std::string& binary_path, int /* retry_count */, int status_fd,
|
||||
int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
|
||||
std::vector<std::string>* cmd) {
|
||||
CHECK(cmd != nullptr);
|
||||
int ret = check_newer_ab_build(zip);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset
|
||||
// in the zip file.
|
||||
@@ -227,7 +263,7 @@ int update_binary_command(const std::string& package, ZipArchiveHandle zip,
|
||||
}
|
||||
long payload_offset = payload_entry.offset;
|
||||
*cmd = {
|
||||
binary_path,
|
||||
"/system/bin/update_engine_sideload",
|
||||
"--payload=file://" + package,
|
||||
android::base::StringPrintf("--offset=%ld", payload_offset),
|
||||
"--headers=" + std::string(payload_properties.begin(), payload_properties.end()),
|
||||
@@ -236,14 +272,11 @@ int update_binary_command(const std::string& package, ZipArchiveHandle zip,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // !AB_OTA_UPDATER
|
||||
|
||||
int update_binary_command(const std::string& package, ZipArchiveHandle zip,
|
||||
const std::string& binary_path, int retry_count, int status_fd,
|
||||
std::vector<std::string>* cmd) {
|
||||
int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
|
||||
int status_fd, std::vector<std::string>* cmd) {
|
||||
CHECK(cmd != nullptr);
|
||||
|
||||
// On traditional updates we extract the update binary from the package.
|
||||
// In non-A/B updates we extract the update binary from the package.
|
||||
static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
|
||||
ZipString binary_name(UPDATE_BINARY_NAME);
|
||||
ZipEntry binary_entry;
|
||||
@@ -252,20 +285,26 @@ int update_binary_command(const std::string& package, ZipArchiveHandle zip,
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
|
||||
const std::string binary_path = Paths::Get().temporary_update_binary();
|
||||
unlink(binary_path.c_str());
|
||||
int fd = open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755);
|
||||
android::base::unique_fd fd(
|
||||
open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755));
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Failed to create " << binary_path;
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
|
||||
close(fd);
|
||||
if (error != 0) {
|
||||
LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
|
||||
// When executing the update binary contained in the package, the arguments passed are:
|
||||
// - the version number for this interface
|
||||
// - an FD to which the program can write in order to update the progress bar.
|
||||
// - the name of the package zip file.
|
||||
// - an optional argument "retry" if this update is a retry of a failed update attempt.
|
||||
*cmd = {
|
||||
binary_path,
|
||||
std::to_string(kRecoveryApiVersion),
|
||||
@@ -277,7 +316,6 @@ int update_binary_command(const std::string& package, ZipArchiveHandle zip,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif // !AB_OTA_UPDATER
|
||||
|
||||
static void log_max_temperature(int* max_temperature, const std::atomic<bool>& logger_finished) {
|
||||
CHECK(max_temperature != nullptr);
|
||||
@@ -292,87 +330,75 @@ static void log_max_temperature(int* max_temperature, const std::atomic<bool>& l
|
||||
// If the package contains an update binary, extract it and run it.
|
||||
static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
|
||||
std::vector<std::string>* log_buffer, int retry_count,
|
||||
int* max_temperature) {
|
||||
read_source_target_build(zip, log_buffer);
|
||||
|
||||
int pipefd[2];
|
||||
pipe(pipefd);
|
||||
|
||||
std::vector<std::string> args;
|
||||
#ifdef AB_OTA_UPDATER
|
||||
int ret = update_binary_command(package, zip, "/sbin/update_engine_sideload", retry_count,
|
||||
pipefd[1], &args);
|
||||
#else
|
||||
int ret = update_binary_command(package, zip, "/tmp/update-binary", retry_count, pipefd[1],
|
||||
&args);
|
||||
#endif
|
||||
if (ret) {
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
|
||||
return ret;
|
||||
int* max_temperature, RecoveryUI* ui) {
|
||||
std::map<std::string, std::string> metadata;
|
||||
if (!ReadMetadataFromPackage(zip, &metadata)) {
|
||||
LOG(ERROR) << "Failed to parse metadata in the zip file";
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
|
||||
// When executing the update binary contained in the package, the
|
||||
// arguments passed are:
|
||||
bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
|
||||
// Verifies against the metadata in the package first.
|
||||
if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : 0;
|
||||
check_status != 0) {
|
||||
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
|
||||
return check_status;
|
||||
}
|
||||
|
||||
ReadSourceTargetBuild(metadata, log_buffer);
|
||||
|
||||
// The updater in child process writes to the pipe to communicate with recovery.
|
||||
android::base::unique_fd pipe_read, pipe_write;
|
||||
// Explicitly disable O_CLOEXEC using 0 as the flags (last) parameter to Pipe
|
||||
// so that the child updater process will recieve a non-closed fd.
|
||||
if (!android::base::Pipe(&pipe_read, &pipe_write, 0)) {
|
||||
PLOG(ERROR) << "Failed to create pipe for updater-recovery communication";
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
|
||||
// The updater-recovery communication protocol.
|
||||
//
|
||||
// - the version number for this interface
|
||||
// progress <frac> <secs>
|
||||
// fill up the next <frac> part of of the progress bar over <secs> seconds. If <secs> is
|
||||
// zero, use `set_progress` commands to manually control the progress of this segment of the
|
||||
// bar.
|
||||
//
|
||||
// - an FD to which the program can write in order to update the
|
||||
// progress bar. The program can write single-line commands:
|
||||
// set_progress <frac>
|
||||
// <frac> should be between 0.0 and 1.0; sets the progress bar within the segment defined by
|
||||
// the most recent progress command.
|
||||
//
|
||||
// progress <frac> <secs>
|
||||
// fill up the next <frac> part of of the progress bar
|
||||
// over <secs> seconds. If <secs> is zero, use
|
||||
// set_progress commands to manually control the
|
||||
// progress of this segment of the bar.
|
||||
// ui_print <string>
|
||||
// display <string> on the screen.
|
||||
//
|
||||
// set_progress <frac>
|
||||
// <frac> should be between 0.0 and 1.0; sets the
|
||||
// progress bar within the segment defined by the most
|
||||
// recent progress command.
|
||||
// wipe_cache
|
||||
// a wipe of cache will be performed following a successful installation.
|
||||
//
|
||||
// ui_print <string>
|
||||
// display <string> on the screen.
|
||||
// clear_display
|
||||
// turn off the text display.
|
||||
//
|
||||
// wipe_cache
|
||||
// a wipe of cache will be performed following a successful
|
||||
// installation.
|
||||
// enable_reboot
|
||||
// packages can explicitly request that they want the user to be able to reboot during
|
||||
// installation (useful for debugging packages that don't exit).
|
||||
//
|
||||
// clear_display
|
||||
// turn off the text display.
|
||||
// retry_update
|
||||
// updater encounters some issue during the update. It requests a reboot to retry the same
|
||||
// package automatically.
|
||||
//
|
||||
// enable_reboot
|
||||
// packages can explicitly request that they want the user
|
||||
// to be able to reboot during installation (useful for
|
||||
// debugging packages that don't exit).
|
||||
//
|
||||
// retry_update
|
||||
// updater encounters some issue during the update. It requests
|
||||
// a reboot to retry the same package automatically.
|
||||
//
|
||||
// log <string>
|
||||
// updater requests logging the string (e.g. cause of the
|
||||
// failure).
|
||||
//
|
||||
// - the name of the package zip file.
|
||||
//
|
||||
// - an optional argument "retry" if this update is a retry of a failed
|
||||
// update attempt.
|
||||
// log <string>
|
||||
// updater requests logging the string (e.g. cause of the failure).
|
||||
//
|
||||
|
||||
// Convert the vector to a NULL-terminated char* array suitable for execv.
|
||||
const char* chr_args[args.size() + 1];
|
||||
chr_args[args.size()] = nullptr;
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
chr_args[i] = args[i].c_str();
|
||||
std::vector<std::string> args;
|
||||
if (int update_status =
|
||||
is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
|
||||
: SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
|
||||
update_status != 0) {
|
||||
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
|
||||
return update_status;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
PLOG(ERROR) << "Failed to fork update binary";
|
||||
log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure));
|
||||
return INSTALL_ERROR;
|
||||
@@ -380,16 +406,18 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b
|
||||
|
||||
if (pid == 0) {
|
||||
umask(022);
|
||||
close(pipefd[0]);
|
||||
execv(chr_args[0], const_cast<char**>(chr_args));
|
||||
// Bug: 34769056
|
||||
// We shouldn't use LOG/PLOG in the forked process, since they may cause
|
||||
// the child process to hang. This deadlock results from an improperly
|
||||
// copied mutex in the ui functions.
|
||||
pipe_read.reset();
|
||||
|
||||
// Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
|
||||
auto chr_args = StringVectorToNullTerminatedArray(args);
|
||||
execv(chr_args[0], chr_args.data());
|
||||
// We shouldn't use LOG/PLOG in the forked process, since they may cause the child process to
|
||||
// hang. This deadlock results from an improperly copied mutex in the ui functions.
|
||||
// (Bug: 34769056)
|
||||
fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
close(pipefd[1]);
|
||||
pipe_write.reset();
|
||||
|
||||
std::atomic<bool> logger_finished(false);
|
||||
std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));
|
||||
@@ -398,7 +426,7 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b
|
||||
bool retry_update = false;
|
||||
|
||||
char buffer[1024];
|
||||
FILE* from_child = fdopen(pipefd[0], "r");
|
||||
FILE* from_child = android::base::Fdopen(std::move(pipe_read), "r");
|
||||
while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
|
||||
std::string line(buffer);
|
||||
size_t space = line.find_first_of(" \n");
|
||||
@@ -463,9 +491,16 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b
|
||||
if (retry_update) {
|
||||
return INSTALL_RETRY;
|
||||
}
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
LOG(ERROR) << "Error in " << package << " (Status " << WEXITSTATUS(status) << ")";
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) != EXIT_SUCCESS) {
|
||||
LOG(ERROR) << "Error in " << package << " (status " << WEXITSTATUS(status) << ")";
|
||||
return INSTALL_ERROR;
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
LOG(ERROR) << "Error in " << package << " (killed by signal " << WTERMSIG(status) << ")";
|
||||
return INSTALL_ERROR;
|
||||
} else {
|
||||
LOG(FATAL) << "Invalid status code " << status;
|
||||
}
|
||||
|
||||
return INSTALL_SUCCESS;
|
||||
@@ -541,7 +576,7 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) {
|
||||
|
||||
static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
|
||||
std::vector<std::string>* log_buffer, int retry_count,
|
||||
int* max_temperature) {
|
||||
int* max_temperature, RecoveryUI* ui) {
|
||||
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
|
||||
ui->Print("Finding update package...\n");
|
||||
// Give verification half the progress bar...
|
||||
@@ -554,40 +589,35 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo
|
||||
|
||||
if (needs_mount) {
|
||||
if (path[0] == '@') {
|
||||
ensure_path_mounted(path.substr(1).c_str());
|
||||
ensure_path_mounted(path.substr(1));
|
||||
} else {
|
||||
ensure_path_mounted(path.c_str());
|
||||
ensure_path_mounted(path);
|
||||
}
|
||||
}
|
||||
|
||||
MemMapping map;
|
||||
if (!map.MapFile(path)) {
|
||||
LOG(ERROR) << "failed to map file";
|
||||
auto package = Package::CreateMemoryPackage(
|
||||
path, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
|
||||
if (!package) {
|
||||
log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
|
||||
// Verify package.
|
||||
if (!verify_package(map.addr, map.length)) {
|
||||
if (!verify_package(package.get(), ui)) {
|
||||
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
|
||||
// Try to open the package.
|
||||
ZipArchiveHandle zip;
|
||||
int err = OpenArchiveFromMemory(map.addr, map.length, path.c_str(), &zip);
|
||||
if (err != 0) {
|
||||
LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);
|
||||
ZipArchiveHandle zip = package->GetZipArchiveHandle();
|
||||
if (!zip) {
|
||||
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
|
||||
|
||||
CloseArchive(zip);
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
|
||||
// Additionally verify the compatibility of the package.
|
||||
if (!verify_package_compatibility(zip)) {
|
||||
// Additionally verify the compatibility of the package if it's a fresh install.
|
||||
if (retry_count == 0 && !verify_package_compatibility(zip)) {
|
||||
log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
|
||||
CloseArchive(zip);
|
||||
return INSTALL_CORRUPT;
|
||||
}
|
||||
|
||||
@@ -597,21 +627,18 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo
|
||||
ui->Print("Retry attempt: %d\n", retry_count);
|
||||
}
|
||||
ui->SetEnableReboot(false);
|
||||
int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
|
||||
int result =
|
||||
try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui);
|
||||
ui->SetEnableReboot(true);
|
||||
ui->Print("\n");
|
||||
|
||||
CloseArchive(zip);
|
||||
return result;
|
||||
}
|
||||
|
||||
int install_package(const std::string& path, bool* wipe_cache, const std::string& install_file,
|
||||
bool needs_mount, int retry_count) {
|
||||
int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount,
|
||||
int retry_count, RecoveryUI* ui) {
|
||||
CHECK(!path.empty());
|
||||
CHECK(!install_file.empty());
|
||||
CHECK(wipe_cache != nullptr);
|
||||
|
||||
modified_flash = true;
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
int start_temperature = GetMaxValueFromThermalZone();
|
||||
@@ -623,8 +650,10 @@ int install_package(const std::string& path, bool* wipe_cache, const std::string
|
||||
LOG(ERROR) << "failed to set up expected mounts for install; aborting";
|
||||
result = INSTALL_ERROR;
|
||||
} else {
|
||||
result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count,
|
||||
&max_temperature);
|
||||
bool updater_wipe_cache = false;
|
||||
result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
|
||||
retry_count, &max_temperature, ui);
|
||||
should_wipe_cache = should_wipe_cache || updater_wipe_cache;
|
||||
}
|
||||
|
||||
// Measure the time spent to apply OTA update in seconds.
|
||||
@@ -671,6 +700,7 @@ int install_package(const std::string& path, bool* wipe_cache, const std::string
|
||||
|
||||
std::string log_content =
|
||||
android::base::Join(log_header, "\n") + "\n" + android::base::Join(log_buffer, "\n") + "\n";
|
||||
const std::string& install_file = Paths::Get().temporary_install_file();
|
||||
if (!android::base::WriteStringToFile(log_content, install_file)) {
|
||||
PLOG(ERROR) << "failed to write " << install_file;
|
||||
}
|
||||
@@ -678,23 +708,28 @@ int install_package(const std::string& path, bool* wipe_cache, const std::string
|
||||
// Write a copy into last_log.
|
||||
LOG(INFO) << log_content;
|
||||
|
||||
if (result == INSTALL_SUCCESS && should_wipe_cache) {
|
||||
if (!WipeCache(ui, nullptr)) {
|
||||
result = INSTALL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool verify_package(const unsigned char* package_data, size_t package_size) {
|
||||
static constexpr const char* PUBLIC_KEYS_FILE = "/res/keys";
|
||||
std::vector<Certificate> loadedKeys;
|
||||
if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) {
|
||||
bool verify_package(Package* package, RecoveryUI* ui) {
|
||||
static constexpr const char* CERTIFICATE_ZIP_FILE = "/system/etc/security/otacerts.zip";
|
||||
std::vector<Certificate> loaded_keys = LoadKeysFromZipfile(CERTIFICATE_ZIP_FILE);
|
||||
if (loaded_keys.empty()) {
|
||||
LOG(ERROR) << "Failed to load keys";
|
||||
return false;
|
||||
}
|
||||
LOG(INFO) << loadedKeys.size() << " key(s) loaded from " << PUBLIC_KEYS_FILE;
|
||||
LOG(INFO) << loaded_keys.size() << " key(s) loaded from " << CERTIFICATE_ZIP_FILE;
|
||||
|
||||
// Verify package.
|
||||
ui->Print("Verifying update package...\n");
|
||||
auto t0 = std::chrono::system_clock::now();
|
||||
int err = verify_file(package_data, package_size, loadedKeys,
|
||||
std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
|
||||
int err = verify_file(package, loaded_keys);
|
||||
std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
|
||||
ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
|
||||
if (err != VERIFY_SUCCESS) {
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 "install/package.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "otautil/error_code.h"
|
||||
#include "otautil/sysutil.h"
|
||||
|
||||
// This class wraps the package in memory, i.e. a memory mapped package, or a package loaded
|
||||
// to a string/vector.
|
||||
class MemoryPackage : public Package {
|
||||
public:
|
||||
// Constructs the class from a file. We will memory maps the file later.
|
||||
MemoryPackage(const std::string& path, std::unique_ptr<MemMapping> map,
|
||||
const std::function<void(float)>& set_progress);
|
||||
|
||||
// Constructs the class from the package bytes in |content|.
|
||||
MemoryPackage(std::vector<uint8_t> content, const std::function<void(float)>& set_progress);
|
||||
|
||||
~MemoryPackage() override;
|
||||
|
||||
// Memory maps the package file if necessary. Initializes the start address and size of the
|
||||
// package.
|
||||
uint64_t GetPackageSize() const override {
|
||||
return package_size_;
|
||||
}
|
||||
|
||||
bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
|
||||
|
||||
ZipArchiveHandle GetZipArchiveHandle() override;
|
||||
|
||||
bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
|
||||
uint64_t length) override;
|
||||
|
||||
private:
|
||||
const uint8_t* addr_; // Start address of the package in memory.
|
||||
uint64_t package_size_; // Package size in bytes.
|
||||
|
||||
// The memory mapped package.
|
||||
std::unique_ptr<MemMapping> map_;
|
||||
// A copy of the package content, valid only if we create the class with the exact bytes of
|
||||
// the package.
|
||||
std::vector<uint8_t> package_content_;
|
||||
// The physical path to the package, empty if we create the class with the package content.
|
||||
std::string path_;
|
||||
|
||||
// The ZipArchiveHandle of the package.
|
||||
ZipArchiveHandle zip_handle_;
|
||||
};
|
||||
|
||||
void Package::SetProgress(float progress) {
|
||||
if (set_progress_) {
|
||||
set_progress_(progress);
|
||||
}
|
||||
}
|
||||
|
||||
class FilePackage : public Package {
|
||||
public:
|
||||
FilePackage(android::base::unique_fd&& fd, uint64_t file_size, const std::string& path,
|
||||
const std::function<void(float)>& set_progress);
|
||||
|
||||
~FilePackage() override;
|
||||
|
||||
uint64_t GetPackageSize() const override {
|
||||
return package_size_;
|
||||
}
|
||||
|
||||
bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
|
||||
|
||||
ZipArchiveHandle GetZipArchiveHandle() override;
|
||||
|
||||
bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
|
||||
uint64_t length) override;
|
||||
|
||||
private:
|
||||
android::base::unique_fd fd_; // The underlying fd to the open package.
|
||||
uint64_t package_size_;
|
||||
std::string path_; // The physical path to the package.
|
||||
|
||||
ZipArchiveHandle zip_handle_;
|
||||
};
|
||||
|
||||
std::unique_ptr<Package> Package::CreateMemoryPackage(
|
||||
const std::string& path, const std::function<void(float)>& set_progress) {
|
||||
std::unique_ptr<MemMapping> mmap = std::make_unique<MemMapping>();
|
||||
if (!mmap->MapFile(path)) {
|
||||
LOG(ERROR) << "failed to map file";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<MemoryPackage>(path, std::move(mmap), set_progress);
|
||||
}
|
||||
|
||||
std::unique_ptr<Package> Package::CreateFilePackage(
|
||||
const std::string& path, const std::function<void(float)>& set_progress) {
|
||||
android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Failed to open " << path;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
off64_t file_size = lseek64(fd.get(), 0, SEEK_END);
|
||||
if (file_size == -1) {
|
||||
PLOG(ERROR) << "Failed to get the package size";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<FilePackage>(std::move(fd), file_size, path, set_progress);
|
||||
}
|
||||
|
||||
std::unique_ptr<Package> Package::CreateMemoryPackage(
|
||||
std::vector<uint8_t> content, const std::function<void(float)>& set_progress) {
|
||||
return std::make_unique<MemoryPackage>(std::move(content), set_progress);
|
||||
}
|
||||
|
||||
MemoryPackage::MemoryPackage(const std::string& path, std::unique_ptr<MemMapping> map,
|
||||
const std::function<void(float)>& set_progress)
|
||||
: map_(std::move(map)), path_(path), zip_handle_(nullptr) {
|
||||
addr_ = map_->addr;
|
||||
package_size_ = map_->length;
|
||||
set_progress_ = set_progress;
|
||||
}
|
||||
|
||||
MemoryPackage::MemoryPackage(std::vector<uint8_t> content,
|
||||
const std::function<void(float)>& set_progress)
|
||||
: package_content_(std::move(content)), zip_handle_(nullptr) {
|
||||
CHECK(!package_content_.empty());
|
||||
addr_ = package_content_.data();
|
||||
package_size_ = package_content_.size();
|
||||
set_progress_ = set_progress;
|
||||
}
|
||||
|
||||
MemoryPackage::~MemoryPackage() {
|
||||
if (zip_handle_) {
|
||||
CloseArchive(zip_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool MemoryPackage::ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) {
|
||||
if (byte_count > package_size_ || offset > package_size_ - byte_count) {
|
||||
LOG(ERROR) << "Out of bound read, offset: " << offset << ", size: " << byte_count
|
||||
<< ", total package_size: " << package_size_;
|
||||
return false;
|
||||
}
|
||||
memcpy(buffer, addr_ + offset, byte_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryPackage::UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers,
|
||||
uint64_t start, uint64_t length) {
|
||||
if (length > package_size_ || start > package_size_ - length) {
|
||||
LOG(ERROR) << "Out of bound read, offset: " << start << ", size: " << length
|
||||
<< ", total package_size: " << package_size_;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& hasher : hashers) {
|
||||
hasher(addr_ + start, length);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ZipArchiveHandle MemoryPackage::GetZipArchiveHandle() {
|
||||
if (zip_handle_) {
|
||||
return zip_handle_;
|
||||
}
|
||||
|
||||
if (auto err = OpenArchiveFromMemory(const_cast<uint8_t*>(addr_), package_size_, path_.c_str(),
|
||||
&zip_handle_);
|
||||
err != 0) {
|
||||
LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return zip_handle_;
|
||||
}
|
||||
|
||||
FilePackage::FilePackage(android::base::unique_fd&& fd, uint64_t file_size, const std::string& path,
|
||||
const std::function<void(float)>& set_progress)
|
||||
: fd_(std::move(fd)), package_size_(file_size), path_(path), zip_handle_(nullptr) {
|
||||
set_progress_ = set_progress;
|
||||
}
|
||||
|
||||
FilePackage::~FilePackage() {
|
||||
if (zip_handle_) {
|
||||
CloseArchive(zip_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool FilePackage::ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) {
|
||||
if (byte_count > package_size_ || offset > package_size_ - byte_count) {
|
||||
LOG(ERROR) << "Out of bound read, offset: " << offset << ", size: " << byte_count
|
||||
<< ", total package_size: " << package_size_;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!android::base::ReadFullyAtOffset(fd_.get(), buffer, byte_count, offset)) {
|
||||
PLOG(ERROR) << "Failed to read " << byte_count << " bytes data at offset " << offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilePackage::UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers,
|
||||
uint64_t start, uint64_t length) {
|
||||
if (length > package_size_ || start > package_size_ - length) {
|
||||
LOG(ERROR) << "Out of bound read, offset: " << start << ", size: " << length
|
||||
<< ", total package_size: " << package_size_;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t so_far = 0;
|
||||
while (so_far < length) {
|
||||
uint64_t read_size = std::min<uint64_t>(length - so_far, 16 * MiB);
|
||||
std::vector<uint8_t> buffer(read_size);
|
||||
if (!ReadFullyAtOffset(buffer.data(), read_size, start + so_far)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& hasher : hashers) {
|
||||
hasher(buffer.data(), read_size);
|
||||
}
|
||||
so_far += read_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ZipArchiveHandle FilePackage::GetZipArchiveHandle() {
|
||||
if (zip_handle_) {
|
||||
return zip_handle_;
|
||||
}
|
||||
|
||||
if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_); err != 0) {
|
||||
LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return zip_handle_;
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* 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 "install/verifier.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#include "otautil/print_sha1.h"
|
||||
#include "private/asn1_decoder.h"
|
||||
|
||||
/*
|
||||
* Simple version of PKCS#7 SignedData extraction. This extracts the
|
||||
* signature OCTET STRING to be used for signature verification.
|
||||
*
|
||||
* For full details, see http://www.ietf.org/rfc/rfc3852.txt
|
||||
*
|
||||
* The PKCS#7 structure looks like:
|
||||
*
|
||||
* SEQUENCE (ContentInfo)
|
||||
* OID (ContentType)
|
||||
* [0] (content)
|
||||
* SEQUENCE (SignedData)
|
||||
* INTEGER (version CMSVersion)
|
||||
* SET (DigestAlgorithmIdentifiers)
|
||||
* SEQUENCE (EncapsulatedContentInfo)
|
||||
* [0] (CertificateSet OPTIONAL)
|
||||
* [1] (RevocationInfoChoices OPTIONAL)
|
||||
* SET (SignerInfos)
|
||||
* SEQUENCE (SignerInfo)
|
||||
* INTEGER (CMSVersion)
|
||||
* SEQUENCE (SignerIdentifier)
|
||||
* SEQUENCE (DigestAlgorithmIdentifier)
|
||||
* SEQUENCE (SignatureAlgorithmIdentifier)
|
||||
* OCTET STRING (SignatureValue)
|
||||
*/
|
||||
static bool read_pkcs7(const uint8_t* pkcs7_der, size_t pkcs7_der_len,
|
||||
std::vector<uint8_t>* sig_der) {
|
||||
CHECK(sig_der != nullptr);
|
||||
sig_der->clear();
|
||||
|
||||
asn1_context ctx(pkcs7_der, pkcs7_der_len);
|
||||
|
||||
std::unique_ptr<asn1_context> pkcs7_seq(ctx.asn1_sequence_get());
|
||||
if (pkcs7_seq == nullptr || !pkcs7_seq->asn1_sequence_next()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<asn1_context> signed_data_app(pkcs7_seq->asn1_constructed_get());
|
||||
if (signed_data_app == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<asn1_context> signed_data_seq(signed_data_app->asn1_sequence_get());
|
||||
if (signed_data_seq == nullptr || !signed_data_seq->asn1_sequence_next() ||
|
||||
!signed_data_seq->asn1_sequence_next() || !signed_data_seq->asn1_sequence_next() ||
|
||||
!signed_data_seq->asn1_constructed_skip_all()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<asn1_context> sig_set(signed_data_seq->asn1_set_get());
|
||||
if (sig_set == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<asn1_context> sig_seq(sig_set->asn1_sequence_get());
|
||||
if (sig_seq == nullptr || !sig_seq->asn1_sequence_next() || !sig_seq->asn1_sequence_next() ||
|
||||
!sig_seq->asn1_sequence_next() || !sig_seq->asn1_sequence_next()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* sig_der_ptr;
|
||||
size_t sig_der_length;
|
||||
if (!sig_seq->asn1_octet_string_get(&sig_der_ptr, &sig_der_length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sig_der->resize(sig_der_length);
|
||||
std::copy(sig_der_ptr, sig_der_ptr + sig_der_length, sig_der->begin());
|
||||
return true;
|
||||
}
|
||||
|
||||
int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys) {
|
||||
CHECK(package);
|
||||
package->SetProgress(0.0);
|
||||
|
||||
// An archive with a whole-file signature will end in six bytes:
|
||||
//
|
||||
// (2-byte signature start) $ff $ff (2-byte comment size)
|
||||
//
|
||||
// (As far as the ZIP format is concerned, these are part of the archive comment.) We start by
|
||||
// reading this footer, this tells us how far back from the end we have to start reading to find
|
||||
// the whole comment.
|
||||
|
||||
#define FOOTER_SIZE 6
|
||||
uint64_t length = package->GetPackageSize();
|
||||
|
||||
if (length < FOOTER_SIZE) {
|
||||
LOG(ERROR) << "not big enough to contain footer";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
uint8_t footer[FOOTER_SIZE];
|
||||
if (!package->ReadFullyAtOffset(footer, FOOTER_SIZE, length - FOOTER_SIZE)) {
|
||||
LOG(ERROR) << "Failed to read footer";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
if (footer[2] != 0xff || footer[3] != 0xff) {
|
||||
LOG(ERROR) << "footer is wrong";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
size_t comment_size = footer[4] + (footer[5] << 8);
|
||||
size_t signature_start = footer[0] + (footer[1] << 8);
|
||||
LOG(INFO) << "comment is " << comment_size << " bytes; signature is " << signature_start
|
||||
<< " bytes from end";
|
||||
|
||||
if (signature_start > comment_size) {
|
||||
LOG(ERROR) << "signature start: " << signature_start
|
||||
<< " is larger than comment size: " << comment_size;
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
if (signature_start <= FOOTER_SIZE) {
|
||||
LOG(ERROR) << "Signature start is in the footer";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
#define EOCD_HEADER_SIZE 22
|
||||
|
||||
// The end-of-central-directory record is 22 bytes plus any comment length.
|
||||
size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
|
||||
|
||||
if (length < eocd_size) {
|
||||
LOG(ERROR) << "not big enough to contain EOCD";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
// Determine how much of the file is covered by the signature. This is everything except the
|
||||
// signature data and length, which includes all of the EOCD except for the comment length field
|
||||
// (2 bytes) and the comment data.
|
||||
uint64_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
|
||||
|
||||
uint8_t eocd[eocd_size];
|
||||
if (!package->ReadFullyAtOffset(eocd, eocd_size, length - eocd_size)) {
|
||||
LOG(ERROR) << "Failed to read EOCD of " << eocd_size << " bytes";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
// If this is really is the EOCD record, it will begin with the magic number $50 $4b $05 $06.
|
||||
if (eocd[0] != 0x50 || eocd[1] != 0x4b || eocd[2] != 0x05 || eocd[3] != 0x06) {
|
||||
LOG(ERROR) << "signature length doesn't match EOCD marker";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
for (size_t i = 4; i < eocd_size - 3; ++i) {
|
||||
if (eocd[i] == 0x50 && eocd[i + 1] == 0x4b && eocd[i + 2] == 0x05 && eocd[i + 3] == 0x06) {
|
||||
// If the sequence $50 $4b $05 $06 appears anywhere after the real one, libziparchive will
|
||||
// find the later (wrong) one, which could be exploitable. Fail the verification if this
|
||||
// sequence occurs anywhere after the real one.
|
||||
LOG(ERROR) << "EOCD marker occurs after start of EOCD";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
bool need_sha1 = false;
|
||||
bool need_sha256 = false;
|
||||
for (const auto& key : keys) {
|
||||
switch (key.hash_len) {
|
||||
case SHA_DIGEST_LENGTH:
|
||||
need_sha1 = true;
|
||||
break;
|
||||
case SHA256_DIGEST_LENGTH:
|
||||
need_sha256 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SHA_CTX sha1_ctx;
|
||||
SHA256_CTX sha256_ctx;
|
||||
SHA1_Init(&sha1_ctx);
|
||||
SHA256_Init(&sha256_ctx);
|
||||
|
||||
std::vector<HasherUpdateCallback> hashers;
|
||||
if (need_sha1) {
|
||||
hashers.emplace_back(
|
||||
std::bind(&SHA1_Update, &sha1_ctx, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
if (need_sha256) {
|
||||
hashers.emplace_back(
|
||||
std::bind(&SHA256_Update, &sha256_ctx, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
double frac = -1.0;
|
||||
uint64_t so_far = 0;
|
||||
while (so_far < signed_len) {
|
||||
// On a Nexus 5X, experiment showed 16MiB beat 1MiB by 6% faster for a 1196MiB full OTA and
|
||||
// 60% for an 89MiB incremental OTA. http://b/28135231.
|
||||
uint64_t read_size = std::min<uint64_t>(signed_len - so_far, 16 * MiB);
|
||||
package->UpdateHashAtOffset(hashers, so_far, read_size);
|
||||
so_far += read_size;
|
||||
|
||||
double f = so_far / static_cast<double>(signed_len);
|
||||
if (f > frac + 0.02 || read_size == so_far) {
|
||||
package->SetProgress(f);
|
||||
frac = f;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t sha1[SHA_DIGEST_LENGTH];
|
||||
SHA1_Final(sha1, &sha1_ctx);
|
||||
uint8_t sha256[SHA256_DIGEST_LENGTH];
|
||||
SHA256_Final(sha256, &sha256_ctx);
|
||||
|
||||
const uint8_t* signature = eocd + eocd_size - signature_start;
|
||||
size_t signature_size = signature_start - FOOTER_SIZE;
|
||||
|
||||
LOG(INFO) << "signature (offset: " << std::hex << (length - signature_start)
|
||||
<< ", length: " << signature_size << "): " << print_hex(signature, signature_size);
|
||||
|
||||
std::vector<uint8_t> sig_der;
|
||||
if (!read_pkcs7(signature, signature_size, &sig_der)) {
|
||||
LOG(ERROR) << "Could not find signature DER block";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
// Check to make sure at least one of the keys matches the signature. Since any key can match,
|
||||
// we need to try each before determining a verification failure has happened.
|
||||
size_t i = 0;
|
||||
for (const auto& key : keys) {
|
||||
const uint8_t* hash;
|
||||
int hash_nid;
|
||||
switch (key.hash_len) {
|
||||
case SHA_DIGEST_LENGTH:
|
||||
hash = sha1;
|
||||
hash_nid = NID_sha1;
|
||||
break;
|
||||
case SHA256_DIGEST_LENGTH:
|
||||
hash = sha256;
|
||||
hash_nid = NID_sha256;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
// The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that the signing tool appends
|
||||
// after the signature itself.
|
||||
if (key.key_type == Certificate::KEY_TYPE_RSA) {
|
||||
if (!RSA_verify(hash_nid, hash, key.hash_len, sig_der.data(), sig_der.size(),
|
||||
key.rsa.get())) {
|
||||
LOG(INFO) << "failed to verify against RSA key " << i;
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(INFO) << "whole-file signature verified against RSA key " << i;
|
||||
return VERIFY_SUCCESS;
|
||||
} else if (key.key_type == Certificate::KEY_TYPE_EC && key.hash_len == SHA256_DIGEST_LENGTH) {
|
||||
if (!ECDSA_verify(0, hash, key.hash_len, sig_der.data(), sig_der.size(), key.ec.get())) {
|
||||
LOG(INFO) << "failed to verify against EC key " << i;
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(INFO) << "whole-file signature verified against EC key " << i;
|
||||
return VERIFY_SUCCESS;
|
||||
} else {
|
||||
LOG(INFO) << "Unknown key type " << key.key_type;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (need_sha1) {
|
||||
LOG(INFO) << "SHA-1 digest: " << print_hex(sha1, SHA_DIGEST_LENGTH);
|
||||
}
|
||||
if (need_sha256) {
|
||||
LOG(INFO) << "SHA-256 digest: " << print_hex(sha256, SHA256_DIGEST_LENGTH);
|
||||
}
|
||||
LOG(ERROR) << "failed to verify whole-file signature";
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
||||
static std::vector<Certificate> IterateZipEntriesAndSearchForKeys(const ZipArchiveHandle& handle) {
|
||||
void* cookie;
|
||||
ZipString suffix("x509.pem");
|
||||
int32_t iter_status = StartIteration(handle, &cookie, nullptr, &suffix);
|
||||
if (iter_status != 0) {
|
||||
LOG(ERROR) << "Failed to iterate over entries in the certificate zipfile: "
|
||||
<< ErrorCodeString(iter_status);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Certificate> result;
|
||||
|
||||
ZipString name;
|
||||
ZipEntry entry;
|
||||
while ((iter_status = Next(cookie, &entry, &name)) == 0) {
|
||||
std::vector<uint8_t> pem_content(entry.uncompressed_length);
|
||||
if (int32_t extract_status =
|
||||
ExtractToMemory(handle, &entry, pem_content.data(), pem_content.size());
|
||||
extract_status != 0) {
|
||||
LOG(ERROR) << "Failed to extract " << std::string(name.name, name.name + name.name_length);
|
||||
return {};
|
||||
}
|
||||
|
||||
Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
|
||||
// Aborts the parsing if we fail to load one of the key file.
|
||||
if (!LoadCertificateFromBuffer(pem_content, &cert)) {
|
||||
LOG(ERROR) << "Failed to load keys from "
|
||||
<< std::string(name.name, name.name + name.name_length);
|
||||
return {};
|
||||
}
|
||||
|
||||
result.emplace_back(std::move(cert));
|
||||
}
|
||||
|
||||
if (iter_status != -1) {
|
||||
LOG(ERROR) << "Error while iterating over zip entries: " << ErrorCodeString(iter_status);
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Certificate> LoadKeysFromZipfile(const std::string& zip_name) {
|
||||
ZipArchiveHandle handle;
|
||||
if (int32_t open_status = OpenArchive(zip_name.c_str(), &handle); open_status != 0) {
|
||||
LOG(ERROR) << "Failed to open " << zip_name << ": " << ErrorCodeString(open_status);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Certificate> result = IterateZipEntriesAndSearchForKeys(handle);
|
||||
CloseArchive(handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa) {
|
||||
if (!rsa) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const BIGNUM* out_n;
|
||||
const BIGNUM* out_e;
|
||||
RSA_get0_key(rsa.get(), &out_n, &out_e, nullptr /* private exponent */);
|
||||
auto modulus_bits = BN_num_bits(out_n);
|
||||
if (modulus_bits != 2048 && modulus_bits != 4096) {
|
||||
LOG(ERROR) << "Modulus should be 2048 or 4096 bits long, actual: " << modulus_bits;
|
||||
return false;
|
||||
}
|
||||
|
||||
BN_ULONG exponent = BN_get_word(out_e);
|
||||
if (exponent != 3 && exponent != 65537) {
|
||||
LOG(ERROR) << "Public exponent should be 3 or 65537, actual: " << exponent;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key) {
|
||||
if (!ec_key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key.get());
|
||||
if (!ec_group) {
|
||||
LOG(ERROR) << "Failed to get the ec_group from the ec_key";
|
||||
return false;
|
||||
}
|
||||
auto degree = EC_GROUP_get_degree(ec_group);
|
||||
if (degree != 256) {
|
||||
LOG(ERROR) << "Field size of the ec key should be 256 bits long, actual: " << degree;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert) {
|
||||
std::unique_ptr<BIO, decltype(&BIO_free)> content(
|
||||
BIO_new_mem_buf(pem_content.data(), pem_content.size()), BIO_free);
|
||||
|
||||
std::unique_ptr<X509, decltype(&X509_free)> x509(
|
||||
PEM_read_bio_X509(content.get(), nullptr, nullptr, nullptr), X509_free);
|
||||
if (!x509) {
|
||||
LOG(ERROR) << "Failed to read x509 certificate";
|
||||
return false;
|
||||
}
|
||||
|
||||
int nid = X509_get_signature_nid(x509.get());
|
||||
switch (nid) {
|
||||
// SignApk has historically accepted md5WithRSA certificates, but treated them as
|
||||
// sha1WithRSA anyway. Continue to do so for backwards compatibility.
|
||||
case NID_md5WithRSA:
|
||||
case NID_md5WithRSAEncryption:
|
||||
case NID_sha1WithRSA:
|
||||
case NID_sha1WithRSAEncryption:
|
||||
cert->hash_len = SHA_DIGEST_LENGTH;
|
||||
break;
|
||||
case NID_sha256WithRSAEncryption:
|
||||
case NID_ecdsa_with_SHA256:
|
||||
cert->hash_len = SHA256_DIGEST_LENGTH;
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "Unrecognized signature nid " << OBJ_nid2ln(nid);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> public_key(X509_get_pubkey(x509.get()),
|
||||
EVP_PKEY_free);
|
||||
if (!public_key) {
|
||||
LOG(ERROR) << "Failed to extract the public key from x509 certificate";
|
||||
return false;
|
||||
}
|
||||
|
||||
int key_type = EVP_PKEY_id(public_key.get());
|
||||
if (key_type == EVP_PKEY_RSA) {
|
||||
cert->key_type = Certificate::KEY_TYPE_RSA;
|
||||
cert->ec.reset();
|
||||
cert->rsa.reset(EVP_PKEY_get1_RSA(public_key.get()));
|
||||
if (!cert->rsa || !CheckRSAKey(cert->rsa)) {
|
||||
LOG(ERROR) << "Failed to validate the rsa key info from public key";
|
||||
return false;
|
||||
}
|
||||
} else if (key_type == EVP_PKEY_EC) {
|
||||
cert->key_type = Certificate::KEY_TYPE_EC;
|
||||
cert->rsa.reset();
|
||||
cert->ec.reset(EVP_PKEY_get1_EC_KEY(public_key.get()));
|
||||
if (!cert->ec || !CheckECKey(cert->ec)) {
|
||||
LOG(ERROR) << "Failed to validate the ec key info from the public key";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << "Unrecognized public key type " << OBJ_nid2ln(key_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 "install/wipe_data.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "otautil/dirutil.h"
|
||||
#include "otautil/logging.h"
|
||||
#include "otautil/roots.h"
|
||||
#include "recovery_ui/ui.h"
|
||||
|
||||
constexpr const char* CACHE_ROOT = "/cache";
|
||||
constexpr const char* DATA_ROOT = "/data";
|
||||
constexpr const char* METADATA_ROOT = "/metadata";
|
||||
|
||||
static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) {
|
||||
bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
|
||||
bool is_data = (strcmp(volume, DATA_ROOT) == 0);
|
||||
|
||||
ui->SetBackground(RecoveryUI::ERASING);
|
||||
ui->SetProgressType(RecoveryUI::INDETERMINATE);
|
||||
|
||||
std::vector<saved_log_file> log_files;
|
||||
if (is_cache) {
|
||||
// If we're reformatting /cache, we load any past logs (i.e. "/cache/recovery/last_*") and the
|
||||
// current log ("/cache/recovery/log") into memory, so we can restore them after the reformat.
|
||||
log_files = ReadLogFilesToMemory();
|
||||
}
|
||||
|
||||
ui->Print("Formatting %s...\n", volume);
|
||||
|
||||
ensure_path_unmounted(volume);
|
||||
|
||||
int result;
|
||||
if (is_data && convert_fbe) {
|
||||
constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe";
|
||||
constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe";
|
||||
// Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not
|
||||
// full disk encryption.
|
||||
if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
|
||||
PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR;
|
||||
return false;
|
||||
}
|
||||
FILE* f = fopen(CONVERT_FBE_FILE, "wbe");
|
||||
if (!f) {
|
||||
PLOG(ERROR) << "Failed to convert to file encryption";
|
||||
return false;
|
||||
}
|
||||
fclose(f);
|
||||
result = format_volume(volume, CONVERT_FBE_DIR);
|
||||
remove(CONVERT_FBE_FILE);
|
||||
rmdir(CONVERT_FBE_DIR);
|
||||
} else {
|
||||
result = format_volume(volume);
|
||||
}
|
||||
|
||||
if (is_cache) {
|
||||
RestoreLogFilesAfterFormat(log_files);
|
||||
}
|
||||
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm_func) {
|
||||
bool has_cache = volume_for_mount_point("/cache") != nullptr;
|
||||
if (!has_cache) {
|
||||
ui->Print("No /cache partition found.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (confirm_func && !confirm_func()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ui->Print("\n-- Wiping cache...\n");
|
||||
bool success = EraseVolume("/cache", ui, false);
|
||||
ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
|
||||
return success;
|
||||
}
|
||||
|
||||
bool WipeData(Device* device, bool convert_fbe) {
|
||||
RecoveryUI* ui = device->GetUI();
|
||||
ui->Print("\n-- Wiping data...\n");
|
||||
bool success = device->PreWipeData();
|
||||
if (success) {
|
||||
success &= EraseVolume(DATA_ROOT, ui, convert_fbe);
|
||||
bool has_cache = volume_for_mount_point("/cache") != nullptr;
|
||||
if (has_cache) {
|
||||
success &= EraseVolume(CACHE_ROOT, ui, false);
|
||||
}
|
||||
if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
|
||||
success &= EraseVolume(METADATA_ROOT, ui, false);
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
success &= device->PostWipeData();
|
||||
}
|
||||
ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
|
||||
return success;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright (C) 2018 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.
|
||||
|
||||
cc_defaults {
|
||||
name: "minadbd_defaults",
|
||||
|
||||
cflags: [
|
||||
"-DADB_HOST=0",
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
|
||||
cpp_std: "experimental",
|
||||
|
||||
include_dirs: [
|
||||
"system/core/adb",
|
||||
],
|
||||
}
|
||||
|
||||
// `libminadbd_services` is analogous to the `libadbd_services` for regular `adbd`, but providing
|
||||
// the sideload service only.
|
||||
cc_library {
|
||||
name: "libminadbd_services",
|
||||
recovery_available: true,
|
||||
|
||||
defaults: [
|
||||
"minadbd_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"fuse_adb_provider.cpp",
|
||||
"minadbd_services.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libadbd",
|
||||
"libbase",
|
||||
"libcrypto",
|
||||
"libfusesideload",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_headers {
|
||||
name: "libminadbd_headers",
|
||||
recovery_available: true,
|
||||
// TODO create a include dir
|
||||
export_include_dirs: [
|
||||
".",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "minadbd",
|
||||
recovery: true,
|
||||
|
||||
defaults: [
|
||||
"minadbd_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"minadbd.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libadbd",
|
||||
"libbase",
|
||||
"libcrypto",
|
||||
"libminadbd_services",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "minadbd_test",
|
||||
isolated: true,
|
||||
|
||||
defaults: [
|
||||
"minadbd_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"fuse_adb_provider_test.cpp",
|
||||
"minadbd_services_test.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libminadbd_services",
|
||||
"libfusesideload",
|
||||
"libadbd",
|
||||
"libcrypto",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
test_suites: [
|
||||
"device-tests",
|
||||
],
|
||||
}
|
||||
@@ -18,39 +18,22 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "fuse_sideload.h"
|
||||
|
||||
int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
|
||||
if (!WriteFdFmt(ad.sfd, "%08u", block)) {
|
||||
bool FuseAdbDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
|
||||
uint32_t start_block) const {
|
||||
if (!WriteFdFmt(fd_, "%08u", start_block)) {
|
||||
fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
|
||||
return -EIO;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadFdExactly(ad.sfd, buffer, fetch_size)) {
|
||||
if (!ReadFdExactly(fd_, buffer, fetch_size)) {
|
||||
fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
|
||||
return -EIO;
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) {
|
||||
adb_data ad;
|
||||
ad.sfd = sfd;
|
||||
ad.file_size = file_size;
|
||||
ad.block_size = block_size;
|
||||
|
||||
provider_vtab vtab;
|
||||
vtab.read_block = std::bind(read_block_adb, ad, std::placeholders::_1, std::placeholders::_2,
|
||||
std::placeholders::_3);
|
||||
vtab.close = [&ad]() { WriteFdExactly(ad.sfd, "DONEDONE"); };
|
||||
|
||||
return run_fuse_sideload(vtab, file_size, block_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
+14
-11
@@ -14,19 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __FUSE_ADB_PROVIDER_H
|
||||
#define __FUSE_ADB_PROVIDER_H
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct adb_data {
|
||||
int sfd; // file descriptor for the adb channel
|
||||
#include "fuse_provider.h"
|
||||
|
||||
uint64_t file_size;
|
||||
uint32_t block_size;
|
||||
// This class reads data from adb server.
|
||||
class FuseAdbDataProvider : public FuseDataProvider {
|
||||
public:
|
||||
FuseAdbDataProvider(int fd, uint64_t file_size, uint32_t block_size)
|
||||
: FuseDataProvider(file_size, block_size), fd_(fd) {}
|
||||
|
||||
bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
|
||||
uint32_t start_block) const override;
|
||||
|
||||
private:
|
||||
// The underlying source to read data from (i.e. the one that talks to the host).
|
||||
int fd_;
|
||||
};
|
||||
|
||||
int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
|
||||
int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,19 +21,19 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "adb_io.h"
|
||||
#include "fuse_adb_provider.h"
|
||||
|
||||
TEST(fuse_adb_provider, read_block_adb) {
|
||||
adb_data data = {};
|
||||
int sockets[2];
|
||||
android::base::unique_fd device_socket;
|
||||
android::base::unique_fd host_socket;
|
||||
|
||||
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
|
||||
data.sfd = sockets[0];
|
||||
ASSERT_TRUE(android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &device_socket, &host_socket));
|
||||
FuseAdbDataProvider data(std::move(device_socket), 0, 0);
|
||||
|
||||
int host_socket = sockets[1];
|
||||
fcntl(host_socket, F_SETFL, O_NONBLOCK);
|
||||
|
||||
const char expected_data[] = "foobar";
|
||||
@@ -46,8 +46,8 @@ TEST(fuse_adb_provider, read_block_adb) {
|
||||
|
||||
uint32_t block = 1234U;
|
||||
const char expected_block[] = "00001234";
|
||||
ASSERT_EQ(0, read_block_adb(data, block, reinterpret_cast<uint8_t*>(block_data),
|
||||
sizeof(expected_data) - 1));
|
||||
ASSERT_TRUE(data.ReadBlockAlignedData(reinterpret_cast<uint8_t*>(block_data),
|
||||
sizeof(expected_data) - 1, block));
|
||||
|
||||
// Check that read_block_adb requested the right block.
|
||||
char block_req[sizeof(expected_block)] = {};
|
||||
@@ -65,26 +65,21 @@ TEST(fuse_adb_provider, read_block_adb) {
|
||||
errno = 0;
|
||||
ASSERT_EQ(-1, read(host_socket, &tmp, 1));
|
||||
ASSERT_EQ(EWOULDBLOCK, errno);
|
||||
|
||||
close(sockets[0]);
|
||||
close(sockets[1]);
|
||||
}
|
||||
|
||||
TEST(fuse_adb_provider, read_block_adb_fail_write) {
|
||||
adb_data data = {};
|
||||
int sockets[2];
|
||||
android::base::unique_fd device_socket;
|
||||
android::base::unique_fd host_socket;
|
||||
|
||||
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
|
||||
data.sfd = sockets[0];
|
||||
ASSERT_TRUE(android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &device_socket, &host_socket));
|
||||
FuseAdbDataProvider data(std::move(device_socket), 0, 0);
|
||||
|
||||
ASSERT_EQ(0, close(sockets[1]));
|
||||
host_socket.reset();
|
||||
|
||||
// write(2) raises SIGPIPE since the reading end has been closed. Ignore the signal to avoid
|
||||
// failing the test.
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
char buf[1];
|
||||
ASSERT_EQ(-EIO, read_block_adb(data, 0, reinterpret_cast<uint8_t*>(buf), 1));
|
||||
|
||||
close(sockets[0]);
|
||||
ASSERT_FALSE(data.ReadBlockAlignedData(reinterpret_cast<uint8_t*>(buf), 1, 0));
|
||||
}
|
||||
|
||||
+46
-9
@@ -14,30 +14,67 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "minadbd.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_auth.h"
|
||||
#include "transport.h"
|
||||
|
||||
int minadbd_main() {
|
||||
#include "minadbd_services.h"
|
||||
#include "minadbd_types.h"
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
android::base::InitLogging(argv, &android::base::StderrLogger);
|
||||
// TODO(xunchang) implement a command parser
|
||||
if ((argc != 3 && argc != 4) || argv[1] != "--socket_fd"s ||
|
||||
(argc == 4 && argv[3] != "--rescue"s)) {
|
||||
LOG(ERROR) << "minadbd has invalid arguments, argc: " << argc;
|
||||
exit(kMinadbdArgumentsParsingError);
|
||||
}
|
||||
|
||||
int socket_fd;
|
||||
if (!android::base::ParseInt(argv[2], &socket_fd)) {
|
||||
LOG(ERROR) << "Failed to parse int in " << argv[2];
|
||||
exit(kMinadbdArgumentsParsingError);
|
||||
}
|
||||
if (fcntl(socket_fd, F_GETFD, 0) == -1) {
|
||||
PLOG(ERROR) << "Failed to get minadbd socket";
|
||||
exit(kMinadbdSocketIOError);
|
||||
}
|
||||
SetMinadbdSocketFd(socket_fd);
|
||||
|
||||
if (argc == 4) {
|
||||
SetMinadbdRescueMode(true);
|
||||
adb_device_banner = "rescue";
|
||||
} else {
|
||||
adb_device_banner = "sideload";
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
// We can't require authentication for sideloading. http://b/22025550.
|
||||
auth_required = false;
|
||||
// We can't require authentication for sideloading. http://b/22025550.
|
||||
auth_required = false;
|
||||
|
||||
init_transport_registration();
|
||||
usb_init();
|
||||
init_transport_registration();
|
||||
usb_init();
|
||||
|
||||
<<<<<<< HEAD
|
||||
//VLOG(ADB) << "Event loop starting";
|
||||
fdevent_loop();
|
||||
=======
|
||||
VLOG(ADB) << "Event loop starting";
|
||||
fdevent_loop();
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
+273
-10
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "minadbd_services.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
@@ -21,14 +23,32 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/memory.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_unique_fd.h"
|
||||
#include "adb_utils.h"
|
||||
#include "fdevent.h"
|
||||
#include "fuse_adb_provider.h"
|
||||
#include "fuse_sideload.h"
|
||||
#include "minadbd_types.h"
|
||||
#include "services.h"
|
||||
#include "sysdeps.h"
|
||||
|
||||
<<<<<<< HEAD
|
||||
typedef struct stinfo stinfo;
|
||||
|
||||
struct stinfo {
|
||||
@@ -63,15 +83,117 @@ static void sideload_host_service(int sfd, const std::string& args) {
|
||||
#if PLATFORM_SDK_VERSION < 26
|
||||
free(args);
|
||||
#endif
|
||||
=======
|
||||
static int minadbd_socket = -1;
|
||||
static bool rescue_mode = false;
|
||||
static std::string sideload_mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT;
|
||||
|
||||
printf("sideload-host file size %d block size %d\n", file_size, block_size);
|
||||
|
||||
int result = run_adb_fuse(sfd, file_size, block_size);
|
||||
|
||||
printf("sideload_host finished\n");
|
||||
exit(result == 0 ? 0 : 1);
|
||||
void SetMinadbdSocketFd(int socket_fd) {
|
||||
minadbd_socket = socket_fd;
|
||||
}
|
||||
|
||||
void SetMinadbdRescueMode(bool rescue) {
|
||||
rescue_mode = rescue;
|
||||
}
|
||||
|
||||
void SetSideloadMountPoint(const std::string& path) {
|
||||
sideload_mount_point = path;
|
||||
}
|
||||
|
||||
static bool WriteCommandToFd(MinadbdCommand cmd, int fd) {
|
||||
char message[kMinadbdMessageSize];
|
||||
memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix));
|
||||
android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd);
|
||||
|
||||
if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
|
||||
PLOG(ERROR) << "Failed to write message " << message;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Blocks and reads the command status from |fd|. Returns false if the received message has a
|
||||
// format error.
|
||||
static bool WaitForCommandStatus(int fd, MinadbdCommandStatus* status) {
|
||||
char buffer[kMinadbdMessageSize];
|
||||
if (!android::base::ReadFully(fd, buffer, kMinadbdMessageSize)) {
|
||||
PLOG(ERROR) << "Failed to response status from socket";
|
||||
exit(kMinadbdSocketIOError);
|
||||
}
|
||||
|
||||
std::string message(buffer, buffer + kMinadbdMessageSize);
|
||||
if (!android::base::StartsWith(message, kMinadbdStatusPrefix)) {
|
||||
LOG(ERROR) << "Failed to parse status in " << message;
|
||||
return false;
|
||||
}
|
||||
|
||||
*status = android::base::get_unaligned<MinadbdCommandStatus>(
|
||||
message.substr(strlen(kMinadbdStatusPrefix)).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args,
|
||||
MinadbdCommandStatus* status) {
|
||||
auto pieces = android::base::Split(args, ":");
|
||||
int64_t file_size;
|
||||
int block_size;
|
||||
if (pieces.size() != 2 || !android::base::ParseInt(pieces[0], &file_size) || file_size <= 0 ||
|
||||
!android::base::ParseInt(pieces[1], &block_size) || block_size <= 0) {
|
||||
LOG(ERROR) << "bad sideload-host arguments: " << args;
|
||||
return kMinadbdHostCommandArgumentError;
|
||||
}
|
||||
|
||||
LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size;
|
||||
|
||||
if (!WriteCommandToFd(MinadbdCommand::kInstall, minadbd_socket)) {
|
||||
return kMinadbdSocketIOError;
|
||||
}
|
||||
|
||||
auto adb_data_reader = std::make_unique<FuseAdbDataProvider>(sfd, file_size, block_size);
|
||||
if (int result = run_fuse_sideload(std::move(adb_data_reader), sideload_mount_point.c_str());
|
||||
result != 0) {
|
||||
LOG(ERROR) << "Failed to start fuse";
|
||||
return kMinadbdFuseStartError;
|
||||
}
|
||||
|
||||
if (!WaitForCommandStatus(minadbd_socket, status)) {
|
||||
return kMinadbdMessageFormatError;
|
||||
}
|
||||
|
||||
// Signal host-side adb to stop. For sideload mode, we always send kMinadbdServicesExitSuccess
|
||||
// (i.e. "DONEDONE") regardless of the install result. For rescue mode, we send failure message on
|
||||
// install error.
|
||||
if (!rescue_mode || *status == MinadbdCommandStatus::kSuccess) {
|
||||
if (!android::base::WriteFully(sfd, kMinadbdServicesExitSuccess,
|
||||
strlen(kMinadbdServicesExitSuccess))) {
|
||||
return kMinadbdHostSocketIOError;
|
||||
}
|
||||
} else {
|
||||
if (!android::base::WriteFully(sfd, kMinadbdServicesExitFailure,
|
||||
strlen(kMinadbdServicesExitFailure))) {
|
||||
return kMinadbdHostSocketIOError;
|
||||
}
|
||||
}
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
return kMinadbdSuccess;
|
||||
}
|
||||
|
||||
// Sideload service always exits after serving an install command.
|
||||
static void SideloadHostService(unique_fd sfd, const std::string& args) {
|
||||
MinadbdCommandStatus status;
|
||||
exit(RunAdbFuseSideload(sfd.get(), args, &status));
|
||||
}
|
||||
|
||||
// Rescue service waits for the next command after an install command.
|
||||
static void RescueInstallHostService(unique_fd sfd, const std::string& args) {
|
||||
MinadbdCommandStatus status;
|
||||
if (auto result = RunAdbFuseSideload(sfd.get(), args, &status); result != kMinadbdSuccess) {
|
||||
exit(result);
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
#if PLATFORM_SDK_VERSION < 26
|
||||
static int create_service_thread(void (*func)(int, void *), void *cookie) {
|
||||
int s[2];
|
||||
@@ -108,15 +230,112 @@ static int create_service_thread(void (*func)(int, const std::string&), const st
|
||||
if (adb_socketpair(s)) {
|
||||
printf("cannot create service socket pair\n");
|
||||
return -1;
|
||||
=======
|
||||
// Answers the query on a given property |prop|, by writing the result to the given |sfd|. The
|
||||
// result will be newline-terminated, so nonexistent or nonallowed query will be answered with "\n".
|
||||
// If given an empty string, dumps all the supported properties (analogous to `adb shell getprop`)
|
||||
// in lines, e.g. "[prop]: [value]".
|
||||
static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) {
|
||||
static const std::set<std::string> kGetpropAllowedProps = {
|
||||
"ro.build.date.utc",
|
||||
"ro.build.fingerprint",
|
||||
"ro.build.flavor",
|
||||
"ro.build.id",
|
||||
"ro.build.product",
|
||||
"ro.build.tags",
|
||||
"ro.build.version.incremental",
|
||||
"ro.product.device",
|
||||
"ro.product.vendor.device",
|
||||
};
|
||||
std::string result;
|
||||
if (prop.empty()) {
|
||||
for (const auto& key : kGetpropAllowedProps) {
|
||||
auto value = android::base::GetProperty(key, "");
|
||||
if (value.empty()) {
|
||||
continue;
|
||||
}
|
||||
result += "[" + key + "]: [" + value + "]\n";
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
} else if (kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end()) {
|
||||
result = android::base::GetProperty(prop, "") + "\n";
|
||||
}
|
||||
if (result.empty()) {
|
||||
result = "\n";
|
||||
}
|
||||
if (!android::base::WriteFully(sfd, result.data(), result.size())) {
|
||||
exit(kMinadbdHostSocketIOError);
|
||||
}
|
||||
|
||||
std::thread([s, func, args]() { func(s[1], args); }).detach();
|
||||
// Send heartbeat signal to keep the rescue service alive.
|
||||
if (!WriteCommandToFd(MinadbdCommand::kNoOp, minadbd_socket)) {
|
||||
exit(kMinadbdSocketIOError);
|
||||
}
|
||||
if (MinadbdCommandStatus status; !WaitForCommandStatus(minadbd_socket, &status)) {
|
||||
exit(kMinadbdMessageFormatError);
|
||||
}
|
||||
}
|
||||
|
||||
// Reboots into the given target. We don't reboot directly from minadbd, but going through recovery
|
||||
// instead. This allows recovery to finish all the pending works (clear BCB, save logs etc) before
|
||||
// the reboot.
|
||||
static void RebootHostService(unique_fd /* sfd */, const std::string& target) {
|
||||
MinadbdCommand command;
|
||||
if (target == "bootloader") {
|
||||
command = MinadbdCommand::kRebootBootloader;
|
||||
} else if (target == "rescue") {
|
||||
command = MinadbdCommand::kRebootRescue;
|
||||
} else if (target == "recovery") {
|
||||
command = MinadbdCommand::kRebootRecovery;
|
||||
} else if (target == "fastboot") {
|
||||
command = MinadbdCommand::kRebootFastboot;
|
||||
} else {
|
||||
command = MinadbdCommand::kRebootAndroid;
|
||||
}
|
||||
if (!WriteCommandToFd(command, minadbd_socket)) {
|
||||
exit(kMinadbdSocketIOError);
|
||||
}
|
||||
MinadbdCommandStatus status;
|
||||
if (!WaitForCommandStatus(minadbd_socket, &status)) {
|
||||
exit(kMinadbdMessageFormatError);
|
||||
}
|
||||
}
|
||||
|
||||
static void WipeDeviceService(unique_fd fd, const std::string& args) {
|
||||
auto pieces = android::base::Split(args, ":");
|
||||
if (pieces.size() != 2 || pieces[0] != "userdata") {
|
||||
LOG(ERROR) << "Failed to parse wipe device command arguments " << args;
|
||||
exit(kMinadbdHostCommandArgumentError);
|
||||
}
|
||||
|
||||
size_t message_size;
|
||||
if (!android::base::ParseUint(pieces[1], &message_size) ||
|
||||
message_size < strlen(kMinadbdServicesExitSuccess)) {
|
||||
LOG(ERROR) << "Failed to parse wipe device message size in " << args;
|
||||
exit(kMinadbdHostCommandArgumentError);
|
||||
}
|
||||
|
||||
WriteCommandToFd(MinadbdCommand::kWipeData, minadbd_socket);
|
||||
MinadbdCommandStatus status;
|
||||
if (!WaitForCommandStatus(minadbd_socket, &status)) {
|
||||
exit(kMinadbdMessageFormatError);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
//VLOG(SERVICES) << "service thread started, " << s[0] << ":" << s[1];
|
||||
return s[0];
|
||||
=======
|
||||
std::string response = (status == MinadbdCommandStatus::kSuccess) ? kMinadbdServicesExitSuccess
|
||||
: kMinadbdServicesExitFailure;
|
||||
response += std::string(message_size - response.size(), '\0');
|
||||
if (!android::base::WriteFully(fd, response.c_str(), response.size())) {
|
||||
exit(kMinadbdHostSocketIOError);
|
||||
}
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
#endif
|
||||
|
||||
<<<<<<< HEAD
|
||||
#if PLATFORM_SDK_VERSION >= 28
|
||||
int service_to_fd(const char* name, atransport* /* transport */) {
|
||||
#else
|
||||
@@ -136,11 +355,55 @@ int service_to_fd(const char* name, const atransport* transport __unused) {
|
||||
std::string arg(name + 14);
|
||||
#endif
|
||||
ret = create_service_thread(sideload_host_service, arg);
|
||||
=======
|
||||
unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) {
|
||||
// Common services that are supported both in sideload and rescue modes.
|
||||
if (ConsumePrefix(&name, "reboot:")) {
|
||||
// "reboot:<target>", where target must be one of the following.
|
||||
std::string args(name);
|
||||
if (args.empty() || args == "bootloader" || args == "rescue" || args == "recovery" ||
|
||||
args == "fastboot") {
|
||||
return create_service_thread("reboot",
|
||||
std::bind(RebootHostService, std::placeholders::_1, args));
|
||||
}
|
||||
return unique_fd{};
|
||||
}
|
||||
if (ret >= 0) {
|
||||
close_on_exec(ret);
|
||||
|
||||
// Rescue-specific services.
|
||||
if (rescue_mode) {
|
||||
if (ConsumePrefix(&name, "rescue-install:")) {
|
||||
// rescue-install:<file-size>:<block-size>
|
||||
std::string args(name);
|
||||
return create_service_thread(
|
||||
"rescue-install", std::bind(RescueInstallHostService, std::placeholders::_1, args));
|
||||
} else if (ConsumePrefix(&name, "rescue-getprop:")) {
|
||||
// rescue-getprop:<prop>
|
||||
std::string args(name);
|
||||
return create_service_thread(
|
||||
"rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args));
|
||||
} else if (ConsumePrefix(&name, "rescue-wipe:")) {
|
||||
// rescue-wipe:target:<message-size>
|
||||
std::string args(name);
|
||||
return create_service_thread("rescue-wipe",
|
||||
std::bind(WipeDeviceService, std::placeholders::_1, args));
|
||||
}
|
||||
|
||||
return unique_fd{};
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
return ret;
|
||||
|
||||
// Sideload-specific services.
|
||||
if (name.starts_with("sideload:")) {
|
||||
// This exit status causes recovery to print a special error message saying to use a newer adb
|
||||
// (that supports sideload-host).
|
||||
exit(kMinadbdAdbVersionError);
|
||||
} else if (ConsumePrefix(&name, "sideload-host:")) {
|
||||
// sideload-host:<file-size>:<block-size>
|
||||
std::string args(name);
|
||||
return create_service_thread("sideload-host",
|
||||
std::bind(SideloadHostService, std::placeholders::_1, args));
|
||||
}
|
||||
return unique_fd{};
|
||||
}
|
||||
|
||||
#if PLATFORM_SDK_VERSION == 23
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
void SetMinadbdSocketFd(int socket_fd);
|
||||
|
||||
void SetMinadbdRescueMode(bool);
|
||||
|
||||
void SetSideloadMountPoint(const std::string& path);
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 <signal.h>
|
||||
#include <strings.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "adb_io.h"
|
||||
#include "fuse_adb_provider.h"
|
||||
#include "fuse_sideload.h"
|
||||
#include "minadbd_services.h"
|
||||
#include "minadbd_types.h"
|
||||
#include "socket.h"
|
||||
|
||||
class MinadbdServicesTest : public ::testing::Test {
|
||||
protected:
|
||||
static constexpr int EXIT_TIME_OUT = 10;
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_TRUE(
|
||||
android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &minadbd_socket_, &recovery_socket_));
|
||||
SetMinadbdSocketFd(minadbd_socket_);
|
||||
SetSideloadMountPoint(mount_point_.path);
|
||||
|
||||
package_path_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME;
|
||||
exit_flag_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// Umount in case the test fails. Ignore the result.
|
||||
umount(mount_point_.path);
|
||||
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
}
|
||||
|
||||
void ReadAndCheckCommandMessage(int fd, MinadbdCommand expected_command) {
|
||||
std::vector<uint8_t> received(kMinadbdMessageSize, '\0');
|
||||
ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize));
|
||||
|
||||
std::vector<uint8_t> expected(kMinadbdMessageSize, '\0');
|
||||
memcpy(expected.data(), kMinadbdCommandPrefix, strlen(kMinadbdCommandPrefix));
|
||||
memcpy(expected.data() + strlen(kMinadbdCommandPrefix), &expected_command,
|
||||
sizeof(expected_command));
|
||||
ASSERT_EQ(expected, received);
|
||||
}
|
||||
|
||||
void WaitForFusePath() {
|
||||
constexpr int TIME_OUT = 10;
|
||||
for (int i = 0; i < TIME_OUT; ++i) {
|
||||
struct stat sb;
|
||||
if (stat(package_path_.c_str(), &sb) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (errno == ENOENT) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
FAIL() << "Timed out waiting for the fuse-provided package " << strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void StatExitFlagAndExitProcess(int exit_code) {
|
||||
struct stat sb;
|
||||
if (stat(exit_flag_.c_str(), &sb) != 0) {
|
||||
PLOG(ERROR) << "Failed to stat " << exit_flag_;
|
||||
}
|
||||
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
void WriteMinadbdCommandStatus(MinadbdCommandStatus status) {
|
||||
std::string status_message(kMinadbdMessageSize, '\0');
|
||||
memcpy(status_message.data(), kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
|
||||
memcpy(status_message.data() + strlen(kMinadbdStatusPrefix), &status, sizeof(status));
|
||||
ASSERT_TRUE(
|
||||
android::base::WriteFully(recovery_socket_, status_message.data(), kMinadbdMessageSize));
|
||||
}
|
||||
|
||||
void ExecuteCommandAndWaitForExit(const std::string& command) {
|
||||
unique_fd fd = daemon_service_to_fd(command, nullptr);
|
||||
ASSERT_NE(-1, fd);
|
||||
sleep(EXIT_TIME_OUT);
|
||||
}
|
||||
|
||||
android::base::unique_fd minadbd_socket_;
|
||||
android::base::unique_fd recovery_socket_;
|
||||
|
||||
TemporaryDir mount_point_;
|
||||
std::string package_path_;
|
||||
std::string exit_flag_;
|
||||
};
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) {
|
||||
ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"),
|
||||
::testing::ExitedWithCode(kMinadbdHostCommandArgumentError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) {
|
||||
ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:10:20"),
|
||||
::testing::ExitedWithCode(kMinadbdFuseStartError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_broken_minadbd_socket) {
|
||||
SetMinadbdSocketFd(-1);
|
||||
ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"),
|
||||
::testing::ExitedWithCode(kMinadbdSocketIOError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_broken_recovery_socket) {
|
||||
recovery_socket_.reset();
|
||||
ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"),
|
||||
::testing::ExitedWithCode(kMinadbdSocketIOError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_wrong_command_format) {
|
||||
auto test_body = [&](const std::string& command) {
|
||||
unique_fd fd = daemon_service_to_fd(command, nullptr);
|
||||
ASSERT_NE(-1, fd);
|
||||
WaitForFusePath();
|
||||
ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall);
|
||||
|
||||
struct stat sb;
|
||||
ASSERT_EQ(0, stat(exit_flag_.c_str(), &sb));
|
||||
ASSERT_TRUE(android::base::WriteStringToFd("12345678", recovery_socket_));
|
||||
sleep(EXIT_TIME_OUT);
|
||||
};
|
||||
|
||||
ASSERT_EXIT(test_body("sideload-host:4096:4096"),
|
||||
::testing::ExitedWithCode(kMinadbdMessageFormatError), "");
|
||||
}
|
||||
|
||||
TEST_F(MinadbdServicesTest, SideloadHostService_read_data_from_fuse) {
|
||||
auto test_body = [&]() {
|
||||
std::vector<uint8_t> content(4096, 'a');
|
||||
// Start a new process instead of a thread to read from the package mounted by FUSE. Because
|
||||
// the test may not exit and report failures correctly when the thread blocks by a syscall.
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
WaitForFusePath();
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(package_path_.c_str(), O_RDONLY)));
|
||||
// Do not use assertion here because we want to stat the exit flag and exit the process.
|
||||
// Otherwise the test will wait for the time out instead of failing immediately.
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Failed to open " << package_path_;
|
||||
StatExitFlagAndExitProcess(1);
|
||||
}
|
||||
std::vector<uint8_t> content_from_fuse(4096);
|
||||
if (!android::base::ReadFully(fd, content_from_fuse.data(), 4096)) {
|
||||
PLOG(ERROR) << "Failed to read from " << package_path_;
|
||||
StatExitFlagAndExitProcess(1);
|
||||
}
|
||||
if (content_from_fuse != content) {
|
||||
LOG(ERROR) << "Content read from fuse doesn't match with the expected value";
|
||||
StatExitFlagAndExitProcess(1);
|
||||
}
|
||||
StatExitFlagAndExitProcess(0);
|
||||
}
|
||||
|
||||
unique_fd fd = daemon_service_to_fd("sideload-host:4096:4096", nullptr);
|
||||
ASSERT_NE(-1, fd);
|
||||
ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall);
|
||||
|
||||
// Mimic the response from adb host.
|
||||
std::string adb_message(8, '\0');
|
||||
ASSERT_TRUE(android::base::ReadFully(fd, adb_message.data(), 8));
|
||||
ASSERT_EQ(android::base::StringPrintf("%08u", 0), adb_message);
|
||||
ASSERT_TRUE(android::base::WriteFully(fd, content.data(), 4096));
|
||||
|
||||
// Check that we read the correct data from fuse.
|
||||
int child_status;
|
||||
waitpid(pid, &child_status, 0);
|
||||
ASSERT_TRUE(WIFEXITED(child_status));
|
||||
ASSERT_EQ(0, WEXITSTATUS(child_status));
|
||||
|
||||
WriteMinadbdCommandStatus(MinadbdCommandStatus::kSuccess);
|
||||
|
||||
// TODO(xunchang) check if adb host-side receives "DONEDONE", there's a race condition between
|
||||
// receiving the message and exit of test body (by detached thread in minadbd service).
|
||||
exit(kMinadbdSuccess);
|
||||
};
|
||||
|
||||
ASSERT_EXIT(test_body(), ::testing::ExitedWithCode(kMinadbdSuccess), "");
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// The message between recovery and minadbd is 8 bytes in size unless the length is explicitly
|
||||
// specified. Both the command and status has the format |prefix(4 bytes) + encoded enum(4 bytes)|.
|
||||
constexpr size_t kMinadbdMessageSize = 8;
|
||||
constexpr char const kMinadbdCommandPrefix[] = "COMD";
|
||||
constexpr char const kMinadbdStatusPrefix[] = "STAT";
|
||||
|
||||
enum MinadbdErrorCode : int {
|
||||
kMinadbdSuccess = 0,
|
||||
kMinadbdArgumentsParsingError = 1,
|
||||
kMinadbdSocketIOError = 2,
|
||||
kMinadbdMessageFormatError = 3,
|
||||
kMinadbdAdbVersionError = 4,
|
||||
kMinadbdHostCommandArgumentError = 5,
|
||||
kMinadbdFuseStartError = 6,
|
||||
kMinadbdUnsupportedCommandError = 7,
|
||||
kMinadbdCommandExecutionError = 8,
|
||||
kMinadbdErrorUnknown = 9,
|
||||
kMinadbdHostSocketIOError = 10,
|
||||
};
|
||||
|
||||
enum class MinadbdCommandStatus : uint32_t {
|
||||
kSuccess = 0,
|
||||
kFailure = 1,
|
||||
};
|
||||
|
||||
enum class MinadbdCommand : uint32_t {
|
||||
kInstall = 0,
|
||||
kUiPrint = 1,
|
||||
kRebootAndroid = 2,
|
||||
kRebootBootloader = 3,
|
||||
kRebootFastboot = 4,
|
||||
kRebootRecovery = 5,
|
||||
kRebootRescue = 6,
|
||||
kWipeCache = 7,
|
||||
kWipeData = 8,
|
||||
kNoOp = 9,
|
||||
|
||||
// Last but invalid command.
|
||||
kError,
|
||||
};
|
||||
|
||||
static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommand));
|
||||
static_assert(kMinadbdMessageSize ==
|
||||
sizeof(kMinadbdStatusPrefix) - 1 + sizeof(MinadbdCommandStatus));
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2018 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.
|
||||
|
||||
cc_library {
|
||||
name: "libminui",
|
||||
recovery_available: true,
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"events.cpp",
|
||||
"graphics.cpp",
|
||||
"graphics_adf.cpp",
|
||||
"graphics_drm.cpp",
|
||||
"graphics_fbdev.cpp",
|
||||
"resources.cpp",
|
||||
],
|
||||
|
||||
whole_static_libs: [
|
||||
"libadf",
|
||||
"libdrm",
|
||||
"libsync",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libpng",
|
||||
"libz",
|
||||
],
|
||||
}
|
||||
+122
-90
@@ -23,42 +23,49 @@
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "minui/minui.h"
|
||||
|
||||
#define MAX_DEVICES 16
|
||||
#define MAX_MISC_FDS 16
|
||||
constexpr size_t MAX_DEVICES = 16;
|
||||
constexpr size_t MAX_MISC_FDS = 16;
|
||||
|
||||
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
|
||||
#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
|
||||
constexpr size_t BITS_PER_LONG = sizeof(unsigned long) * 8;
|
||||
constexpr size_t BITS_TO_LONGS(size_t bits) {
|
||||
return ((bits + BITS_PER_LONG - 1) / BITS_PER_LONG);
|
||||
}
|
||||
|
||||
struct fd_info {
|
||||
int fd;
|
||||
struct FdInfo {
|
||||
android::base::unique_fd fd;
|
||||
ev_callback cb;
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
void* data;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int g_epoll_fd;
|
||||
static epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS];
|
||||
static int npolledevents;
|
||||
static android::base::unique_fd g_epoll_fd;
|
||||
static epoll_event g_polled_events[MAX_DEVICES + MAX_MISC_FDS];
|
||||
static int g_polled_events_count;
|
||||
|
||||
static fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
|
||||
static FdInfo ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
|
||||
|
||||
static unsigned ev_count = 0;
|
||||
static unsigned ev_dev_count = 0;
|
||||
static unsigned ev_misc_count = 0;
|
||||
static size_t g_ev_count = 0;
|
||||
static size_t g_ev_dev_count = 0;
|
||||
static size_t g_ev_misc_count = 0;
|
||||
|
||||
static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
|
||||
return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
|
||||
return (array[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
|
||||
}
|
||||
|
||||
#ifdef TW_USE_MINUI_WITH_OPTIONAL_TOUCH_EVENTS
|
||||
int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
|
||||
<<<<<<< HEAD
|
||||
#else
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
int ev_init(ev_callback input_cb, void* data) {
|
||||
@@ -70,45 +77,44 @@ int ev_init(ev_callback input_cb) {
|
||||
|
||||
g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
|
||||
if (g_epoll_fd == -1) {
|
||||
=======
|
||||
g_epoll_fd.reset();
|
||||
|
||||
android::base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
|
||||
if (epoll_fd == -1) {
|
||||
>>>>>>> android-10.0.0_r25
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool epollctlfail = false;
|
||||
DIR* dir = opendir("/dev/input");
|
||||
if (dir != nullptr) {
|
||||
dirent* de;
|
||||
while ((de = readdir(dir))) {
|
||||
if (strncmp(de->d_name, "event", 5)) continue;
|
||||
int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
|
||||
if (fd == -1) continue;
|
||||
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/dev/input"), closedir);
|
||||
if (!dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Use unsigned long to match ioctl's parameter type.
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
|
||||
bool epoll_ctl_failed = false;
|
||||
dirent* de;
|
||||
while ((de = readdir(dir.get())) != nullptr) {
|
||||
if (strncmp(de->d_name, "event", 5)) continue;
|
||||
android::base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC));
|
||||
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. EV_ABS is also
|
||||
// allowed if allow_touch_inputs is set.
|
||||
if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
|
||||
if (!allow_touch_inputs || !test_bit(EV_ABS, 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;
|
||||
// Use unsigned long to match ioctl's parameter type.
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
|
||||
|
||||
// Read the evbits of the input device.
|
||||
if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also
|
||||
// allowed if allow_touch_inputs is set.
|
||||
if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
|
||||
if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
ev_fdinfo[ev_count].fd = fd;
|
||||
ev_fdinfo[ev_count].cb = std::move(input_cb);
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
@@ -117,38 +123,55 @@ int ev_init(ev_callback input_cb) {
|
||||
ev_count++;
|
||||
ev_dev_count++;
|
||||
if (ev_dev_count == MAX_DEVICES) break;
|
||||
=======
|
||||
epoll_event ev;
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr = &ev_fdinfo[g_ev_count];
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
||||
epoll_ctl_failed = true;
|
||||
continue;
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
ev_fdinfo[g_ev_count].fd.reset(fd.release());
|
||||
ev_fdinfo[g_ev_count].cb = input_cb;
|
||||
g_ev_count++;
|
||||
g_ev_dev_count++;
|
||||
if (g_ev_dev_count == MAX_DEVICES) break;
|
||||
}
|
||||
|
||||
if (epollctlfail && !ev_count) {
|
||||
close(g_epoll_fd);
|
||||
g_epoll_fd = -1;
|
||||
if (epoll_ctl_failed && !g_ev_count) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_epoll_fd.reset(epoll_fd.release());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ev_get_epollfd(void) {
|
||||
return g_epoll_fd;
|
||||
return g_epoll_fd.get();
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
int ev_add_fd(int fd, ev_callback cb, void* data) {
|
||||
#else
|
||||
int ev_add_fd(int fd, ev_callback cb) {
|
||||
#endif
|
||||
if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
|
||||
=======
|
||||
int ev_add_fd(android::base::unique_fd&& fd, ev_callback cb) {
|
||||
if (g_ev_misc_count == MAX_MISC_FDS || cb == nullptr) {
|
||||
>>>>>>> android-10.0.0_r25
|
||||
return -1;
|
||||
}
|
||||
|
||||
epoll_event ev;
|
||||
ev.events = EPOLLIN | EPOLLWAKEUP;
|
||||
ev.data.ptr = static_cast<void*>(&ev_fdinfo[ev_count]);
|
||||
ev.data.ptr = static_cast<void*>(&ev_fdinfo[g_ev_count]);
|
||||
int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
|
||||
if (!ret) {
|
||||
<<<<<<< HEAD
|
||||
ev_fdinfo[ev_count].fd = fd;
|
||||
ev_fdinfo[ev_count].cb = std::move(cb);
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
@@ -156,38 +179,48 @@ int ev_add_fd(int fd, ev_callback cb) {
|
||||
#endif
|
||||
ev_count++;
|
||||
ev_misc_count++;
|
||||
=======
|
||||
ev_fdinfo[g_ev_count].fd.reset(fd.release());
|
||||
ev_fdinfo[g_ev_count].cb = std::move(cb);
|
||||
g_ev_count++;
|
||||
g_ev_misc_count++;
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ev_exit(void) {
|
||||
while (ev_count > 0) {
|
||||
close(ev_fdinfo[--ev_count].fd);
|
||||
}
|
||||
ev_misc_count = 0;
|
||||
ev_dev_count = 0;
|
||||
close(g_epoll_fd);
|
||||
while (g_ev_count > 0) {
|
||||
ev_fdinfo[--g_ev_count].fd.reset();
|
||||
}
|
||||
g_ev_misc_count = 0;
|
||||
g_ev_dev_count = 0;
|
||||
g_epoll_fd.reset();
|
||||
}
|
||||
|
||||
int ev_wait(int timeout) {
|
||||
npolledevents = epoll_wait(g_epoll_fd, polledevents, ev_count, timeout);
|
||||
if (npolledevents <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
g_polled_events_count = epoll_wait(g_epoll_fd, g_polled_events, g_ev_count, timeout);
|
||||
if (g_polled_events_count <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ev_dispatch(void) {
|
||||
for (int n = 0; n < npolledevents; n++) {
|
||||
fd_info* fdi = static_cast<fd_info*>(polledevents[n].data.ptr);
|
||||
for (int n = 0; n < g_polled_events_count; n++) {
|
||||
FdInfo* fdi = static_cast<FdInfo*>(g_polled_events[n].data.ptr);
|
||||
const ev_callback& cb = fdi->cb;
|
||||
if (cb) {
|
||||
<<<<<<< HEAD
|
||||
#ifdef TW_USE_MINUI_WITH_DATA
|
||||
cb(fdi->fd, polledevents[n].events, fdi->data);
|
||||
#else
|
||||
cb(fdi->fd, polledevents[n].events);
|
||||
#endif
|
||||
=======
|
||||
cb(fdi->fd, g_polled_events[n].events);
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,7 +244,7 @@ int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
|
||||
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) {
|
||||
for (size_t i = 0; i < g_ev_dev_count; ++i) {
|
||||
memset(ev_bits, 0, sizeof(ev_bits));
|
||||
memset(key_bits, 0, sizeof(key_bits));
|
||||
|
||||
@@ -240,38 +273,37 @@ int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
|
||||
}
|
||||
|
||||
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
|
||||
// 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 < g_ev_dev_count; ++i) {
|
||||
memset(ev_bits, 0, sizeof(ev_bits));
|
||||
memset(key_bits, 0, sizeof(key_bits));
|
||||
|
||||
// Does this device even have keys?
|
||||
if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
|
||||
continue;
|
||||
}
|
||||
if (!test_bit(EV_KEY, ev_bits)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int rc = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits);
|
||||
if (rc == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int key_code = 0; key_code <= KEY_MAX; ++key_code) {
|
||||
if (test_bit(key_code, key_bits)) {
|
||||
f(key_code);
|
||||
}
|
||||
}
|
||||
// Does this device even have keys?
|
||||
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, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int key_code = 0; key_code <= KEY_MAX; ++key_code) {
|
||||
if (test_bit(key_code, key_bits)) {
|
||||
f(key_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TW_USE_MINUI_WITH_OPTIONAL_TOUCH_EVENTS
|
||||
void ev_iterate_touch_inputs(const std::function<void(int)>& action) {
|
||||
for (size_t i = 0; i < ev_dev_count; ++i) {
|
||||
for (size_t i = 0; i < g_ev_dev_count; ++i) {
|
||||
// Use unsigned long to match ioctl's parameter type.
|
||||
unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)] = {}; // NOLINT
|
||||
if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
struct {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned char_width;
|
||||
unsigned char_height;
|
||||
unsigned char rundata[2973];
|
||||
} font = {
|
||||
.width = 960,
|
||||
.height = 18,
|
||||
.char_width = 10,
|
||||
.char_height = 18,
|
||||
.rundata = {
|
||||
0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x55,0x82,0x06,0x82,0x02,0x82,0x10,0x82,
|
||||
0x11,0x83,0x08,0x82,0x0a,0x82,0x04,0x82,0x46,0x82,0x08,0x82,0x07,0x84,0x06,
|
||||
0x84,0x0a,0x81,0x03,0x88,0x04,0x84,0x04,0x88,0x04,0x84,0x06,0x84,0x1e,0x81,
|
||||
0x0e,0x81,0x0a,0x84,0x06,0x84,0x07,0x82,0x05,0x85,0x07,0x84,0x04,0x86,0x04,
|
||||
0x88,0x02,0x88,0x04,0x84,0x04,0x82,0x04,0x82,0x02,0x88,0x05,0x86,0x01,0x82,
|
||||
0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04,
|
||||
0x86,0x06,0x84,0x04,0x86,0x06,0x84,0x04,0x88,0x02,0x82,0x04,0x82,0x02,0x82,
|
||||
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,
|
||||
0x88,0x03,0x86,0x0e,0x86,0x06,0x82,0x11,0x82,0x10,0x82,0x18,0x82,0x0f,0x84,
|
||||
0x0d,0x82,0x1c,0x82,0x09,0x84,0x7f,0x16,0x84,0x05,0x82,0x05,0x84,0x07,0x83,
|
||||
0x02,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x03,0x86,0x04,
|
||||
0x83,0x02,0x82,0x03,0x82,0x01,0x82,0x07,0x82,0x09,0x82,0x06,0x82,0x3e,0x82,
|
||||
0x04,0x84,0x06,0x83,0x06,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x03,
|
||||
0x82,0x09,0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,
|
||||
0x1c,0x82,0x0e,0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x05,0x84,0x04,
|
||||
0x82,0x02,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,
|
||||
0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x04,
|
||||
0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x03,0x82,0x02,0x82,
|
||||
0x03,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x02,
|
||||
0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
|
||||
0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x03,0x82,0x08,0x82,0x0c,
|
||||
0x82,0x05,0x84,0x11,0x82,0x0f,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82,
|
||||
0x1c,0x82,0x0b,0x82,0x7f,0x15,0x82,0x08,0x82,0x08,0x82,0x05,0x82,0x01,0x82,
|
||||
0x01,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x02,0x82,0x01,
|
||||
0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x07,0x82,
|
||||
0x08,0x82,0x08,0x82,0x3d,0x82,0x03,0x82,0x02,0x82,0x04,0x84,0x05,0x82,0x04,
|
||||
0x82,0x02,0x82,0x04,0x82,0x06,0x83,0x03,0x82,0x08,0x82,0x04,0x81,0x09,0x82,
|
||||
0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x1a,0x82,0x10,0x82,0x06,0x82,0x04,
|
||||
0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,
|
||||
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,
|
||||
0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83,
|
||||
0x02,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,
|
||||
0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,
|
||||
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04,
|
||||
0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x08,0x82,0x0c,0x82,0x04,0x82,0x02,0x82,
|
||||
0x11,0x82,0x0e,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82,0x0b,0x82,0x0b,
|
||||
0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x05,0x82,
|
||||
0x02,0x83,0x1a,0x82,0x07,0x81,0x02,0x81,0x07,0x82,0x01,0x82,0x02,0x82,0x01,
|
||||
0x82,0x05,0x82,0x01,0x84,0x04,0x82,0x01,0x82,0x07,0x82,0x08,0x82,0x08,0x82,
|
||||
0x06,0x82,0x02,0x82,0x06,0x82,0x28,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01,
|
||||
0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x84,0x03,0x82,0x08,0x82,
|
||||
0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x19,0x82,0x12,0x82,0x05,
|
||||
0x82,0x04,0x82,0x02,0x82,0x02,0x84,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82,
|
||||
0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04,
|
||||
0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83,0x02,0x83,
|
||||
0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
|
||||
0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82,0x05,0x82,0x04,0x82,0x02,0x82,
|
||||
0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,
|
||||
0x82,0x04,0x82,0x09,0x82,0x0b,0x82,0x03,0x82,0x04,0x82,0x20,0x82,0x18,0x82,
|
||||
0x0e,0x82,0x10,0x82,0x0b,0x82,0x0b,0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45,
|
||||
0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x88,0x01,0x82,0x01,0x82,0x06,0x83,
|
||||
0x01,0x82,0x04,0x84,0x08,0x81,0x08,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06,
|
||||
0x82,0x28,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x08,0x82,0x04,0x82,
|
||||
0x01,0x82,0x03,0x82,0x08,0x82,0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,
|
||||
0x82,0x18,0x82,0x06,0x88,0x06,0x82,0x04,0x82,0x04,0x82,0x02,0x82,0x01,0x85,
|
||||
0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02,
|
||||
0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,
|
||||
0x02,0x82,0x04,0x82,0x08,0x88,0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02,
|
||||
0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82,
|
||||
0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x84,0x06,
|
||||
0x84,0x08,0x82,0x05,0x82,0x09,0x82,0x0b,0x82,0x2b,0x82,0x18,0x82,0x0e,0x82,
|
||||
0x10,0x82,0x1c,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x26,
|
||||
0x82,0x11,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x09,0x82,0x06,0x82,0x12,0x82,
|
||||
0x0a,0x82,0x06,0x84,0x07,0x82,0x27,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0b,
|
||||
0x82,0x07,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01,0x83,0x04,0x82,0x01,0x83,
|
||||
0x08,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x83,0x07,0x83,0x05,
|
||||
0x82,0x16,0x82,0x08,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,
|
||||
0x02,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,
|
||||
0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,
|
||||
0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,
|
||||
0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,
|
||||
0x0a,0x82,0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,
|
||||
0x82,0x04,0x84,0x06,0x84,0x08,0x82,0x05,0x82,0x0a,0x82,0x0a,0x82,0x23,0x85,
|
||||
0x03,0x82,0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x84,0x04,0x86,0x05,
|
||||
0x85,0x01,0x81,0x02,0x82,0x01,0x83,0x05,0x84,0x09,0x84,0x02,0x82,0x03,0x82,
|
||||
0x06,0x82,0x05,0x81,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x83,0x06,0x84,0x04,
|
||||
0x82,0x01,0x83,0x06,0x83,0x01,0x82,0x02,0x82,0x01,0x84,0x04,0x86,0x03,0x86,
|
||||
0x04,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
|
||||
0x82,0x02,0x82,0x04,0x82,0x03,0x87,0x05,0x82,0x08,0x82,0x08,0x82,0x26,0x82,
|
||||
0x11,0x82,0x01,0x82,0x04,0x86,0x07,0x82,0x05,0x83,0x12,0x82,0x0a,0x82,0x04,
|
||||
0x88,0x02,0x88,0x0c,0x88,0x10,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,
|
||||
0x06,0x83,0x04,0x82,0x03,0x82,0x03,0x83,0x02,0x82,0x03,0x83,0x02,0x82,0x07,
|
||||
0x82,0x06,0x84,0x05,0x82,0x02,0x83,0x05,0x83,0x07,0x83,0x04,0x82,0x18,0x82,
|
||||
0x06,0x82,0x04,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x86,0x04,
|
||||
0x82,0x08,0x82,0x04,0x82,0x02,0x86,0x04,0x86,0x04,0x82,0x02,0x84,0x02,0x88,
|
||||
0x05,0x82,0x0a,0x82,0x03,0x85,0x05,0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02,
|
||||
0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,
|
||||
0x04,0x82,0x02,0x82,0x03,0x82,0x05,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x03,
|
||||
0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x08,0x82,0x08,0x82,
|
||||
0x06,0x82,0x0a,0x82,0x0a,0x82,0x22,0x82,0x03,0x82,0x02,0x83,0x02,0x82,0x04,
|
||||
0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x02,0x82,0x05,0x82,0x06,0x82,
|
||||
0x03,0x83,0x02,0x83,0x02,0x82,0x06,0x82,0x0b,0x82,0x02,0x82,0x02,0x82,0x07,
|
||||
0x82,0x05,0x88,0x02,0x83,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82,
|
||||
0x04,0x82,0x02,0x83,0x03,0x83,0x02,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06,
|
||||
0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,
|
||||
0x03,0x82,0x04,0x82,0x08,0x82,0x02,0x84,0x09,0x82,0x09,0x84,0x23,0x82,0x11,
|
||||
0x82,0x01,0x82,0x06,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x01,0x82,0x11,0x82,
|
||||
0x0a,0x82,0x06,0x84,0x07,0x82,0x26,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x08,
|
||||
0x83,0x09,0x82,0x03,0x82,0x03,0x82,0x09,0x82,0x02,0x82,0x04,0x82,0x05,0x82,
|
||||
0x06,0x82,0x02,0x82,0x05,0x83,0x01,0x82,0x17,0x82,0x16,0x82,0x06,0x82,0x05,
|
||||
0x82,0x01,0x82,0x01,0x82,0x02,0x88,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82,
|
||||
0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,
|
||||
0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82,
|
||||
0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x86,0x04,0x82,0x04,0x82,0x02,
|
||||
0x86,0x09,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82,
|
||||
0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09,0x82,0x27,
|
||||
0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
|
||||
0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,
|
||||
0x82,0x01,0x82,0x08,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,
|
||||
0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x07,
|
||||
0x82,0x0a,0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82,
|
||||
0x04,0x84,0x04,0x82,0x04,0x82,0x07,0x82,0x06,0x82,0x08,0x82,0x08,0x82,0x26,
|
||||
0x82,0x0f,0x88,0x05,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x02,0x82,0x01,0x82,
|
||||
0x0d,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06,0x82,0x26,0x82,0x05,0x82,0x04,
|
||||
0x82,0x05,0x82,0x07,0x82,0x0c,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82,
|
||||
0x05,0x82,0x05,0x82,0x04,0x82,0x08,0x82,0x18,0x82,0x14,0x82,0x07,0x82,0x05,
|
||||
0x82,0x01,0x84,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,
|
||||
0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,
|
||||
0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82,
|
||||
0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,
|
||||
0x82,0x02,0x82,0x0a,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82,
|
||||
0x01,0x82,0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09,
|
||||
0x82,0x22,0x87,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x88,
|
||||
0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,
|
||||
0x84,0x09,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,
|
||||
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x08,0x86,0x05,
|
||||
0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82,
|
||||
0x05,0x82,0x05,0x82,0x04,0x82,0x06,0x82,0x07,0x82,0x08,0x82,0x08,0x82,0x26,
|
||||
0x82,0x10,0x82,0x01,0x82,0x07,0x82,0x01,0x82,0x04,0x82,0x01,0x83,0x02,0x82,
|
||||
0x03,0x83,0x0f,0x82,0x08,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x25,0x82,0x07,
|
||||
0x82,0x02,0x82,0x06,0x82,0x06,0x82,0x07,0x82,0x04,0x82,0x07,0x82,0x09,0x82,
|
||||
0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04,0x82,0x08,0x82,0x19,0x82,0x05,
|
||||
0x88,0x05,0x82,0x08,0x82,0x05,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x02,0x82,
|
||||
0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,
|
||||
0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82,0x03,0x82,0x03,0x82,
|
||||
0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02,
|
||||
0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x03,0x82,0x09,0x82,0x05,0x82,
|
||||
0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x06,
|
||||
0x82,0x06,0x82,0x08,0x82,0x0c,0x82,0x08,0x82,0x21,0x82,0x04,0x82,0x02,0x82,
|
||||
0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a,0x82,0x06,0x82,0x03,
|
||||
0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x85,0x08,0x82,0x05,0x82,
|
||||
0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
|
||||
0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82,0x06,0x82,0x04,0x82,
|
||||
0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05,
|
||||
0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x38,0x82,0x01,0x82,0x04,0x82,0x01,0x82,
|
||||
0x01,0x82,0x04,0x84,0x01,0x82,0x01,0x82,0x03,0x82,0x10,0x82,0x08,0x82,0x30,
|
||||
0x83,0x06,0x82,0x07,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x08,0x82,0x04,0x82,
|
||||
0x07,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04,
|
||||
0x82,0x03,0x81,0x04,0x82,0x1a,0x82,0x10,0x82,0x10,0x82,0x08,0x82,0x04,0x82,
|
||||
0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,
|
||||
0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82,
|
||||
0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,
|
||||
0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x02,0x84,0x02,0x82,0x03,0x82,0x03,0x82,
|
||||
0x04,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x05,0x83,0x02,0x83,0x03,
|
||||
0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x09,0x82,0x0c,0x82,0x08,0x82,0x21,0x82,
|
||||
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a,
|
||||
0x82,0x07,0x85,0x04,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x02,0x82,
|
||||
0x07,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
|
||||
0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82,
|
||||
0x06,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x04,0x84,0x04,
|
||||
0x82,0x04,0x82,0x04,0x82,0x09,0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x82,
|
||||
0x01,0x82,0x05,0x86,0x04,0x82,0x01,0x82,0x01,0x82,0x01,0x83,0x01,0x84,0x10,
|
||||
0x82,0x06,0x82,0x1d,0x83,0x11,0x83,0x05,0x82,0x09,0x84,0x07,0x82,0x05,0x82,
|
||||
0x09,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
|
||||
0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x06,0x83,0x07,0x83,0x09,0x82,
|
||||
0x0e,0x82,0x0a,0x82,0x06,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03,
|
||||
0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x09,0x82,
|
||||
0x02,0x83,0x02,0x82,0x04,0x82,0x05,0x82,0x06,0x82,0x01,0x82,0x04,0x82,0x04,
|
||||
0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,
|
||||
0x03,0x82,0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x06,
|
||||
0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
|
||||
0x05,0x82,0x05,0x82,0x09,0x82,0x0d,0x82,0x07,0x82,0x21,0x82,0x04,0x82,0x02,
|
||||
0x83,0x02,0x82,0x04,0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x03,0x82,
|
||||
0x04,0x82,0x06,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x03,
|
||||
0x82,0x06,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x03,0x82,
|
||||
0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x07,0x82,0x04,
|
||||
0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x02,0x83,0x05,0x82,0x05,0x88,0x03,0x82,
|
||||
0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x0a,0x82,0x08,0x82,0x08,0x82,0x26,
|
||||
0x82,0x1c,0x82,0x06,0x82,0x02,0x83,0x03,0x84,0x02,0x82,0x10,0x82,0x04,0x82,
|
||||
0x1e,0x83,0x11,0x83,0x05,0x82,0x0a,0x82,0x05,0x88,0x02,0x88,0x04,0x84,0x09,
|
||||
0x82,0x05,0x84,0x06,0x84,0x05,0x82,0x09,0x84,0x06,0x84,0x07,0x83,0x07,0x83,
|
||||
0x0a,0x81,0x0e,0x81,0x0b,0x82,0x07,0x85,0x03,0x82,0x04,0x82,0x02,0x86,0x06,
|
||||
0x84,0x04,0x86,0x04,0x88,0x02,0x82,0x0a,0x84,0x01,0x81,0x02,0x82,0x04,0x82,
|
||||
0x02,0x88,0x04,0x83,0x05,0x82,0x04,0x82,0x02,0x88,0x02,0x82,0x04,0x82,0x02,
|
||||
0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x0a,0x85,0x03,0x82,0x04,0x82,0x04,0x84,
|
||||
0x07,0x82,0x07,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,
|
||||
0x82,0x05,0x88,0x03,0x86,0x09,0x82,0x03,0x86,0x22,0x85,0x01,0x81,0x02,0x82,
|
||||
0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x85,0x05,0x82,0x07,0x86,0x03,
|
||||
0x82,0x04,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x88,0x02,0x82,
|
||||
0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x83,0x06,
|
||||
0x83,0x01,0x82,0x03,0x82,0x08,0x86,0x06,0x84,0x05,0x83,0x01,0x82,0x05,0x82,
|
||||
0x06,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x83,0x01,0x82,0x03,0x87,0x06,
|
||||
0x84,0x05,0x82,0x05,0x84,0x7f,0x15,0x83,0x7f,0x14,0x83,0x7f,0x5e,0x82,0x7f,
|
||||
0x05,0x89,0x47,0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x4e,
|
||||
0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a,0x82,0x04,0x82,0x17,0x82,0x03,0x82,
|
||||
0x34,0x82,0x0e,0x82,0x48,0x82,0x04,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a,
|
||||
0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x49,0x82,0x02,0x82,
|
||||
0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0c,0x86,0x19,0x85,0x35,0x82,0x0e,0x82,0x4a,
|
||||
0x84,0x3f,
|
||||
0x00,
|
||||
}
|
||||
};
|
||||
+163
-55
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifdef BOARD_USE_CUSTOM_RECOVERY_FONT
|
||||
#include BOARD_USE_CUSTOM_RECOVERY_FONT
|
||||
#else
|
||||
@@ -30,6 +31,10 @@
|
||||
#endif
|
||||
|
||||
#ifndef MSM_BSP
|
||||
=======
|
||||
#include <android-base/properties.h>
|
||||
|
||||
>>>>>>> android-10.0.0_r25
|
||||
#include "graphics_adf.h"
|
||||
#endif
|
||||
#include "graphics_drm.h"
|
||||
@@ -37,10 +42,9 @@
|
||||
#include "graphics_overlay.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
static GRFont* gr_font = NULL;
|
||||
static GRFont* gr_font = nullptr;
|
||||
static MinuiBackend* gr_backend = nullptr;
|
||||
|
||||
static int overscan_percent = OVERSCAN_PERCENT;
|
||||
static int overscan_offset_x = 0;
|
||||
static int overscan_offset_y = 0;
|
||||
|
||||
@@ -58,12 +62,15 @@ static unsigned char gr_current_b5 = 31;
|
||||
static uint32_t gr_current = ~0;
|
||||
static constexpr uint32_t alpha_mask = 0xff000000;
|
||||
|
||||
static GRSurface* gr_draw = NULL;
|
||||
static GRRotation rotation = ROTATION_NONE;
|
||||
// gr_draw is owned by backends.
|
||||
static GRSurface* gr_draw = nullptr;
|
||||
static GRRotation rotation = GRRotation::NONE;
|
||||
static PixelFormat pixel_format = PixelFormat::UNKNOWN;
|
||||
|
||||
static bool outside(int x, int y) {
|
||||
return x < 0 || x >= (rotation % 2 ? gr_draw->height : gr_draw->width) || y < 0 ||
|
||||
y >= (rotation % 2 ? gr_draw->width : gr_draw->height);
|
||||
auto swapped = (rotation == GRRotation::LEFT || rotation == GRRotation::RIGHT);
|
||||
return x < 0 || x >= (swapped ? gr_draw->height : gr_draw->width) || y < 0 ||
|
||||
y >= (swapped ? gr_draw->width : gr_draw->height);
|
||||
}
|
||||
|
||||
#ifdef TW_NO_MINUI_CUSTOM_FONTS
|
||||
@@ -82,13 +89,26 @@ const GRFont* gr_sys_font() {
|
||||
return gr_font;
|
||||
}
|
||||
|
||||
PixelFormat gr_pixel_format() {
|
||||
return pixel_format;
|
||||
}
|
||||
|
||||
int gr_measure(const GRFont* font, const char* s) {
|
||||
if (font == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return font->char_width * strlen(s);
|
||||
}
|
||||
|
||||
void gr_font_size(const GRFont* font, int* x, int* y) {
|
||||
int gr_font_size(const GRFont* font, int* x, int* y) {
|
||||
if (font == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*x = font->char_width;
|
||||
*y = font->char_height;
|
||||
return 0;
|
||||
}
|
||||
#endif // TW_NO_MINUI_CUSTOM_FONTS
|
||||
|
||||
@@ -202,6 +222,7 @@ static inline uint32_t pixel_blend(uint8_t alpha, uint32_t pix) {
|
||||
return (out_r & 0xff) | (out_g & 0xff00) | (out_b & 0xff0000) | (gr_current & 0xff000000);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// increments pixel pointer right, with current rotation.
|
||||
static void incr_x16(uint16_t** p, int row_pixels) {
|
||||
if (rotation % 2) {
|
||||
@@ -221,23 +242,35 @@ static void incr_y16(uint16_t** p, int row_pixels) {
|
||||
}
|
||||
|
||||
// increments pixel pointer right, with current rotation.
|
||||
=======
|
||||
// Increments pixel pointer right, with current rotation.
|
||||
>>>>>>> android-10.0.0_r25
|
||||
static void incr_x(uint32_t** p, int row_pixels) {
|
||||
if (rotation % 2) {
|
||||
*p = *p + (rotation == 1 ? 1 : -1) * row_pixels;
|
||||
} else {
|
||||
*p = *p + (rotation ? -1 : 1);
|
||||
if (rotation == GRRotation::LEFT) {
|
||||
*p = *p - row_pixels;
|
||||
} else if (rotation == GRRotation::RIGHT) {
|
||||
*p = *p + row_pixels;
|
||||
} else if (rotation == GRRotation::DOWN) {
|
||||
*p = *p - 1;
|
||||
} else { // GRRotation::NONE
|
||||
*p = *p + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// increments pixel pointer down, with current rotation.
|
||||
// Increments pixel pointer down, with current rotation.
|
||||
static void incr_y(uint32_t** p, int row_pixels) {
|
||||
if (rotation % 2) {
|
||||
*p = *p + (rotation == 1 ? -1 : 1);
|
||||
} else {
|
||||
*p = *p + (rotation ? -1 : 1) * row_pixels;
|
||||
if (rotation == GRRotation::LEFT) {
|
||||
*p = *p + 1;
|
||||
} else if (rotation == GRRotation::RIGHT) {
|
||||
*p = *p - 1;
|
||||
} else if (rotation == GRRotation::DOWN) {
|
||||
*p = *p - row_pixels;
|
||||
} else { // GRRotation::NONE
|
||||
*p = *p + row_pixels;
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// returns pixel pointer at given coordinates with rotation adjustment.
|
||||
static uint16_t* pixel_at16(GRSurface* surf, int x, int y, int row_pixels) {
|
||||
switch (rotation) {
|
||||
@@ -258,22 +291,28 @@ static uint16_t* pixel_at16(GRSurface* surf, int x, int y, int row_pixels) {
|
||||
|
||||
// returns pixel pointer at given coordinates with rotation adjustment.
|
||||
static uint32_t* pixel_at(GRSurface* surf, int x, int y, int row_pixels) {
|
||||
=======
|
||||
// Returns pixel pointer at given coordinates with rotation adjustment.
|
||||
static uint32_t* PixelAt(GRSurface* surface, int x, int y, int row_pixels) {
|
||||
>>>>>>> android-10.0.0_r25
|
||||
switch (rotation) {
|
||||
case ROTATION_NONE:
|
||||
return reinterpret_cast<uint32_t*>(surf->data) + y * row_pixels + x;
|
||||
case ROTATION_RIGHT:
|
||||
return reinterpret_cast<uint32_t*>(surf->data) + x * row_pixels + (surf->width - y);
|
||||
case ROTATION_DOWN:
|
||||
return reinterpret_cast<uint32_t*>(surf->data) + (surf->height - 1 - y) * row_pixels +
|
||||
(surf->width - 1 - x);
|
||||
case ROTATION_LEFT:
|
||||
return reinterpret_cast<uint32_t*>(surf->data) + (surf->height - 1 - x) * row_pixels + y;
|
||||
case GRRotation::NONE:
|
||||
return reinterpret_cast<uint32_t*>(surface->data()) + y * row_pixels + x;
|
||||
case GRRotation::RIGHT:
|
||||
return reinterpret_cast<uint32_t*>(surface->data()) + x * row_pixels + (surface->width - y);
|
||||
case GRRotation::DOWN:
|
||||
return reinterpret_cast<uint32_t*>(surface->data()) + (surface->height - 1 - y) * row_pixels +
|
||||
(surface->width - 1 - x);
|
||||
case GRRotation::LEFT:
|
||||
return reinterpret_cast<uint32_t*>(surface->data()) + (surface->height - 1 - x) * row_pixels +
|
||||
y;
|
||||
default:
|
||||
printf("invalid rotation %d", rotation);
|
||||
printf("invalid rotation %d", static_cast<int>(rotation));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
static void text_blend16(uint8_t* src_p, int src_row_bytes, uint16_t* dst_p, int dst_row_pixels,
|
||||
int width, int height) {
|
||||
uint8_t alpha_current = static_cast<uint8_t>((alpha_mask & gr_current) >> 24);
|
||||
@@ -292,9 +331,13 @@ static void text_blend16(uint8_t* src_p, int src_row_bytes, uint16_t* dst_p, int
|
||||
|
||||
static void text_blend(uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels,
|
||||
int width, int height) {
|
||||
=======
|
||||
static void TextBlend(const uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels,
|
||||
int width, int height) {
|
||||
>>>>>>> android-10.0.0_r25
|
||||
uint8_t alpha_current = static_cast<uint8_t>((alpha_mask & gr_current) >> 24);
|
||||
for (int j = 0; j < height; ++j) {
|
||||
uint8_t* sx = src_p;
|
||||
const uint8_t* sx = src_p;
|
||||
uint32_t* px = dst_p;
|
||||
for (int i = 0; i < width; ++i, incr_x(&px, dst_row_pixels)) {
|
||||
uint8_t a = *sx++;
|
||||
@@ -361,6 +404,7 @@ void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) {
|
||||
}
|
||||
|
||||
int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
|
||||
<<<<<<< HEAD
|
||||
|
||||
uint8_t* src_p = font->texture->data + ((ch - ' ') * font->char_width) +
|
||||
(bold ? font->char_height * font->texture->row_bytes : 0);
|
||||
@@ -375,13 +419,22 @@ void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) {
|
||||
text_blend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width,
|
||||
font->char_height);
|
||||
}
|
||||
=======
|
||||
const uint8_t* src_p = font->texture->data() + ((ch - ' ') * font->char_width) +
|
||||
(bold ? font->char_height * font->texture->row_bytes : 0);
|
||||
uint32_t* dst_p = PixelAt(gr_draw, x, y, row_pixels);
|
||||
|
||||
TextBlend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width,
|
||||
font->char_height);
|
||||
|
||||
>>>>>>> android-10.0.0_r25
|
||||
x += font->char_width;
|
||||
}
|
||||
}
|
||||
#endif //TW_NO_MINUI_CUSTOM_FONTS
|
||||
|
||||
void gr_texticon(int x, int y, GRSurface* icon) {
|
||||
if (icon == NULL) return;
|
||||
void gr_texticon(int x, int y, const GRSurface* icon) {
|
||||
if (icon == nullptr) return;
|
||||
|
||||
if (icon->pixel_bytes != 1) {
|
||||
printf("gr_texticon: source has wrong format\n");
|
||||
@@ -394,6 +447,7 @@ void gr_texticon(int x, int y, GRSurface* icon) {
|
||||
if (outside(x, y) || outside(x + icon->width - 1, y + icon->height - 1)) return;
|
||||
|
||||
int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
|
||||
<<<<<<< HEAD
|
||||
uint8_t* src_p = icon->data;
|
||||
if (gr_draw->pixel_bytes == 2) {
|
||||
uint16_t* dst_p = pixel_at16(gr_draw, x, y, row_pixels);
|
||||
@@ -404,6 +458,11 @@ void gr_texticon(int x, int y, GRSurface* icon) {
|
||||
uint32_t* dst_p = pixel_at(gr_draw, x, y, row_pixels);
|
||||
|
||||
text_blend(src_p, icon->row_bytes, dst_p, row_pixels, icon->width, icon->height);
|
||||
=======
|
||||
const uint8_t* src_p = icon->data();
|
||||
uint32_t* dst_p = PixelAt(gr_draw, x, y, row_pixels);
|
||||
TextBlend(src_p, icon->row_bytes, dst_p, row_pixels, icon->width, icon->height);
|
||||
>>>>>>> android-10.0.0_r25
|
||||
}
|
||||
|
||||
void gr_convert_rgb_555(unsigned char r, unsigned char g, unsigned char b)
|
||||
@@ -436,11 +495,11 @@ void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a
|
||||
}
|
||||
|
||||
uint32_t r32 = r, g32 = g, b32 = b, a32 = a;
|
||||
#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
|
||||
gr_current = (a32 << 24) | (r32 << 16) | (g32 << 8) | b32;
|
||||
#else
|
||||
gr_current = (a32 << 24) | (b32 << 16) | (g32 << 8) | r32;
|
||||
#endif
|
||||
if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) {
|
||||
gr_current = (a32 << 24) | (r32 << 16) | (g32 << 8) | b32;
|
||||
} else {
|
||||
gr_current = (a32 << 24) | (b32 << 16) | (g32 << 8) | r32;
|
||||
}
|
||||
}
|
||||
|
||||
void gr_clear() {
|
||||
@@ -454,9 +513,9 @@ void gr_clear() {
|
||||
(gr_current & 0xff) == ((gr_current >> 16) & 0xff) &&
|
||||
(gr_current & 0xff) == ((gr_current >> 24) & 0xff) &&
|
||||
gr_draw->row_bytes == gr_draw->width * gr_draw->pixel_bytes) {
|
||||
memset(gr_draw->data, gr_current & 0xff, gr_draw->height * gr_draw->row_bytes);
|
||||
memset(gr_draw->data(), gr_current & 0xff, gr_draw->height * gr_draw->row_bytes);
|
||||
} else {
|
||||
uint32_t* px = reinterpret_cast<uint32_t*>(gr_draw->data);
|
||||
uint32_t* px = reinterpret_cast<uint32_t*>(gr_draw->data());
|
||||
int row_diff = gr_draw->row_bytes / gr_draw->pixel_bytes - gr_draw->width;
|
||||
for (int y = 0; y < gr_draw->height; ++y) {
|
||||
for (int x = 0; x < gr_draw->width; ++x) {
|
||||
@@ -477,6 +536,7 @@ void gr_fill(int x1, int y1, int x2, int y2) {
|
||||
if (outside(x1, y1) || outside(x2 - 1, y2 - 1)) return;
|
||||
|
||||
int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
|
||||
<<<<<<< HEAD
|
||||
if (gr_draw->pixel_bytes == 2) {
|
||||
uint16_t* p = pixel_at16(gr_draw, x1, y1, row_pixels);
|
||||
uint8_t alpha = static_cast<uint8_t>(((gr_current & alpha_mask) >> 24));
|
||||
@@ -494,6 +554,9 @@ void gr_fill(int x1, int y1, int x2, int y2) {
|
||||
}
|
||||
{ // open brace to maintain separation between uint16_t p and uint32_t p
|
||||
uint32_t* p = pixel_at(gr_draw, x1, y1, row_pixels);
|
||||
=======
|
||||
uint32_t* p = PixelAt(gr_draw, x1, y1, row_pixels);
|
||||
>>>>>>> android-10.0.0_r25
|
||||
uint8_t alpha = static_cast<uint8_t>(((gr_current & alpha_mask) >> 24));
|
||||
if (alpha > 0) {
|
||||
for (int y = y1; y < y2; ++y) {
|
||||
@@ -550,8 +613,8 @@ void gr_blit_32to16(GRSurface* source, int sx, int sy, int w, int h, int dx, int
|
||||
}
|
||||
}
|
||||
|
||||
void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
|
||||
if (source == NULL) return;
|
||||
void gr_blit(const GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
|
||||
if (source == nullptr) return;
|
||||
|
||||
if (gr_draw->pixel_bytes != source->pixel_bytes) {
|
||||
if (gr_draw->pixel_bytes == 2 && source->pixel_bytes == 4) {
|
||||
@@ -568,14 +631,15 @@ void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
|
||||
|
||||
if (outside(dx, dy) || outside(dx + w - 1, dy + h - 1)) return;
|
||||
|
||||
if (rotation) {
|
||||
if (rotation != GRRotation::NONE) {
|
||||
int src_row_pixels = source->row_bytes / source->pixel_bytes;
|
||||
int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
|
||||
uint32_t* src_py = reinterpret_cast<uint32_t*>(source->data) + sy * source->row_bytes / 4 + sx;
|
||||
uint32_t* dst_py = pixel_at(gr_draw, dx, dy, row_pixels);
|
||||
const uint32_t* src_py =
|
||||
reinterpret_cast<const uint32_t*>(source->data()) + sy * source->row_bytes / 4 + sx;
|
||||
uint32_t* dst_py = PixelAt(gr_draw, dx, dy, row_pixels);
|
||||
|
||||
for (int y = 0; y < h; y += 1) {
|
||||
uint32_t* src_px = src_py;
|
||||
const uint32_t* src_px = src_py;
|
||||
uint32_t* dst_px = dst_py;
|
||||
for (int x = 0; x < w; x += 1) {
|
||||
*dst_px = *src_px++;
|
||||
@@ -585,11 +649,10 @@ void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
|
||||
incr_y(&dst_py, row_pixels);
|
||||
}
|
||||
} else {
|
||||
unsigned char* src_p = source->data + sy * source->row_bytes + sx * source->pixel_bytes;
|
||||
unsigned char* dst_p = gr_draw->data + dy * gr_draw->row_bytes + dx * gr_draw->pixel_bytes;
|
||||
const uint8_t* src_p = source->data() + sy * source->row_bytes + sx * source->pixel_bytes;
|
||||
uint8_t* dst_p = gr_draw->data() + dy * gr_draw->row_bytes + dx * gr_draw->pixel_bytes;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < h; ++i) {
|
||||
for (int i = 0; i < h; ++i) {
|
||||
memcpy(dst_p, src_p, w * source->pixel_bytes);
|
||||
src_p += source->row_bytes;
|
||||
dst_p += gr_draw->row_bytes;
|
||||
@@ -597,15 +660,15 @@ void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int gr_get_width(GRSurface* surface) {
|
||||
if (surface == NULL) {
|
||||
unsigned int gr_get_width(const GRSurface* surface) {
|
||||
if (surface == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return surface->width;
|
||||
}
|
||||
|
||||
unsigned int gr_get_height(GRSurface* surface) {
|
||||
if (surface == NULL) {
|
||||
unsigned int gr_get_height(const GRSurface* surface) {
|
||||
if (surface == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return surface->height;
|
||||
@@ -677,6 +740,7 @@ int gr_init_font(const char* name, GRFont** dest) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
static void gr_init_font(void) {
|
||||
int res = gr_init_font("font", &gr_font);
|
||||
if (res == 0) {
|
||||
@@ -708,13 +772,36 @@ static void gr_init_font(void) {
|
||||
}
|
||||
#endif // TW_NO_MINUI_CUSTOM_FONTS
|
||||
|
||||
=======
|
||||
>>>>>>> android-10.0.0_r25
|
||||
void gr_flip() {
|
||||
gr_draw = gr_backend->Flip();
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
int gr_init(void)
|
||||
{
|
||||
gr_init_font();
|
||||
=======
|
||||
int gr_init() {
|
||||
// pixel_format needs to be set before loading any resources or initializing backends.
|
||||
std::string format = android::base::GetProperty("ro.minui.pixel_format", "");
|
||||
if (format == "ABGR_8888") {
|
||||
pixel_format = PixelFormat::ABGR;
|
||||
} else if (format == "RGBX_8888") {
|
||||
pixel_format = PixelFormat::RGBX;
|
||||
} else if (format == "BGRA_8888") {
|
||||
pixel_format = PixelFormat::BGRA;
|
||||
} else {
|
||||
pixel_format = PixelFormat::UNKNOWN;
|
||||
}
|
||||
|
||||
int ret = gr_init_font("font", &gr_font);
|
||||
if (ret != 0) {
|
||||
printf("Failed to init font: %d, continuing graphic backend initialization without font file\n",
|
||||
ret);
|
||||
}
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
auto backend = std::unique_ptr<MinuiBackend>{ std::make_unique<MinuiBackendOverlay>() };
|
||||
gr_draw = backend->Init();
|
||||
@@ -756,13 +843,28 @@ int gr_init(void)
|
||||
|
||||
gr_backend = backend.release();
|
||||
|
||||
int overscan_percent = android::base::GetIntProperty("ro.minui.overscan_percent", 0);
|
||||
overscan_offset_x = gr_draw->width * overscan_percent / 100;
|
||||
overscan_offset_y = gr_draw->height * overscan_percent / 100;
|
||||
|
||||
gr_flip();
|
||||
gr_flip();
|
||||
if (!gr_draw) {
|
||||
printf("gr_init: gr_draw becomes nullptr after gr_flip\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gr_rotate(DEFAULT_ROTATION);
|
||||
std::string rotation_str =
|
||||
android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
|
||||
if (rotation_str == "ROTATION_RIGHT") {
|
||||
gr_rotate(GRRotation::RIGHT);
|
||||
} else if (rotation_str == "ROTATION_DOWN") {
|
||||
gr_rotate(GRRotation::DOWN);
|
||||
} else if (rotation_str == "ROTATION_LEFT") {
|
||||
gr_rotate(GRRotation::LEFT);
|
||||
} else { // "ROTATION_NONE" or unknown string
|
||||
gr_rotate(GRRotation::NONE);
|
||||
}
|
||||
|
||||
if (gr_draw->pixel_bytes != 4) {
|
||||
printf("gr_init: Only 4-byte pixel formats supported\n");
|
||||
@@ -773,16 +875,22 @@ int gr_init(void)
|
||||
|
||||
void gr_exit() {
|
||||
delete gr_backend;
|
||||
gr_backend = nullptr;
|
||||
|
||||
delete gr_font;
|
||||
gr_font = nullptr;
|
||||
}
|
||||
|
||||
int gr_fb_width() {
|
||||
return rotation % 2 ? gr_draw->height - 2 * overscan_offset_y
|
||||
: gr_draw->width - 2 * overscan_offset_x;
|
||||
return (rotation == GRRotation::LEFT || rotation == GRRotation::RIGHT)
|
||||
? gr_draw->height - 2 * overscan_offset_y
|
||||
: gr_draw->width - 2 * overscan_offset_x;
|
||||
}
|
||||
|
||||
int gr_fb_height() {
|
||||
return rotation % 2 ? gr_draw->width - 2 * overscan_offset_x
|
||||
: gr_draw->height - 2 * overscan_offset_y;
|
||||
return (rotation == GRRotation::LEFT || rotation == GRRotation::RIGHT)
|
||||
? gr_draw->width - 2 * overscan_offset_x
|
||||
: gr_draw->height - 2 * overscan_offset_y;
|
||||
}
|
||||
|
||||
void gr_fb_blank(bool blank) {
|
||||
|
||||
+69
-50
@@ -20,6 +20,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -30,51 +31,60 @@
|
||||
|
||||
#include "minui/minui.h"
|
||||
|
||||
MinuiBackendAdf::MinuiBackendAdf()
|
||||
: intf_fd(-1), dev(), current_surface(0), n_surfaces(0), surfaces() {}
|
||||
|
||||
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;
|
||||
GRSurfaceAdf::~GRSurfaceAdf() {
|
||||
if (mmapped_buffer_) {
|
||||
munmap(mmapped_buffer_, pitch * height);
|
||||
}
|
||||
|
||||
surf->width = mode->hdisplay;
|
||||
surf->height = mode->vdisplay;
|
||||
surf->row_bytes = surf->pitch;
|
||||
surf->pixel_bytes = (format == DRM_FORMAT_RGB565) ? 2 : 4;
|
||||
|
||||
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;
|
||||
if (fence_fd != -1) {
|
||||
close(fence_fd);
|
||||
}
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<GRSurfaceAdf> GRSurfaceAdf::Create(int intf_fd, const drm_mode_modeinfo* mode,
|
||||
__u32 format, int* err) {
|
||||
__u32 offset;
|
||||
__u32 pitch;
|
||||
auto fd = adf_interface_simple_buffer_alloc(intf_fd, mode->hdisplay, mode->vdisplay, format,
|
||||
&offset, &pitch);
|
||||
|
||||
if (fd < 0) {
|
||||
*err = fd;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<GRSurfaceAdf> surf = std::unique_ptr<GRSurfaceAdf>(
|
||||
new GRSurfaceAdf(mode->hdisplay, mode->vdisplay, pitch, (format == DRM_FORMAT_RGB565 ? 2 : 4),
|
||||
offset, pitch, fd));
|
||||
|
||||
auto mmapped =
|
||||
mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset);
|
||||
if (mmapped == MAP_FAILED) {
|
||||
*err = -errno;
|
||||
return nullptr;
|
||||
}
|
||||
surf->mmapped_buffer_ = static_cast<uint8_t*>(mmapped);
|
||||
return surf;
|
||||
}
|
||||
|
||||
MinuiBackendAdf::MinuiBackendAdf() : intf_fd(-1), dev(), current_surface(0), n_surfaces(0) {}
|
||||
|
||||
int MinuiBackendAdf::InterfaceInit() {
|
||||
adf_interface_data intf_data;
|
||||
int err = adf_get_interface_data(intf_fd, &intf_data);
|
||||
if (err < 0) return err;
|
||||
if (int err = adf_get_interface_data(intf_fd, &intf_data); err < 0) return err;
|
||||
|
||||
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;
|
||||
int result = 0;
|
||||
surfaces[0] = GRSurfaceAdf::Create(intf_fd, &intf_data.current_mode, format, &result);
|
||||
if (!surfaces[0]) {
|
||||
fprintf(stderr, "Failed to allocate surface 0: %s\n", strerror(-result));
|
||||
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] = {};
|
||||
surfaces[1] = GRSurfaceAdf::Create(intf_fd, &intf_data.current_mode, format, &result);
|
||||
if (!surfaces[1]) {
|
||||
fprintf(stderr, "Failed to allocate surface 1: %s\n", strerror(-result));
|
||||
n_surfaces = 1;
|
||||
} else {
|
||||
n_surfaces = 2;
|
||||
@@ -82,7 +92,7 @@ int MinuiBackendAdf::InterfaceInit() {
|
||||
|
||||
done:
|
||||
adf_free_interface_data(&intf_data);
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
int MinuiBackendAdf::DeviceInit(adf_device* dev) {
|
||||
@@ -93,7 +103,7 @@ int MinuiBackendAdf::DeviceInit(adf_device* dev) {
|
||||
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);
|
||||
intf_fd = adf_interface_open(dev, intf_id, O_RDWR | O_CLOEXEC);
|
||||
if (intf_fd < 0) return intf_fd;
|
||||
|
||||
err = InterfaceInit();
|
||||
@@ -106,6 +116,7 @@ int MinuiBackendAdf::DeviceInit(adf_device* dev) {
|
||||
}
|
||||
|
||||
GRSurface* MinuiBackendAdf::Init() {
|
||||
<<<<<<< HEAD
|
||||
#if defined(RECOVERY_ABGR)
|
||||
format = DRM_FORMAT_ABGR8888;
|
||||
#elif defined(RECOVERY_BGRA)
|
||||
@@ -117,6 +128,18 @@ GRSurface* MinuiBackendAdf::Init() {
|
||||
#else
|
||||
format = DRM_FORMAT_RGB565;
|
||||
#endif
|
||||
=======
|
||||
PixelFormat pixel_format = gr_pixel_format();
|
||||
if (pixel_format == PixelFormat::ABGR) {
|
||||
format = DRM_FORMAT_ABGR8888;
|
||||
} else if (pixel_format == PixelFormat::BGRA) {
|
||||
format = DRM_FORMAT_BGRA8888;
|
||||
} else if (pixel_format == PixelFormat::RGBX) {
|
||||
format = DRM_FORMAT_RGBX8888;
|
||||
} else {
|
||||
format = DRM_FORMAT_RGB565;
|
||||
}
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
adf_id_t* dev_ids = nullptr;
|
||||
ssize_t n_dev_ids = adf_devices(&dev_ids);
|
||||
@@ -155,14 +178,19 @@ GRSurface* MinuiBackendAdf::Init() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
void MinuiBackendAdf::Sync(__unused GRSurfaceAdf* surf) {
|
||||
#ifdef HAS_LIBSYNC
|
||||
static constexpr unsigned int warningTimeout = 3000;
|
||||
=======
|
||||
void MinuiBackendAdf::Sync(GRSurfaceAdf* surf) {
|
||||
static constexpr unsigned int kWarningTimeout = 3000;
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
if (surf == nullptr) return;
|
||||
|
||||
if (surf->fence_fd >= 0) {
|
||||
int err = sync_wait(surf->fence_fd, warningTimeout);
|
||||
int err = sync_wait(surf->fence_fd, kWarningTimeout);
|
||||
if (err < 0) {
|
||||
perror("adf sync fence wait error\n");
|
||||
}
|
||||
@@ -174,31 +202,22 @@ void MinuiBackendAdf::Sync(__unused GRSurfaceAdf* surf) {
|
||||
}
|
||||
|
||||
GRSurface* MinuiBackendAdf::Flip() {
|
||||
GRSurfaceAdf* surf = &surfaces[current_surface];
|
||||
const auto& 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];
|
||||
Sync(surfaces[current_surface].get());
|
||||
return surfaces[current_surface].get();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
+34
-16
@@ -14,45 +14,63 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _GRAPHICS_ADF_H_
|
||||
#define _GRAPHICS_ADF_H_
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <adf/adf.h>
|
||||
|
||||
#include "graphics.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
class GRSurfaceAdf : public GRSurface {
|
||||
private:
|
||||
int fence_fd;
|
||||
int fd;
|
||||
__u32 offset;
|
||||
__u32 pitch;
|
||||
public:
|
||||
~GRSurfaceAdf() override;
|
||||
|
||||
static std::unique_ptr<GRSurfaceAdf> Create(int intf_fd, const drm_mode_modeinfo* mode,
|
||||
__u32 format, int* err);
|
||||
|
||||
uint8_t* data() override {
|
||||
return mmapped_buffer_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class MinuiBackendAdf;
|
||||
|
||||
GRSurfaceAdf(size_t width, size_t height, size_t row_bytes, size_t pixel_bytes, __u32 offset,
|
||||
__u32 pitch, int fd)
|
||||
: GRSurface(width, height, row_bytes, pixel_bytes), offset(offset), pitch(pitch), fd(fd) {}
|
||||
|
||||
const __u32 offset;
|
||||
const __u32 pitch;
|
||||
|
||||
int fd;
|
||||
int fence_fd{ -1 };
|
||||
uint8_t* mmapped_buffer_{ nullptr };
|
||||
};
|
||||
|
||||
class MinuiBackendAdf : public MinuiBackend {
|
||||
public:
|
||||
MinuiBackendAdf();
|
||||
~MinuiBackendAdf() override;
|
||||
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];
|
||||
size_t current_surface;
|
||||
size_t n_surfaces;
|
||||
std::unique_ptr<GRSurfaceAdf> surfaces[2];
|
||||
};
|
||||
|
||||
#endif // _GRAPHICS_ADF_H_
|
||||
|
||||
+140
-115
@@ -17,78 +17,44 @@
|
||||
#include "graphics_drm.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
#include "minui/minui.h"
|
||||
|
||||
#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
|
||||
|
||||
MinuiBackendDrm::MinuiBackendDrm()
|
||||
: GRSurfaceDrms(), main_monitor_crtc(nullptr), main_monitor_connector(nullptr), drm_fd(-1) {}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (ret) {
|
||||
printf("drmModeSetCrtc failed ret=%d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
void MinuiBackendDrm::Blank(bool blank) {
|
||||
if (blank) {
|
||||
DrmDisableCrtc(drm_fd, main_monitor_crtc);
|
||||
} else {
|
||||
DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]);
|
||||
}
|
||||
}
|
||||
|
||||
void MinuiBackendDrm::DrmDestroySurface(GRSurfaceDrm* surface) {
|
||||
if (!surface) return;
|
||||
|
||||
if (surface->data) {
|
||||
munmap(surface->data, surface->row_bytes * surface->height);
|
||||
GRSurfaceDrm::~GRSurfaceDrm() {
|
||||
if (mmapped_buffer_) {
|
||||
munmap(mmapped_buffer_, row_bytes * height);
|
||||
}
|
||||
|
||||
if (surface->fb_id) {
|
||||
int ret = drmModeRmFB(drm_fd, surface->fb_id);
|
||||
if (ret) {
|
||||
printf("drmModeRmFB failed ret=%d\n", ret);
|
||||
if (fb_id) {
|
||||
if (drmModeRmFB(drm_fd_, fb_id) != 0) {
|
||||
perror("Failed to drmModeRmFB");
|
||||
// Falling through to free other resources.
|
||||
}
|
||||
}
|
||||
|
||||
if (surface->handle) {
|
||||
if (handle) {
|
||||
drm_gem_close gem_close = {};
|
||||
gem_close.handle = surface->handle;
|
||||
gem_close.handle = handle;
|
||||
|
||||
int ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
|
||||
if (ret) {
|
||||
printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
|
||||
if (drmIoctl(drm_fd_, DRM_IOCTL_GEM_CLOSE, &gem_close) != 0) {
|
||||
perror("Failed to DRM_IOCTL_GEM_CLOSE");
|
||||
}
|
||||
}
|
||||
|
||||
delete surface;
|
||||
}
|
||||
|
||||
static int drm_format_to_bpp(uint32_t format) {
|
||||
@@ -108,11 +74,9 @@ static int drm_format_to_bpp(uint32_t format) {
|
||||
}
|
||||
}
|
||||
|
||||
GRSurfaceDrm* MinuiBackendDrm::DrmCreateSurface(int width, int height) {
|
||||
GRSurfaceDrm* surface = new GRSurfaceDrm;
|
||||
*surface = {};
|
||||
|
||||
std::unique_ptr<GRSurfaceDrm> GRSurfaceDrm::Create(int drm_fd, int width, int height) {
|
||||
uint32_t format;
|
||||
<<<<<<< HEAD
|
||||
#if defined(RECOVERY_ABGR)
|
||||
format = DRM_FORMAT_RGBA8888;
|
||||
#elif defined(RECOVERY_BGRA)
|
||||
@@ -124,6 +88,22 @@ GRSurfaceDrm* MinuiBackendDrm::DrmCreateSurface(int width, int height) {
|
||||
#else
|
||||
format = DRM_FORMAT_RGB565;
|
||||
#endif
|
||||
=======
|
||||
PixelFormat pixel_format = gr_pixel_format();
|
||||
// PixelFormat comes in byte order, whereas DRM_FORMAT_* uses little-endian
|
||||
// (external/libdrm/include/drm/drm_fourcc.h). Note that although drm_fourcc.h also defines a
|
||||
// macro of DRM_FORMAT_BIG_ENDIAN, it doesn't seem to be actually supported (see the discussion
|
||||
// in https://lists.freedesktop.org/archives/amd-gfx/2017-May/008560.html).
|
||||
if (pixel_format == PixelFormat::ABGR) {
|
||||
format = DRM_FORMAT_RGBA8888;
|
||||
} else if (pixel_format == PixelFormat::BGRA) {
|
||||
format = DRM_FORMAT_ARGB8888;
|
||||
} else if (pixel_format == PixelFormat::RGBX) {
|
||||
format = DRM_FORMAT_XBGR8888;
|
||||
} else {
|
||||
format = DRM_FORMAT_RGB565;
|
||||
}
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
drm_mode_create_dumb create_dumb = {};
|
||||
create_dumb.height = height;
|
||||
@@ -131,53 +111,74 @@ GRSurfaceDrm* MinuiBackendDrm::DrmCreateSurface(int width, int height) {
|
||||
create_dumb.bpp = drm_format_to_bpp(format);
|
||||
create_dumb.flags = 0;
|
||||
|
||||
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);
|
||||
if (drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) != 0) {
|
||||
perror("Failed to DRM_IOCTL_MODE_CREATE_DUMB");
|
||||
return nullptr;
|
||||
}
|
||||
surface->handle = create_dumb.handle;
|
||||
|
||||
// Cannot use std::make_unique to access non-public ctor.
|
||||
auto surface = std::unique_ptr<GRSurfaceDrm>(new GRSurfaceDrm(
|
||||
width, height, create_dumb.pitch, create_dumb.bpp / 8, drm_fd, create_dumb.handle));
|
||||
|
||||
uint32_t handles[4], pitches[4], offsets[4];
|
||||
|
||||
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);
|
||||
DrmDestroySurface(surface);
|
||||
if (drmModeAddFB2(drm_fd, width, height, format, handles, pitches, offsets, &surface->fb_id, 0) !=
|
||||
0) {
|
||||
perror("Failed to drmModeAddFB2");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
if (drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb) != 0) {
|
||||
perror("Failed to DRM_IOCTL_MODE_MAP_DUMB");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
auto mmapped = mmap(nullptr, surface->height * surface->row_bytes, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, drm_fd, map_dumb.offset);
|
||||
if (mmapped == MAP_FAILED) {
|
||||
perror("Failed to mmap()");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
surface->mmapped_buffer_ = static_cast<uint8_t*>(mmapped);
|
||||
return surface;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
bool MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc,
|
||||
const std::unique_ptr<GRSurfaceDrm>& surface) {
|
||||
if (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) != 0) {
|
||||
perror("Failed to drmModeSetCrtc");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MinuiBackendDrm::Blank(bool blank) {
|
||||
if (blank) {
|
||||
DrmDisableCrtc(drm_fd, main_monitor_crtc);
|
||||
} else {
|
||||
DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]);
|
||||
}
|
||||
}
|
||||
|
||||
static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources,
|
||||
drmModeConnector* connector) {
|
||||
// Find the encoder. If we already have one, just use it.
|
||||
@@ -259,7 +260,7 @@ drmModeConnector* MinuiBackendDrm::FindMainMonitor(int fd, drmModeRes* resources
|
||||
do {
|
||||
main_monitor_connector = find_used_connector_by_type(fd, resources, kConnectorPriority[i]);
|
||||
i++;
|
||||
} while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
|
||||
} while (!main_monitor_connector && i < arraysize(kConnectorPriority));
|
||||
|
||||
/* If we didn't find a connector, grab the first one that is connected. */
|
||||
if (!main_monitor_connector) {
|
||||
@@ -293,60 +294,53 @@ void MinuiBackendDrm::DisableNonMainCrtcs(int fd, drmModeRes* resources, drmMode
|
||||
|
||||
GRSurface* MinuiBackendDrm::Init() {
|
||||
drmModeRes* res = nullptr;
|
||||
drm_fd = -1;
|
||||
|
||||
/* 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;
|
||||
auto dev_name = android::base::StringPrintf(DRM_DEV_NAME, DRM_DIR_NAME, i);
|
||||
android::base::unique_fd fd(open(dev_name.c_str(), O_RDWR | O_CLOEXEC));
|
||||
if (fd == -1) continue;
|
||||
|
||||
drm_fd = open(dev_name, O_RDWR, 0);
|
||||
free(dev_name);
|
||||
if (drm_fd < 0) continue;
|
||||
|
||||
uint64_t cap = 0;
|
||||
/* We need dumb buffers. */
|
||||
ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
|
||||
if (ret || cap == 0) {
|
||||
close(drm_fd);
|
||||
if (uint64_t cap = 0; drmGetCap(fd.get(), DRM_CAP_DUMB_BUFFER, &cap) != 0 || cap == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
res = drmModeGetResources(drm_fd);
|
||||
res = drmModeGetResources(fd.get());
|
||||
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;
|
||||
if (find_first_connected_connector(fd.get(), res)) {
|
||||
drm_fd = fd.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
drmModeFreeResources(res);
|
||||
close(drm_fd);
|
||||
res = nullptr;
|
||||
}
|
||||
|
||||
if (drm_fd < 0 || res == nullptr) {
|
||||
perror("cannot find/open a drm device");
|
||||
if (drm_fd == -1 || res == nullptr) {
|
||||
perror("Failed to 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");
|
||||
fprintf(stderr, "Failed to find main_monitor_connector\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");
|
||||
fprintf(stderr, "Failed to find main_monitor_crtc\n");
|
||||
drmModeFreeResources(res);
|
||||
close(drm_fd);
|
||||
return nullptr;
|
||||
@@ -361,35 +355,66 @@ GRSurface* MinuiBackendDrm::Init() {
|
||||
|
||||
drmModeFreeResources(res);
|
||||
|
||||
GRSurfaceDrms[0] = DrmCreateSurface(width, height);
|
||||
GRSurfaceDrms[1] = DrmCreateSurface(width, height);
|
||||
GRSurfaceDrms[0] = GRSurfaceDrm::Create(drm_fd, width, height);
|
||||
GRSurfaceDrms[1] = GRSurfaceDrm::Create(drm_fd, 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]);
|
||||
// We will likely encounter errors in the backend functions (i.e. Flip) if EnableCrtc fails.
|
||||
if (!DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[1])) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return GRSurfaceDrms[0];
|
||||
return GRSurfaceDrms[0].get();
|
||||
}
|
||||
|
||||
static void page_flip_complete(__unused int fd,
|
||||
__unused unsigned int sequence,
|
||||
__unused unsigned int tv_sec,
|
||||
__unused unsigned int tv_usec,
|
||||
void *user_data) {
|
||||
*static_cast<bool*>(user_data) = false;
|
||||
}
|
||||
|
||||
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);
|
||||
bool ongoing_flip = true;
|
||||
if (drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, GRSurfaceDrms[current_buffer]->fb_id,
|
||||
DRM_MODE_PAGE_FLIP_EVENT, &ongoing_flip) != 0) {
|
||||
perror("Failed to drmModePageFlip");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (ongoing_flip) {
|
||||
struct pollfd fds = {
|
||||
.fd = drm_fd,
|
||||
.events = POLLIN
|
||||
};
|
||||
|
||||
if (poll(&fds, 1, -1) == -1 || !(fds.revents & POLLIN)) {
|
||||
perror("Failed to poll() on drm fd");
|
||||
break;
|
||||
}
|
||||
|
||||
drmEventContext evctx = {
|
||||
.version = DRM_EVENT_CONTEXT_VERSION,
|
||||
.page_flip_handler = page_flip_complete
|
||||
};
|
||||
|
||||
if (drmHandleEvent(drm_fd, &evctx) != 0) {
|
||||
perror("Failed to drmHandleEvent");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current_buffer = 1 - current_buffer;
|
||||
return GRSurfaceDrms[current_buffer];
|
||||
return GRSurfaceDrms[current_buffer].get();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
+33
-17
@@ -14,45 +14,61 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _GRAPHICS_DRM_H_
|
||||
#define _GRAPHICS_DRM_H_
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
#include "graphics.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
class GRSurfaceDrm : public GRSurface {
|
||||
private:
|
||||
uint32_t fb_id;
|
||||
uint32_t handle;
|
||||
public:
|
||||
~GRSurfaceDrm() override;
|
||||
|
||||
// Creates a GRSurfaceDrm instance.
|
||||
static std::unique_ptr<GRSurfaceDrm> Create(int drm_fd, int width, int height);
|
||||
|
||||
uint8_t* data() override {
|
||||
return mmapped_buffer_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class MinuiBackendDrm;
|
||||
|
||||
GRSurfaceDrm(size_t width, size_t height, size_t row_bytes, size_t pixel_bytes, int drm_fd,
|
||||
uint32_t handle)
|
||||
: GRSurface(width, height, row_bytes, pixel_bytes), drm_fd_(drm_fd), handle(handle) {}
|
||||
|
||||
const int drm_fd_;
|
||||
|
||||
uint32_t fb_id{ 0 };
|
||||
uint32_t handle{ 0 };
|
||||
uint8_t* mmapped_buffer_{ nullptr };
|
||||
};
|
||||
|
||||
class MinuiBackendDrm : public MinuiBackend {
|
||||
public:
|
||||
MinuiBackendDrm() = default;
|
||||
~MinuiBackendDrm() override;
|
||||
|
||||
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);
|
||||
bool DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, const std::unique_ptr<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;
|
||||
std::unique_ptr<GRSurfaceDrm> GRSurfaceDrms[2];
|
||||
int current_buffer{ 0 };
|
||||
drmModeCrtc* main_monitor_crtc{ nullptr };
|
||||
drmModeConnector* main_monitor_connector{ nullptr };
|
||||
int drm_fd{ -1 };
|
||||
};
|
||||
|
||||
#endif // _GRAPHICS_DRM_H_
|
||||
|
||||
+36
-51
@@ -26,9 +26,17 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "minui/minui.h"
|
||||
|
||||
MinuiBackendFbdev::MinuiBackendFbdev() : gr_draw(nullptr), fb_fd(-1) {}
|
||||
std::unique_ptr<GRSurfaceFbdev> GRSurfaceFbdev::Create(size_t width, size_t height,
|
||||
size_t row_bytes, size_t pixel_bytes) {
|
||||
// Cannot use std::make_unique to access non-public ctor.
|
||||
return std::unique_ptr<GRSurfaceFbdev>(new GRSurfaceFbdev(width, height, row_bytes, pixel_bytes));
|
||||
}
|
||||
|
||||
void MinuiBackendFbdev::Blank(bool blank) {
|
||||
#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
|
||||
@@ -52,12 +60,12 @@ void MinuiBackendFbdev::Blank(bool blank) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void MinuiBackendFbdev::SetDisplayedFramebuffer(unsigned n) {
|
||||
void MinuiBackendFbdev::SetDisplayedFramebuffer(size_t 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;
|
||||
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");
|
||||
}
|
||||
@@ -65,7 +73,7 @@ void MinuiBackendFbdev::SetDisplayedFramebuffer(unsigned n) {
|
||||
}
|
||||
|
||||
GRSurface* MinuiBackendFbdev::Init() {
|
||||
int fd = open("/dev/graphics/fb0", O_RDWR);
|
||||
android::base::unique_fd fd(open("/dev/graphics/fb0", O_RDWR | O_CLOEXEC));
|
||||
if (fd == -1) {
|
||||
perror("cannot open fb0");
|
||||
return nullptr;
|
||||
@@ -74,13 +82,11 @@ GRSurface* MinuiBackendFbdev::Init() {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -107,50 +113,41 @@ GRSurface* MinuiBackendFbdev::Init() {
|
||||
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);
|
||||
gr_framebuffer[0] =
|
||||
GRSurfaceFbdev::Create(vi.xres, vi.yres, fi.line_length, vi.bits_per_pixel / 8);
|
||||
gr_framebuffer[0]->buffer_ = static_cast<uint8_t*>(bits);
|
||||
memset(gr_framebuffer[0]->buffer_, 0, gr_framebuffer[0]->height * gr_framebuffer[0]->row_bytes);
|
||||
|
||||
gr_framebuffer[1] =
|
||||
GRSurfaceFbdev::Create(gr_framebuffer[0]->width, gr_framebuffer[0]->height,
|
||||
gr_framebuffer[0]->row_bytes, gr_framebuffer[0]->pixel_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;
|
||||
|
||||
gr_framebuffer[1]->buffer_ =
|
||||
gr_framebuffer[0]->buffer_ + gr_framebuffer[0]->height * gr_framebuffer[0]->row_bytes;
|
||||
} 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;
|
||||
}
|
||||
// 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.
|
||||
memory_buffer.resize(gr_framebuffer[1]->height * gr_framebuffer[1]->row_bytes);
|
||||
gr_framebuffer[1]->buffer_ = memory_buffer.data();
|
||||
}
|
||||
|
||||
memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
|
||||
fb_fd = fd;
|
||||
gr_draw = gr_framebuffer[1].get();
|
||||
memset(gr_draw->buffer_, 0, gr_draw->height * gr_draw->row_bytes);
|
||||
fb_fd = std::move(fd);
|
||||
SetDisplayedFramebuffer(0);
|
||||
|
||||
printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
|
||||
printf("framebuffer: %d (%zu x %zu)\n", fb_fd.get(), gr_draw->width, gr_draw->height);
|
||||
|
||||
Blank(true);
|
||||
Blank(false);
|
||||
@@ -160,25 +157,13 @@ GRSurface* MinuiBackendFbdev::Init() {
|
||||
|
||||
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;
|
||||
// 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].get();
|
||||
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);
|
||||
memcpy(gr_framebuffer[0]->buffer_, gr_draw->buffer_, gr_draw->height * gr_draw->row_bytes);
|
||||
}
|
||||
return gr_draw;
|
||||
}
|
||||
|
||||
MinuiBackendFbdev::~MinuiBackendFbdev() {
|
||||
close(fb_fd);
|
||||
fb_fd = -1;
|
||||
|
||||
if (!double_buffered && gr_draw) {
|
||||
free(gr_draw->data);
|
||||
free(gr_draw);
|
||||
}
|
||||
gr_draw = nullptr;
|
||||
}
|
||||
|
||||
+38
-11
@@ -14,31 +14,58 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _GRAPHICS_FBDEV_H_
|
||||
#define _GRAPHICS_FBDEV_H_
|
||||
#pragma once
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "graphics.h"
|
||||
#include "minui/minui.h"
|
||||
|
||||
class GRSurfaceFbdev : public GRSurface {
|
||||
public:
|
||||
// Creates and returns a GRSurfaceFbdev instance, or nullptr on error.
|
||||
static std::unique_ptr<GRSurfaceFbdev> Create(size_t width, size_t height, size_t row_bytes,
|
||||
size_t pixel_bytes);
|
||||
|
||||
uint8_t* data() override {
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
protected:
|
||||
using GRSurface::GRSurface;
|
||||
|
||||
private:
|
||||
friend class MinuiBackendFbdev;
|
||||
|
||||
// Points to the start of the buffer: either the mmap'd framebuffer or one allocated in-memory.
|
||||
uint8_t* buffer_{ nullptr };
|
||||
};
|
||||
|
||||
class MinuiBackendFbdev : public MinuiBackend {
|
||||
public:
|
||||
MinuiBackendFbdev() = default;
|
||||
~MinuiBackendFbdev() override = default;
|
||||
|
||||
GRSurface* Init() override;
|
||||
GRSurface* Flip() override;
|
||||
void Blank(bool) override;
|
||||
~MinuiBackendFbdev() override;
|
||||
MinuiBackendFbdev();
|
||||
|
||||
private:
|
||||
void SetDisplayedFramebuffer(unsigned n);
|
||||
void SetDisplayedFramebuffer(size_t n);
|
||||
|
||||
GRSurface gr_framebuffer[2];
|
||||
std::unique_ptr<GRSurfaceFbdev> gr_framebuffer[2];
|
||||
// Points to the current surface (i.e. one of the two gr_framebuffer's).
|
||||
GRSurfaceFbdev* gr_draw{ nullptr };
|
||||
bool double_buffered;
|
||||
GRSurface* gr_draw;
|
||||
int displayed_buffer;
|
||||
std::vector<uint8_t> memory_buffer;
|
||||
size_t displayed_buffer{ 0 };
|
||||
fb_var_screeninfo vi;
|
||||
int fb_fd;
|
||||
android::base::unique_fd fb_fd;
|
||||
};
|
||||
|
||||
#endif // _GRAPHICS_FBDEV_H_
|
||||
|
||||
+105
-18
@@ -14,27 +14,78 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _MINUI_H_
|
||||
#define _MINUI_H_
|
||||
#pragma once
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifndef TW_USE_MINUI_21
|
||||
|
||||
=======
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
>>>>>>> android-10.0.0_r25
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
//
|
||||
// Graphics.
|
||||
//
|
||||
|
||||
struct GRSurface {
|
||||
int width;
|
||||
int height;
|
||||
int row_bytes;
|
||||
int pixel_bytes;
|
||||
unsigned char* data;
|
||||
class GRSurface {
|
||||
public:
|
||||
static constexpr size_t kSurfaceDataAlignment = 8;
|
||||
|
||||
virtual ~GRSurface() = default;
|
||||
|
||||
// Creates and returns a GRSurface instance that's sufficient for storing an image of the given
|
||||
// size (i.e. row_bytes * height). The starting address of the surface data is aligned to
|
||||
// kSurfaceDataAlignment. Returns the created GRSurface instance (in std::unique_ptr), or nullptr
|
||||
// on error.
|
||||
static std::unique_ptr<GRSurface> Create(size_t width, size_t height, size_t row_bytes,
|
||||
size_t pixel_bytes);
|
||||
|
||||
// Clones the current GRSurface instance (i.e. an image).
|
||||
std::unique_ptr<GRSurface> Clone() const;
|
||||
|
||||
virtual uint8_t* data() {
|
||||
return data_.get();
|
||||
}
|
||||
|
||||
const uint8_t* data() const {
|
||||
return const_cast<const uint8_t*>(const_cast<GRSurface*>(this)->data());
|
||||
}
|
||||
|
||||
size_t data_size() const {
|
||||
return data_size_;
|
||||
}
|
||||
|
||||
size_t width;
|
||||
size_t height;
|
||||
size_t row_bytes;
|
||||
size_t pixel_bytes;
|
||||
|
||||
protected:
|
||||
GRSurface(size_t width, size_t height, size_t row_bytes, size_t pixel_bytes)
|
||||
: width(width), height(height), row_bytes(row_bytes), pixel_bytes(pixel_bytes) {}
|
||||
|
||||
private:
|
||||
// The deleter for data_, whose data is allocated via aligned_alloc(3).
|
||||
struct DataDeleter {
|
||||
void operator()(uint8_t* data) {
|
||||
free(data);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<uint8_t, DataDeleter> data_;
|
||||
size_t data_size_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GRSurface);
|
||||
};
|
||||
|
||||
struct GRFont {
|
||||
@@ -43,14 +94,27 @@ struct GRFont {
|
||||
int char_height;
|
||||
};
|
||||
|
||||
enum GRRotation {
|
||||
ROTATION_NONE = 0,
|
||||
ROTATION_RIGHT = 1,
|
||||
ROTATION_DOWN = 2,
|
||||
ROTATION_LEFT = 3,
|
||||
enum class GRRotation : int {
|
||||
NONE = 0,
|
||||
RIGHT = 1,
|
||||
DOWN = 2,
|
||||
LEFT = 3,
|
||||
};
|
||||
|
||||
enum class PixelFormat : int {
|
||||
UNKNOWN = 0,
|
||||
ABGR = 1,
|
||||
RGBX = 2,
|
||||
BGRA = 3,
|
||||
};
|
||||
|
||||
// Initializes the graphics backend and loads font file. Returns 0 on success, or -1 on error. Note
|
||||
// that the font initialization failure would be non-fatal, as caller may not need to draw any text
|
||||
// at all. Caller can check the font initialization result via gr_sys_font() as needed.
|
||||
int gr_init();
|
||||
|
||||
// Frees the allocated resources. The function is idempotent, and safe to be called if gr_init()
|
||||
// didn't finish successfully.
|
||||
void gr_exit();
|
||||
|
||||
int gr_fb_width();
|
||||
@@ -59,10 +123,12 @@ int gr_fb_height();
|
||||
void gr_flip();
|
||||
void gr_fb_blank(bool blank);
|
||||
|
||||
void gr_clear(); // clear entire surface to current color
|
||||
// Clears entire surface to current color.
|
||||
void gr_clear();
|
||||
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);
|
||||
|
||||
<<<<<<< HEAD
|
||||
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);
|
||||
@@ -70,21 +136,33 @@ int gr_measure(const char *s);
|
||||
void gr_font_size(int *x, int *y);
|
||||
void gr_set_font(__attribute__ ((unused))const char* name);
|
||||
#else
|
||||
=======
|
||||
void gr_texticon(int x, int y, const GRSurface* icon);
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
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);
|
||||
// Returns -1 if font is nullptr.
|
||||
int gr_measure(const GRFont* font, const char* s);
|
||||
<<<<<<< HEAD
|
||||
void gr_font_size(const GRFont* font, int* x, int* y);
|
||||
#endif
|
||||
=======
|
||||
// Returns -1 if font is nullptr.
|
||||
int gr_font_size(const GRFont* font, int* x, int* y);
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
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);
|
||||
void gr_blit(const GRSurface* source, int sx, int sy, int w, int h, int dx, int dy);
|
||||
unsigned int gr_get_width(const GRSurface* surface);
|
||||
unsigned int gr_get_height(const GRSurface* surface);
|
||||
|
||||
// Set rotation, flips gr_fb_width/height if 90 degree rotation difference
|
||||
// Sets rotation, flips gr_fb_width/height if 90 degree rotation difference
|
||||
void gr_rotate(GRRotation rotation);
|
||||
|
||||
// Returns the current PixelFormat being used.
|
||||
PixelFormat gr_pixel_format();
|
||||
|
||||
//
|
||||
// Input events.
|
||||
//
|
||||
@@ -104,6 +182,12 @@ using ev_set_key_callback = std::function<int(int code, int value)>;
|
||||
|
||||
#ifdef TW_USE_MINUI_WITH_OPTIONAL_TOUCH_EVENTS
|
||||
int ev_init(ev_callback input_cb, bool allow_touch_inputs = false);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
void ev_exit();
|
||||
int ev_add_fd(android::base::unique_fd&& fd, ev_callback cb);
|
||||
void ev_iterate_available_keys(const std::function<void(int)>& f);
|
||||
>>>>>>> android-10.0.0_r25
|
||||
void ev_iterate_touch_inputs(const std::function<void(int)>& action);
|
||||
#else
|
||||
int ev_init(ev_callback input_cb);
|
||||
@@ -170,6 +254,7 @@ std::vector<std::string> get_locales_in_png(const std::string& png_name);
|
||||
// Free a surface allocated by any of the res_create_*_surface()
|
||||
// functions.
|
||||
void res_free_surface(GRSurface* surface);
|
||||
<<<<<<< HEAD
|
||||
|
||||
#else //ifndef TW_USE_MINUI_21
|
||||
|
||||
@@ -263,3 +348,5 @@ void gr_clear();
|
||||
|
||||
#endif // ifndef TW_USE_MINUI_21
|
||||
#endif // ifndef _MINUI_H_
|
||||
=======
|
||||
>>>>>>> android-10.0.0_r25
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
// This class handles the PNG file parsing. It also holds the ownership of the PNG pointer and the
|
||||
// opened file pointer. Both will be destroyed / closed when this object goes out of scope.
|
||||
class PngHandler {
|
||||
public:
|
||||
// Constructs an instance by loading the PNG file from '/res/images/<name>.png', or '<name>'.
|
||||
PngHandler(const std::string& name);
|
||||
|
||||
~PngHandler();
|
||||
|
||||
png_uint_32 width() const {
|
||||
return width_;
|
||||
}
|
||||
|
||||
png_uint_32 height() const {
|
||||
return height_;
|
||||
}
|
||||
|
||||
png_byte channels() const {
|
||||
return channels_;
|
||||
}
|
||||
|
||||
int bit_depth() const {
|
||||
return bit_depth_;
|
||||
}
|
||||
|
||||
int color_type() const {
|
||||
return color_type_;
|
||||
}
|
||||
|
||||
png_structp png_ptr() const {
|
||||
return png_ptr_;
|
||||
}
|
||||
|
||||
png_infop info_ptr() const {
|
||||
return info_ptr_;
|
||||
}
|
||||
|
||||
int error_code() const {
|
||||
return error_code_;
|
||||
};
|
||||
|
||||
operator bool() const {
|
||||
return error_code_ == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
png_structp png_ptr_{ nullptr };
|
||||
png_infop info_ptr_{ nullptr };
|
||||
png_uint_32 width_;
|
||||
png_uint_32 height_;
|
||||
png_byte channels_;
|
||||
int bit_depth_;
|
||||
int color_type_;
|
||||
|
||||
// The |error_code_| is set to a negative value if an error occurs when opening the png file.
|
||||
int error_code_{ 0 };
|
||||
// After initialization, we'll keep the file pointer open before destruction of PngHandler.
|
||||
std::unique_ptr<FILE, decltype(&fclose)> png_fp_{ nullptr, fclose };
|
||||
};
|
||||
|
||||
// Overrides the default resource dir, for testing purpose.
|
||||
void res_set_resource_dir(const std::string&);
|
||||
@@ -1,54 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char *argv)
|
||||
{
|
||||
unsigned n;
|
||||
unsigned char *x;
|
||||
unsigned m;
|
||||
unsigned run_val;
|
||||
unsigned run_count;
|
||||
|
||||
n = gimp_image.width * gimp_image.height;
|
||||
m = 0;
|
||||
x = gimp_image.pixel_data;
|
||||
|
||||
printf("struct {\n");
|
||||
printf(" unsigned width;\n");
|
||||
printf(" unsigned height;\n");
|
||||
printf(" unsigned cwidth;\n");
|
||||
printf(" unsigned cheight;\n");
|
||||
printf(" unsigned char rundata[];\n");
|
||||
printf("} font = {\n");
|
||||
printf(" .width = %d,\n .height = %d,\n .cwidth = %d,\n .cheight = %d,\n", gimp_image.width, gimp_image.height,
|
||||
gimp_image.width / 96, gimp_image.height);
|
||||
printf(" .rundata = {\n");
|
||||
|
||||
run_val = (*x ? 0 : 255);
|
||||
run_count = 1;
|
||||
n--;
|
||||
x+=3;
|
||||
|
||||
while(n-- > 0) {
|
||||
unsigned val = (*x ? 0 : 255);
|
||||
x+=3;
|
||||
if((val == run_val) && (run_count < 127)) {
|
||||
run_count++;
|
||||
} else {
|
||||
eject:
|
||||
printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00));
|
||||
run_val = val;
|
||||
run_count = 1;
|
||||
m += 5;
|
||||
if(m >= 75) {
|
||||
printf("\n");
|
||||
m = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00));
|
||||
printf("\n0x00,");
|
||||
printf("\n");
|
||||
printf(" }\n};\n");
|
||||
return 0;
|
||||
}
|
||||
+115
-158
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "private/resources.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/kd.h>
|
||||
@@ -25,89 +27,68 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
<<<<<<< HEAD
|
||||
//#include <android-base/stringprintf.h> // does not exist in 6.0
|
||||
//#include <android-base/strings.h> // does not exist in 6.0
|
||||
=======
|
||||
#include <android-base/strings.h>
|
||||
>>>>>>> android-10.0.0_r25
|
||||
#include <png.h>
|
||||
|
||||
#include "minui/minui.h"
|
||||
|
||||
#define SURFACE_DATA_ALIGNMENT 8
|
||||
static std::string g_resource_dir{ "/res/images" };
|
||||
|
||||
static GRSurface* malloc_surface(size_t data_size) {
|
||||
size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT;
|
||||
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) +
|
||||
(SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT));
|
||||
return surface;
|
||||
std::unique_ptr<GRSurface> GRSurface::Create(size_t width, size_t height, size_t row_bytes,
|
||||
size_t pixel_bytes) {
|
||||
if (width == 0 || row_bytes == 0 || height == 0 || pixel_bytes == 0) return nullptr;
|
||||
if (std::numeric_limits<size_t>::max() / row_bytes < height) return nullptr;
|
||||
|
||||
// Cannot use std::make_unique to access non-public ctor.
|
||||
auto result = std::unique_ptr<GRSurface>(new GRSurface(width, height, row_bytes, pixel_bytes));
|
||||
size_t data_size = row_bytes * height;
|
||||
result->data_size_ =
|
||||
(data_size + kSurfaceDataAlignment - 1) / kSurfaceDataAlignment * kSurfaceDataAlignment;
|
||||
result->data_.reset(
|
||||
static_cast<uint8_t*>(aligned_alloc(kSurfaceDataAlignment, result->data_size_)));
|
||||
if (!result->data_) return nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
// This class handles the png file parsing. It also holds the ownership of the png pointer and the
|
||||
// opened file pointer. Both will be destroyed/closed when this object goes out of scope.
|
||||
class PngHandler {
|
||||
public:
|
||||
PngHandler(const std::string& name);
|
||||
|
||||
~PngHandler();
|
||||
|
||||
png_uint_32 width() const {
|
||||
return width_;
|
||||
}
|
||||
|
||||
png_uint_32 height() const {
|
||||
return height_;
|
||||
}
|
||||
|
||||
png_byte channels() const {
|
||||
return channels_;
|
||||
}
|
||||
|
||||
png_structp png_ptr() const {
|
||||
return png_ptr_;
|
||||
}
|
||||
|
||||
png_infop info_ptr() const {
|
||||
return info_ptr_;
|
||||
}
|
||||
|
||||
int error_code() const {
|
||||
return error_code_;
|
||||
};
|
||||
|
||||
operator bool() const {
|
||||
return error_code_ == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
png_structp png_ptr_{ nullptr };
|
||||
png_infop info_ptr_{ nullptr };
|
||||
png_uint_32 width_;
|
||||
png_uint_32 height_;
|
||||
png_byte channels_;
|
||||
|
||||
// The |error_code_| is set to a negative value if an error occurs when opening the png file.
|
||||
int error_code_;
|
||||
// After initialization, we'll keep the file pointer open before destruction of PngHandler.
|
||||
std::unique_ptr<FILE, decltype(&fclose)> png_fp_;
|
||||
};
|
||||
std::unique_ptr<GRSurface> GRSurface::Clone() const {
|
||||
auto result = GRSurface::Create(width, height, row_bytes, pixel_bytes);
|
||||
if (!result) return nullptr;
|
||||
memcpy(result->data(), data(), data_size_);
|
||||
return result;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
PngHandler::PngHandler(const std::string& name) : error_code_(0), png_fp_(nullptr, fclose) {
|
||||
char res_path[PATH_MAX];
|
||||
sprintf(res_path, "/res/images/%s.png", name.c_str());
|
||||
//std::string res_path = sprintf("/res/images/%s.png", name.c_str());
|
||||
png_fp_.reset(fopen(res_path, "rbe"));
|
||||
=======
|
||||
PngHandler::PngHandler(const std::string& name) {
|
||||
std::string res_path = g_resource_dir + "/" + name + ".png";
|
||||
png_fp_.reset(fopen(res_path.c_str(), "rbe"));
|
||||
// Try to read from |name| if the resource path does not work.
|
||||
if (!png_fp_) {
|
||||
png_fp_.reset(fopen(name.c_str(), "rbe"));
|
||||
}
|
||||
>>>>>>> android-10.0.0_r25
|
||||
if (!png_fp_) {
|
||||
error_code_ = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char header[8];
|
||||
uint8_t header[8];
|
||||
size_t bytesRead = fread(header, 1, sizeof(header), png_fp_.get());
|
||||
if (bytesRead != sizeof(header)) {
|
||||
error_code_ = -2;
|
||||
@@ -140,19 +121,17 @@ PngHandler::PngHandler(const std::string& name) : error_code_(0), png_fp_(nullpt
|
||||
png_set_sig_bytes(png_ptr_, sizeof(header));
|
||||
png_read_info(png_ptr_, info_ptr_);
|
||||
|
||||
int color_type;
|
||||
int bit_depth;
|
||||
png_get_IHDR(png_ptr_, info_ptr_, &width_, &height_, &bit_depth, &color_type, nullptr, nullptr,
|
||||
png_get_IHDR(png_ptr_, info_ptr_, &width_, &height_, &bit_depth_, &color_type_, nullptr, nullptr,
|
||||
nullptr);
|
||||
|
||||
channels_ = png_get_channels(png_ptr_, info_ptr_);
|
||||
|
||||
if (bit_depth == 8 && channels_ == 3 && color_type == PNG_COLOR_TYPE_RGB) {
|
||||
if (bit_depth_ == 8 && channels_ == 3 && color_type_ == PNG_COLOR_TYPE_RGB) {
|
||||
// 8-bit RGB images: great, nothing to do.
|
||||
} else if (bit_depth <= 8 && channels_ == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
|
||||
} else if (bit_depth_ <= 8 && channels_ == 1 && color_type_ == PNG_COLOR_TYPE_GRAY) {
|
||||
// 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
|
||||
png_set_expand_gray_1_2_4_to_8(png_ptr_);
|
||||
} else if (bit_depth <= 8 && channels_ == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
} else if (bit_depth_ <= 8 && channels_ == 1 && color_type_ == PNG_COLOR_TYPE_PALETTE) {
|
||||
// paletted images: expand to 8-bit RGB. Note that we DON'T
|
||||
// currently expand the tRNS chunk (if any) to an alpha
|
||||
// channel, because minui doesn't support alpha channels in
|
||||
@@ -160,8 +139,8 @@ PngHandler::PngHandler(const std::string& name) : error_code_(0), png_fp_(nullpt
|
||||
png_set_palette_to_rgb(png_ptr_);
|
||||
channels_ = 3;
|
||||
} else {
|
||||
fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", bit_depth,
|
||||
channels_, color_type);
|
||||
fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", bit_depth_,
|
||||
channels_, color_type_);
|
||||
error_code_ = -7;
|
||||
}
|
||||
}
|
||||
@@ -172,70 +151,49 @@ PngHandler::~PngHandler() {
|
||||
}
|
||||
}
|
||||
|
||||
// "display" surfaces are transformed into the framebuffer's required
|
||||
// pixel format (currently only RGBX is supported) at load time, so
|
||||
// gr_blit() can be nothing more than a memcpy() for each row. The
|
||||
// next two functions are the only ones that know anything about the
|
||||
// framebuffer pixel format; they need to be modified if the
|
||||
// framebuffer format changes (but nothing else should).
|
||||
// "display" surfaces are transformed into the framebuffer's required pixel format (currently only
|
||||
// RGBX is supported) at load time, so gr_blit() can be nothing more than a memcpy() for each row.
|
||||
|
||||
// Allocate and return a GRSurface* sufficient for storing an image of
|
||||
// the indicated size in the framebuffer pixel format.
|
||||
static GRSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
|
||||
GRSurface* surface = malloc_surface(width * height * 4);
|
||||
if (surface == NULL) return NULL;
|
||||
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
surface->row_bytes = width * 4;
|
||||
surface->pixel_bytes = 4;
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
// Copy 'input_row' to 'output_row', transforming it to the
|
||||
// framebuffer pixel format. The input format depends on the value of
|
||||
// 'channels':
|
||||
// Copies 'input_row' to 'output_row', transforming it to the framebuffer pixel format. The input
|
||||
// format depends on the value of 'channels':
|
||||
//
|
||||
// 1 - input is 8-bit grayscale
|
||||
// 3 - input is 24-bit RGB
|
||||
// 4 - input is 32-bit RGBA/RGBX
|
||||
//
|
||||
// 'width' is the number of pixels in the row.
|
||||
static void transform_rgb_to_draw(unsigned char* input_row,
|
||||
unsigned char* output_row,
|
||||
int channels, int width) {
|
||||
int x;
|
||||
unsigned char* ip = input_row;
|
||||
unsigned char* op = output_row;
|
||||
static void TransformRgbToDraw(const uint8_t* input_row, uint8_t* output_row, int channels,
|
||||
int width) {
|
||||
const uint8_t* ip = input_row;
|
||||
uint8_t* op = output_row;
|
||||
|
||||
switch (channels) {
|
||||
case 1:
|
||||
// expand gray level to RGBX
|
||||
for (x = 0; x < width; ++x) {
|
||||
*op++ = *ip;
|
||||
*op++ = *ip;
|
||||
*op++ = *ip;
|
||||
*op++ = 0xff;
|
||||
ip++;
|
||||
}
|
||||
break;
|
||||
switch (channels) {
|
||||
case 1:
|
||||
// expand gray level to RGBX
|
||||
for (int x = 0; x < width; ++x) {
|
||||
*op++ = *ip;
|
||||
*op++ = *ip;
|
||||
*op++ = *ip;
|
||||
*op++ = 0xff;
|
||||
ip++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// expand RGBA to RGBX
|
||||
for (x = 0; x < width; ++x) {
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
*op++ = 0xff;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
// expand RGBA to RGBX
|
||||
for (int x = 0; x < width; ++x) {
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
*op++ = 0xff;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// copy RGBA to RGBX
|
||||
memcpy(output_row, input_row, width*4);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
// copy RGBA to RGBX
|
||||
memcpy(output_row, input_row, width * 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int res_create_display_surface(const char* name, GRSurface** pSurface) {
|
||||
@@ -248,23 +206,24 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) {
|
||||
png_uint_32 width = png_handler.width();
|
||||
png_uint_32 height = png_handler.height();
|
||||
|
||||
GRSurface* surface = init_display_surface(width, height);
|
||||
auto surface = GRSurface::Create(width, height, width * 4, 4);
|
||||
if (!surface) {
|
||||
return -8;
|
||||
}
|
||||
|
||||
#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
|
||||
png_set_bgr(png_ptr);
|
||||
#endif
|
||||
|
||||
for (png_uint_32 y = 0; y < height; ++y) {
|
||||
std::vector<unsigned char> p_row(width * 4);
|
||||
png_read_row(png_ptr, p_row.data(), nullptr);
|
||||
transform_rgb_to_draw(p_row.data(), surface->data + y * surface->row_bytes,
|
||||
png_handler.channels(), width);
|
||||
PixelFormat pixel_format = gr_pixel_format();
|
||||
if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) {
|
||||
png_set_bgr(png_ptr);
|
||||
}
|
||||
|
||||
*pSurface = surface;
|
||||
for (png_uint_32 y = 0; y < height; ++y) {
|
||||
std::vector<uint8_t> p_row(width * 4);
|
||||
png_read_row(png_ptr, p_row.data(), nullptr);
|
||||
TransformRgbToDraw(p_row.data(), surface->data() + y * surface->row_bytes,
|
||||
png_handler.channels(), width);
|
||||
}
|
||||
|
||||
*pSurface = surface.release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -317,23 +276,24 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps,
|
||||
goto exit;
|
||||
}
|
||||
for (int i = 0; i < *frames; ++i) {
|
||||
surface[i] = init_display_surface(width, height / *frames);
|
||||
if (!surface[i]) {
|
||||
auto created_surface = GRSurface::Create(width, height / *frames, width * 4, 4);
|
||||
if (!created_surface) {
|
||||
result = -8;
|
||||
goto exit;
|
||||
}
|
||||
surface[i] = created_surface.release();
|
||||
}
|
||||
|
||||
#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
|
||||
png_set_bgr(png_ptr);
|
||||
#endif
|
||||
if (gr_pixel_format() == PixelFormat::ABGR || gr_pixel_format() == PixelFormat::BGRA) {
|
||||
png_set_bgr(png_ptr);
|
||||
}
|
||||
|
||||
for (png_uint_32 y = 0; y < height; ++y) {
|
||||
std::vector<unsigned char> p_row(width * 4);
|
||||
std::vector<uint8_t> p_row(width * 4);
|
||||
png_read_row(png_ptr, p_row.data(), nullptr);
|
||||
int frame = y % *frames;
|
||||
unsigned char* out_row = surface[frame]->data + (y / *frames) * surface[frame]->row_bytes;
|
||||
transform_rgb_to_draw(p_row.data(), out_row, png_handler.channels(), width);
|
||||
uint8_t* out_row = surface[frame]->data() + (y / *frames) * surface[frame]->row_bytes;
|
||||
TransformRgbToDraw(p_row.data(), out_row, png_handler.channels(), width);
|
||||
}
|
||||
|
||||
*pSurface = surface;
|
||||
@@ -370,29 +330,30 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) {
|
||||
png_uint_32 width = png_handler.width();
|
||||
png_uint_32 height = png_handler.height();
|
||||
|
||||
GRSurface* surface = malloc_surface(width * height);
|
||||
auto surface = GRSurface::Create(width, height, width, 1);
|
||||
if (!surface) {
|
||||
return -8;
|
||||
}
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
surface->row_bytes = width;
|
||||
surface->pixel_bytes = 1;
|
||||
|
||||
#if defined(RECOVERY_ABGR) || defined(RECOVERY_BGRA)
|
||||
png_set_bgr(png_ptr);
|
||||
#endif
|
||||
PixelFormat pixel_format = gr_pixel_format();
|
||||
if (pixel_format == PixelFormat::ABGR || pixel_format == PixelFormat::BGRA) {
|
||||
png_set_bgr(png_ptr);
|
||||
}
|
||||
|
||||
for (png_uint_32 y = 0; y < height; ++y) {
|
||||
unsigned char* p_row = surface->data + y * surface->row_bytes;
|
||||
uint8_t* p_row = surface->data() + y * surface->row_bytes;
|
||||
png_read_row(png_ptr, p_row, nullptr);
|
||||
}
|
||||
|
||||
*pSurface = surface;
|
||||
*pSurface = surface.release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void res_set_resource_dir(const std::string& dirname) {
|
||||
g_resource_dir = dirname;
|
||||
}
|
||||
|
||||
// This function tests if a locale string stored in PNG (prefix) matches
|
||||
// the locale string provided by the system (locale).
|
||||
bool matches_locale(const std::string& prefix, const std::string& locale) {
|
||||
@@ -430,7 +391,7 @@ std::vector<std::string> get_locales_in_png(const std::string& png_name) {
|
||||
}
|
||||
|
||||
std::vector<std::string> result;
|
||||
std::vector<unsigned char> row(png_handler.width());
|
||||
std::vector<uint8_t> row(png_handler.width());
|
||||
for (png_uint_32 y = 0; y < png_handler.height(); ++y) {
|
||||
png_read_row(png_handler.png_ptr(), row.data(), nullptr);
|
||||
int h = (row[3] << 8) | row[2];
|
||||
@@ -466,7 +427,7 @@ int res_create_localized_alpha_surface(const char* name,
|
||||
png_uint_32 height = png_handler.height();
|
||||
|
||||
for (png_uint_32 y = 0; y < height; ++y) {
|
||||
std::vector<unsigned char> row(width);
|
||||
std::vector<uint8_t> row(width);
|
||||
png_read_row(png_ptr, row.data(), nullptr);
|
||||
int w = (row[1] << 8) | row[0];
|
||||
int h = (row[3] << 8) | row[2];
|
||||
@@ -476,21 +437,17 @@ int res_create_localized_alpha_surface(const char* name,
|
||||
if (y + 1 + h >= height || matches_locale(loc, locale)) {
|
||||
printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
|
||||
|
||||
GRSurface* surface = malloc_surface(w * h);
|
||||
auto surface = GRSurface::Create(w, h, w, 1);
|
||||
if (!surface) {
|
||||
return -8;
|
||||
}
|
||||
surface->width = w;
|
||||
surface->height = h;
|
||||
surface->row_bytes = w;
|
||||
surface->pixel_bytes = 1;
|
||||
|
||||
for (int i = 0; i < h; ++i, ++y) {
|
||||
png_read_row(png_ptr, row.data(), nullptr);
|
||||
memcpy(surface->data + i * w, row.data(), w);
|
||||
memcpy(surface->data() + i * w, row.data(), w);
|
||||
}
|
||||
|
||||
*pSurface = surface;
|
||||
*pSurface = surface.release();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (C) 2019 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.
|
||||
//
|
||||
|
||||
cc_binary {
|
||||
name: "misc_writer",
|
||||
vendor: true,
|
||||
|
||||
srcs: [
|
||||
"misc_writer.cpp",
|
||||
],
|
||||
|
||||
cpp_std: "experimental",
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libbootloader_message_vendor",
|
||||
"libfstab",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 <getopt.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <bootloader_message/bootloader_message.h>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
static std::vector<uint8_t> ParseHexString(std::string_view hex_string) {
|
||||
auto length = hex_string.size();
|
||||
if (length % 2 != 0 || length == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<uint8_t> result(length / 2);
|
||||
for (size_t i = 0; i < length / 2; i++) {
|
||||
auto sub = "0x" + std::string(hex_string.substr(i * 2, 2));
|
||||
if (!android::base::ParseUint(sub, &result[i])) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int Usage(std::string_view name) {
|
||||
std::cerr << name << " usage:\n";
|
||||
std::cerr << name << " [--vendor-space-offset <offset>] --hex-string 0xABCDEF\n";
|
||||
std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc "
|
||||
"partition. Offset defaults to 0 if unspecified.\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// misc_writer is a vendor tool that writes data to the vendor space in /misc.
|
||||
int main(int argc, char** argv) {
|
||||
constexpr struct option OPTIONS[] = {
|
||||
{ "vendor-space-offset", required_argument, nullptr, 0 },
|
||||
{ "hex-string", required_argument, nullptr, 0 },
|
||||
{ nullptr, 0, nullptr, 0 },
|
||||
};
|
||||
|
||||
// Offset defaults to 0 if unspecified.
|
||||
size_t offset = 0;
|
||||
std::string_view hex_string;
|
||||
|
||||
int arg;
|
||||
int option_index;
|
||||
while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
|
||||
if (arg != 0) {
|
||||
LOG(ERROR) << "Invalid command argument";
|
||||
return Usage(argv[0]);
|
||||
}
|
||||
auto option_name = OPTIONS[option_index].name;
|
||||
if (option_name == "vendor-space-offset"s) {
|
||||
if (!android::base::ParseUint(optarg, &offset)) {
|
||||
LOG(ERROR) << "Failed to parse the offset: " << optarg;
|
||||
return Usage(argv[0]);
|
||||
}
|
||||
} else if (option_name == "hex-string"s) {
|
||||
hex_string = optarg;
|
||||
}
|
||||
}
|
||||
|
||||
if (hex_string.starts_with("0x") || hex_string.starts_with("0X")) {
|
||||
hex_string = hex_string.substr(2);
|
||||
}
|
||||
if (hex_string.empty()) {
|
||||
LOG(ERROR) << "Invalid input hex string: " << hex_string;
|
||||
return Usage(argv[0]);
|
||||
}
|
||||
|
||||
auto data = ParseHexString(hex_string);
|
||||
if (data.empty()) {
|
||||
LOG(ERROR) << "Failed to parse the input hex string: " << hex_string;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (std::string err; !WriteMiscPartitionVendorSpace(data.data(), data.size(), offset, &err)) {
|
||||
LOG(ERROR) << "Failed to write to misc partition: " << err;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// 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.
|
||||
|
||||
cc_library_static {
|
||||
name: "libotafault",
|
||||
|
||||
host_supported: true,
|
||||
|
||||
srcs: [
|
||||
"config.cpp",
|
||||
"ota_io.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
"libziparchive",
|
||||
],
|
||||
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wthread-safety",
|
||||
"-Wthread-safety-negative",
|
||||
],
|
||||
|
||||
target: {
|
||||
darwin: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "otafault_test",
|
||||
|
||||
srcs: ["test.cpp"],
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
|
||||
static_executable: true,
|
||||
|
||||
static_libs: [
|
||||
"libotafault",
|
||||
"libziparchive",
|
||||
"libbase",
|
||||
"liblog",
|
||||
],
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "otafault/config.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#include "otafault/ota_io.h"
|
||||
|
||||
#define OTAIO_MAX_FNAME_SIZE 128
|
||||
|
||||
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(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 == 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];
|
||||
}
|
||||
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() {
|
||||
return should_fault_inject(OTAIO_CACHE);
|
||||
}
|
||||
|
||||
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);
|
||||
ZipString zip_type_path(type_path.c_str());
|
||||
ZipEntry entry;
|
||||
if (FindEntry(archive, zip_type_path, &entry) != 0) {
|
||||
return {};
|
||||
}
|
||||
ExtractToMemory(archive, &entry, reinterpret_cast<uint8_t*>(&fname[0]), OTAIO_MAX_FNAME_SIZE);
|
||||
return fname;
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Read configuration files in the OTA package to determine which files, if any, will trigger
|
||||
* errors.
|
||||
*
|
||||
* OTA packages can be modified to trigger errors by adding a top-level directory called
|
||||
* .libotafault, which may optionally contain up to three files called READ, WRITE, and FSYNC.
|
||||
* Each one of these optional files contains the name of a single file on the device disk which
|
||||
* will cause an IO error on the first call of the appropriate I/O action to that file.
|
||||
*
|
||||
* Example:
|
||||
* ota.zip
|
||||
* <normal package contents>
|
||||
* .libotafault
|
||||
* WRITE
|
||||
*
|
||||
* If the contents of the file WRITE were /system/build.prop, the first write action to
|
||||
* /system/build.prop would fail with EIO. Note that READ and FSYNC files are absent, so these
|
||||
* actions will not cause an error.
|
||||
*/
|
||||
|
||||
#ifndef _UPDATER_OTA_IO_CFG_H_
|
||||
#define _UPDATER_OTA_IO_CFG_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <ziparchive/zip_archive.h>
|
||||
|
||||
#define OTAIO_BASE_DIR ".libotafault"
|
||||
#define OTAIO_READ "READ"
|
||||
#define OTAIO_WRITE "WRITE"
|
||||
#define OTAIO_FSYNC "FSYNC"
|
||||
#define OTAIO_CACHE "CACHE"
|
||||
|
||||
/*
|
||||
* Initialize libotafault by providing a reference to the OTA package.
|
||||
*/
|
||||
void ota_io_init(ZipArchiveHandle zip, bool retry);
|
||||
|
||||
/*
|
||||
* Return true if a config file is present for the given IO type.
|
||||
*/
|
||||
bool should_fault_inject(const char* io_type);
|
||||
|
||||
/*
|
||||
* Return true if an EIO should occur on the next hit to /cache/saved.file
|
||||
* instead of the next hit to the specified file.
|
||||
*/
|
||||
bool should_hit_cache();
|
||||
|
||||
/*
|
||||
* Return the name of the file that should cause an error for the
|
||||
* given IO type.
|
||||
*/
|
||||
std::string fault_fname(const char* io_type);
|
||||
|
||||
#endif
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Provide a series of proxy functions for basic file accessors.
|
||||
* The behavior of these functions can be changed to return different
|
||||
* errors under a variety of conditions.
|
||||
*/
|
||||
|
||||
#ifndef _UPDATER_OTA_IO_H_
|
||||
#define _UPDATER_OTA_IO_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h> // mode_t
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#define OTAIO_CACHE_FNAME "/cache/saved.file"
|
||||
|
||||
void ota_set_fault_files();
|
||||
|
||||
int ota_open(const char* path, int oflags);
|
||||
|
||||
int ota_open(const char* path, int oflags, mode_t mode);
|
||||
|
||||
FILE* ota_fopen(const char* filename, const char* mode);
|
||||
|
||||
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);
|
||||
|
||||
size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
|
||||
|
||||
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
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "otafault/ota_io.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/thread_annotations.h>
|
||||
|
||||
#include "otafault/config.h"
|
||||
|
||||
static std::mutex filename_mutex;
|
||||
static std::map<intptr_t, const char*> filename_cache GUARDED_BY(filename_mutex);
|
||||
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, 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));
|
||||
}
|
||||
|
||||
void ota_set_fault_files() {
|
||||
if (should_fault_inject(OTAIO_READ)) {
|
||||
read_fault_file_name = fault_fname(OTAIO_READ);
|
||||
}
|
||||
if (should_fault_inject(OTAIO_WRITE)) {
|
||||
write_fault_file_name = fault_fname(OTAIO_WRITE);
|
||||
}
|
||||
if (should_fault_inject(OTAIO_FSYNC)) {
|
||||
fsync_fault_file_name = fault_fname(OTAIO_FSYNC);
|
||||
}
|
||||
}
|
||||
|
||||
bool have_eio_error = false;
|
||||
|
||||
int ota_open(const char* path, int oflags) {
|
||||
// Let the caller handle errors; we do not care if open succeeds or fails
|
||||
int fd = open(path, oflags);
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
filename_cache[fd] = path;
|
||||
return fd;
|
||||
}
|
||||
|
||||
int ota_open(const char* path, int oflags, mode_t mode) {
|
||||
int fd = open(path, oflags, mode);
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
filename_cache[fd] = path;
|
||||
return fd;
|
||||
}
|
||||
|
||||
FILE* ota_fopen(const char* path, const char* mode) {
|
||||
FILE* fh = fopen(path, mode);
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
filename_cache[(intptr_t)fh] = path;
|
||||
return fh;
|
||||
}
|
||||
|
||||
static int __ota_close(int fd) {
|
||||
// descriptors can be reused, so make sure not to leave them in the cache
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
filename_cache.erase(fd);
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
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) {
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
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)) {
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
auto cached = filename_cache.find((intptr_t)stream);
|
||||
const char* cached_path = cached->second;
|
||||
if (cached != filename_cache.end() &&
|
||||
get_hit_file(cached_path, read_fault_file_name)) {
|
||||
read_fault_file_name = "";
|
||||
errno = EIO;
|
||||
have_eio_error = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
ssize_t ota_read(int fd, void* buf, size_t nbyte) {
|
||||
if (should_fault_inject(OTAIO_READ)) {
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
auto cached = filename_cache.find(fd);
|
||||
const char* cached_path = cached->second;
|
||||
if (cached != filename_cache.end()
|
||||
&& get_hit_file(cached_path, read_fault_file_name)) {
|
||||
read_fault_file_name = "";
|
||||
errno = EIO;
|
||||
have_eio_error = true;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ssize_t status = read(fd, buf, nbyte);
|
||||
if (status == -1 && errno == EIO) {
|
||||
have_eio_error = true;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) {
|
||||
if (should_fault_inject(OTAIO_WRITE)) {
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
auto cached = filename_cache.find((intptr_t)stream);
|
||||
const char* cached_path = cached->second;
|
||||
if (cached != filename_cache.end() &&
|
||||
get_hit_file(cached_path, write_fault_file_name)) {
|
||||
write_fault_file_name = "";
|
||||
errno = EIO;
|
||||
have_eio_error = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
size_t status = fwrite(ptr, size, count, stream);
|
||||
if (status != count && errno == EIO) {
|
||||
have_eio_error = true;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
ssize_t ota_write(int fd, const void* buf, size_t nbyte) {
|
||||
if (should_fault_inject(OTAIO_WRITE)) {
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
auto cached = filename_cache.find(fd);
|
||||
const char* cached_path = cached->second;
|
||||
if (cached != filename_cache.end() &&
|
||||
get_hit_file(cached_path, write_fault_file_name)) {
|
||||
write_fault_file_name = "";
|
||||
errno = EIO;
|
||||
have_eio_error = true;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ssize_t status = write(fd, buf, nbyte);
|
||||
if (status == -1 && errno == EIO) {
|
||||
have_eio_error = true;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int ota_fsync(int fd) {
|
||||
if (should_fault_inject(OTAIO_FSYNC)) {
|
||||
std::lock_guard<std::mutex> lock(filename_mutex);
|
||||
auto cached = filename_cache.find(fd);
|
||||
const char* cached_path = cached->second;
|
||||
if (cached != filename_cache.end() &&
|
||||
get_hit_file(cached_path, fsync_fault_file_name)) {
|
||||
fsync_fault_file_name = "";
|
||||
errno = EIO;
|
||||
have_eio_error = true;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
int status = fsync(fd);
|
||||
if (status == -1 && errno == EIO) {
|
||||
have_eio_error = true;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
+40
-8
@@ -16,28 +16,60 @@ cc_library_static {
|
||||
name: "libotautil",
|
||||
|
||||
host_supported: true,
|
||||
recovery_available: true,
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
// Minimal set of files to support host build.
|
||||
srcs: [
|
||||
<<<<<<< HEAD
|
||||
"SysUtil.cpp",
|
||||
"DirUtil.cpp",
|
||||
"ZipUtil.cpp",
|
||||
"ThermalUtil.cpp",
|
||||
"cache_location.cpp",
|
||||
=======
|
||||
"paths.cpp",
|
||||
>>>>>>> android-10.0.0_r25
|
||||
"rangeset.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libselinux",
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-D_FILE_OFFSET_BITS=64",
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
],
|
||||
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
|
||||
target: {
|
||||
android: {
|
||||
srcs: [
|
||||
"dirutil.cpp",
|
||||
"logging.cpp",
|
||||
"mounts.cpp",
|
||||
"parse_install_logs.cpp",
|
||||
"roots.cpp",
|
||||
"sysutil.cpp",
|
||||
"thermalutil.cpp",
|
||||
],
|
||||
|
||||
include_dirs: [
|
||||
"system/vold",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libfstab",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libcutils",
|
||||
"libext4_utils",
|
||||
"libfs_mgr",
|
||||
"libselinux",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "otautil/DirUtil.h"
|
||||
#include "otautil/dirutil.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
* Use this to keep track of mapped segments.
|
||||
*/
|
||||
class MemMapping {
|
||||
public:
|
||||
~MemMapping();
|
||||
// Map a file into a private, read-only memory segment. If 'filename' begins with an '@'
|
||||
// character, it is a map of blocks to be mapped, otherwise it is treated as an ordinary file.
|
||||
bool MapFile(const std::string& filename);
|
||||
size_t ranges() const {
|
||||
return ranges_.size();
|
||||
};
|
||||
|
||||
unsigned char* addr; // start of data
|
||||
size_t length; // length of data
|
||||
|
||||
private:
|
||||
struct MappedRange {
|
||||
void* addr;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
bool MapBlockFile(const std::string& filename);
|
||||
bool MapFD(int fd);
|
||||
|
||||
std::vector<MappedRange> ranges_;
|
||||
};
|
||||
|
||||
#endif // _OTAUTIL_SYSUTIL
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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_OTAUTIL_CACHE_LOCATION_H_
|
||||
#define _OTAUTIL_OTAUTIL_CACHE_LOCATION_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
// A singleton class to maintain the update related locations. The locations should be only set
|
||||
// once at the start of the program.
|
||||
class CacheLocation {
|
||||
public:
|
||||
static CacheLocation& location();
|
||||
|
||||
// getter and setter functions.
|
||||
std::string cache_temp_source() const {
|
||||
return cache_temp_source_;
|
||||
}
|
||||
void set_cache_temp_source(const std::string& temp_source) {
|
||||
cache_temp_source_ = temp_source;
|
||||
}
|
||||
|
||||
std::string last_command_file() const {
|
||||
return last_command_file_;
|
||||
}
|
||||
void set_last_command_file(const std::string& last_command) {
|
||||
last_command_file_ = last_command;
|
||||
}
|
||||
|
||||
std::string stash_directory_base() const {
|
||||
return stash_directory_base_;
|
||||
}
|
||||
void set_stash_directory_base(const std::string& base) {
|
||||
stash_directory_base_ = base;
|
||||
}
|
||||
|
||||
private:
|
||||
CacheLocation();
|
||||
DISALLOW_COPY_AND_ASSIGN(CacheLocation);
|
||||
|
||||
// When there isn't enough room on the target filesystem to hold the patched version of the file,
|
||||
// we copy the original here and delete it to free up space. If the expected source file doesn't
|
||||
// exist, or is corrupted, we look to see if the cached file contains the bits we want and use it
|
||||
// as the source instead. The default location for the cached source is "/cache/saved.file".
|
||||
std::string cache_temp_source_;
|
||||
|
||||
// Location to save the last command that stashes blocks.
|
||||
std::string last_command_file_;
|
||||
|
||||
// The base directory to write stashes during update.
|
||||
std::string stash_directory_base_;
|
||||
};
|
||||
|
||||
#endif // _OTAUTIL_OTAUTIL_CACHE_LOCATION_H_
|
||||
@@ -48,6 +48,8 @@ enum CauseCode : int {
|
||||
kRebootFailure,
|
||||
kPackageExtractFileFailure,
|
||||
kPatchApplicationFailure,
|
||||
kHashTreeComputationFailure,
|
||||
kEioFailure,
|
||||
kVendorFailure = 200
|
||||
};
|
||||
|
||||
|
||||
@@ -14,17 +14,35 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _ROTATE_LOGS_H
|
||||
#define _ROTATE_LOGS_H
|
||||
#ifndef _LOGGING_H
|
||||
#define _LOGGING_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
<<<<<<< HEAD:rotate_logs.h
|
||||
//#include <log/log_id.h>
|
||||
#include <private/android_logger.h> /* private pmsg functions */
|
||||
=======
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <log/log_id.h>
|
||||
>>>>>>> android-10.0.0_r25:otautil/include/otautil/logging.h
|
||||
|
||||
static constexpr int KEEP_LOG_COUNT = 10;
|
||||
|
||||
struct selabel_handle;
|
||||
|
||||
struct saved_log_file {
|
||||
std::string name;
|
||||
struct stat sb;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
void SetLoggingSehandle(selabel_handle* handle);
|
||||
|
||||
ssize_t logbasename(log_id_t id, char prio, const char* filename, const char* buf, size_t len,
|
||||
void* arg);
|
||||
|
||||
@@ -36,4 +54,17 @@ ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf,
|
||||
// Overwrite any existing last_log.$max and last_kmsg.$max.
|
||||
void rotate_logs(const char* last_log_file, const char* last_kmsg_file);
|
||||
|
||||
#endif //_ROTATE_LOG_H
|
||||
// In turn fflush(3)'s, fsync(3)'s and fclose(3)'s the given stream.
|
||||
void check_and_fclose(FILE* fp, const std::string& name);
|
||||
|
||||
void copy_log_file_to_pmsg(const std::string& source, const std::string& destination);
|
||||
void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle);
|
||||
void reset_tmplog_offset();
|
||||
|
||||
void save_kernel_log(const char* destination);
|
||||
|
||||
std::vector<saved_log_file> ReadLogFilesToMemory();
|
||||
|
||||
bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files);
|
||||
|
||||
#endif //_LOGGING_H
|
||||
@@ -14,8 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef MOUNTS_H_
|
||||
#define MOUNTS_H_
|
||||
#pragma once
|
||||
|
||||
struct MountedVolume;
|
||||
|
||||
@@ -24,5 +23,3 @@ bool scan_mounted_volumes();
|
||||
MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point);
|
||||
|
||||
int unmount_mounted_volume(MountedVolume* volume);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
constexpr const char* LAST_INSTALL_FILE = "/data/misc/recovery/last_install";
|
||||
constexpr const char* LAST_INSTALL_FILE_IN_CACHE = "/cache/recovery/last_install";
|
||||
|
||||
// Parses the metrics of update applied under recovery mode in |lines|, and returns a map with
|
||||
// "name: value".
|
||||
std::map<std::string, int64_t> ParseRecoveryUpdateMetrics(const std::vector<std::string>& lines);
|
||||
// Parses the sideload history and update metrics in the last_install file. Returns a map with
|
||||
// entries as "metrics_name: value". If no such file exists, returns an empty map.
|
||||
std::map<std::string, int64_t> ParseLastInstall(const std::string& file_name);
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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_PATHS_H_
|
||||
#define _OTAUTIL_PATHS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
|
||||
// A singleton class to maintain the update related paths. The paths should be only set once at the
|
||||
// start of the program.
|
||||
class Paths {
|
||||
public:
|
||||
static Paths& Get();
|
||||
|
||||
std::string cache_log_directory() const {
|
||||
return cache_log_directory_;
|
||||
}
|
||||
void set_cache_log_directory(const std::string& log_dir) {
|
||||
cache_log_directory_ = log_dir;
|
||||
}
|
||||
|
||||
std::string cache_temp_source() const {
|
||||
return cache_temp_source_;
|
||||
}
|
||||
void set_cache_temp_source(const std::string& temp_source) {
|
||||
cache_temp_source_ = temp_source;
|
||||
}
|
||||
|
||||
std::string last_command_file() const {
|
||||
return last_command_file_;
|
||||
}
|
||||
void set_last_command_file(const std::string& last_command_file) {
|
||||
last_command_file_ = last_command_file;
|
||||
}
|
||||
|
||||
std::string resource_dir() const {
|
||||
return resource_dir_;
|
||||
}
|
||||
void set_resource_dir(const std::string& resource_dir) {
|
||||
resource_dir_ = resource_dir;
|
||||
}
|
||||
|
||||
std::string stash_directory_base() const {
|
||||
return stash_directory_base_;
|
||||
}
|
||||
void set_stash_directory_base(const std::string& base) {
|
||||
stash_directory_base_ = base;
|
||||
}
|
||||
|
||||
std::string temporary_install_file() const {
|
||||
return temporary_install_file_;
|
||||
}
|
||||
void set_temporary_install_file(const std::string& install_file) {
|
||||
temporary_install_file_ = install_file;
|
||||
}
|
||||
|
||||
std::string temporary_log_file() const {
|
||||
return temporary_log_file_;
|
||||
}
|
||||
void set_temporary_log_file(const std::string& log_file) {
|
||||
temporary_log_file_ = log_file;
|
||||
}
|
||||
|
||||
std::string temporary_update_binary() const {
|
||||
return temporary_update_binary_;
|
||||
}
|
||||
void set_temporary_update_binary(const std::string& update_binary) {
|
||||
temporary_update_binary_ = update_binary;
|
||||
}
|
||||
|
||||
private:
|
||||
Paths();
|
||||
DISALLOW_COPY_AND_ASSIGN(Paths);
|
||||
|
||||
// Path to the directory that contains last_log and last_kmsg log files.
|
||||
std::string cache_log_directory_;
|
||||
|
||||
// Path to the temporary source file on /cache. When there isn't enough room on the target
|
||||
// filesystem to hold the patched version of the file, we copy the original here and delete it to
|
||||
// free up space. If the expected source file doesn't exist, or is corrupted, we look to see if
|
||||
// the cached file contains the bits we want and use it as the source instead.
|
||||
std::string cache_temp_source_;
|
||||
|
||||
// Path to the last command file.
|
||||
std::string last_command_file_;
|
||||
|
||||
// Path to the resource dir;
|
||||
std::string resource_dir_;
|
||||
|
||||
// Path to the base directory to write stashes during update.
|
||||
std::string stash_directory_base_;
|
||||
|
||||
// Path to the temporary file that contains the install result.
|
||||
std::string temporary_install_file_;
|
||||
|
||||
// Path to the temporary log file while under recovery.
|
||||
std::string temporary_log_file_;
|
||||
|
||||
// Path to the temporary update binary while installing a non-A/B package.
|
||||
std::string temporary_update_binary_;
|
||||
};
|
||||
|
||||
#endif // _OTAUTIL_PATHS_H_
|
||||
@@ -14,12 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef RECOVERY_ROOTS_H_
|
||||
#define RECOVERY_ROOTS_H_
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
typedef struct fstab_rec Volume;
|
||||
#include <fstab/fstab.h>
|
||||
|
||||
using Volume = android::fs_mgr::FstabEntry;
|
||||
|
||||
typedef struct fstab_rec Volume;
|
||||
|
||||
@@ -31,28 +32,30 @@ Volume* volume_for_mount_point(const std::string& mount_point);
|
||||
|
||||
// Make sure that the volume 'path' is on is mounted. Returns 0 on
|
||||
// success (volume is mounted).
|
||||
int ensure_path_mounted(const char* path);
|
||||
int ensure_path_mounted(const std::string& path);
|
||||
|
||||
// Similar to ensure_path_mounted, but allows one to specify the mount_point.
|
||||
int ensure_path_mounted_at(const char* path, const char* mount_point);
|
||||
int ensure_path_mounted_at(const std::string& path, const std::string& mount_point);
|
||||
|
||||
// Make sure that the volume 'path' is on is unmounted. Returns 0 on
|
||||
// success (volume is unmounted);
|
||||
int ensure_path_unmounted(const char* path);
|
||||
int ensure_path_unmounted(const std::string& path);
|
||||
|
||||
// Reformat the given volume (must be the mount point only, eg
|
||||
// "/cache"), no paths permitted. Attempts to unmount the volume if
|
||||
// it is mounted.
|
||||
int format_volume(const char* volume);
|
||||
int format_volume(const std::string& volume);
|
||||
|
||||
// Reformat the given volume (must be the mount point only, eg
|
||||
// "/cache"), no paths permitted. Attempts to unmount the volume if
|
||||
// it is mounted.
|
||||
// Copies 'directory' to root of the newly formatted volume
|
||||
int format_volume(const char* volume, const char* directory);
|
||||
int format_volume(const std::string& volume, const std::string& directory);
|
||||
|
||||
// Ensure that all and only the volumes that packages expect to find
|
||||
// mounted (/tmp and /cache) are mounted. Returns 0 on success.
|
||||
int setup_install_mounts();
|
||||
|
||||
#endif // RECOVERY_ROOTS_H_
|
||||
bool logical_partitions_mapped();
|
||||
|
||||
std::string get_system_root();
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rangeset.h"
|
||||
|
||||
// This class holds the content of a block map file.
|
||||
class BlockMapData {
|
||||
public:
|
||||
// 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 BlockMapData ParseBlockMapFile(const std::string& block_map_path);
|
||||
|
||||
explicit operator bool() const {
|
||||
return !path_.empty();
|
||||
}
|
||||
|
||||
std::string path() const {
|
||||
return path_;
|
||||
}
|
||||
uint64_t file_size() const {
|
||||
return file_size_;
|
||||
}
|
||||
uint32_t block_size() const {
|
||||
return block_size_;
|
||||
}
|
||||
RangeSet block_ranges() const {
|
||||
return block_ranges_;
|
||||
}
|
||||
|
||||
private:
|
||||
BlockMapData() = default;
|
||||
|
||||
BlockMapData(const std::string& path, uint64_t file_size, uint32_t block_size,
|
||||
RangeSet block_ranges)
|
||||
: path_(path),
|
||||
file_size_(file_size),
|
||||
block_size_(block_size),
|
||||
block_ranges_(std::move(block_ranges)) {}
|
||||
|
||||
std::string path_;
|
||||
uint64_t file_size_ = 0;
|
||||
uint32_t block_size_ = 0;
|
||||
RangeSet block_ranges_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Use this to keep track of mapped segments.
|
||||
*/
|
||||
class MemMapping {
|
||||
public:
|
||||
~MemMapping();
|
||||
// Map a file into a private, read-only memory segment. If 'filename' begins with an '@'
|
||||
// character, it is a map of blocks to be mapped, otherwise it is treated as an ordinary file.
|
||||
bool MapFile(const std::string& filename);
|
||||
size_t ranges() const {
|
||||
return ranges_.size();
|
||||
};
|
||||
|
||||
unsigned char* addr; // start of data
|
||||
size_t length; // length of data
|
||||
|
||||
private:
|
||||
struct MappedRange {
|
||||
void* addr;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
bool MapBlockFile(const std::string& filename);
|
||||
bool MapFD(int fd);
|
||||
|
||||
std::vector<MappedRange> ranges_;
|
||||
};
|
||||
|
||||
// Wrapper function to trigger a reboot, by additionally handling quiescent reboot mode. The
|
||||
// command should start with "reboot," (e.g. "reboot,bootloader" or "reboot,").
|
||||
bool reboot(const std::string& command);
|
||||
|
||||
// Returns a null-terminated char* array, where the elements point to the C-strings in the given
|
||||
// vector, plus an additional nullptr at the end. This is a helper function that facilitates
|
||||
// calling C functions (such as getopt(3)) that expect an array of C-strings.
|
||||
std::vector<char*> StringVectorToNullTerminatedArray(const std::vector<std::string>& args);
|
||||
|
||||
#endif // _OTAUTIL_SYSUTIL
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* 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 "otautil/logging.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/klog.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <private/android_filesystem_config.h> /* for AID_SYSTEM */
|
||||
#include <private/android_logger.h> /* private pmsg functions */
|
||||
#include <selinux/label.h>
|
||||
|
||||
#include "otautil/dirutil.h"
|
||||
#include "otautil/paths.h"
|
||||
#include "otautil/roots.h"
|
||||
|
||||
constexpr const char* LOG_FILE = "/cache/recovery/log";
|
||||
constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
|
||||
constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
|
||||
constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
|
||||
|
||||
constexpr const char* LAST_KMSG_FILTER = "recovery/last_kmsg";
|
||||
constexpr const char* LAST_LOG_FILTER = "recovery/last_log";
|
||||
|
||||
constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
|
||||
|
||||
static struct selabel_handle* logging_sehandle;
|
||||
|
||||
void SetLoggingSehandle(selabel_handle* handle) {
|
||||
logging_sehandle = handle;
|
||||
}
|
||||
|
||||
// fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the
|
||||
// file pointer, or nullptr on error.
|
||||
static FILE* fopen_path(const std::string& path, const char* mode, const selabel_handle* sehandle) {
|
||||
if (ensure_path_mounted(path) != 0) {
|
||||
LOG(ERROR) << "Can't mount " << path;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// When writing, try to create the containing directory, if necessary. Use generous permissions,
|
||||
// the system (init.rc) will reset them.
|
||||
if (strchr("wa", mode[0])) {
|
||||
mkdir_recursively(path, 0777, true, sehandle);
|
||||
}
|
||||
return fopen(path.c_str(), mode);
|
||||
}
|
||||
|
||||
void check_and_fclose(FILE* fp, const std::string& name) {
|
||||
fflush(fp);
|
||||
if (fsync(fileno(fp)) == -1) {
|
||||
PLOG(ERROR) << "Failed to fsync " << name;
|
||||
}
|
||||
if (ferror(fp)) {
|
||||
PLOG(ERROR) << "Error in " << name;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// close a file, log an error if the error indicator is set
|
||||
ssize_t logbasename(log_id_t /* id */, char /* prio */, const char* filename, const char* /* buf */,
|
||||
size_t len, void* arg) {
|
||||
bool* do_rotate = static_cast<bool*>(arg);
|
||||
if (std::string(LAST_KMSG_FILTER).find(filename) != std::string::npos ||
|
||||
std::string(LAST_LOG_FILTER).find(filename) != std::string::npos) {
|
||||
*do_rotate = true;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf, size_t len,
|
||||
void* arg) {
|
||||
bool* do_rotate = static_cast<bool*>(arg);
|
||||
if (!*do_rotate) {
|
||||
return __android_log_pmsg_file_write(id, prio, filename, buf, len);
|
||||
}
|
||||
|
||||
std::string name(filename);
|
||||
size_t dot = name.find_last_of('.');
|
||||
std::string sub = name.substr(0, dot);
|
||||
|
||||
if (std::string(LAST_KMSG_FILTER).find(sub) == std::string::npos &&
|
||||
std::string(LAST_LOG_FILTER).find(sub) == std::string::npos) {
|
||||
return __android_log_pmsg_file_write(id, prio, filename, buf, len);
|
||||
}
|
||||
|
||||
// filename rotation
|
||||
if (dot == std::string::npos) {
|
||||
name += ".1";
|
||||
} else {
|
||||
std::string number = name.substr(dot + 1);
|
||||
if (!isdigit(number[0])) {
|
||||
name += ".1";
|
||||
} else {
|
||||
size_t i;
|
||||
if (!android::base::ParseUint(number, &i)) {
|
||||
LOG(ERROR) << "failed to parse uint in " << number;
|
||||
return -1;
|
||||
}
|
||||
name = sub + "." + std::to_string(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return __android_log_pmsg_file_write(id, prio, name.c_str(), buf, len);
|
||||
}
|
||||
|
||||
// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max.
|
||||
// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max.
|
||||
// Overwrite any existing last_log.$max and last_kmsg.$max.
|
||||
void rotate_logs(const char* last_log_file, const char* last_kmsg_file) {
|
||||
// Logs should only be rotated once.
|
||||
static bool rotated = false;
|
||||
if (rotated) {
|
||||
return;
|
||||
}
|
||||
rotated = true;
|
||||
|
||||
for (int i = KEEP_LOG_COUNT - 1; i >= 0; --i) {
|
||||
std::string old_log = android::base::StringPrintf("%s", last_log_file);
|
||||
if (i > 0) {
|
||||
old_log += "." + std::to_string(i);
|
||||
}
|
||||
std::string new_log = android::base::StringPrintf("%s.%d", last_log_file, i + 1);
|
||||
// Ignore errors if old_log doesn't exist.
|
||||
rename(old_log.c_str(), new_log.c_str());
|
||||
|
||||
std::string old_kmsg = android::base::StringPrintf("%s", last_kmsg_file);
|
||||
if (i > 0) {
|
||||
old_kmsg += "." + std::to_string(i);
|
||||
}
|
||||
std::string new_kmsg = android::base::StringPrintf("%s.%d", last_kmsg_file, i + 1);
|
||||
rename(old_kmsg.c_str(), new_kmsg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Writes content to the current pmsg session.
|
||||
static ssize_t __pmsg_write(const std::string& filename, const std::string& buf) {
|
||||
return __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filename.c_str(),
|
||||
buf.data(), buf.size());
|
||||
}
|
||||
|
||||
void copy_log_file_to_pmsg(const std::string& source, const std::string& destination) {
|
||||
std::string content;
|
||||
android::base::ReadFileToString(source, &content);
|
||||
__pmsg_write(destination, content);
|
||||
}
|
||||
|
||||
// How much of the temp log we have copied to the copy in cache.
|
||||
static off_t tmplog_offset = 0;
|
||||
|
||||
void reset_tmplog_offset() {
|
||||
tmplog_offset = 0;
|
||||
}
|
||||
|
||||
static void copy_log_file(const std::string& source, const std::string& destination, bool append,
|
||||
const selabel_handle* sehandle) {
|
||||
FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", sehandle);
|
||||
if (dest_fp == nullptr) {
|
||||
PLOG(ERROR) << "Can't open " << destination;
|
||||
} else {
|
||||
FILE* source_fp = fopen(source.c_str(), "re");
|
||||
if (source_fp != nullptr) {
|
||||
if (append) {
|
||||
fseeko(source_fp, tmplog_offset, SEEK_SET); // Since last write
|
||||
}
|
||||
char buf[4096];
|
||||
size_t bytes;
|
||||
while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
|
||||
fwrite(buf, 1, bytes, dest_fp);
|
||||
}
|
||||
if (append) {
|
||||
tmplog_offset = ftello(source_fp);
|
||||
}
|
||||
check_and_fclose(source_fp, source);
|
||||
}
|
||||
check_and_fclose(dest_fp, destination);
|
||||
}
|
||||
}
|
||||
|
||||
void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle) {
|
||||
// We only rotate and record the log of the current session if explicitly requested. This usually
|
||||
// happens after wipes, installation from BCB or menu selections. This is to avoid unnecessary
|
||||
// rotation (and possible deletion) of log files, if it does not do anything loggable.
|
||||
if (!save_current_log) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always write to pmsg, this allows the OTA logs to be caught in `logcat -L`.
|
||||
copy_log_file_to_pmsg(Paths::Get().temporary_log_file(), LAST_LOG_FILE);
|
||||
copy_log_file_to_pmsg(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE);
|
||||
|
||||
// We can do nothing for now if there's no /cache partition.
|
||||
if (!has_cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensure_path_mounted(LAST_LOG_FILE);
|
||||
ensure_path_mounted(LAST_KMSG_FILE);
|
||||
rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE);
|
||||
|
||||
// Copy logs to cache so the system can find out what happened.
|
||||
copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true, sehandle);
|
||||
copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false, sehandle);
|
||||
copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false, sehandle);
|
||||
save_kernel_log(LAST_KMSG_FILE);
|
||||
chmod(LOG_FILE, 0600);
|
||||
chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM);
|
||||
chmod(LAST_KMSG_FILE, 0600);
|
||||
chown(LAST_KMSG_FILE, AID_SYSTEM, AID_SYSTEM);
|
||||
chmod(LAST_LOG_FILE, 0640);
|
||||
chmod(LAST_INSTALL_FILE, 0644);
|
||||
chown(LAST_INSTALL_FILE, AID_SYSTEM, AID_SYSTEM);
|
||||
sync();
|
||||
}
|
||||
|
||||
// Read from kernel log into buffer and write out to file.
|
||||
void save_kernel_log(const char* destination) {
|
||||
int klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0);
|
||||
if (klog_buf_len <= 0) {
|
||||
PLOG(ERROR) << "Error getting klog size";
|
||||
return;
|
||||
}
|
||||
|
||||
std::string buffer(klog_buf_len, 0);
|
||||
int n = klogctl(KLOG_READ_ALL, &buffer[0], klog_buf_len);
|
||||
if (n == -1) {
|
||||
PLOG(ERROR) << "Error in reading klog";
|
||||
return;
|
||||
}
|
||||
buffer.resize(n);
|
||||
android::base::WriteStringToFile(buffer, destination);
|
||||
}
|
||||
|
||||
std::vector<saved_log_file> ReadLogFilesToMemory() {
|
||||
ensure_path_mounted("/cache");
|
||||
|
||||
struct dirent* de;
|
||||
std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
|
||||
if (!d) {
|
||||
if (errno != ENOENT) {
|
||||
PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<saved_log_file> log_files;
|
||||
while ((de = readdir(d.get())) != nullptr) {
|
||||
if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
|
||||
std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
|
||||
|
||||
struct stat sb;
|
||||
if (stat(path.c_str(), &sb) != 0) {
|
||||
PLOG(ERROR) << "Failed to stat " << path;
|
||||
continue;
|
||||
}
|
||||
// Truncate files to 512kb
|
||||
size_t read_size = std::min<size_t>(sb.st_size, 1 << 19);
|
||||
std::string data(read_size, '\0');
|
||||
|
||||
android::base::unique_fd log_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY)));
|
||||
if (log_fd == -1 || !android::base::ReadFully(log_fd, data.data(), read_size)) {
|
||||
PLOG(ERROR) << "Failed to read log file " << path;
|
||||
continue;
|
||||
}
|
||||
|
||||
log_files.emplace_back(saved_log_file{ path, sb, data });
|
||||
}
|
||||
}
|
||||
|
||||
return log_files;
|
||||
}
|
||||
|
||||
bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files) {
|
||||
// Re-create the log dir and write back the log entries.
|
||||
if (ensure_path_mounted(CACHE_LOG_DIR) != 0) {
|
||||
PLOG(ERROR) << "Failed to mount " << CACHE_LOG_DIR;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mkdir_recursively(CACHE_LOG_DIR, 0777, false, logging_sehandle) != 0) {
|
||||
PLOG(ERROR) << "Failed to create " << CACHE_LOG_DIR;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& log : log_files) {
|
||||
if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
|
||||
log.sb.st_gid)) {
|
||||
PLOG(ERROR) << "Failed to write to " << log.name;
|
||||
}
|
||||
}
|
||||
|
||||
// Any part of the log we'd copied to cache is now gone.
|
||||
// Reset the pointer so we copy from the beginning of the temp
|
||||
// log.
|
||||
reset_tmplog_offset();
|
||||
copy_logs(true /* save_current_log */, true /* has_cache */, logging_sehandle);
|
||||
|
||||
return true;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user