Merge "Add recoverable GWP-ASan."
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 23f01d1..22bd0e7 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -40,15 +40,6 @@
chown system log /data/misc/bootstat/time_since_last_boot
# end ota transitional support
-# Record the time at which the user has successfully entered the pin to decrypt
-# the device, /data is decrypted, and the system is entering the main boot phase.
-#
-# post-fs-data: /data is writable
-# property:init.svc.bootanim=running: The boot animation is running
-# property:ro.crypto.type=block: FDE device
-on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
- exec_background - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
-
# Initialize bootstat state machine.
#
# sys.bootstat.first_boot_completed: responsible for making sure that record_boot_complete happens
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f904d48..895c111 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -36,6 +36,7 @@
#include <string>
#include <thread>
+#include <android/dlext.h>
#include <android/fdsan.h>
#include <android/set_abort_message.h>
#include <bionic/malloc.h>
@@ -1963,7 +1964,7 @@
ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
}
-static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+static std::string GetTestLibraryPath() {
std::string test_lib(testing::internal::GetArgvs()[0]);
auto const value = test_lib.find_last_of('/');
if (value == std::string::npos) {
@@ -1971,7 +1972,62 @@
} else {
test_lib = test_lib.substr(0, value + 1) + "./";
}
- test_lib += "libcrash_test.so";
+ return test_lib + "libcrash_test.so";
+}
+
+static void CreateEmbeddedLibrary(int out_fd) {
+ std::string test_lib(GetTestLibraryPath());
+ android::base::unique_fd fd(open(test_lib.c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_NE(fd.get(), -1);
+ off_t file_size = lseek(fd, 0, SEEK_END);
+ ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
+ std::vector<uint8_t> contents(file_size);
+ ASSERT_TRUE(android::base::ReadFully(fd, contents.data(), contents.size()));
+
+ // Put the shared library data at a pagesize() offset.
+ ASSERT_EQ(lseek(out_fd, 4 * getpagesize(), SEEK_CUR), 4 * getpagesize());
+ ASSERT_EQ(static_cast<size_t>(write(out_fd, contents.data(), contents.size())), contents.size());
+}
+
+TEST_F(CrasherTest, non_zero_offset_in_library) {
+ int intercept_result;
+ unique_fd output_fd;
+ TemporaryFile tf;
+ CreateEmbeddedLibrary(tf.fd);
+ StartProcess([&tf]() {
+ android_dlextinfo extinfo{};
+ extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
+ extinfo.library_fd = tf.fd;
+ extinfo.library_fd_offset = 4 * getpagesize();
+ void* handle = android_dlopen_ext(tf.path, RTLD_NOW, &extinfo);
+ if (handle == nullptr) {
+ _exit(1);
+ }
+ void (*crash_func)() = reinterpret_cast<void (*)()>(dlsym(handle, "crash"));
+ if (crash_func == nullptr) {
+ _exit(1);
+ }
+ crash_func();
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ // Verify the crash includes an offset value in the backtrace.
+ std::string match_str = android::base::StringPrintf("%s\\!libcrash_test.so \\(offset 0x%x\\)",
+ tf.path, 4 * getpagesize());
+ ASSERT_MATCH(result, match_str);
+}
+
+static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+ std::string test_lib(GetTestLibraryPath());
*tmp_so_name = std::string(tmp_dir) + "/libcrash_test.so";
std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 28154a7..e4d68f8 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -176,8 +176,14 @@
build_id = StringPrintf(" (BuildId: %s)", frame.build_id().c_str());
}
- CB(should_log, " #%02d pc %0*" PRIx64 " %s%s%s", index++, pointer_width(tombstone) * 2,
- frame.rel_pc(), frame.file_name().c_str(), function.c_str(), build_id.c_str());
+ std::string line =
+ StringPrintf(" #%02d pc %0*" PRIx64 " %s", index++, pointer_width(tombstone) * 2,
+ frame.rel_pc(), frame.file_name().c_str());
+ if (frame.file_map_offset() != 0) {
+ line += StringPrintf(" (offset 0x%" PRIx64 ")", frame.file_map_offset());
+ }
+ line += function + build_id;
+ CB(should_log, "%s", line.c_str());
}
}
@@ -303,86 +309,8 @@
}
}
-static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
- const Thread& thread) {
+static void print_memory_maps(CallbackType callback, const Tombstone& tombstone) {
int word_size = pointer_width(tombstone);
- print_thread_header(callback, tombstone, thread, true);
-
- const Signal& signal_info = tombstone.signal_info();
- std::string sender_desc;
-
- if (signal_info.has_sender()) {
- sender_desc =
- StringPrintf(" from pid %d, uid %d", signal_info.sender_pid(), signal_info.sender_uid());
- }
-
- if (!tombstone.has_signal_info()) {
- CBL("signal information missing");
- } else {
- std::string fault_addr_desc;
- if (signal_info.has_fault_address()) {
- fault_addr_desc = StringPrintf("0x%0*" PRIx64, 2 * word_size, signal_info.fault_address());
- } else {
- fault_addr_desc = "--------";
- }
-
- CBL("signal %d (%s), code %d (%s%s), fault addr %s", signal_info.number(),
- signal_info.name().c_str(), signal_info.code(), signal_info.code_name().c_str(),
- sender_desc.c_str(), fault_addr_desc.c_str());
- }
-
- if (tombstone.causes_size() == 1) {
- CBL("Cause: %s", tombstone.causes(0).human_readable().c_str());
- }
-
- if (!tombstone.abort_message().empty()) {
- CBL("Abort message: '%s'", tombstone.abort_message().c_str());
- }
-
- print_thread_registers(callback, tombstone, thread, true);
- print_thread_backtrace(callback, tombstone, thread, true);
-
- if (tombstone.causes_size() > 1) {
- CBS("");
- CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
- "order of likelihood.");
- }
-
- for (const Cause& cause : tombstone.causes()) {
- if (tombstone.causes_size() > 1) {
- CBS("");
- CBL("Cause: %s", cause.human_readable().c_str());
- }
-
- if (cause.has_memory_error() && cause.memory_error().has_heap()) {
- const HeapObject& heap_object = cause.memory_error().heap();
-
- if (heap_object.deallocation_backtrace_size() != 0) {
- CBS("");
- CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
- print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), true);
- }
-
- if (heap_object.allocation_backtrace_size() != 0) {
- CBS("");
- CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
- print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), true);
- }
- }
- }
-
- print_tag_dump(callback, tombstone);
-
- print_thread_memory_dump(callback, tombstone, thread);
-
- CBS("");
-
- // No memory maps to print.
- if (tombstone.memory_mappings().empty()) {
- CBS("No memory maps found");
- return;
- }
-
const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
if (word_size == 8) {
uint64_t top = ptr >> 32;
@@ -397,6 +325,7 @@
StringPrintf("memory map (%d %s):", tombstone.memory_mappings().size(),
tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
+ const Signal& signal_info = tombstone.signal_info();
bool has_fault_address = signal_info.has_fault_address();
uint64_t fault_address = untag_address(signal_info.fault_address());
bool preamble_printed = false;
@@ -456,6 +385,106 @@
}
}
+static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
+ const Thread& thread) {
+ print_thread_header(callback, tombstone, thread, true);
+
+ const Signal& signal_info = tombstone.signal_info();
+ std::string sender_desc;
+
+ if (signal_info.has_sender()) {
+ sender_desc =
+ StringPrintf(" from pid %d, uid %d", signal_info.sender_pid(), signal_info.sender_uid());
+ }
+
+ bool is_async_mte_crash = false;
+ bool is_mte_crash = false;
+ if (!tombstone.has_signal_info()) {
+ CBL("signal information missing");
+ } else {
+ std::string fault_addr_desc;
+ if (signal_info.has_fault_address()) {
+ fault_addr_desc =
+ StringPrintf("0x%0*" PRIx64, 2 * pointer_width(tombstone), signal_info.fault_address());
+ } else {
+ fault_addr_desc = "--------";
+ }
+
+ CBL("signal %d (%s), code %d (%s%s), fault addr %s", signal_info.number(),
+ signal_info.name().c_str(), signal_info.code(), signal_info.code_name().c_str(),
+ sender_desc.c_str(), fault_addr_desc.c_str());
+#ifdef SEGV_MTEAERR
+ is_async_mte_crash = signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTEAERR;
+ is_mte_crash = is_async_mte_crash ||
+ (signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTESERR);
+#endif
+ }
+
+ if (tombstone.causes_size() == 1) {
+ CBL("Cause: %s", tombstone.causes(0).human_readable().c_str());
+ }
+
+ if (!tombstone.abort_message().empty()) {
+ CBL("Abort message: '%s'", tombstone.abort_message().c_str());
+ }
+
+ print_thread_registers(callback, tombstone, thread, true);
+ if (is_async_mte_crash) {
+ CBL("Note: This crash is a delayed async MTE crash. Memory corruption has occurred");
+ CBL(" in this process. The stack trace below is the first system call or context");
+ CBL(" switch that was executed after the memory corruption happened.");
+ }
+ print_thread_backtrace(callback, tombstone, thread, true);
+
+ if (tombstone.causes_size() > 1) {
+ CBS("");
+ CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
+ "order of likelihood.");
+ }
+
+ for (const Cause& cause : tombstone.causes()) {
+ if (tombstone.causes_size() > 1) {
+ CBS("");
+ CBL("Cause: %s", cause.human_readable().c_str());
+ }
+
+ if (cause.has_memory_error() && cause.memory_error().has_heap()) {
+ const HeapObject& heap_object = cause.memory_error().heap();
+
+ if (heap_object.deallocation_backtrace_size() != 0) {
+ CBS("");
+ CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
+ print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), true);
+ }
+
+ if (heap_object.allocation_backtrace_size() != 0) {
+ CBS("");
+ CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
+ print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), true);
+ }
+ }
+ }
+
+ print_tag_dump(callback, tombstone);
+
+ if (is_mte_crash) {
+ CBS("");
+ CBL("Learn more about MTE reports: "
+ "https://source.android.com/docs/security/test/memory-safety/mte-reports");
+ }
+
+ print_thread_memory_dump(callback, tombstone, thread);
+
+ CBS("");
+
+ // No memory maps to print.
+ if (!tombstone.memory_mappings().empty()) {
+ print_memory_maps(callback, tombstone);
+ } else {
+ CBS("No memory maps found");
+ }
+}
+
void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {
for (const auto& buffer : tombstone.log_buffers()) {
if (tail) {
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 765174b..9512e7e 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -170,7 +170,7 @@
"android.hardware.fastboot@1.1",
"android.hardware.fastboot-V1-ndk",
"android.hardware.health@2.0",
- "android.hardware.health-V1-ndk",
+ "android.hardware.health-V2-ndk",
"libasyncio",
"libbase",
"libbinder_ndk",
@@ -286,6 +286,7 @@
"fastboot.cpp",
"fs.cpp",
"socket.cpp",
+ "super_flash_helper.cpp",
"tcp.cpp",
"udp.cpp",
"util.cpp",
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 9676f87..8f7cced 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -53,6 +53,7 @@
#include <android-base/endian.h>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <android-base/parsenetaddress.h>
@@ -62,6 +63,7 @@
#include <build/version.h>
#include <libavb/libavb.h>
#include <liblp/liblp.h>
+#include <liblp/super_layout_builder.h>
#include <platform_tools_version.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -71,6 +73,7 @@
#include "diagnose_usb.h"
#include "fastboot_driver.h"
#include "fs.h"
+#include "super_flash_helper.h"
#include "tcp.h"
#include "transport.h"
#include "udp.h"
@@ -113,7 +116,7 @@
struct fastboot_buffer {
enum fb_buffer_type type;
- void* data;
+ std::vector<SparsePtr> files;
int64_t sz;
unique_fd fd;
int64_t image_size;
@@ -246,14 +249,6 @@
fprintf(stderr, "(bootloader) %s\n", info.c_str());
}
-static int64_t get_file_size(borrowed_fd fd) {
- struct stat sb;
- if (fstat(fd.get(), &sb) == -1) {
- die("could not get file size");
- }
- return sb.st_size;
-}
-
bool ReadFileToVector(const std::string& file, std::vector<char>* out) {
out->clear();
@@ -824,27 +819,32 @@
fprintf(stderr, "--------------------------------------------\n");
}
-static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
- struct sparse_file* s = sparse_file_import_auto(fd, false, true);
- if (!s) die("cannot sparse read file");
-
+static std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size) {
if (max_size <= 0 || max_size > std::numeric_limits<uint32_t>::max()) {
die("invalid max size %" PRId64, max_size);
}
- int files = sparse_file_resparse(s, max_size, nullptr, 0);
+ const int files = sparse_file_resparse(s, max_size, nullptr, 0);
if (files < 0) die("Failed to resparse");
- sparse_file** out_s =
- reinterpret_cast<sparse_file**>(calloc(sizeof(struct sparse_file*), files + 1));
- if (!out_s) die("Failed to allocate sparse file array");
+ auto temp = std::make_unique<sparse_file*[]>(files);
+ const int rv = sparse_file_resparse(s, max_size, temp.get(), files);
+ if (rv < 0) die("Failed to resparse");
- files = sparse_file_resparse(s, max_size, out_s, files);
- if (files < 0) die("Failed to resparse");
-
+ std::vector<SparsePtr> out_s;
+ for (int i = 0; i < files; i++) {
+ out_s.emplace_back(temp[i], sparse_file_destroy);
+ }
return out_s;
}
+static std::vector<SparsePtr> load_sparse_files(int fd, int64_t max_size) {
+ SparsePtr s(sparse_file_import_auto(fd, false, true), sparse_file_destroy);
+ if (!s) die("cannot sparse read file");
+
+ return resparse_file(s.get(), max_size);
+}
+
static uint64_t get_uint_var(const char* var_name) {
std::string value_str;
if (fb->GetVar(var_name, &value_str) != fastboot::SUCCESS || value_str.empty()) {
@@ -903,15 +903,13 @@
int64_t limit = get_sparse_limit(sz);
buf->fd = std::move(fd);
if (limit) {
- sparse_file** s = load_sparse_files(buf->fd.get(), limit);
- if (s == nullptr) {
+ buf->files = load_sparse_files(buf->fd.get(), limit);
+ if (buf->files.empty()) {
return false;
}
buf->type = FB_BUFFER_SPARSE;
- buf->data = s;
} else {
buf->type = FB_BUFFER_FD;
- buf->data = nullptr;
buf->sz = sz;
}
@@ -996,6 +994,8 @@
fb->GetVar("partition-type:vbmeta_b", &partition_type) == fastboot::SUCCESS;
}
+// Note: this only works in userspace fastboot. In the bootloader, use
+// should_flash_in_userspace().
static bool is_logical(const std::string& partition) {
std::string value;
return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
@@ -1078,9 +1078,16 @@
lseek(buf->fd.get(), 0, SEEK_SET);
}
-static void flash_buf(const std::string& partition, struct fastboot_buffer* buf) {
- sparse_file** s;
+static void flash_partition_files(const std::string& partition,
+ const std::vector<SparsePtr>& files) {
+ for (size_t i = 0; i < files.size(); i++) {
+ sparse_file* s = files[i].get();
+ int64_t sz = sparse_file_len(s, true, false);
+ fb->FlashPartition(partition, s, sz, i + 1, files.size());
+ }
+}
+static void flash_buf(const std::string& partition, struct fastboot_buffer* buf) {
if (partition == "boot" || partition == "boot_a" || partition == "boot_b" ||
partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b" ||
partition == "recovery" || partition == "recovery_a" || partition == "recovery_b") {
@@ -1103,18 +1110,7 @@
switch (buf->type) {
case FB_BUFFER_SPARSE: {
- std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
- s = reinterpret_cast<sparse_file**>(buf->data);
- while (*s) {
- int64_t sz = sparse_file_len(*s, true, false);
- sparse_files.emplace_back(*s, sz);
- ++s;
- }
-
- for (size_t i = 0; i < sparse_files.size(); ++i) {
- const auto& pair = sparse_files[i];
- fb->FlashPartition(partition, pair.first, pair.second, i + 1, sparse_files.size());
- }
+ flash_partition_files(partition, buf->files);
break;
}
case FB_BUFFER_FD:
@@ -1402,13 +1398,6 @@
}
}
-class ImageSource {
- public:
- virtual ~ImageSource(){};
- virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
- virtual unique_fd OpenFile(const std::string& name) const = 0;
-};
-
class FlashAllTool {
public:
FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary,
@@ -1418,20 +1407,30 @@
private:
void CheckRequirements();
- void DetermineSecondarySlot();
+ void DetermineSlot();
void CollectImages();
void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
void UpdateSuperPartition();
+ bool OptimizedFlashSuper();
+
+ // If the image uses the default slot, or the user specified "all", then
+ // the paired string will be empty. If the image requests a specific slot
+ // (for example, system_other) it is specified instead.
+ using ImageEntry = std::pair<const Image*, std::string>;
+
+ std::string GetPartitionName(const ImageEntry& entry);
const ImageSource& source_;
std::string slot_override_;
bool skip_secondary_;
bool wipe_;
bool force_flash_;
+ std::string current_slot_;
std::string secondary_slot_;
- std::vector<std::pair<const Image*, std::string>> boot_images_;
- std::vector<std::pair<const Image*, std::string>> os_images_;
+
+ std::vector<ImageEntry> boot_images_;
+ std::vector<ImageEntry> os_images_;
};
FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override,
@@ -1454,7 +1453,7 @@
set_active(slot_override_);
}
- DetermineSecondarySlot();
+ DetermineSlot();
CollectImages();
CancelSnapshotIfNeeded();
@@ -1463,24 +1462,92 @@
// or in bootloader fastboot.
FlashImages(boot_images_);
- // Sync the super partition. This will reboot to userspace fastboot if needed.
- UpdateSuperPartition();
+ if (!OptimizedFlashSuper()) {
+ // Sync the super partition. This will reboot to userspace fastboot if needed.
+ UpdateSuperPartition();
- // Resize any logical partition to 0, so each partition is reset to 0
- // extents, and will achieve more optimal allocation.
- for (const auto& [image, slot] : os_images_) {
- auto resize_partition = [](const std::string& partition) -> void {
- if (is_logical(partition)) {
- fb->ResizePartition(partition, "0");
- }
- };
- do_for_partitions(image->part_name, slot, resize_partition, false);
+ // Resize any logical partition to 0, so each partition is reset to 0
+ // extents, and will achieve more optimal allocation.
+ for (const auto& [image, slot] : os_images_) {
+ auto resize_partition = [](const std::string& partition) -> void {
+ if (is_logical(partition)) {
+ fb->ResizePartition(partition, "0");
+ }
+ };
+ do_for_partitions(image->part_name, slot, resize_partition, false);
+ }
}
// Flash OS images, resizing logical partitions as needed.
FlashImages(os_images_);
}
+bool FlashAllTool::OptimizedFlashSuper() {
+ if (!supports_AB()) {
+ LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
+ return false;
+ }
+ if (slot_override_ == "all") {
+ LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
+ return false;
+ }
+
+ // Does this device use dynamic partitions at all?
+ unique_fd fd = source_.OpenFile("super_empty.img");
+ if (fd < 0) {
+ LOG(VERBOSE) << "could not open super_empty.img";
+ return false;
+ }
+
+ // Try to find whether there is a super partition.
+ std::string super_name;
+ if (fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
+ super_name = "super";
+ }
+ std::string partition_size_str;
+ if (fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
+ LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
+ return false;
+ }
+
+ SuperFlashHelper helper(source_);
+ if (!helper.Open(fd)) {
+ return false;
+ }
+
+ for (const auto& entry : os_images_) {
+ auto partition = GetPartitionName(entry);
+ auto image = entry.first;
+
+ if (!helper.AddPartition(partition, image->img_name, image->optional_if_no_image)) {
+ return false;
+ }
+ }
+
+ auto s = helper.GetSparseLayout();
+ if (!s) {
+ return false;
+ }
+
+ std::vector<SparsePtr> files;
+ if (int limit = get_sparse_limit(sparse_file_len(s.get(), false, false))) {
+ files = resparse_file(s.get(), limit);
+ } else {
+ files.emplace_back(std::move(s));
+ }
+
+ // Send the data to the device.
+ flash_partition_files(super_name, files);
+
+ // Remove images that we already flashed, just in case we have non-dynamic OS images.
+ auto remove_if_callback = [&, this](const ImageEntry& entry) -> bool {
+ return helper.WillFlash(GetPartitionName(entry));
+ };
+ os_images_.erase(std::remove_if(os_images_.begin(), os_images_.end(), remove_if_callback),
+ os_images_.end());
+ return true;
+}
+
void FlashAllTool::CheckRequirements() {
std::vector<char> contents;
if (!source_.ReadFile("android-info.txt", &contents)) {
@@ -1489,7 +1556,13 @@
::CheckRequirements({contents.data(), contents.size()}, force_flash_);
}
-void FlashAllTool::DetermineSecondarySlot() {
+void FlashAllTool::DetermineSlot() {
+ if (slot_override_.empty()) {
+ current_slot_ = get_current_slot();
+ } else {
+ current_slot_ = slot_override_;
+ }
+
if (skip_secondary_) {
return;
}
@@ -1588,6 +1661,20 @@
}
}
+std::string FlashAllTool::GetPartitionName(const ImageEntry& entry) {
+ auto slot = entry.second;
+ if (slot.empty()) {
+ slot = current_slot_;
+ }
+ if (slot.empty()) {
+ return entry.first->part_name;
+ }
+ if (slot == "all") {
+ LOG(FATAL) << "Cannot retrieve a singular name when using all slots";
+ }
+ return entry.first->part_name + "_" + slot;
+}
+
class ZipImageSource final : public ImageSource {
public:
explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
@@ -1782,20 +1869,7 @@
if (!metadata) {
return false;
}
- for (const auto& partition : metadata->partitions) {
- auto candidate = android::fs_mgr::GetPartitionName(partition);
- if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
- // On retrofit devices, we don't know if, or whether, the A or B
- // slot has been flashed for dynamic partitions. Instead we add
- // both names to the list as a conservative guess.
- if (candidate + "_a" == partition_name || candidate + "_b" == partition_name) {
- return true;
- }
- } else if (candidate == partition_name) {
- return true;
- }
- }
- return false;
+ return should_flash_in_userspace(*metadata.get(), partition_name);
}
static bool wipe_super(const android::fs_mgr::LpMetadata& metadata, const std::string& slot,
@@ -1868,7 +1942,19 @@
}
}
+static void FastbootLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */,
+ const char* /* tag */, const char* /* file */, unsigned int /* line */,
+ const char* message) {
+ verbose("%s", message);
+}
+
+static void FastbootAborter(const char* message) {
+ die("%s", message);
+}
+
int FastBootTool::Main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, FastbootLogger, FastbootAborter);
+
bool wants_wipe = false;
bool wants_reboot = false;
bool wants_reboot_bootloader = false;
diff --git a/fastboot/super_flash_helper.cpp b/fastboot/super_flash_helper.cpp
new file mode 100644
index 0000000..b617ce8
--- /dev/null
+++ b/fastboot/super_flash_helper.cpp
@@ -0,0 +1,125 @@
+//
+// Copyright (C) 2023 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 "super_flash_helper.h"
+
+#include <android-base/logging.h>
+
+#include "util.h"
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using android::fs_mgr::SuperImageExtent;
+
+SuperFlashHelper::SuperFlashHelper(const ImageSource& source) : source_(source) {}
+
+bool SuperFlashHelper::Open(borrowed_fd fd) {
+ if (!builder_.Open(fd)) {
+ LOG(VERBOSE) << "device does not support optimized super flashing";
+ return false;
+ }
+
+ base_metadata_ = builder_.Export();
+ return !!base_metadata_;
+}
+
+bool SuperFlashHelper::IncludeInSuper(const std::string& partition) {
+ return should_flash_in_userspace(*base_metadata_.get(), partition);
+}
+
+bool SuperFlashHelper::AddPartition(const std::string& partition, const std::string& image_name,
+ bool optional) {
+ if (!IncludeInSuper(partition)) {
+ return true;
+ }
+ auto iter = image_fds_.find(image_name);
+ if (iter == image_fds_.end()) {
+ unique_fd fd = source_.OpenFile(image_name);
+ if (fd < 0) {
+ if (!optional) {
+ LOG(VERBOSE) << "could not find partition image: " << image_name;
+ return false;
+ }
+ return true;
+ }
+ if (is_sparse_file(fd)) {
+ LOG(VERBOSE) << "cannot optimize dynamic partitions with sparse images";
+ return false;
+ }
+ iter = image_fds_.emplace(image_name, std::move(fd)).first;
+ }
+
+ if (!builder_.AddPartition(partition, image_name, get_file_size(iter->second))) {
+ return false;
+ }
+
+ will_flash_.emplace(partition);
+ return true;
+}
+
+SparsePtr SuperFlashHelper::GetSparseLayout() {
+ // Cache extents since the sparse ptr depends on data pointers.
+ if (extents_.empty()) {
+ extents_ = builder_.GetImageLayout();
+ if (extents_.empty()) {
+ LOG(VERBOSE) << "device does not support optimized super flashing";
+ return {nullptr, nullptr};
+ }
+ }
+
+ unsigned int block_size = base_metadata_->geometry.logical_block_size;
+ int64_t flashed_size = extents_.back().offset + extents_.back().size;
+ SparsePtr s(sparse_file_new(block_size, flashed_size), sparse_file_destroy);
+
+ for (const auto& extent : extents_) {
+ if (extent.offset / block_size > UINT_MAX) {
+ // Super image is too big to send via sparse files (>8TB).
+ LOG(VERBOSE) << "super image is too big to flash";
+ return {nullptr, nullptr};
+ }
+ unsigned int block = extent.offset / block_size;
+
+ int rv = 0;
+ switch (extent.type) {
+ case SuperImageExtent::Type::DONTCARE:
+ break;
+ case SuperImageExtent::Type::ZERO:
+ rv = sparse_file_add_fill(s.get(), 0, extent.size, block);
+ break;
+ case SuperImageExtent::Type::DATA:
+ rv = sparse_file_add_data(s.get(), extent.blob->data(), extent.size, block);
+ break;
+ case SuperImageExtent::Type::PARTITION: {
+ auto iter = image_fds_.find(extent.image_name);
+ if (iter == image_fds_.end()) {
+ LOG(FATAL) << "image added but not found: " << extent.image_name;
+ return {nullptr, nullptr};
+ }
+ rv = sparse_file_add_fd(s.get(), iter->second.get(), extent.image_offset,
+ extent.size, block);
+ break;
+ }
+ default:
+ LOG(VERBOSE) << "unrecognized extent type in super image layout";
+ return {nullptr, nullptr};
+ }
+ if (rv) {
+ LOG(VERBOSE) << "sparse failure building super image layout";
+ return {nullptr, nullptr};
+ }
+ }
+ return s;
+}
diff --git a/fastboot/super_flash_helper.h b/fastboot/super_flash_helper.h
new file mode 100644
index 0000000..29c15d0
--- /dev/null
+++ b/fastboot/super_flash_helper.h
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2023 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 <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <android-base/unique_fd.h>
+#include <liblp/liblp.h>
+#include <liblp/super_layout_builder.h>
+
+#include "util.h"
+
+class SuperFlashHelper final {
+ public:
+ explicit SuperFlashHelper(const ImageSource& source);
+
+ bool Open(android::base::borrowed_fd fd);
+ bool IncludeInSuper(const std::string& partition);
+ bool AddPartition(const std::string& partition, const std::string& image_name, bool optional);
+
+ // Note: the SparsePtr if non-null should not outlive SuperFlashHelper, since
+ // it depends on open fds and data pointers.
+ SparsePtr GetSparseLayout();
+
+ bool WillFlash(const std::string& partition) const {
+ return will_flash_.find(partition) != will_flash_.end();
+ }
+
+ private:
+ const ImageSource& source_;
+ android::fs_mgr::SuperLayoutBuilder builder_;
+ std::unique_ptr<android::fs_mgr::LpMetadata> base_metadata_;
+ std::vector<android::fs_mgr::SuperImageExtent> extents_;
+
+ // Cache open image fds. This keeps them alive while we flash the sparse
+ // file.
+ std::unordered_map<std::string, android::base::unique_fd> image_fds_;
+ std::unordered_set<std::string> will_flash_;
+};
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 900d6ea..ded54a5 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -30,11 +30,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
+#include <sys/stat.h>
#include <sys/time.h>
#include "util.h"
+using android::base::borrowed_fd;
+
static bool g_verbose = false;
double now() {
@@ -73,3 +75,34 @@
}
fprintf(stderr, "\n");
}
+
+bool should_flash_in_userspace(const android::fs_mgr::LpMetadata& metadata,
+ const std::string& partition_name) {
+ for (const auto& partition : metadata.partitions) {
+ auto candidate = android::fs_mgr::GetPartitionName(partition);
+ if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
+ // On retrofit devices, we don't know if, or whether, the A or B
+ // slot has been flashed for dynamic partitions. Instead we add
+ // both names to the list as a conservative guess.
+ if (candidate + "_a" == partition_name || candidate + "_b" == partition_name) {
+ return true;
+ }
+ } else if (candidate == partition_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool is_sparse_file(borrowed_fd fd) {
+ SparsePtr s(sparse_file_import(fd.get(), false, false), sparse_file_destroy);
+ return !!s;
+}
+
+int64_t get_file_size(borrowed_fd fd) {
+ struct stat sb;
+ if (fstat(fd.get(), &sb) == -1) {
+ die("could not get file size");
+ }
+ return sb.st_size;
+}
diff --git a/fastboot/util.h b/fastboot/util.h
index c719df2..290d0d5 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -4,8 +4,14 @@
#include <stdlib.h>
#include <string>
+#include <vector>
+#include <android-base/unique_fd.h>
#include <bootimg.h>
+#include <liblp/liblp.h>
+#include <sparse/sparse.h>
+
+using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
/* util stuff */
double now();
@@ -19,3 +25,15 @@
void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
void die(const std::string& str) __attribute__((__noreturn__));
+
+bool should_flash_in_userspace(const android::fs_mgr::LpMetadata& metadata,
+ const std::string& partition_name);
+bool is_sparse_file(android::base::borrowed_fd fd);
+int64_t get_file_size(android::base::borrowed_fd fd);
+
+class ImageSource {
+ public:
+ virtual ~ImageSource(){};
+ virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
+ virtual android::base::unique_fd OpenFile(const std::string& name) const = 0;
+};
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 2b5e337..b6710d5 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -22,6 +22,12 @@
"name": "vts_libsnapshot_test"
},
{
+ "name": "vab_legacy_tests"
+ },
+ {
+ "name": "vabc_legacy_tests"
+ },
+ {
"name": "libsnapshot_fuzzer_test"
},
{
@@ -37,6 +43,12 @@
},
{
"name": "vts_libsnapshot_test"
+ },
+ {
+ "name": "vab_legacy_tests"
+ },
+ {
+ "name": "vabc_legacy_tests"
}
]
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 36bd75d..598a3d2 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -304,19 +304,16 @@
if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
}
- } else if (StartsWith(flag, "zoned_device=")) {
- std::string zoned;
- if (ReadFileToString("/sys/class/block/" + arg + "/queue/zoned", &zoned) &&
- android::base::StartsWith(zoned, "host-managed")) {
- entry->zoned_device = "/dev/block/" + arg;
+ } else if (flag == "zoned_device") {
+ if (access("/dev/block/by-name/zoned_device", F_OK) == 0) {
+ entry->zoned_device = "/dev/block/by-name/zoned_device";
// atgc in f2fs does not support a zoned device
auto options = Split(entry->fs_options, ",");
options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end());
entry->fs_options = android::base::Join(options, ",");
- LINFO << "Removed ATGC in fs_options as " << entry->fs_options;
- } else {
- LWARNING << "Warning: cannot find the zoned device: " << arg;
+ LINFO << "Removed ATGC in fs_options as " << entry->fs_options
+ << " for zoned device=" << entry->zoned_device;
}
} else {
LWARNING << "Warning: unknown flag: " << flag;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 46f54cc..46cdb62 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -33,7 +33,7 @@
*/
#define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr "
-#define FS_MGR_TAG "[libfs_mgr]"
+#define FS_MGR_TAG "[libfs_mgr] "
// Logs a message to kernel
#define LINFO LOG(INFO) << FS_MGR_TAG
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 2bb9035..5cc0346 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -90,19 +90,3 @@
min_shipping_api_level: 29,
},
}
-
-cc_fuzz {
- name: "dm_linear_table_fuzzer",
- defaults: ["fs_mgr_defaults"],
- srcs: [
- "dm_linear_fuzzer.cpp",
- "test_util.cpp",
- ],
- static_libs: [
- "libdm",
- "libbase",
- "libext2_uuid",
- "libfs_mgr",
- "liblog",
- ],
-}
diff --git a/fs_mgr/libdm/dm_linear_fuzzer.cpp b/fs_mgr/libdm/dm_linear_fuzzer.cpp
deleted file mode 100644
index 8462901..0000000
--- a/fs_mgr/libdm/dm_linear_fuzzer.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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 <stddef.h>
-#include <stdint.h>
-#include <string.h>
-
-#include <chrono>
-
-#include <android-base/file.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm_table.h>
-#include <libdm/loop_control.h>
-
-#include "test_util.h"
-
-using namespace android;
-using namespace android::base;
-using namespace android::dm;
-using namespace std;
-using namespace std::chrono_literals;
-
-/*
- * This test aims at making the library crash, so these functions are not
- * really useful.
- * Keeping them here for future use.
- */
-template <class T, class C>
-void ASSERT_EQ(const T& /*a*/, const C& /*b*/) {
- // if (a != b) {}
-}
-
-template <class T>
-void ASSERT_FALSE(const T& /*a*/) {
- // if (a) {}
-}
-
-template <class T, class C>
-void ASSERT_GE(const T& /*a*/, const C& /*b*/) {
- // if (a < b) {}
-}
-
-template <class T, class C>
-void ASSERT_NE(const T& /*a*/, const C& /*b*/) {
- // if (a == b) {}
-}
-
-template <class T>
-void ASSERT_TRUE(const T& /*a*/) {
- // if (!a) {}
-}
-
-template <class T, class C>
-void EXPECT_EQ(const T& a, const C& b) {
- ASSERT_EQ(a, b);
-}
-
-template <class T>
-void EXPECT_TRUE(const T& a) {
- ASSERT_TRUE(a);
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- uint64_t val[6];
-
- if (size != sizeof(val)) {
- return 0;
- }
-
- memcpy(&val, &data[0], sizeof(*val));
-
- unique_fd tmp1(CreateTempFile("file_1", 4096));
- ASSERT_GE(tmp1, 0);
- unique_fd tmp2(CreateTempFile("file_2", 4096));
- ASSERT_GE(tmp2, 0);
-
- LoopDevice loop_a(tmp1, 10s);
- ASSERT_TRUE(loop_a.valid());
- LoopDevice loop_b(tmp2, 10s);
- ASSERT_TRUE(loop_b.valid());
-
- // Define a 2-sector device, with each sector mapping to the first sector
- // of one of our loop devices.
- DmTable table;
- ASSERT_TRUE(table.Emplace<DmTargetLinear>(val[0], val[1], loop_a.device(), val[2]));
- ASSERT_TRUE(table.Emplace<DmTargetLinear>(val[3], val[4], loop_b.device(), val[5]));
- ASSERT_TRUE(table.valid());
- ASSERT_EQ(2u, table.num_sectors());
-
- TempDevice dev("libdm-test-dm-linear", table);
- ASSERT_TRUE(dev.valid());
- ASSERT_FALSE(dev.path().empty());
-
- auto& dm = DeviceMapper::Instance();
-
- dev_t dev_number;
- ASSERT_TRUE(dm.GetDeviceNumber(dev.name(), &dev_number));
- ASSERT_NE(dev_number, 0);
-
- std::string dev_string;
- ASSERT_TRUE(dm.GetDeviceString(dev.name(), &dev_string));
- ASSERT_FALSE(dev_string.empty());
-
- // Test GetTableStatus.
- vector<DeviceMapper::TargetInfo> targets;
- ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
- ASSERT_EQ(targets.size(), 2);
- EXPECT_EQ(strcmp(targets[0].spec.target_type, "linear"), 0);
- EXPECT_TRUE(targets[0].data.empty());
- EXPECT_EQ(targets[0].spec.sector_start, 0);
- EXPECT_EQ(targets[0].spec.length, 1);
- EXPECT_EQ(strcmp(targets[1].spec.target_type, "linear"), 0);
- EXPECT_TRUE(targets[1].data.empty());
- EXPECT_EQ(targets[1].spec.sector_start, 1);
- EXPECT_EQ(targets[1].spec.length, 1);
-
- // Test GetTargetType().
- EXPECT_EQ(DeviceMapper::GetTargetType(targets[0].spec), std::string{"linear"});
- EXPECT_EQ(DeviceMapper::GetTargetType(targets[1].spec), std::string{"linear"});
-
- // Normally the TestDevice destructor would delete this, but at least one
- // test should ensure that device deletion works.
- ASSERT_TRUE(dev.Destroy());
-
- return 0;
-}
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 4448d35..788cf51 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -19,6 +19,7 @@
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
@@ -692,7 +693,17 @@
}
TEST(libdm, UeventAfterLoadTable) {
- static const char* kDeviceName = "libmd-test-uevent-load-table";
+ static const char* kDeviceName = "libdm-test-uevent-load-table";
+
+ struct utsname u;
+ ASSERT_EQ(uname(&u), 0);
+
+ unsigned int major, minor;
+ ASSERT_EQ(sscanf(u.release, "%u.%u", &major, &minor), 2);
+
+ if (major < 5 || (major == 5 && minor < 15)) {
+ GTEST_SKIP() << "Skipping test on kernel < 5.15";
+ }
DeviceMapper& dm = DeviceMapper::Instance();
ASSERT_TRUE(dm.CreateEmptyDevice(kDeviceName));
diff --git a/fs_mgr/libfiemap/README.md b/fs_mgr/libfiemap/README.md
index 62d610a..cdc80b2 100644
--- a/fs_mgr/libfiemap/README.md
+++ b/fs_mgr/libfiemap/README.md
@@ -35,18 +35,18 @@
We break the problem down into three scenarios.
-### FDE and Metadata Encrypted Devices
+### Metadata Encrypted Devices
-When FDE or metadata encryption is used, `/data` is not mounted from
+When metadata encryption is used, `/data` is not mounted from
`/dev/block/by-name/data`. Instead, it is mounted from an intermediate
-`dm-crypt` or `dm-default-key` device. This means the underlying device is
-not marked in use, and we can create new dm-linear devices on top of it.
+`dm-default-key` device. This means the underlying device is not marked in use,
+and we can create new dm-linear devices on top of it.
On these devices, a block device for an image will consist of a single
device-mapper device with a `dm-linear` table entry for each extent in the
backing file.
-### Unencrypted and FBE-encrypted Devices
+### Unencrypted and FBE-only Devices
When a device is unencrypted, or is encrypted with FBE but not metadata
encryption, we instead use a loop device with `LOOP_SET_DIRECT_IO` enabled.
diff --git a/fs_mgr/libfiemap/metadata.cpp b/fs_mgr/libfiemap/metadata.cpp
index b0dfb5c..22b8afb 100644
--- a/fs_mgr/libfiemap/metadata.cpp
+++ b/fs_mgr/libfiemap/metadata.cpp
@@ -30,6 +30,7 @@
namespace fiemap {
using namespace android::fs_mgr;
+using android::base::unique_fd;
static constexpr uint32_t kMaxMetadataSize = 256 * 1024;
@@ -109,10 +110,18 @@
if (exported->partitions.empty() && android::base::RemoveFileIfExists(metadata_file)) {
return true;
}
- if (!WriteToImageFile(metadata_file, *exported.get())) {
+
+ unique_fd fd(open(metadata_file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY | O_SYNC, 0644));
+ if (fd < 0) {
+ LOG(ERROR) << "open failed: " << metadata_file;
+ return false;
+ }
+
+ if (!WriteToImageFile(fd, *exported.get())) {
LOG(ERROR) << "Unable to save new metadata";
return false;
}
+
return true;
}
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index 46072bb..a119bfc 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -26,8 +26,10 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/fs.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/stat.h>
#include <string>
@@ -96,13 +98,11 @@
return AVB_IO_RESULT_OK;
}
-static AvbIOResult no_op_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
- const char* partition ATTRIBUTE_UNUSED,
- uint64_t* out_size_num_byte) {
- // The function is for bootloader to load entire content of AVB HASH partitions.
- // In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
- *out_size_num_byte = 0;
- return AVB_IO_RESULT_OK;
+static AvbIOResult get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+ const char* partition ATTRIBUTE_UNUSED,
+ uint64_t* out_size_num_byte) {
+ return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->GetSizeOfPartition(partition,
+ out_size_num_byte);
}
// Converts a partition name (with ab_suffix) to the corresponding mount point.
@@ -131,7 +131,7 @@
avb_ops_.validate_vbmeta_public_key = no_op_validate_vbmeta_public_key;
avb_ops_.read_is_device_unlocked = no_op_read_is_device_unlocked;
avb_ops_.get_unique_guid_for_partition = no_op_get_unique_guid_for_partition;
- avb_ops_.get_size_of_partition = no_op_get_size_of_partition;
+ avb_ops_.get_size_of_partition = get_size_of_partition;
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
avb_ops_.user_data = this;
@@ -167,13 +167,8 @@
return "";
}
-
-AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
- size_t num_bytes, void* buffer,
- size_t* out_num_read) {
+std::string FsManagerAvbOps::GetPartitionPath(const char* partition) {
std::string path = "/dev/block/by-name/"s + partition;
-
- // Ensures the device path (a symlink created by init) is ready to access.
if (!WaitForFile(path, 1s)) {
LERROR << "Device path not found: " << path;
// Falls back to logical path if the physical path is not found.
@@ -182,8 +177,36 @@
// the bootloader failed to read a physical partition, it will failed to boot
// the HLOS and we won't reach the code here.
path = GetLogicalPath(partition);
- if (path.empty() || !WaitForFile(path, 1s)) return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
- LINFO << "Fallback to use logical device path: " << path;
+ if (path.empty() || !WaitForFile(path, 1s)) return "";
+ }
+ return path;
+}
+
+AvbIOResult FsManagerAvbOps::GetSizeOfPartition(const char* partition,
+ uint64_t* out_size_num_byte) {
+ const auto path = GetPartitionPath(partition);
+ if (path.empty()) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PERROR << "Failed to open " << path;
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ int err = ioctl(fd, BLKGETSIZE64, out_size_num_byte);
+ if (err) {
+ *out_size_num_byte = 0;
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
+ size_t num_bytes, void* buffer,
+ size_t* out_num_read) {
+ std::string path = GetPartitionPath(partition);
+ if (path.empty()) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
}
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
diff --git a/fs_mgr/libfs_avb/avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
index b39812d..12686a6 100644
--- a/fs_mgr/libfs_avb/avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -56,12 +56,14 @@
AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,
void* buffer, size_t* out_num_read);
+ AvbIOResult GetSizeOfPartition(const char* partition, uint64_t* out_size_num_byte);
AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
std::vector<VBMetaData>* out_vbmeta_images);
private:
std::string GetLogicalPath(const std::string& partition_name);
+ std::string GetPartitionPath(const char* partition_name);
AvbOps avb_ops_;
Fstab fstab_;
};
diff --git a/fs_mgr/libfs_avb/util.h b/fs_mgr/libfs_avb/util.h
index 427ab7c..29d1e9c 100644
--- a/fs_mgr/libfs_avb/util.h
+++ b/fs_mgr/libfs_avb/util.h
@@ -31,7 +31,7 @@
using android::base::ErrnoError;
using android::base::Result;
-#define FS_AVB_TAG "[libfs_avb]"
+#define FS_AVB_TAG "[libfs_avb] "
// Logs a message to kernel
#define LINFO LOG(INFO) << FS_AVB_TAG
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 4b81c2c..996ffd7 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -39,6 +39,7 @@
],
srcs: [
"builder.cpp",
+ "super_layout_builder.cpp",
"images.cpp",
"partition_opener.cpp",
"property_fetcher.cpp",
@@ -62,17 +63,6 @@
export_include_dirs: ["include"],
}
-filegroup {
- name: "liblp_test_srcs",
- srcs: [
- "builder_test.cpp",
- "device_test.cpp",
- "io_test.cpp",
- "test_partition_opener.cpp",
- "utility_test.cpp",
- ],
-}
-
cc_defaults {
name: "liblp_test_defaults",
defaults: ["fs_mgr_defaults"],
@@ -82,22 +72,39 @@
static_libs: [
"libcutils",
"libgmock",
- "libfs_mgr",
"liblp",
"libcrypto_static",
] + liblp_lib_deps,
header_libs: [
"libstorage_literals_headers",
],
+ target: {
+ android: {
+ srcs: [
+ "device_test.cpp",
+ "io_test.cpp",
+ ],
+ static_libs: [
+ "libfs_mgr",
+ ],
+ }
+ },
stl: "libc++_static",
- srcs: [":liblp_test_srcs"],
+ srcs: [
+ "builder_test.cpp",
+ "super_layout_builder_test.cpp",
+ "test_partition_opener.cpp",
+ "utility_test.cpp",
+ ],
}
cc_test {
name: "liblp_test",
defaults: ["liblp_test_defaults"],
- test_config: "liblp_test.xml",
test_suites: ["device-tests"],
+ auto_gen_config: true,
+ require_root: true,
+ host_supported: true
}
cc_test {
diff --git a/fs_mgr/liblp/include/liblp/super_layout_builder.h b/fs_mgr/liblp/include/liblp/super_layout_builder.h
new file mode 100644
index 0000000..d920855
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/super_layout_builder.h
@@ -0,0 +1,104 @@
+//
+// Copyright (C) 2023 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 <memory>
+#include <ostream>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include <android-base/unique_fd.h>
+#include <liblp/builder.h>
+
+namespace android {
+namespace fs_mgr {
+
+struct SuperImageExtent {
+ enum class Type { INVALID, DATA, PARTITION, ZERO, DONTCARE };
+
+ SuperImageExtent(const SuperImageExtent& other) = default;
+ SuperImageExtent(SuperImageExtent&& other) = default;
+ SuperImageExtent(uint64_t offset, uint64_t size, Type type)
+ : offset(offset), size(size), type(type) {}
+
+ SuperImageExtent(uint64_t offset, std::shared_ptr<std::string> blob)
+ : SuperImageExtent(offset, blob->size(), Type::DATA) {
+ this->blob = blob;
+ }
+
+ SuperImageExtent(uint64_t offset, uint64_t size, const std::string& image_name,
+ uint64_t image_offset)
+ : SuperImageExtent(offset, size, Type::PARTITION) {
+ this->image_name = image_name;
+ this->image_offset = image_offset;
+ }
+
+ SuperImageExtent& operator=(const SuperImageExtent& other) = default;
+ SuperImageExtent& operator=(SuperImageExtent&& other) = default;
+
+ bool operator<(const SuperImageExtent& other) const { return offset < other.offset; }
+ bool operator==(const SuperImageExtent& other) const;
+
+ // Location, size, and type of the extent.
+ uint64_t offset = 0;
+ uint64_t size = 0;
+ Type type = Type::INVALID;
+
+ // If type == DATA, this contains the bytes to write.
+ std::shared_ptr<std::string> blob;
+ // If type == PARTITION, this contains the partition image name and
+ // offset within that file.
+ std::string image_name;
+ uint64_t image_offset = 0;
+};
+
+// The SuperLayoutBuilder allows building a sparse view of a super image. This
+// is useful for efficient flashing, eg to bypass fastbootd and directly flash
+// super without physically building and storing the image.
+class SuperLayoutBuilder final {
+ public:
+ // Open a super_empty.img, return false on failure. This must be called to
+ // initialize the tool. If it returns false, either the image failed to
+ // parse, or the tool is not compatible with how the device is configured
+ // (in which case fastbootd should be preferred).
+ [[nodiscard]] bool Open(android::base::borrowed_fd fd);
+ [[nodiscard]] bool Open(const void* data, size_t bytes);
+ [[nodiscard]] bool Open(const LpMetadata& metadata);
+
+ // Add a partition's image and size to the work list. If false is returned,
+ // there was either a duplicate partition or not enough space in super.
+ bool AddPartition(const std::string& partition_name, const std::string& image_name,
+ uint64_t partition_size);
+
+ // Return the list of extents describing the super image. If this list is
+ // empty, then there was an unrecoverable error in building the list.
+ std::vector<SuperImageExtent> GetImageLayout();
+
+ // Return the current metadata.
+ std::unique_ptr<LpMetadata> Export() const { return builder_->Export(); }
+
+ private:
+ std::unique_ptr<MetadataBuilder> builder_;
+ std::unordered_map<std::string, std::string> image_map_;
+};
+
+std::ostream& operator<<(std::ostream& stream, const SuperImageExtent& extent);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/liblp_test.xml b/fs_mgr/liblp/liblp_test.xml
deleted file mode 100644
index 98414b1..0000000
--- a/fs_mgr/liblp/liblp_test.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for liblp_test">
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="liblp_test" />
- </test>
-</configuration>
diff --git a/fs_mgr/liblp/super_layout_builder.cpp b/fs_mgr/liblp/super_layout_builder.cpp
new file mode 100644
index 0000000..37f28e1
--- /dev/null
+++ b/fs_mgr/liblp/super_layout_builder.cpp
@@ -0,0 +1,241 @@
+//
+// Copyright (C) 2023 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 <liblp/super_layout_builder.h>
+
+#include <liblp/liblp.h>
+
+#include "images.h"
+#include "utility.h"
+#include "writer.h"
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+namespace android {
+namespace fs_mgr {
+
+bool SuperLayoutBuilder::Open(borrowed_fd fd) {
+ auto metadata = ReadFromImageFile(fd.get());
+ if (!metadata) {
+ return false;
+ }
+ return Open(*metadata.get());
+}
+
+bool SuperLayoutBuilder::Open(const void* data, size_t size) {
+ auto metadata = ReadFromImageBlob(data, size);
+ if (!metadata) {
+ return false;
+ }
+ return Open(*metadata.get());
+}
+
+bool SuperLayoutBuilder::Open(const LpMetadata& metadata) {
+ for (const auto& partition : metadata.partitions) {
+ if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
+ // Retrofit devices are not supported.
+ return false;
+ }
+ if (!(partition.attributes & LP_PARTITION_ATTR_READONLY)) {
+ // Writable partitions are not supported.
+ return false;
+ }
+ }
+ if (!metadata.extents.empty()) {
+ // Partitions that already have extents are not supported (should
+ // never be true of super_empty.img).
+ return false;
+ }
+ if (metadata.block_devices.size() != 1) {
+ // Only one "super" is supported.
+ return false;
+ }
+
+ builder_ = MetadataBuilder::New(metadata);
+ return !!builder_;
+}
+
+bool SuperLayoutBuilder::AddPartition(const std::string& partition_name,
+ const std::string& image_name, uint64_t partition_size) {
+ auto p = builder_->FindPartition(partition_name);
+ if (!p) {
+ return false;
+ }
+ if (!builder_->ResizePartition(p, partition_size)) {
+ return false;
+ }
+ image_map_.emplace(partition_name, image_name);
+ return true;
+}
+
+// Fill the space between each extent, if any, with either a fill or dontcare
+// extent. The caller constructs a sample extent to re-use.
+static bool AddGapExtents(std::vector<SuperImageExtent>* extents, SuperImageExtent::Type gap_type) {
+ std::vector<SuperImageExtent> old = std::move(*extents);
+ std::sort(old.begin(), old.end());
+
+ *extents = {};
+
+ uint64_t current_offset = 0;
+ for (const auto& extent : old) {
+ // Check for overlapping extents - this would be a serious error.
+ if (current_offset > extent.offset) {
+ LOG(INFO) << "Overlapping extents detected; cannot layout temporary super image";
+ return false;
+ }
+
+ if (extent.offset != current_offset) {
+ uint64_t gap_size = extent.offset - current_offset;
+ extents->emplace_back(current_offset, gap_size, gap_type);
+ current_offset = extent.offset;
+ }
+
+ extents->emplace_back(extent);
+ current_offset += extent.size;
+ }
+ return true;
+}
+
+std::vector<SuperImageExtent> SuperLayoutBuilder::GetImageLayout() {
+ auto metadata = builder_->Export();
+ if (!metadata) {
+ return {};
+ }
+
+ std::vector<SuperImageExtent> extents;
+
+ // Write the primary and backup copies of geometry.
+ std::string geometry_bytes = SerializeGeometry(metadata->geometry);
+ auto blob = std::make_shared<std::string>(std::move(geometry_bytes));
+
+ extents.emplace_back(0, GetPrimaryGeometryOffset(), SuperImageExtent::Type::ZERO);
+ extents.emplace_back(GetPrimaryGeometryOffset(), blob);
+ extents.emplace_back(GetBackupGeometryOffset(), blob);
+
+ // Write the primary and backup copies of each metadata slot. When flashing,
+ // all metadata copies are the same, even for different slots.
+ std::string metadata_bytes = SerializeMetadata(*metadata.get());
+
+ // Align metadata size to 4KB. This makes the layout easily compatible with
+ // libsparse.
+ static constexpr size_t kSparseAlignment = 4096;
+ size_t metadata_aligned_bytes;
+ if (!AlignTo(metadata_bytes.size(), kSparseAlignment, &metadata_aligned_bytes)) {
+ LOG(ERROR) << "Unable to align metadata size " << metadata_bytes.size() << " to "
+ << kSparseAlignment;
+ return {};
+ }
+ metadata_bytes.resize(metadata_aligned_bytes, '\0');
+
+ // However, alignment can cause larger-than-supported metadata blocks. Fall
+ // back to fastbootd/update-super.
+ if (metadata_bytes.size() > metadata->geometry.metadata_max_size) {
+ LOG(VERBOSE) << "Aligned metadata size " << metadata_bytes.size()
+ << " is larger than maximum metadata size "
+ << metadata->geometry.metadata_max_size;
+ return {};
+ }
+
+ blob = std::make_shared<std::string>(std::move(metadata_bytes));
+ for (uint32_t i = 0; i < metadata->geometry.metadata_slot_count; i++) {
+ int64_t metadata_primary = GetPrimaryMetadataOffset(metadata->geometry, i);
+ int64_t metadata_backup = GetBackupMetadataOffset(metadata->geometry, i);
+ extents.emplace_back(metadata_primary, blob);
+ extents.emplace_back(metadata_backup, blob);
+ }
+
+ // Add extents for each partition.
+ for (const auto& partition : metadata->partitions) {
+ auto partition_name = GetPartitionName(partition);
+ auto image_name_iter = image_map_.find(partition_name);
+ if (image_name_iter == image_map_.end()) {
+ if (partition.num_extents != 0) {
+ LOG(ERROR) << "Partition " << partition_name
+ << " has extents but no image filename";
+ return {};
+ }
+ continue;
+ }
+ const auto& image_name = image_name_iter->second;
+
+ uint64_t image_offset = 0;
+ for (uint32_t i = 0; i < partition.num_extents; i++) {
+ const auto& e = metadata->extents[partition.first_extent_index + i];
+
+ if (e.target_type != LP_TARGET_TYPE_LINEAR) {
+ // Any type other than LINEAR isn't understood here. We don't even
+ // bother with ZERO, which is never generated.
+ LOG(INFO) << "Unknown extent type from liblp: " << e.target_type;
+ return {};
+ }
+
+ size_t size = e.num_sectors * LP_SECTOR_SIZE;
+ uint64_t super_offset = e.target_data * LP_SECTOR_SIZE;
+ extents.emplace_back(super_offset, size, image_name, image_offset);
+
+ image_offset += size;
+ }
+ }
+
+ if (!AddGapExtents(&extents, SuperImageExtent::Type::DONTCARE)) {
+ return {};
+ }
+ return extents;
+}
+
+bool SuperImageExtent::operator==(const SuperImageExtent& other) const {
+ if (offset != other.offset) {
+ return false;
+ }
+ if (size != other.size) {
+ return false;
+ }
+ if (type != other.type) {
+ return false;
+ }
+ switch (type) {
+ case Type::DATA:
+ return *blob == *other.blob;
+ case Type::PARTITION:
+ return image_name == other.image_name && image_offset == other.image_offset;
+ default:
+ return true;
+ }
+}
+
+std::ostream& operator<<(std::ostream& stream, const SuperImageExtent& extent) {
+ stream << "extent:" << extent.offset << ":" << extent.size << ":";
+ switch (extent.type) {
+ case SuperImageExtent::Type::DATA:
+ stream << "data";
+ break;
+ case SuperImageExtent::Type::PARTITION:
+ stream << "partition:" << extent.image_name << ":" << extent.image_offset;
+ break;
+ case SuperImageExtent::Type::ZERO:
+ stream << "zero";
+ break;
+ case SuperImageExtent::Type::DONTCARE:
+ stream << "dontcare";
+ break;
+ default:
+ stream << "invalid";
+ }
+ return stream;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/super_layout_builder_test.cpp b/fs_mgr/liblp/super_layout_builder_test.cpp
new file mode 100644
index 0000000..714b6b4
--- /dev/null
+++ b/fs_mgr/liblp/super_layout_builder_test.cpp
@@ -0,0 +1,141 @@
+//
+// Copyright (C) 2023 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <liblp/super_layout_builder.h>
+#include <storage_literals/storage_literals.h>
+
+#include "images.h"
+#include "writer.h"
+
+using namespace android::fs_mgr;
+using namespace android::storage_literals;
+
+TEST(SuperImageTool, Layout) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(p, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_TRUE(tool.Open(*metadata.get()));
+ ASSERT_TRUE(tool.AddPartition("system_a", "system.img", 16_KiB));
+
+ // Get a copy of the metadata we'd expect if flashing.
+ ASSERT_TRUE(builder->ResizePartition(p, 16_KiB));
+ metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ auto geometry_blob = std::make_shared<std::string>(SerializeGeometry(metadata->geometry));
+ auto metadata_blob = std::make_shared<std::string>(SerializeMetadata(*metadata.get()));
+ metadata_blob->resize(4_KiB, '\0');
+
+ auto extents = tool.GetImageLayout();
+ ASSERT_EQ(extents.size(), 12);
+ EXPECT_EQ(extents[0], SuperImageExtent(0, 4096, SuperImageExtent::Type::ZERO));
+ EXPECT_EQ(extents[1], SuperImageExtent(4096, geometry_blob));
+ EXPECT_EQ(extents[2], SuperImageExtent(8192, geometry_blob));
+ EXPECT_EQ(extents[3], SuperImageExtent(12288, metadata_blob));
+ EXPECT_EQ(extents[4], SuperImageExtent(16384, 4096, SuperImageExtent::Type::DONTCARE));
+ EXPECT_EQ(extents[5], SuperImageExtent(20480, metadata_blob));
+ EXPECT_EQ(extents[6], SuperImageExtent(24576, 4096, SuperImageExtent::Type::DONTCARE));
+ EXPECT_EQ(extents[7], SuperImageExtent(28672, metadata_blob));
+ EXPECT_EQ(extents[8], SuperImageExtent(32768, 4096, SuperImageExtent::Type::DONTCARE));
+ EXPECT_EQ(extents[9], SuperImageExtent(36864, metadata_blob));
+ EXPECT_EQ(extents[10], SuperImageExtent(40960, 4096, SuperImageExtent::Type::DONTCARE));
+ EXPECT_EQ(extents[11], SuperImageExtent(45056, 16384, "system.img", 0));
+}
+
+TEST(SuperImageTool, NoWritablePartitions) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system_a", 0);
+ ASSERT_NE(p, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, NoRetrofit) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(p, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ // Add an extra block device.
+ metadata->block_devices.emplace_back(metadata->block_devices[0]);
+
+ SuperLayoutBuilder tool;
+ ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, NoRetrofit2) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition(
+ "system_a", LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED);
+ ASSERT_NE(p, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, NoFixedPartitions) {
+ auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 4_KiB));
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, LargeAlignedMetadata) {
+ auto builder = MetadataBuilder::New(4_MiB, 512, 2);
+ ASSERT_NE(builder, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ SuperLayoutBuilder tool;
+ ASSERT_TRUE(tool.Open(*metadata.get()));
+
+ auto extents = tool.GetImageLayout();
+ ASSERT_TRUE(extents.empty());
+}
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index aa3a6a0..32a59a5 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -30,7 +30,7 @@
#include "liblp/liblp.h"
-#define LP_TAG "[liblp]"
+#define LP_TAG "[liblp] "
#define LWARN LOG(WARNING) << LP_TAG
#define LINFO LOG(INFO) << LP_TAG
#define LERROR LOG(ERROR) << LP_TAG
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 2165961..c2f435f 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -269,14 +269,19 @@
defaults: ["libsnapshot_test_defaults", "libsnapshot_hal_deps"],
}
-sh_test {
- name: "run_snapshot_tests",
- src: "run_snapshot_tests.sh",
- test_suites: [
- "device-tests",
+cc_test {
+ name: "vab_legacy_tests",
+ defaults: ["libsnapshot_test_defaults", "libsnapshot_hal_deps"],
+ cppflags: [
+ "-DLIBSNAPSHOT_TEST_VAB_LEGACY",
],
- required: [
- "vts_libsnapshot_test",
+}
+
+cc_test {
+ name: "vabc_legacy_tests",
+ defaults: ["libsnapshot_test_defaults", "libsnapshot_hal_deps"],
+ cppflags: [
+ "-DLIBSNAPSHOT_TEST_VABC_LEGACY",
],
}
diff --git a/fs_mgr/libsnapshot/run_snapshot_tests.sh b/fs_mgr/libsnapshot/run_snapshot_tests.sh
deleted file mode 100644
index b03a4e0..0000000
--- a/fs_mgr/libsnapshot/run_snapshot_tests.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/system/bin/sh
-
-# Detect host or AOSP.
-getprop ro.build.version.sdk > /dev/null 2>&1
-if [ $? -eq 0 ]; then
- cmd_prefix=""
- local_root=""
-else
- cmd_prefix="adb shell"
- local_root="${ANDROID_PRODUCT_OUT}"
- set -e
- set -x
- adb root
- adb sync data
- set +x
- set +e
-fi
-
-testpath64="/data/nativetest64/vts_libsnapshot_test/vts_libsnapshot_test"
-testpath32="/data/nativetest/vts_libsnapshot_test/vts_libsnapshot_test"
-if [ -f "${local_root}/${testpath64}" ]; then
- testpath="${testpath64}"
-elif [ -f "${local_root}/${testpath32}" ]; then
- testpath="${testpath32}"
-else
- echo "ERROR: vts_libsnapshot_test not found." 1>&2
- echo "Make sure to build vts_libsnapshot_test or snapshot_tests first." 1>&2
- exit 1
-fi
-
-# Verbose, error on failure.
-set -x
-set -e
-
-time ${cmd_prefix} ${testpath}
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 2c01cf6..4014380 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -53,7 +53,15 @@
#include <libsnapshot/mock_device_info.h>
#include <libsnapshot/mock_snapshot.h>
-DEFINE_string(force_mode, "",
+#if defined(LIBSNAPSHOT_TEST_VAB_LEGACY)
+#define DEFAULT_MODE "vab-legacy"
+#elif defined(LIBSNAPSHOT_TEST_VABC_LEGACY)
+#define DEFAULT_MODE "vabc-legacy"
+#else
+#define DEFAULT_MODE ""
+#endif
+
+DEFINE_string(force_mode, DEFAULT_MODE,
"Force testing older modes (vab-legacy, vabc-legacy) ignoring device config.");
DEFINE_string(force_iouring_disable, "",
"Force testing mode (iouring_disabled) - disable io_uring");
diff --git a/fs_mgr/libvbmeta/utility.h b/fs_mgr/libvbmeta/utility.h
index 91db0ad..ab9828d 100644
--- a/fs_mgr/libvbmeta/utility.h
+++ b/fs_mgr/libvbmeta/utility.h
@@ -19,7 +19,7 @@
#include <android-base/logging.h>
#include <android-base/result.h>
-#define VBMETA_TAG "[libvbmeta]"
+#define VBMETA_TAG "[libvbmeta] "
#define LWARN LOG(WARNING) << VBMETA_TAG
#define LINFO LOG(INFO) << VBMETA_TAG
#define LERROR LOG(ERROR) << VBMETA_TAG
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 76b6ad0..235303f 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -4,7 +4,6 @@
cc_defaults {
name: "libbatterymonitor_defaults",
- srcs: ["BatteryMonitor.cpp"],
cflags: ["-Wall", "-Werror"],
vendor_available: true,
recovery_available: true,
@@ -75,8 +74,9 @@
cc_library_static {
name: "libbatterymonitor",
defaults: ["libbatterymonitor_defaults"],
+ srcs: ["BatteryMonitor.cpp"],
static_libs: [
- "android.hardware.health-V1-ndk",
+ "android.hardware.health-V2-ndk",
],
whole_static_libs: [
// Need to translate HIDL to AIDL to support legacy APIs in
@@ -89,6 +89,7 @@
cc_library_static {
name: "libbatterymonitor-V1",
defaults: ["libbatterymonitor_defaults"],
+ srcs: ["BatteryMonitor_v1.cpp"],
static_libs: [
"android.hardware.health-V1-ndk",
],
@@ -202,12 +203,12 @@
defaults: ["libhealthd_charger_ui_defaults"],
static_libs: [
- "android.hardware.health-V1-ndk",
+ "android.hardware.health-V2-ndk",
"android.hardware.health-translate-ndk",
],
export_static_lib_headers: [
- "android.hardware.health-V1-ndk",
+ "android.hardware.health-V2-ndk",
],
}
@@ -279,7 +280,7 @@
static_libs: [
// common
"android.hardware.health@1.0-convert",
- "android.hardware.health-V1-ndk",
+ "android.hardware.health-V2-ndk",
"libbatterymonitor",
"libcharger_sysprop",
"libhealthd_charger_nops",
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index a7571a2..4ea452a 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -55,7 +55,10 @@
using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
using aidl::android::hardware::health::BatteryCapacityLevel;
+using aidl::android::hardware::health::BatteryChargingPolicy;
+using aidl::android::hardware::health::BatteryChargingState;
using aidl::android::hardware::health::BatteryHealth;
+using aidl::android::hardware::health::BatteryHealthData;
using aidl::android::hardware::health::BatteryStatus;
using aidl::android::hardware::health::HealthInfo;
@@ -227,6 +230,37 @@
return *ret;
}
+BatteryChargingPolicy getBatteryChargingPolicy(const char* chargingPolicy) {
+ static SysfsStringEnumMap<BatteryChargingPolicy> batteryChargingPolicyMap[] = {
+ {"0", BatteryChargingPolicy::INVALID}, {"1", BatteryChargingPolicy::DEFAULT},
+ {"2", BatteryChargingPolicy::LONG_LIFE}, {"3", BatteryChargingPolicy::ADAPTIVE},
+ {NULL, BatteryChargingPolicy::DEFAULT},
+ };
+
+ auto ret = mapSysfsString(chargingPolicy, batteryChargingPolicyMap);
+ if (!ret) {
+ *ret = BatteryChargingPolicy::DEFAULT;
+ }
+
+ return *ret;
+}
+
+BatteryChargingState getBatteryChargingState(const char* chargingState) {
+ static SysfsStringEnumMap<BatteryChargingState> batteryChargingStateMap[] = {
+ {"0", BatteryChargingState::INVALID}, {"1", BatteryChargingState::NORMAL},
+ {"2", BatteryChargingState::TOO_COLD}, {"3", BatteryChargingState::TOO_HOT},
+ {"4", BatteryChargingState::LONG_LIFE}, {"5", BatteryChargingState::ADAPTIVE},
+ {NULL, BatteryChargingState::NORMAL},
+ };
+
+ auto ret = mapSysfsString(chargingState, batteryChargingStateMap);
+ if (!ret) {
+ *ret = BatteryChargingState::NORMAL;
+ }
+
+ return *ret;
+}
+
static int readFromFile(const String8& path, std::string* buf) {
buf->clear();
if (android::base::ReadFileToString(path.c_str(), buf)) {
@@ -235,6 +269,10 @@
return buf->length();
}
+static bool writeToFile(const String8& path, int32_t in_value) {
+ return android::base::WriteStringToFile(std::to_string(in_value), path.c_str());
+}
+
static BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {
static SysfsStringEnumMap<int> supplyTypeMap[] = {
{"Unknown", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
@@ -336,6 +374,17 @@
mHealthInfo->batteryFullChargeDesignCapacityUah =
getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
+ if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+ mHealthInfo->batteryStateOfHealth = getIntField(mHealthdConfig->batteryStateOfHealthPath);
+
+ if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+ mHealthInfo->batteryHealthData->batteryManufacturingDateSeconds =
+ getIntField(mHealthdConfig->batteryManufacturingDatePath);
+
+ if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+ mHealthInfo->batteryHealthData->batteryFirstUsageSeconds =
+ getIntField(mHealthdConfig->batteryFirstUsageDatePath);
+
mHealthInfo->batteryTemperatureTenthsCelsius =
mBatteryFixedTemperature ? mBatteryFixedTemperature
: getIntField(mHealthdConfig->batteryTemperaturePath);
@@ -354,6 +403,12 @@
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
mHealthInfo->batteryTechnology = String8(buf.c_str());
+ if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
+ mHealthInfo->chargingPolicy = getBatteryChargingPolicy(buf.c_str());
+
+ if (readFromFile(mHealthdConfig->chargingStatePath, &buf) > 0)
+ mHealthInfo->chargingState = getBatteryChargingState(buf.c_str());
+
double MaxPower = 0;
for (size_t i = 0; i < mChargerNames.size(); i++) {
@@ -476,6 +531,43 @@
return static_cast<int>(result);
}
+status_t BatteryMonitor::setChargingPolicy(int value) {
+ status_t ret = NAME_NOT_FOUND;
+ bool result;
+ if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+ result = writeToFile(mHealthdConfig->chargingPolicyPath, value);
+ if (!result) {
+ KLOG_WARNING(LOG_TAG, "setChargingPolicy fail\n");
+ ret = BAD_VALUE;
+ } else {
+ ret = OK;
+ }
+ }
+ return ret;
+}
+
+int BatteryMonitor::getChargingPolicy() {
+ BatteryChargingPolicy result = BatteryChargingPolicy::DEFAULT;
+ if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+ std::string buf;
+ if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
+ result = getBatteryChargingPolicy(buf.c_str());
+ }
+ return static_cast<int>(result);
+}
+
+int BatteryMonitor::getBatteryHealthData(int id) {
+ if (id == BATTERY_PROP_MANUFACTURING_DATE) {
+ if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+ return getIntField(mHealthdConfig->batteryManufacturingDatePath);
+ }
+ if (id == BATTERY_PROP_FIRST_USAGE_DATE) {
+ if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+ return getIntField(mHealthdConfig->batteryFirstUsageDatePath);
+ }
+ return 0;
+}
+
status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
status_t ret = BAD_VALUE;
std::string buf;
@@ -536,6 +628,21 @@
ret = OK;
break;
+ case BATTERY_PROP_CHARGING_POLICY:
+ val->valueInt64 = getChargingPolicy();
+ ret = OK;
+ break;
+
+ case BATTERY_PROP_MANUFACTURING_DATE:
+ val->valueInt64 = getBatteryHealthData(BATTERY_PROP_MANUFACTURING_DATE);
+ ret = OK;
+ break;
+
+ case BATTERY_PROP_FIRST_USAGE_DATE:
+ val->valueInt64 = getBatteryHealthData(BATTERY_PROP_FIRST_USAGE_DATE);
+ ret = OK;
+ break;
+
default:
break;
}
@@ -758,6 +865,44 @@
mHealthdConfig->batteryTechnologyPath = path;
}
+ if (mHealthdConfig->batteryStateOfHealthPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/state_of_health", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0) {
+ mHealthdConfig->batteryStateOfHealthPath = path;
+ } else {
+ path.clear();
+ path.appendFormat("%s/%s/health_index", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryStateOfHealthPath = path;
+ }
+ }
+
+ if (mHealthdConfig->batteryManufacturingDatePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/manufacturing_date", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryManufacturingDatePath = path;
+ }
+
+ if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/first_usage_date", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0) mHealthdConfig->batteryFirstUsageDatePath = path;
+ }
+
+ if (mHealthdConfig->chargingStatePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/charging_state", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0) mHealthdConfig->chargingStatePath = path;
+ }
+
+ if (mHealthdConfig->chargingPolicyPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/charging_policy", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;
+ }
+
break;
case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
@@ -810,6 +955,16 @@
KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
+ if (mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "batteryStateOfHealthPath not found\n");
+ if (mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "batteryManufacturingDatePath not found\n");
+ if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "batteryFirstUsageDatePath not found\n");
+ if (mHealthdConfig->chargingStatePath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "chargingStatePath not found\n");
+ if (mHealthdConfig->chargingPolicyPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "chargingPolicyPath not found\n");
}
if (property_get("ro.boot.fake_battery", pval, NULL) > 0
diff --git a/healthd/BatteryMonitor_v1.cpp b/healthd/BatteryMonitor_v1.cpp
new file mode 100644
index 0000000..b87c493
--- /dev/null
+++ b/healthd/BatteryMonitor_v1.cpp
@@ -0,0 +1,822 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "healthd"
+
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor_v1.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <optional>
+
+#include <aidl/android/hardware/health/HealthInfo.h>
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android/hardware/health/2.1/types.h>
+#include <android/hardware/health/translate-ndk.h>
+#include <batteryservice/BatteryService.h>
+#include <cutils/klog.h>
+#include <cutils/properties.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#define POWER_SUPPLY_SUBSYSTEM "power_supply"
+#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
+#define FAKE_BATTERY_CAPACITY 42
+#define FAKE_BATTERY_TEMPERATURE 424
+#define MILLION 1.0e6
+#define DEFAULT_VBUS_VOLTAGE 5000000
+
+using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
+using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
+using aidl::android::hardware::health::BatteryCapacityLevel;
+using aidl::android::hardware::health::BatteryHealth;
+using aidl::android::hardware::health::BatteryStatus;
+using aidl::android::hardware::health::HealthInfo;
+
+namespace {
+
+// Translate from AIDL back to HIDL definition for getHealthInfo_*_* calls.
+// Skips storageInfo and diskStats.
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+ ::android::hardware::health::V1_0::HealthInfo* out) {
+ out->chargerAcOnline = in.chargerAcOnline;
+ out->chargerUsbOnline = in.chargerUsbOnline;
+ out->chargerWirelessOnline = in.chargerWirelessOnline;
+ out->maxChargingCurrent = in.maxChargingCurrentMicroamps;
+ out->maxChargingVoltage = in.maxChargingVoltageMicrovolts;
+ out->batteryStatus =
+ static_cast<::android::hardware::health::V1_0::BatteryStatus>(in.batteryStatus);
+ out->batteryHealth =
+ static_cast<::android::hardware::health::V1_0::BatteryHealth>(in.batteryHealth);
+ out->batteryPresent = in.batteryPresent;
+ out->batteryLevel = in.batteryLevel;
+ out->batteryVoltage = in.batteryVoltageMillivolts;
+ out->batteryTemperature = in.batteryTemperatureTenthsCelsius;
+ out->batteryCurrent = in.batteryCurrentMicroamps;
+ out->batteryCycleCount = in.batteryCycleCount;
+ out->batteryFullCharge = in.batteryFullChargeUah;
+ out->batteryChargeCounter = in.batteryChargeCounterUah;
+ out->batteryTechnology = in.batteryTechnology;
+}
+
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+ ::android::hardware::health::V2_0::HealthInfo* out) {
+ translateToHidl(in, &out->legacy);
+ out->batteryCurrentAverage = in.batteryCurrentAverageMicroamps;
+ // Skip storageInfo and diskStats
+}
+
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+ ::android::hardware::health::V2_1::HealthInfo* out) {
+ translateToHidl(in, &out->legacy);
+ out->batteryCapacityLevel = static_cast<android::hardware::health::V2_1::BatteryCapacityLevel>(
+ in.batteryCapacityLevel);
+ out->batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;
+ out->batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;
+}
+
+} // namespace
+
+namespace android {
+
+template <typename T>
+struct SysfsStringEnumMap {
+ const char* s;
+ T val;
+};
+
+template <typename T>
+static std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {
+ for (int i = 0; map[i].s; i++)
+ if (!strcmp(str, map[i].s))
+ return map[i].val;
+
+ return std::nullopt;
+}
+
+static void initHealthInfo(HealthInfo* health_info) {
+ *health_info = {
+ .batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED,
+ .batteryChargeTimeToFullNowSeconds =
+ (int64_t)HealthInfo::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED,
+ .batteryStatus = BatteryStatus::UNKNOWN,
+ .batteryHealth = BatteryHealth::UNKNOWN,
+ };
+}
+
+BatteryMonitor::BatteryMonitor()
+ : mHealthdConfig(nullptr),
+ mBatteryDevicePresent(false),
+ mBatteryFixedCapacity(0),
+ mBatteryFixedTemperature(0),
+ mHealthInfo(std::make_unique<HealthInfo>()) {
+ initHealthInfo(mHealthInfo.get());
+}
+
+BatteryMonitor::~BatteryMonitor() {}
+
+HealthInfo_1_0 BatteryMonitor::getHealthInfo_1_0() const {
+ HealthInfo_1_0 health_info_1_0;
+ translateToHidl(*mHealthInfo, &health_info_1_0);
+ return health_info_1_0;
+}
+
+HealthInfo_2_0 BatteryMonitor::getHealthInfo_2_0() const {
+ HealthInfo_2_0 health_info_2_0;
+ translateToHidl(*mHealthInfo, &health_info_2_0);
+ return health_info_2_0;
+}
+
+HealthInfo_2_1 BatteryMonitor::getHealthInfo_2_1() const {
+ HealthInfo_2_1 health_info_2_1;
+ translateToHidl(*mHealthInfo, &health_info_2_1);
+ return health_info_2_1;
+}
+
+const HealthInfo& BatteryMonitor::getHealthInfo() const {
+ return *mHealthInfo;
+}
+
+BatteryStatus getBatteryStatus(const char* status) {
+ static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {
+ {"Unknown", BatteryStatus::UNKNOWN},
+ {"Charging", BatteryStatus::CHARGING},
+ {"Discharging", BatteryStatus::DISCHARGING},
+ {"Not charging", BatteryStatus::NOT_CHARGING},
+ {"Full", BatteryStatus::FULL},
+ {NULL, BatteryStatus::UNKNOWN},
+ };
+
+ auto ret = mapSysfsString(status, batteryStatusMap);
+ if (!ret) {
+ KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
+ *ret = BatteryStatus::UNKNOWN;
+ }
+
+ return *ret;
+}
+
+BatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {
+ static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {
+ {"Unknown", BatteryCapacityLevel::UNKNOWN},
+ {"Critical", BatteryCapacityLevel::CRITICAL},
+ {"Low", BatteryCapacityLevel::LOW},
+ {"Normal", BatteryCapacityLevel::NORMAL},
+ {"High", BatteryCapacityLevel::HIGH},
+ {"Full", BatteryCapacityLevel::FULL},
+ {NULL, BatteryCapacityLevel::UNSUPPORTED},
+ };
+
+ auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
+ if (!ret) {
+ KLOG_WARNING(LOG_TAG, "Unsupported battery capacity level '%s'\n", capacityLevel);
+ *ret = BatteryCapacityLevel::UNSUPPORTED;
+ }
+
+ return *ret;
+}
+
+BatteryHealth getBatteryHealth(const char* status) {
+ static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
+ {"Unknown", BatteryHealth::UNKNOWN},
+ {"Good", BatteryHealth::GOOD},
+ {"Overheat", BatteryHealth::OVERHEAT},
+ {"Dead", BatteryHealth::DEAD},
+ {"Over voltage", BatteryHealth::OVER_VOLTAGE},
+ {"Unspecified failure", BatteryHealth::UNSPECIFIED_FAILURE},
+ {"Cold", BatteryHealth::COLD},
+ // battery health values from JEITA spec
+ {"Warm", BatteryHealth::GOOD},
+ {"Cool", BatteryHealth::GOOD},
+ {"Hot", BatteryHealth::OVERHEAT},
+ {NULL, BatteryHealth::UNKNOWN},
+ };
+
+ auto ret = mapSysfsString(status, batteryHealthMap);
+ if (!ret) {
+ KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
+ *ret = BatteryHealth::UNKNOWN;
+ }
+
+ return *ret;
+}
+
+static int readFromFile(const String8& path, std::string* buf) {
+ buf->clear();
+ if (android::base::ReadFileToString(path.c_str(), buf)) {
+ *buf = android::base::Trim(*buf);
+ }
+ return buf->length();
+}
+
+static BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {
+ static SysfsStringEnumMap<int> supplyTypeMap[] = {
+ {"Unknown", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
+ {"Battery", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_BATTERY},
+ {"UPS", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"Mains", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"USB_DCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_HVDCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_CDP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_ACA", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_C", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD_DRP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"Wireless", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+ {"Dock", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_DOCK},
+ {NULL, 0},
+ };
+ std::string buf;
+
+ if (readFromFile(path, &buf) <= 0) {
+ return BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ }
+
+ auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
+ if (!ret) {
+ KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
+ *ret = BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ }
+
+ return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
+}
+
+static bool getBooleanField(const String8& path) {
+ std::string buf;
+ bool value = false;
+
+ if (readFromFile(path, &buf) > 0)
+ if (buf[0] != '0')
+ value = true;
+
+ return value;
+}
+
+static int getIntField(const String8& path) {
+ std::string buf;
+ int value = 0;
+
+ if (readFromFile(path, &buf) > 0)
+ android::base::ParseInt(buf, &value);
+
+ return value;
+}
+
+static bool isScopedPowerSupply(const char* name) {
+ constexpr char kScopeDevice[] = "Device";
+
+ String8 path;
+ path.appendFormat("%s/%s/scope", POWER_SUPPLY_SYSFS_PATH, name);
+ std::string scope;
+ return (readFromFile(path, &scope) > 0 && scope == kScopeDevice);
+}
+
+void BatteryMonitor::updateValues(void) {
+ initHealthInfo(mHealthInfo.get());
+
+ if (!mHealthdConfig->batteryPresentPath.isEmpty())
+ mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
+ else
+ mHealthInfo->batteryPresent = mBatteryDevicePresent;
+
+ mHealthInfo->batteryLevel = mBatteryFixedCapacity
+ ? mBatteryFixedCapacity
+ : getIntField(mHealthdConfig->batteryCapacityPath);
+ mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
+
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
+
+ if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+ mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
+
+ if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+ mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
+
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+ mHealthInfo->batteryChargeCounterUah =
+ getIntField(mHealthdConfig->batteryChargeCounterPath);
+
+ if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+ mHealthInfo->batteryCurrentAverageMicroamps =
+ getIntField(mHealthdConfig->batteryCurrentAvgPath);
+
+ if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ mHealthInfo->batteryChargeTimeToFullNowSeconds =
+ getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
+
+ if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ mHealthInfo->batteryFullChargeDesignCapacityUah =
+ getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
+
+ mHealthInfo->batteryTemperatureTenthsCelsius =
+ mBatteryFixedTemperature ? mBatteryFixedTemperature
+ : getIntField(mHealthdConfig->batteryTemperaturePath);
+
+ std::string buf;
+
+ if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)
+ mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
+
+ if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
+ mHealthInfo->batteryStatus = getBatteryStatus(buf.c_str());
+
+ if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
+ mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
+
+ if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
+ mHealthInfo->batteryTechnology = String8(buf.c_str());
+
+ double MaxPower = 0;
+
+ for (size_t i = 0; i < mChargerNames.size(); i++) {
+ String8 path;
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ if (getIntField(path)) {
+ path.clear();
+ path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ switch(readPowerSupplyType(path)) {
+ case ANDROID_POWER_SUPPLY_TYPE_AC:
+ mHealthInfo->chargerAcOnline = true;
+ break;
+ case ANDROID_POWER_SUPPLY_TYPE_USB:
+ mHealthInfo->chargerUsbOnline = true;
+ break;
+ case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+ mHealthInfo->chargerWirelessOnline = true;
+ break;
+ case ANDROID_POWER_SUPPLY_TYPE_DOCK:
+ mHealthInfo->chargerDockOnline = true;
+ break;
+ default:
+ path.clear();
+ path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ if (access(path.string(), R_OK) == 0)
+ mHealthInfo->chargerDockOnline = true;
+ else
+ KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
+ mChargerNames[i].string());
+ }
+ path.clear();
+ path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ int ChargingCurrent =
+ (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+
+ path.clear();
+ path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+
+ int ChargingVoltage =
+ (access(path.string(), R_OK) == 0) ? getIntField(path) :
+ DEFAULT_VBUS_VOLTAGE;
+
+ double power = ((double)ChargingCurrent / MILLION) *
+ ((double)ChargingVoltage / MILLION);
+ if (MaxPower < power) {
+ mHealthInfo->maxChargingCurrentMicroamps = ChargingCurrent;
+ mHealthInfo->maxChargingVoltageMicrovolts = ChargingVoltage;
+ MaxPower = power;
+ }
+ }
+ }
+}
+
+static void doLogValues(const HealthInfo& props, const struct healthd_config& healthd_config) {
+ char dmesgline[256];
+ size_t len;
+ if (props.batteryPresent) {
+ snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
+ props.batteryLevel, props.batteryVoltageMillivolts,
+ props.batteryTemperatureTenthsCelsius < 0 ? "-" : "",
+ abs(props.batteryTemperatureTenthsCelsius / 10),
+ abs(props.batteryTemperatureTenthsCelsius % 10), props.batteryHealth,
+ props.batteryStatus);
+
+ len = strlen(dmesgline);
+ if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
+ len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
+ props.batteryCurrentMicroamps);
+ }
+
+ if (!healthd_config.batteryFullChargePath.isEmpty()) {
+ len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
+ props.batteryFullChargeUah);
+ }
+
+ if (!healthd_config.batteryCycleCountPath.isEmpty()) {
+ len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
+ props.batteryCycleCount);
+ }
+ } else {
+ len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
+ }
+
+ snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s%s",
+ props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
+ props.chargerWirelessOnline ? "w" : "", props.chargerDockOnline ? "d" : "");
+
+ KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
+}
+
+void BatteryMonitor::logValues(const HealthInfo_2_1& health_info,
+ const struct healthd_config& healthd_config) {
+ HealthInfo aidl_health_info;
+ (void)android::h2a::translate(health_info, &aidl_health_info);
+ doLogValues(aidl_health_info, healthd_config);
+}
+
+void BatteryMonitor::logValues(void) {
+ doLogValues(*mHealthInfo, *mHealthdConfig);
+}
+
+bool BatteryMonitor::isChargerOnline() {
+ const HealthInfo& props = *mHealthInfo;
+ return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |
+ props.chargerDockOnline;
+}
+
+int BatteryMonitor::getChargeStatus() {
+ BatteryStatus result = BatteryStatus::UNKNOWN;
+ if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+ std::string buf;
+ if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
+ result = getBatteryStatus(buf.c_str());
+ }
+ return static_cast<int>(result);
+}
+
+status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
+ status_t ret = BAD_VALUE;
+ std::string buf;
+
+ val->valueInt64 = LONG_MIN;
+
+ switch(id) {
+ case BATTERY_PROP_CHARGE_COUNTER:
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ val->valueInt64 =
+ getIntField(mHealthdConfig->batteryChargeCounterPath);
+ ret = OK;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CURRENT_NOW:
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ val->valueInt64 =
+ getIntField(mHealthdConfig->batteryCurrentNowPath);
+ ret = OK;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CURRENT_AVG:
+ if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ val->valueInt64 =
+ getIntField(mHealthdConfig->batteryCurrentAvgPath);
+ ret = OK;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CAPACITY:
+ if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ val->valueInt64 =
+ getIntField(mHealthdConfig->batteryCapacityPath);
+ ret = OK;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_ENERGY_COUNTER:
+ if (mHealthdConfig->energyCounter) {
+ ret = mHealthdConfig->energyCounter(&val->valueInt64);
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_BATTERY_STATUS:
+ val->valueInt64 = getChargeStatus();
+ ret = OK;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+void BatteryMonitor::dumpState(int fd) {
+ int v;
+ char vs[128];
+ const HealthInfo& props = *mHealthInfo;
+
+ snprintf(vs, sizeof(vs),
+ "ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
+ props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,
+ props.chargerDockOnline, props.maxChargingCurrentMicroamps,
+ props.maxChargingVoltageMicrovolts);
+ write(fd, vs, strlen(vs));
+ snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
+ props.batteryStatus, props.batteryHealth, props.batteryPresent);
+ write(fd, vs, strlen(vs));
+ snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", props.batteryLevel,
+ props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
+ write(fd, vs, strlen(vs));
+
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ v = getIntField(mHealthdConfig->batteryCurrentNowPath);
+ snprintf(vs, sizeof(vs), "current now: %d\n", v);
+ write(fd, vs, strlen(vs));
+ }
+
+ if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
+ snprintf(vs, sizeof(vs), "current avg: %d\n", v);
+ write(fd, vs, strlen(vs));
+ }
+
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ v = getIntField(mHealthdConfig->batteryChargeCounterPath);
+ snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
+ write(fd, vs, strlen(vs));
+ }
+
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
+ write(fd, vs, strlen(vs));
+ }
+
+ if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
+ write(fd, vs, strlen(vs));
+ }
+
+ if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
+ write(fd, vs, strlen(vs));
+ }
+}
+
+void BatteryMonitor::init(struct healthd_config *hc) {
+ String8 path;
+ char pval[PROPERTY_VALUE_MAX];
+
+ mHealthdConfig = hc;
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
+ if (dir == NULL) {
+ KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
+ } else {
+ struct dirent* entry;
+
+ while ((entry = readdir(dir.get()))) {
+ const char* name = entry->d_name;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ continue;
+
+ std::vector<String8>::iterator itIgnoreName =
+ find(hc->ignorePowerSupplyNames.begin(), hc->ignorePowerSupplyNames.end(),
+ String8(name));
+ if (itIgnoreName != hc->ignorePowerSupplyNames.end())
+ continue;
+
+ // Look for "type" file in each subdirectory
+ path.clear();
+ path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
+ switch(readPowerSupplyType(path)) {
+ case ANDROID_POWER_SUPPLY_TYPE_AC:
+ case ANDROID_POWER_SUPPLY_TYPE_USB:
+ case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+ case ANDROID_POWER_SUPPLY_TYPE_DOCK:
+ path.clear();
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path.string(), R_OK) == 0)
+ mChargerNames.add(String8(name));
+ break;
+
+ case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
+ // Some devices expose the battery status of sub-component like
+ // stylus. Such a device-scoped battery info needs to be skipped
+ // in BatteryMonitor, which is intended to report the status of
+ // the battery supplying the power to the whole system.
+ if (isScopedPowerSupply(name)) continue;
+ mBatteryDevicePresent = true;
+
+ if (mHealthdConfig->batteryStatusPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryStatusPath = path;
+ }
+
+ if (mHealthdConfig->batteryHealthPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryHealthPath = path;
+ }
+
+ if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryPresentPath = path;
+ }
+
+ if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCapacityPath = path;
+ }
+
+ if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/voltage_now",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0) {
+ mHealthdConfig->batteryVoltagePath = path;
+ }
+ }
+
+ if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/charge_full",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryFullChargePath = path;
+ }
+
+ if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/current_now",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCurrentNowPath = path;
+ }
+
+ if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/cycle_count",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCycleCountPath = path;
+ }
+
+ if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+ }
+
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryChargeTimeToFullNowPath = path;
+ }
+
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
+ }
+
+ if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/current_avg",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCurrentAvgPath = path;
+ }
+
+ if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/charge_counter",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryChargeCounterPath = path;
+ }
+
+ if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
+ name);
+ if (access(path, R_OK) == 0) {
+ mHealthdConfig->batteryTemperaturePath = path;
+ }
+ }
+
+ if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/technology",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryTechnologyPath = path;
+ }
+
+ break;
+
+ case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
+ break;
+ }
+
+ // Look for "is_dock" file
+ path.clear();
+ path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path.string(), R_OK) == 0) {
+ path.clear();
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path.string(), R_OK) == 0)
+ mChargerNames.add(String8(name));
+
+ }
+ }
+ }
+
+ // Typically the case for devices which do not have a battery and
+ // and are always plugged into AC mains.
+ if (!mBatteryDevicePresent) {
+ KLOG_WARNING(LOG_TAG, "No battery devices found\n");
+ hc->periodic_chores_interval_fast = -1;
+ hc->periodic_chores_interval_slow = -1;
+ } else {
+ if (mHealthdConfig->batteryStatusPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
+ if (mHealthdConfig->batteryHealthPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
+ if (mHealthdConfig->batteryPresentPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
+ if (mHealthdConfig->batteryCapacityPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
+ if (mHealthdConfig->batteryVoltagePath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
+ if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
+ if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
+ if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
+ if (mHealthdConfig->batteryFullChargePath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
+ if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
+ if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
+ if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
+ if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+ KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
+ }
+
+ if (property_get("ro.boot.fake_battery", pval, NULL) > 0
+ && strtol(pval, NULL, 10) != 0) {
+ mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
+ mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
+ }
+}
+
+}; // namespace android
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 8cbf5ea..4af2a87 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -72,6 +72,10 @@
void logValues(void);
bool isChargerOnline();
+ int setChargingPolicy(int value);
+ int getChargingPolicy();
+ int getBatteryHealthData(int id);
+
static void logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
const struct healthd_config& healthd_config);
diff --git a/healthd/include/healthd/BatteryMonitor_v1.h b/healthd/include/healthd/BatteryMonitor_v1.h
new file mode 100644
index 0000000..49f6f9d
--- /dev/null
+++ b/healthd/include/healthd/BatteryMonitor_v1.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 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 HEALTHD_BATTERYMONITOR_V1_H
+#define HEALTHD_BATTERYMONITOR_V1_H
+
+#include <memory>
+
+#include <batteryservice/BatteryService.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <healthd/healthd.h>
+
+namespace aidl::android::hardware::health {
+class HealthInfo;
+} // namespace aidl::android::hardware::health
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V1_0 {
+struct HealthInfo;
+} // namespace V1_0
+namespace V2_0 {
+struct HealthInfo;
+} // namespace V2_0
+namespace V2_1 {
+struct HealthInfo;
+} // namespace V2_1
+} // namespace health
+} // namespace hardware
+
+class BatteryMonitor {
+ public:
+
+ enum PowerSupplyType {
+ ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,
+ ANDROID_POWER_SUPPLY_TYPE_AC,
+ ANDROID_POWER_SUPPLY_TYPE_USB,
+ ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
+ ANDROID_POWER_SUPPLY_TYPE_BATTERY,
+ ANDROID_POWER_SUPPLY_TYPE_DOCK
+ };
+
+ BatteryMonitor();
+ ~BatteryMonitor();
+ void init(struct healthd_config *hc);
+ int getChargeStatus();
+ status_t getProperty(int id, struct BatteryProperty *val);
+ void dumpState(int fd);
+
+ android::hardware::health::V1_0::HealthInfo getHealthInfo_1_0() const;
+ android::hardware::health::V2_0::HealthInfo getHealthInfo_2_0() const;
+ android::hardware::health::V2_1::HealthInfo getHealthInfo_2_1() const;
+ const aidl::android::hardware::health::HealthInfo& getHealthInfo() const;
+
+ void updateValues(void);
+ void logValues(void);
+ bool isChargerOnline();
+
+ static void logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
+ const struct healthd_config& healthd_config);
+
+ private:
+ struct healthd_config *mHealthdConfig;
+ Vector<String8> mChargerNames;
+ bool mBatteryDevicePresent;
+ int mBatteryFixedCapacity;
+ int mBatteryFixedTemperature;
+ std::unique_ptr<aidl::android::hardware::health::HealthInfo> mHealthInfo;
+};
+
+}; // namespace android
+
+#endif // HEALTHD_BATTERYMONITOR_V1_H
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 706c332..e1c357c 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -72,6 +72,11 @@
android::String8 batteryCapacityLevelPath;
android::String8 batteryChargeTimeToFullNowPath;
android::String8 batteryFullChargeDesignCapacityUahPath;
+ android::String8 batteryStateOfHealthPath;
+ android::String8 batteryManufacturingDatePath;
+ android::String8 batteryFirstUsageDatePath;
+ android::String8 chargingStatePath;
+ android::String8 chargingPolicyPath;
int (*energyCounter)(int64_t *);
int boot_min_cap;
diff --git a/init/Android.bp b/init/Android.bp
index c7e7de8..1aba4b3 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -454,15 +454,7 @@
defaults: ["init_defaults"],
require_root: true,
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
+ compile_multilib: "first",
srcs: [
"devices_test.cpp",
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 6f22ab7..8b05484 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -22,7 +22,6 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
- <option name="append-bitness" value="true" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
<option name="throw-on-error" value="false" />
diff --git a/init/devices.cpp b/init/devices.cpp
index 8bc6e52..39442a0 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -431,6 +431,12 @@
}
}
+ std::string model;
+ if (ReadFileToString("/sys/class/block/" + uevent.device_name + "/queue/zoned", &model) &&
+ !StartsWith(model, "none")) {
+ links.emplace_back("/dev/block/by-name/zoned_device");
+ }
+
auto last_slash = uevent.path.rfind('/');
links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
diff --git a/init/init.cpp b/init/init.cpp
index 4262191..f964c60 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -952,6 +952,8 @@
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
+ SelinuxSetupKernelLogging();
+
// Update $PATH in the case the second stage init is newer than first stage init, where it is
// first set.
if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
@@ -1012,7 +1014,6 @@
MountExtraFilesystems();
// Now set up SELinux for second stage.
- SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index a987b85..f847782 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -22,76 +22,60 @@
#include <dlfcn.h>
#include <errno.h>
-#include <fcntl.h>
#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
#include <log/log.h>
-class netdHandler {
- public:
+struct netdHandler {
int (*netdTagSocket)(int, uint32_t, uid_t);
int (*netdUntagSocket)(int);
};
-int stubTagSocket(int, uint32_t, uid_t) {
+static int stubTagSocket(int, uint32_t, uid_t) {
return -EREMOTEIO;
}
-int stubUntagSocket(int) {
+static int stubUntagSocket(int) {
return -EREMOTEIO;
}
-netdHandler initHandler(void) {
- netdHandler handler = {stubTagSocket, stubUntagSocket};
+static netdHandler initHandler(void) {
+ const netdHandler stubHandler = { stubTagSocket, stubUntagSocket };
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
if (!netdClientHandle) {
ALOGE("Failed to open libnetd_client.so: %s", dlerror());
- return handler;
+ return stubHandler;
}
+ netdHandler handler;
handler.netdTagSocket = (int (*)(int, uint32_t, uid_t))dlsym(netdClientHandle, "tagSocket");
if (!handler.netdTagSocket) {
ALOGE("load netdTagSocket handler failed: %s", dlerror());
+ return stubHandler;
}
handler.netdUntagSocket = (int (*)(int))dlsym(netdClientHandle, "untagSocket");
if (!handler.netdUntagSocket) {
ALOGE("load netdUntagSocket handler failed: %s", dlerror());
+ return stubHandler;
}
return handler;
}
// The language guarantees that this object will be initialized in a thread-safe way.
-static netdHandler& getHandler() {
- static netdHandler instance = initHandler();
+static const netdHandler& getHandler() {
+ static const netdHandler instance = initHandler();
return instance;
}
int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {
- // Check the socket fd passed to us is still valid before we load the netd
- // client. Pass a already closed socket fd to netd client may let netd open
- // the unix socket with the same fd number and pass it to server for
- // tagging.
- // TODO: move the check into netdTagSocket.
- int res = fcntl(sockfd, F_GETFD);
- if (res < 0) return res;
-
ALOGV("Tagging socket %d with tag %u for uid %d", sockfd, tag, uid);
return getHandler().netdTagSocket(sockfd, tag, uid);
}
int qtaguid_untagSocket(int sockfd) {
- // Similiar to tag socket. We need a check before untag to make sure untag a closed socket fail
- // as expected.
- // TODO: move the check into netdTagSocket.
- int res = fcntl(sockfd, F_GETFD);
- if (res < 0) return res;
-
ALOGV("Untagging socket %d", sockfd);
return getHandler().netdUntagSocket(sockfd);
}
diff --git a/libcutils/sched_policy_test.cpp b/libcutils/sched_policy_test.cpp
index b9e2832..50bd6d0 100644
--- a/libcutils/sched_policy_test.cpp
+++ b/libcutils/sched_policy_test.cpp
@@ -75,9 +75,11 @@
}
ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+ ASSERT_EQ(0, set_cpuset_policy(0, SP_BACKGROUND));
AssertPolicy(SP_BACKGROUND);
ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+ ASSERT_EQ(0, set_cpuset_policy(0, SP_FOREGROUND));
AssertPolicy(SP_FOREGROUND);
}
diff --git a/libstats/expresslog/.clang-format b/libstats/expresslog/.clang-format
new file mode 100644
index 0000000..cead3a0
--- /dev/null
+++ b/libstats/expresslog/.clang-format
@@ -0,0 +1,17 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: false
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
+IncludeCategories:
+ - Regex: '^"Log\.h"'
+ Priority: -1
diff --git a/libstats/expresslog/Android.bp b/libstats/expresslog/Android.bp
new file mode 100644
index 0000000..9cdc2c3
--- /dev/null
+++ b/libstats/expresslog/Android.bp
@@ -0,0 +1,71 @@
+
+//
+// Copyright (C) 2023 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.
+//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+ name: "libexpresslog",
+ srcs: [
+ "Counter.cpp",
+ ],
+ cflags: [
+ "-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
+ "-Wall",
+ "-Werror",
+ ],
+ header_libs: [
+ "libtextclassifier_hash_headers",
+ ],
+ static_libs: [
+ "libstatslog_express",
+ "libtextclassifier_hash_static",
+ ],
+ shared_libs: [
+ "libbase",
+ "libstatssocket",
+ ],
+ export_include_dirs: ["include"],
+}
+
+genrule {
+ name: "statslog_express.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_express.h --module expresslog --namespace android,expresslog",
+ out: [
+ "statslog_express.h",
+ ],
+}
+
+genrule {
+ name: "statslog_express.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_express.cpp --module expresslog --namespace android,expresslog --importHeader statslog_express.h",
+ out: [
+ "statslog_express.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libstatslog_express",
+ generated_sources: ["statslog_express.cpp"],
+ generated_headers: ["statslog_express.h"],
+ export_generated_headers: ["statslog_express.h"],
+ shared_libs: [
+ "libstatssocket",
+ ],
+}
diff --git a/libstats/expresslog/Counter.cpp b/libstats/expresslog/Counter.cpp
new file mode 100644
index 0000000..bee1303
--- /dev/null
+++ b/libstats/expresslog/Counter.cpp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2023 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 "include/Counter.h"
+
+#include <statslog_express.h>
+#include <string.h>
+#include <utils/hash/farmhash.h>
+
+namespace android {
+namespace expresslog {
+
+void Counter::logIncrement(const char* metricName, int64_t amount) {
+ const int64_t metricIdHash = farmhash::Fingerprint64(metricName, strlen(metricName));
+ stats_write(EXPRESS_EVENT_REPORTED, metricIdHash, amount);
+}
+
+} // namespace expresslog
+} // namespace android
diff --git a/libstats/expresslog/include/Counter.h b/libstats/expresslog/include/Counter.h
new file mode 100644
index 0000000..57328f5
--- /dev/null
+++ b/libstats/expresslog/include/Counter.h
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2023 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>
+
+namespace android {
+namespace expresslog {
+
+/** Counter encapsulates StatsD write API calls */
+class Counter final {
+public:
+ static void logIncrement(const char* metricId, int64_t amount = 1);
+};
+
+} // namespace expresslog
+} // namespace android
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index fe23b62..3dd269a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -213,4 +213,15 @@
$(hide) $(foreach lib,$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES), \
echo $(lib) >> $@;)
+#######################################
+# ramdisk_node_list
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := ramdisk_node_list
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)
+
+include $(BUILD_PREBUILT)
+
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a2fb88a..86c6eaa 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -643,7 +643,7 @@
chmod 0755 /sys/kernel/tracing
chmod 0755 /sys/kernel/debug/tracing
- # HALs required before storage encryption can get unlocked (FBE/FDE)
+ # HALs required before storage encryption can get unlocked (FBE)
class_start early_hal
# Load trusted keys from dm-verity protected partitions
@@ -746,9 +746,8 @@
# /data/apex is now available. Start apexd to scan and activate APEXes.
#
- # To handle userspace reboots as well as devices that use FDE, make sure
- # that apexd is started cleanly here (set apexd.status="") and that it is
- # restarted if it's already running.
+ # To handle userspace reboots, make sure that apexd is started cleanly here
+ # (set apexd.status="") and that it is restarted if it's already running.
#
# /data/apex uses encryption=None because direct I/O support is needed on
# APEX files, but some devices don't support direct I/O on encrypted files.
@@ -1289,11 +1288,13 @@
shutdown critical
on property:ro.debuggable=1
- # Give writes to anyone for the trace folder on debug builds.
+ # Give writes to the same group for the trace folder on debug builds,
+ # it's further protected by selinux policy.
# The folder is used to store method traces.
chmod 0773 /data/misc/trace
- # Give reads to anyone for the window trace folder on debug builds.
- chmod 0775 /data/misc/wmtrace
+ # Give writes and reads to anyone for the window trace folder on debug builds,
+ # it's further protected by selinux policy.
+ chmod 0777 /data/misc/wmtrace
# Give reads to anyone for the accessibility trace folder on debug builds.
chmod 0775 /data/misc/a11ytrace
diff --git a/rootdir/ramdisk_node_list b/rootdir/ramdisk_node_list
new file mode 100644
index 0000000..d3ab8a6
--- /dev/null
+++ b/rootdir/ramdisk_node_list
@@ -0,0 +1,3 @@
+dir dev 0755 0 0
+nod dev/null 0600 0 0 c 1 3
+nod dev/console 0600 0 0 c 5 1
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 7960af3..c3447d2 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -24,7 +24,7 @@
shared_libs: [
"android.hardware.health@1.0",
"android.hardware.health@2.0",
- "android.hardware.health-V1-ndk",
+ "android.hardware.health-V2-ndk",
"libbase",
"libbinder",
"libbinder_ndk",
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 711586a..17f8156 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -23,8 +23,11 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
#include <modprobe/modprobe.h>
+#include <sys/utsname.h>
+
namespace {
enum modprobe_mode {
@@ -37,9 +40,8 @@
void print_usage(void) {
LOG(INFO) << "Usage:";
LOG(INFO);
- // -d option is required on Android
- LOG(INFO) << " modprobe [options] -d DIR [--all=FILE|MODULE]...";
- LOG(INFO) << " modprobe [options] -d DIR MODULE [symbol=value]...";
+ LOG(INFO) << " modprobe [options] [-d DIR] [--all=FILE|MODULE]...";
+ LOG(INFO) << " modprobe [options] [-d DIR] MODULE [symbol=value]...";
LOG(INFO);
LOG(INFO) << "Options:";
LOG(INFO) << " --all=FILE: FILE to acquire module names from";
@@ -189,6 +191,12 @@
}
}
+ if (mod_dirs.empty()) {
+ utsname uts;
+ uname(&uts);
+ mod_dirs.emplace_back(android::base::StringPrintf("/lib/modules/%s", uts.release));
+ }
+
LOG(DEBUG) << "mode is " << mode;
LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
diff --git a/trusty/keymaster/TEST_MAPPING b/trusty/keymaster/TEST_MAPPING
index ae48327..0dd39fb 100644
--- a/trusty/keymaster/TEST_MAPPING
+++ b/trusty/keymaster/TEST_MAPPING
@@ -1,9 +1,6 @@
{
"presubmit": [
{
- "name": "RemoteProvisionerUnitTests"
- },
- {
"name": "VtsAidlKeyMintTargetTest"
},
{
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index 086051d..9d94ec4 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -33,5 +33,6 @@
// TODO(b/170753563): cc_fuzz can't deal with vendor components. Build
// libtrusty for system and vendor.
vendor_available: true,
+ recovery_available: true,
defaults: ["libtrusty_defaults"],
}