Merge tag 'android-10.0.0_r25' into aosp10-4

Android 10.0.0 release 25
This commit is contained in:
bigbiff
2020-03-23 09:56:16 -04:00
785 changed files with 22968 additions and 9422 deletions
+30
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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/
+133
View File
@@ -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
-2
View File
@@ -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
View File
@@ -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(), &copy_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;
}
+4 -7
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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
+2 -8
View File
@@ -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
View File
@@ -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;
}
+2 -2
View File
@@ -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
View File
@@ -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;
+70 -23
View File
@@ -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
+37
View File
@@ -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",
],
}
-33
View File
@@ -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)
+22 -4
View File
@@ -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,
}
+61 -2
View File
@@ -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>
+15
View File
@@ -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
View File
@@ -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;
}
}
-110
View File
@@ -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
View File
@@ -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) {
+9 -12
View File
@@ -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
View File
@@ -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
View File
@@ -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}
+81
View File
@@ -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;
}
+24
View File
@@ -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);
+151
View File
@@ -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
-83
View File
@@ -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;
}
+41
View File
@@ -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",
],
}
+71
View File
@@ -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));
+72
View File
@@ -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
-43
View File
@@ -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_
+83
View File
@@ -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",
],
}
+390
View File
@@ -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) {
+206
View File
@@ -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);
+71
View File
@@ -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);
+53
View File
@@ -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_;
};
+101
View File
@@ -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
+30
View File
@@ -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 {
+39
View File
@@ -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
View File
@@ -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) {
+262
View File
@@ -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_;
}
+468
View File
@@ -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;
}
+123
View File
@@ -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;
}
+112
View File
@@ -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",
],
}
+7 -24
View File
@@ -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
View File
@@ -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
+13 -18
View File
@@ -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
View File
@@ -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
View File
@@ -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
+25
View File
@@ -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);
+213
View File
@@ -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), "");
}
+64
View File
@@ -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));
+47
View File
@@ -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
View File
@@ -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) {
-214
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+87
View File
@@ -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&);
-54
View File
@@ -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
View File
@@ -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;
}
+40
View File
@@ -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",
],
}
+106
View File
@@ -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;
}
-68
View File
@@ -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",
],
}
-75
View File
@@ -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;
}
-72
View File
@@ -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
-70
View File
@@ -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
-212
View File
@@ -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
View File
@@ -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",
],
},
},
}
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
#include "otautil/DirUtil.h"
#include "otautil/dirutil.h"
#include <dirent.h>
#include <errno.h>
-53
View File
@@ -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
-69
View File
@@ -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_
+2
View File
@@ -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);
+118
View File
@@ -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_
+12 -9
View File
@@ -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();
+113
View File
@@ -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
+325
View File
@@ -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