Merge "libnativeloader: add jiyong to OWNERS."
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index eb46903..830b35d 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -29,10 +29,13 @@
#include <sys/vfs.h>
#include <unistd.h>
+#include <set>
#include <string>
#include <vector>
#include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
#include <ext4_utils/ext4_utils.h>
#include "adb.h"
@@ -142,7 +145,7 @@
return true;
}
-static bool remount_partition(int fd, const char* dir, std::vector<std::string>& dedup) {
+static bool remount_partition(int fd, const char* dir) {
if (!directory_exists(dir)) {
return true;
}
@@ -168,32 +171,83 @@
return false;
}
if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
- if (errno == EROFS && fs_has_shared_blocks(dev.c_str())) {
- if (!can_unshare_blocks(fd, dev.c_str())) {
- return false;
- }
- // We return true so remount_service() can detect that the only
- // failure was deduplicated filesystems.
- dedup.push_back(dev);
- return true;
- }
WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
return false;
}
return true;
}
+static void reboot_for_remount(int fd, bool need_fsck) {
+ std::string reboot_cmd = "reboot";
+ if (need_fsck) {
+ const std::vector<std::string> options = {"--fsck_unshare_blocks"};
+ std::string err;
+ if (!write_bootloader_message(options, &err)) {
+ WriteFdFmt(fd, "Failed to set bootloader message: %s\n", err.c_str());
+ return;
+ }
+
+ WriteFdExactly(fd,
+ "The device will now reboot to recovery and attempt "
+ "un-deduplication.\n");
+ reboot_cmd = "reboot,recovery";
+ }
+
+ sync();
+ android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
+}
+
void remount_service(int fd, void* cookie) {
+ unique_fd close_fd(fd);
+
+ const char* cmd = reinterpret_cast<const char*>(cookie);
+ bool user_requested_reboot = cmd && !strcmp(cmd, "-R");
+
if (getuid() != 0) {
WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
- adb_close(fd);
return;
}
bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
- if (system_verified || vendor_verified) {
+ std::vector<std::string> partitions = {"/odm", "/oem", "/product", "/vendor"};
+ if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
+ partitions.push_back("/");
+ } else {
+ partitions.push_back("/system");
+ }
+
+ // Find partitions that are deduplicated, and can be un-deduplicated.
+ std::set<std::string> dedup;
+ for (const auto& partition : partitions) {
+ std::string dev = find_mount(partition.c_str(), partition == "/");
+ if (dev.empty() || !fs_has_shared_blocks(dev.c_str())) {
+ continue;
+ }
+ if (can_unshare_blocks(fd, dev.c_str())) {
+ dedup.emplace(partition);
+ }
+ }
+
+ bool verity_enabled = (system_verified || vendor_verified);
+
+ // Reboot now if the user requested it (and an operation needs a reboot).
+ if (user_requested_reboot) {
+ if (!dedup.empty() || verity_enabled) {
+ if (verity_enabled) {
+ set_verity_enabled_state_service(fd, nullptr);
+ }
+ reboot_for_remount(fd, !dedup.empty());
+ return;
+ }
+ WriteFdExactly(fd, "No reboot needed, skipping -R.\n");
+ }
+
+ // If we need to disable-verity, but we also need to perform a recovery
+ // fsck for deduplicated partitions, hold off on warning about verity. We
+ // can handle both verity and the recovery fsck in the same reboot cycle.
+ if (verity_enabled && dedup.empty()) {
// Allow remount but warn of likely bad effects
bool both = system_verified && vendor_verified;
WriteFdFmt(fd,
@@ -206,32 +260,40 @@
"Use \"adb disable-verity\" to disable verity.\n"
"If you do not, remount may succeed, however, you will still "
"not be able to write to these volumes.\n");
+ WriteFdExactly(fd,
+ "Alternately, use \"adb remount -R\" to disable verity "
+ "and automatically reboot.\n");
}
bool success = true;
- std::vector<std::string> dedup;
- if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
- success &= remount_partition(fd, "/", dedup);
- } else {
- success &= remount_partition(fd, "/system", dedup);
+ for (const auto& partition : partitions) {
+ // Don't try to remount partitions that need an fsck in recovery.
+ if (dedup.count(partition)) {
+ continue;
+ }
+ success &= remount_partition(fd, partition.c_str());
}
- success &= remount_partition(fd, "/odm", dedup);
- success &= remount_partition(fd, "/oem", dedup);
- success &= remount_partition(fd, "/product", dedup);
- success &= remount_partition(fd, "/vendor", dedup);
- if (!success) {
- WriteFdExactly(fd, "remount failed\n");
- } else if (dedup.empty()) {
- WriteFdExactly(fd, "remount succeeded\n");
- } else {
+ if (!dedup.empty()) {
WriteFdExactly(fd,
- "The following partitions are deduplicated and could "
- "not be remounted:\n");
+ "The following partitions are deduplicated and cannot "
+ "yet be remounted:\n");
for (const std::string& name : dedup) {
WriteFdFmt(fd, " %s\n", name.c_str());
}
+
+ WriteFdExactly(fd,
+ "To reboot and un-deduplicate the listed partitions, "
+ "please retry with adb remount -R.\n");
+ if (system_verified || vendor_verified) {
+ WriteFdExactly(fd, "Note: verity will be automatically disabled after reboot.\n");
+ }
+ return;
}
- adb_close(fd);
+ if (!success) {
+ WriteFdExactly(fd, "remount failed\n");
+ } else {
+ WriteFdExactly(fd, "remount succeeded\n");
+ }
}
diff --git a/adb/services.cpp b/adb/services.cpp
index 0b0c161..a757d90 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -291,7 +291,9 @@
} else if(!strncmp(name, "sync:", 5)) {
ret = create_service_thread("sync", file_sync_service, nullptr);
} else if(!strncmp(name, "remount:", 8)) {
- ret = create_service_thread("remount", remount_service, nullptr);
+ const char* options = name + strlen("remount:");
+ void* cookie = const_cast<void*>(reinterpret_cast<const void*>(options));
+ ret = create_service_thread("remount", remount_service, cookie);
} else if(!strncmp(name, "reboot:", 7)) {
void* arg = strdup(name + 7);
if (arg == NULL) return -1;
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 4b32b9d..f31337d 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -197,6 +197,7 @@
fprintf(stderr, " LOG-FATAL call libbase LOG(FATAL)\n");
fprintf(stderr, "\n");
fprintf(stderr, " SIGFPE cause a SIGFPE\n");
+ fprintf(stderr, " SIGILL cause a SIGILL\n");
fprintf(stderr, " SIGSEGV cause a SIGSEGV at address 0x0 (synonym: crash)\n");
fprintf(stderr, " SIGSEGV-non-null cause a SIGSEGV at a non-zero address\n");
fprintf(stderr, " SIGSEGV-unmapped mmap/munmap a region of memory and then attempt to access it\n");
@@ -268,6 +269,16 @@
} else if (!strcasecmp(arg, "SIGFPE")) {
raise(SIGFPE);
return EXIT_SUCCESS;
+ } else if (!strcasecmp(arg, "SIGILL")) {
+#if defined(__aarch64__)
+ __asm__ volatile(".word 0\n");
+#elif defined(__arm__)
+ __asm__ volatile(".word 0xe7f0def0\n");
+#elif defined(__i386__) || defined(__x86_64__)
+ __asm__ volatile("ud2\n");
+#else
+#error
+#endif
} else if (!strcasecmp(arg, "SIGTRAP")) {
raise(SIGTRAP);
return EXIT_SUCCESS;
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e11be1e..433bb46 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -102,10 +102,17 @@
if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
}
-static void dump_signal_info(log_t* log, const ThreadInfo& thread_info) {
- char addr_desc[32]; // ", fault addr 0x1234"
+static void dump_signal_info(log_t* log, const ThreadInfo& thread_info, Memory* process_memory) {
+ char addr_desc[64]; // ", fault addr 0x1234"
if (signal_has_si_addr(thread_info.siginfo)) {
- snprintf(addr_desc, sizeof(addr_desc), "%p", thread_info.siginfo->si_addr);
+ void* addr = thread_info.siginfo->si_addr;
+ if (thread_info.siginfo->si_signo == SIGILL) {
+ uint32_t instruction = {};
+ process_memory->Read(reinterpret_cast<uint64_t>(addr), &instruction, sizeof(instruction));
+ snprintf(addr_desc, sizeof(addr_desc), "%p (*pc=%#08x)", addr, instruction);
+ } else {
+ snprintf(addr_desc, sizeof(addr_desc), "%p", addr);
+ }
} else {
snprintf(addr_desc, sizeof(addr_desc), "--------");
}
@@ -418,7 +425,7 @@
dump_thread_info(log, thread_info);
if (thread_info.siginfo) {
- dump_signal_info(log, thread_info);
+ dump_signal_info(log, thread_info, process_memory);
}
if (primary_thread) {
diff --git a/init/Android.bp b/init/Android.bp
index a3083c1..25877c0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -170,6 +170,7 @@
srcs: [
"devices_test.cpp",
"init_test.cpp",
+ "keychords_test.cpp",
"persistent_properties_test.cpp",
"property_service_test.cpp",
"property_type_test.cpp",
@@ -235,7 +236,8 @@
"epoll.cpp",
"keychords.cpp",
"import_parser.cpp",
- "host_init_parser.cpp",
+ "host_import_parser.cpp",
+ "host_init_verifier.cpp",
"host_init_stubs.cpp",
"parser.cpp",
"rlimit_parser.cpp",
@@ -248,7 +250,10 @@
proto: {
type: "lite",
},
- generated_headers: ["generated_stub_builtin_function_map"],
+ generated_headers: [
+ "generated_stub_builtin_function_map",
+ "generated_android_ids"
+ ],
target: {
android: {
enabled: false,
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index f9b96eb..8a4b518 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -21,10 +21,6 @@
#include "stable_properties.h"
-#if !defined(__ANDROID__)
-#include "host_init_stubs.h"
-#endif
-
using android::base::GetBoolProperty;
using android::base::StartsWith;
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
new file mode 100644
index 0000000..faf6fc1
--- /dev/null
+++ b/init/host_import_parser.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host_import_parser.h"
+
+#include <android-base/strings.h>
+
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
+ if (args.size() != 2) {
+ return Error() << "single argument needed for import\n";
+ }
+
+ auto import_path = args[1];
+
+ if (StartsWith(import_path, "/system") || StartsWith(import_path, "/product") ||
+ StartsWith(import_path, "/odm") || StartsWith(import_path, "/vendor")) {
+ import_path = out_dir_ + "/" + import_path;
+ } else {
+ import_path = out_dir_ + "/root/" + import_path;
+ }
+
+ return ImportParser::ParseSection({"import", import_path}, filename, line);
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
new file mode 100644
index 0000000..e2980b2
--- /dev/null
+++ b/init/host_import_parser.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "import_parser.h"
+#include "parser.h"
+
+namespace android {
+namespace init {
+
+class HostImportParser : public ImportParser {
+ public:
+ HostImportParser(const std::string& out_dir, Parser* parser)
+ : ImportParser(parser), out_dir_(out_dir) {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+
+ private:
+ std::string out_dir_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
deleted file mode 100644
index df497ea..0000000
--- a/init/host_init_parser.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <pwd.h>
-
-#include <android-base/logging.h>
-
-#include "action.h"
-#include "action_manager.h"
-#include "action_parser.h"
-#include "parser.h"
-#include "result.h"
-#include "service.h"
-
-// The host passwd file won't have the Android entries, so we fake success here.
-passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
- char dummy_buf[] = "dummy";
- static passwd dummy_passwd = {
- .pw_name = dummy_buf,
- .pw_dir = dummy_buf,
- .pw_shell = dummy_buf,
- .pw_uid = 123,
- .pw_gid = 123,
- };
- return &dummy_passwd;
-}
-
-namespace android {
-namespace init {
-
-static Result<Success> do_stub(const BuiltinArguments& args) {
- return Success();
-}
-
-#include "generated_stub_builtin_function_map.h"
-
-int main(int argc, char** argv) {
- android::base::InitLogging(argv, &android::base::StdioLogger);
- if (argc != 2) {
- LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
- return -1;
- }
- const BuiltinFunctionMap function_map;
- Action::set_function_map(&function_map);
- ActionManager& am = ActionManager::GetInstance();
- ServiceList& sl = ServiceList::GetInstance();
- Parser parser;
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
-
- size_t num_errors = 0;
- if (!parser.ParseConfig(argv[1], &num_errors)) {
- LOG(ERROR) << "Failed to find script";
- return -1;
- }
- if (num_errors > 0) {
- LOG(ERROR) << "Parse failed with " << num_errors << " errors";
- return -1;
- }
- LOG(INFO) << "Parse success!";
- return 0;
-}
-
-} // namespace init
-} // namespace android
-
-int main(int argc, char** argv) {
- android::init::main(argc, argv);
-}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index c40a254..2352fc7 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -16,6 +16,8 @@
#include "host_init_stubs.h"
+#include <android-base/properties.h>
+
// unistd.h
int setgroups(size_t __size, const gid_t* __list) {
return 0;
@@ -28,7 +30,11 @@
std::string default_console = "/dev/console";
// property_service.h
-uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
+uint32_t SetProperty(const std::string& key, const std::string& value) {
+ android::base::SetProperty(key, value);
+ return 0;
+}
+uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
std::string*) {
return 0;
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
new file mode 100644
index 0000000..d6884af
--- /dev/null
+++ b/init/host_init_verifier.cpp
@@ -0,0 +1,162 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "host_import_parser.h"
+#include "host_init_stubs.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+#define EXCLUDE_FS_CONFIG_STRUCTURES
+#include "generated_android_ids.h"
+
+using namespace std::literals;
+
+using android::base::ParseInt;
+using android::base::ReadFileToString;
+using android::base::Split;
+
+static std::string out_dir;
+
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+ std::string passwd;
+ if (!ReadFileToString(out_dir + "/vendor/etc/passwd", &passwd)) {
+ return {};
+ }
+
+ std::vector<std::pair<std::string, int>> result;
+ auto passwd_lines = Split(passwd, "\n");
+ for (const auto& line : passwd_lines) {
+ auto split_line = Split(line, ":");
+ if (split_line.size() < 3) {
+ continue;
+ }
+ int uid = 0;
+ if (!ParseInt(split_line[2], &uid)) {
+ continue;
+ }
+ result.emplace_back(split_line[0], uid);
+ }
+ return result;
+}
+
+passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
+ // This isn't thread safe, but that's okay for our purposes.
+ static char static_name[32] = "";
+ static char static_dir[32] = "/";
+ static char static_shell[32] = "/system/bin/sh";
+ static passwd static_passwd = {
+ .pw_name = static_name,
+ .pw_dir = static_dir,
+ .pw_shell = static_shell,
+ .pw_uid = 0,
+ .pw_gid = 0,
+ };
+
+ for (size_t n = 0; n < android_id_count; ++n) {
+ if (!strcmp(android_ids[n].name, login)) {
+ snprintf(static_name, sizeof(static_name), "%s", android_ids[n].name);
+ static_passwd.pw_uid = android_ids[n].aid;
+ static_passwd.pw_gid = android_ids[n].aid;
+ return &static_passwd;
+ }
+ }
+
+ static const auto vendor_passwd = GetVendorPasswd();
+
+ for (const auto& [name, uid] : vendor_passwd) {
+ if (name == login) {
+ snprintf(static_name, sizeof(static_name), "%s", name.c_str());
+ static_passwd.pw_uid = uid;
+ static_passwd.pw_gid = uid;
+ return &static_passwd;
+ }
+ }
+
+ errno = ENOENT;
+ return nullptr;
+}
+
+namespace android {
+namespace init {
+
+static Result<Success> do_stub(const BuiltinArguments& args) {
+ return Success();
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::StdioLogger);
+ android::base::SetMinimumLogSeverity(android::base::ERROR);
+ if (argc != 3) {
+ LOG(ERROR) << "Usage: " << argv[0] << " <out directory> <properties>";
+ return -1;
+ }
+
+ out_dir = argv[1];
+
+ auto properties = Split(argv[2], ",");
+ for (const auto& property : properties) {
+ auto split_property = Split(property, "=");
+ if (split_property.size() != 2) {
+ continue;
+ }
+ property_set(split_property[0], split_property[1]);
+ }
+
+ const BuiltinFunctionMap function_map;
+ Action::set_function_map(&function_map);
+ ActionManager& am = ActionManager::GetInstance();
+ ServiceList& sl = ServiceList::GetInstance();
+ Parser parser;
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+ parser.AddSectionParser("import", std::make_unique<HostImportParser>(out_dir, &parser));
+
+ if (!parser.ParseConfig(argv[1] + "/root/init.rc"s)) {
+ LOG(ERROR) << "Failed to find root init.rc script";
+ return -1;
+ }
+ if (parser.parse_error_count() > 0) {
+ LOG(ERROR) << "Init script parsing failed with " << parser.parse_error_count() << " errors";
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace init
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::init::main(argc, argv);
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index e335fd1..fb3185e 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -41,14 +41,15 @@
return Success();
}
+Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+ return Error() << "Unexpected line found after import statement";
+}
+
void ImportParser::EndFile() {
auto current_imports = std::move(imports_);
imports_.clear();
for (const auto& [import, line_num] : current_imports) {
- if (!parser_->ParseConfig(import)) {
- PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
- << "'";
- }
+ parser_->ParseConfig(import);
}
}
diff --git a/init/import_parser.h b/init/import_parser.h
index 5a2f894..7bc72e6 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -30,6 +30,7 @@
ImportParser(Parser* parser) : parser_(parser) {}
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
void EndFile() override;
private:
diff --git a/init/init.cpp b/init/init.cpp
index 5652c5e..82648d9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -553,22 +553,30 @@
}
}
-void HandleKeychord(int id) {
+void HandleKeychord(const std::vector<int>& keycodes) {
// Only handle keychords if adb is enabled.
std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
- if (adb_enabled == "running") {
- Service* svc = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
- if (svc) {
- LOG(INFO) << "Starting service '" << svc->name() << "' from keychord " << id;
+ if (adb_enabled != "running") {
+ LOG(WARNING) << "Not starting service for keychord " << android::base::Join(keycodes, ' ')
+ << " because ADB is disabled";
+ return;
+ }
+
+ auto found = false;
+ for (const auto& service : ServiceList::GetInstance()) {
+ auto svc = service.get();
+ if (svc->keycodes() == keycodes) {
+ found = true;
+ LOG(INFO) << "Starting service '" << svc->name() << "' from keychord "
+ << android::base::Join(keycodes, ' ');
if (auto result = svc->Start(); !result) {
- LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord " << id
- << ": " << result.error();
+ LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord "
+ << android::base::Join(keycodes, ' ') << ": " << result.error();
}
- } else {
- LOG(ERROR) << "Service for keychord " << id << " not found";
}
- } else {
- LOG(WARNING) << "Not starting service for keychord " << id << " because ADB is disabled";
+ }
+ if (!found) {
+ LOG(ERROR) << "Service for keychord " << android::base::Join(keycodes, ' ') << " not found";
}
}
@@ -753,7 +761,7 @@
am.QueueBuiltinAction(
[&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
for (const auto& svc : ServiceList::GetInstance()) {
- svc->set_keychord_id(keychords.GetId(svc->keycodes()));
+ keychords.Register(svc->keycodes());
}
keychords.Start(&epoll, HandleKeychord);
return Success();
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 34de640..db60ce1 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -123,7 +123,7 @@
bool enabled = false;
import_kernel_cmdline(
false, [&enabled](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.lrap" && value == "1") {
+ if (key == "androidboot.logical_partitions" && value == "1") {
enabled = true;
}
});
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 9aa8b2a..1af06dd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -37,7 +37,7 @@
namespace android {
namespace init {
-Keychords::Keychords() : epoll_(nullptr), count_(0), inotify_fd_(-1) {}
+Keychords::Keychords() : epoll_(nullptr), inotify_fd_(-1) {}
Keychords::~Keychords() noexcept {
if (inotify_fd_ >= 0) {
@@ -108,23 +108,22 @@
}
}
-Keychords::Entry::Entry(const std::vector<int>& keycodes, int id)
- : keycodes(keycodes), id(id), notified(false) {}
+Keychords::Entry::Entry() : notified(false) {}
void Keychords::LambdaCheck() {
- for (auto& e : entries_) {
+ for (auto& [keycodes, entry] : entries_) {
auto found = true;
- for (auto& code : e.keycodes) {
+ for (auto& code : keycodes) {
if (!current_.GetBit(code)) {
- e.notified = false;
+ entry.notified = false;
found = false;
break;
}
}
if (!found) continue;
- if (e.notified) continue;
- e.notified = true;
- handler_(e.id);
+ if (entry.notified) continue;
+ entry.notified = true;
+ handler_(keycodes);
}
}
@@ -158,8 +157,8 @@
#endif
Keychords::Mask mask;
- for (auto& e : entries_) {
- for (auto& code : e.keycodes) {
+ for (auto& [keycodes, entry] : entries_) {
+ for (auto& code : keycodes) {
mask.resize(code);
mask.SetBit(code);
}
@@ -271,17 +270,15 @@
}
}
-int Keychords::GetId(const std::vector<int>& keycodes) {
- if (keycodes.empty()) return 0;
- ++count_;
- entries_.emplace_back(Entry(keycodes, count_));
- return count_;
+void Keychords::Register(const std::vector<int>& keycodes) {
+ if (keycodes.empty()) return;
+ entries_.try_emplace(keycodes, Entry());
}
-void Keychords::Start(Epoll* epoll, std::function<void(int)> handler) {
+void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
epoll_ = epoll;
handler_ = handler;
- if (count_) GeteventOpenDevice();
+ if (entries_.size()) GeteventOpenDevice();
}
} // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index 74a9195..00ed205 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -36,8 +36,8 @@
Keychords& operator=(Keychords&&) = delete;
~Keychords() noexcept;
- int GetId(const std::vector<int>& keycodes);
- void Start(Epoll* epoll, std::function<void(int)> handler);
+ void Register(const std::vector<int>& keycodes);
+ void Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler);
private:
// Bit management
@@ -65,10 +65,8 @@
};
struct Entry {
- Entry(const std::vector<int>& keycodes, int id);
+ Entry();
- const std::vector<int> keycodes;
- const int id;
bool notified;
};
@@ -84,12 +82,11 @@
void GeteventCloseDevice(const std::string& device);
Epoll* epoll_;
- std::function<void(int)> handler_;
+ std::function<void(const std::vector<int>&)> handler_;
std::map<std::string, int> registration_;
- int count_;
- std::vector<Entry> entries_;
+ std::map<const std::vector<int>, Entry> entries_;
Mask current_;
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
new file mode 100644
index 0000000..c8c47a8
--- /dev/null
+++ b/init/keychords_test.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "keychords.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "epoll.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace init {
+
+namespace {
+
+// This class is used to inject keys.
+class EventHandler {
+ public:
+ EventHandler();
+ EventHandler(const EventHandler&) = delete;
+ EventHandler(EventHandler&&);
+ EventHandler& operator=(const EventHandler&) = delete;
+ EventHandler& operator=(EventHandler&&);
+ ~EventHandler() noexcept;
+
+ bool init();
+
+ bool send(struct input_event& e);
+ bool send(uint16_t type, uint16_t code, uint16_t value);
+ bool send(uint16_t code, bool value);
+
+ private:
+ int fd_;
+};
+
+EventHandler::EventHandler() : fd_(-1) {}
+
+EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+ rval.fd_ = -1;
+}
+
+EventHandler& EventHandler::operator=(EventHandler&& rval) {
+ fd_ = rval.fd_;
+ rval.fd_ = -1;
+ return *this;
+}
+
+EventHandler::~EventHandler() {
+ if (fd_ == -1) return;
+ ::ioctl(fd_, UI_DEV_DESTROY);
+ ::close(fd_);
+}
+
+bool EventHandler::init() {
+ if (fd_ != -1) return true;
+ auto fd = TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK | O_CLOEXEC));
+ if (fd == -1) return false;
+ if (::ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {
+ ::close(fd);
+ return false;
+ }
+
+ static const struct uinput_user_dev u = {
+ .name = "com.google.android.init.test",
+ .id.bustype = BUS_VIRTUAL,
+ .id.vendor = 0x1AE0, // Google
+ .id.product = 0x494E, // IN
+ .id.version = 1,
+ };
+ if (TEMP_FAILURE_RETRY(::write(fd, &u, sizeof(u))) != sizeof(u)) {
+ ::close(fd);
+ return false;
+ }
+
+ // all keys
+ for (uint16_t i = 0; i < KEY_MAX; ++i) {
+ if (::ioctl(fd, UI_SET_KEYBIT, i) == -1) {
+ ::close(fd);
+ return false;
+ }
+ }
+ if (::ioctl(fd, UI_DEV_CREATE) == -1) {
+ ::close(fd);
+ return false;
+ }
+ fd_ = fd;
+ return true;
+}
+
+bool EventHandler::send(struct input_event& e) {
+ gettimeofday(&e.time, nullptr);
+ return TEMP_FAILURE_RETRY(::write(fd_, &e, sizeof(e))) == sizeof(e);
+}
+
+bool EventHandler::send(uint16_t type, uint16_t code, uint16_t value) {
+ struct input_event e = {.type = type, .code = code, .value = value};
+ return send(e);
+}
+
+bool EventHandler::send(uint16_t code, bool value) {
+ return (code < KEY_MAX) && init() && send(EV_KEY, code, value) && send(EV_SYN, SYN_REPORT, 0);
+}
+
+std::string InitFds(const char* prefix, pid_t pid = getpid()) {
+ std::string ret;
+
+ std::string init_fds("/proc/");
+ init_fds += std::to_string(pid) + "/fd";
+ std::unique_ptr<DIR, decltype(&closedir)> fds(opendir(init_fds.c_str()), closedir);
+ if (!fds) return ret;
+
+ dirent* entry;
+ while ((entry = readdir(fds.get()))) {
+ if (entry->d_name[0] == '.') continue;
+ std::string devname = init_fds + '/' + entry->d_name;
+ char buf[256];
+ auto retval = readlink(devname.c_str(), buf, sizeof(buf) - 1);
+ if ((retval < 0) || (size_t(retval) >= (sizeof(buf) - 1))) continue;
+ buf[retval] = '\0';
+ if (!android::base::StartsWith(buf, prefix)) continue;
+ if (ret.size() != 0) ret += ",";
+ ret += buf;
+ }
+ return ret;
+}
+
+std::string InitInputFds() {
+ return InitFds("/dev/input/");
+}
+
+std::string InitInotifyFds() {
+ return InitFds("anon_inode:inotify");
+}
+
+// NB: caller (this series of tests, or conversely the service parser in init)
+// is responsible for validation, sorting and uniqueness of the chords, so no
+// fuzzing is advised.
+
+const std::vector<int> escape_chord = {KEY_ESC};
+const std::vector<int> triple1_chord = {KEY_BACKSPACE, KEY_VOLUMEDOWN, KEY_VOLUMEUP};
+const std::vector<int> triple2_chord = {KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_BACK};
+
+const std::vector<const std::vector<int>> empty_chords;
+const std::vector<const std::vector<int>> chords = {
+ escape_chord,
+ triple1_chord,
+ triple2_chord,
+};
+
+class TestFrame {
+ public:
+ TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev = nullptr);
+
+ void RelaxForMs(std::chrono::milliseconds wait = 1ms);
+
+ void SetChord(int key, bool value = true);
+ void SetChords(const std::vector<int>& chord, bool value = true);
+ void ClrChord(int key);
+ void ClrChords(const std::vector<int>& chord);
+
+ bool IsOnlyChord(const std::vector<int>& chord) const;
+ bool IsNoChord() const;
+ bool IsChord(const std::vector<int>& chord) const;
+ void WaitForChord(const std::vector<int>& chord);
+
+ std::string Format() const;
+
+ private:
+ static std::string Format(const std::vector<const std::vector<int>>& chords);
+
+ Epoll epoll_;
+ Keychords keychords_;
+ std::vector<const std::vector<int>> keycodes_;
+ EventHandler* ev_;
+};
+
+TestFrame::TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev)
+ : ev_(ev) {
+ if (!epoll_.Open()) return;
+ for (const auto& keycodes : chords) keychords_.Register(keycodes);
+ keychords_.Start(&epoll_, [this](const std::vector<int>& keycodes) {
+ this->keycodes_.emplace_back(keycodes);
+ });
+}
+
+void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
+ epoll_.Wait(wait);
+}
+
+void TestFrame::SetChord(int key, bool value) {
+ ASSERT_TRUE(!!ev_);
+ RelaxForMs();
+ EXPECT_TRUE(ev_->send(key, value));
+}
+
+void TestFrame::SetChords(const std::vector<int>& chord, bool value) {
+ ASSERT_TRUE(!!ev_);
+ for (auto& key : chord) SetChord(key, value);
+ RelaxForMs();
+}
+
+void TestFrame::ClrChord(int key) {
+ ASSERT_TRUE(!!ev_);
+ SetChord(key, false);
+}
+
+void TestFrame::ClrChords(const std::vector<int>& chord) {
+ ASSERT_TRUE(!!ev_);
+ SetChords(chord, false);
+}
+
+bool TestFrame::IsOnlyChord(const std::vector<int>& chord) const {
+ auto ret = false;
+ for (const auto& keycode : keycodes_) {
+ if (keycode != chord) return false;
+ ret = true;
+ }
+ return ret;
+}
+
+bool TestFrame::IsNoChord() const {
+ return keycodes_.empty();
+}
+
+bool TestFrame::IsChord(const std::vector<int>& chord) const {
+ for (const auto& keycode : keycodes_) {
+ if (keycode == chord) return true;
+ }
+ return false;
+}
+
+void TestFrame::WaitForChord(const std::vector<int>& chord) {
+ for (int retry = 1000; retry && !IsChord(chord); --retry) RelaxForMs();
+}
+
+std::string TestFrame::Format(const std::vector<const std::vector<int>>& chords) {
+ std::string ret("{");
+ if (!chords.empty()) {
+ ret += android::base::Join(chords.front(), ' ');
+ for (auto it = std::next(chords.begin()); it != chords.end(); ++it) {
+ ret += ',';
+ ret += android::base::Join(*it, ' ');
+ }
+ }
+ return ret + '}';
+}
+
+std::string TestFrame::Format() const {
+ return Format(keycodes_);
+}
+
+} // namespace
+
+TEST(keychords, not_instantiated) {
+ TestFrame test_frame(empty_chords);
+ EXPECT_TRUE(InitInotifyFds().size() == 0);
+}
+
+TEST(keychords, instantiated) {
+ // Test if a valid set of chords results in proper instantiation of the
+ // underlying mechanisms for /dev/input/ attachment.
+ TestFrame test_frame(chords);
+ EXPECT_TRUE(InitInotifyFds().size() != 0);
+}
+
+TEST(keychords, init_inotify) {
+ std::string before(InitInputFds());
+
+ TestFrame test_frame(chords);
+
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+
+ for (int retry = 1000; retry && before == InitInputFds(); --retry) test_frame.RelaxForMs();
+ std::string after(InitInputFds());
+ EXPECT_NE(before, after);
+}
+
+TEST(keychords, key) {
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+ TestFrame test_frame(chords, &ev);
+
+ test_frame.SetChords(escape_chord);
+ test_frame.WaitForChord(escape_chord);
+ test_frame.ClrChords(escape_chord);
+ EXPECT_TRUE(test_frame.IsOnlyChord(escape_chord))
+ << "expected only " << android::base::Join(escape_chord, ' ') << " got "
+ << test_frame.Format();
+}
+
+TEST(keychords, keys_in_series) {
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+ TestFrame test_frame(chords, &ev);
+
+ for (auto& key : triple1_chord) {
+ test_frame.SetChord(key);
+ test_frame.ClrChord(key);
+ }
+ test_frame.WaitForChord(triple1_chord);
+ EXPECT_TRUE(test_frame.IsNoChord()) << "expected nothing got " << test_frame.Format();
+}
+
+TEST(keychords, keys_in_parallel) {
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+ TestFrame test_frame(chords, &ev);
+
+ test_frame.SetChords(triple2_chord);
+ test_frame.WaitForChord(triple2_chord);
+ test_frame.ClrChords(triple2_chord);
+ EXPECT_TRUE(test_frame.IsOnlyChord(triple2_chord))
+ << "expected only " << android::base::Join(triple2_chord, ' ') << " got "
+ << test_frame.Format();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/parser.cpp b/init/parser.cpp
index 4453aaa..ee6ee06 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -39,7 +39,7 @@
line_callbacks_.emplace_back(prefix, callback);
}
-void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
+void Parser::ParseData(const std::string& filename, const std::string& data) {
// TODO: Use a parser with const input and remove this copy
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
@@ -57,7 +57,7 @@
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
@@ -81,7 +81,7 @@
end_section();
if (auto result = callback(std::move(args)); !result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
break;
@@ -94,16 +94,20 @@
if (auto result =
section_parser->ParseSection(std::move(args), filename, state.line);
!result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) {
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
+ } else {
+ parse_error_count_++;
+ LOG(ERROR) << filename << ": " << state.line
+ << ": Invalid section keyword found";
}
args.clear();
break;
@@ -114,17 +118,17 @@
}
}
-bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
auto config_contents = ReadFile(path);
if (!config_contents) {
- LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
+ LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
return false;
}
config_contents->push_back('\n'); // TODO: fix parse_config.
- ParseData(path, *config_contents, parse_errors);
+ ParseData(path, *config_contents);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
@@ -133,11 +137,11 @@
return true;
}
-bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigDir(const std::string& path) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir) {
- PLOG(ERROR) << "Could not import directory '" << path << "'";
+ PLOG(INFO) << "Could not import directory '" << path << "'";
return false;
}
dirent* current_file;
@@ -153,7 +157,7 @@
// Sort first so we load files in a consistent order (bug 31996208)
std::sort(files.begin(), files.end());
for (const auto& file : files) {
- if (!ParseConfigFile(file, parse_errors)) {
+ if (!ParseConfigFile(file)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
@@ -161,16 +165,10 @@
}
bool Parser::ParseConfig(const std::string& path) {
- size_t parse_errors;
- return ParseConfig(path, &parse_errors);
-}
-
-bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
- *parse_errors = 0;
if (is_dir(path.c_str())) {
- return ParseConfigDir(path, parse_errors);
+ return ParseConfigDir(path);
}
- return ParseConfigFile(path, parse_errors);
+ return ParseConfigFile(path);
}
} // namespace init
diff --git a/init/parser.h b/init/parser.h
index f6e237f..3501d8c 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,17 +72,19 @@
Parser();
bool ParseConfig(const std::string& path);
- bool ParseConfig(const std::string& path, size_t* parse_errors);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ size_t parse_error_count() const { return parse_error_count_; }
+
private:
- void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
- bool ParseConfigFile(const std::string& path, size_t* parse_errors);
- bool ParseConfigDir(const std::string& path, size_t* parse_errors);
+ void ParseData(const std::string& filename, const std::string& data);
+ bool ParseConfigFile(const std::string& path);
+ bool ParseConfigDir(const std::string& path);
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
+ size_t parse_error_count_ = 0;
};
} // namespace init
diff --git a/init/service.cpp b/init/service.cpp
index a698735..565cae7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -228,7 +228,6 @@
seclabel_(seclabel),
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
"onrestart", {}),
- keychord_id_(0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -548,7 +547,7 @@
for (auto& key : keycodes_) {
if (key == code) return Error() << "duplicate keycode: " << args[i];
}
- keycodes_.emplace_back(code);
+ keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
} else {
return Error() << "invalid keycode: " << args[i];
}
diff --git a/init/service.h b/init/service.h
index cbfd52f..ea79a07 100644
--- a/init/service.h
+++ b/init/service.h
@@ -108,8 +108,6 @@
const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
const std::string& seclabel() const { return seclabel_; }
const std::vector<int>& keycodes() const { return keycodes_; }
- int keychord_id() const { return keychord_id_; }
- void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
IoSchedClass ioprio_class() const { return ioprio_class_; }
int ioprio_pri() const { return ioprio_pri_; }
const std::set<std::string>& interfaces() const { return interfaces_; }
@@ -199,9 +197,8 @@
std::set<std::string> interfaces_; // e.g. some.package.foo@1.0::IBaz/instance-name
- // keycodes for triggering this service via /dev/keychord
+ // keycodes for triggering this service via /dev/input/input*
std::vector<int> keycodes_;
- int keychord_id_;
IoSchedClass ioprio_class_;
int ioprio_pri_;
diff --git a/init/service_test.cpp b/init/service_test.cpp
index b43c2e9..194aa2b 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -46,7 +46,6 @@
EXPECT_EQ(0U, service_in_old_memory->uid());
EXPECT_EQ(0U, service_in_old_memory->gid());
EXPECT_EQ(0U, service_in_old_memory->namespace_flags());
- EXPECT_EQ(0, service_in_old_memory->keychord_id());
EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
EXPECT_EQ(0, service_in_old_memory->priority());
@@ -66,7 +65,6 @@
EXPECT_EQ(0U, service_in_old_memory2->uid());
EXPECT_EQ(0U, service_in_old_memory2->gid());
EXPECT_EQ(0U, service_in_old_memory2->namespace_flags());
- EXPECT_EQ(0, service_in_old_memory2->keychord_id());
EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
EXPECT_EQ(0, service_in_old_memory2->priority());
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 0f2b460..6169324 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -80,6 +80,7 @@
{ 00775, AID_ROOT, AID_ROOT, 0, "data/preloads" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "product/bin" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
@@ -195,6 +196,7 @@
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "product/bin/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
@@ -237,9 +239,10 @@
return fd;
}
-// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>" or
+// "vendor/<stuff>"
static bool is_partition(const char* path, size_t len) {
- static const char* partitions[] = {"vendor/", "oem/", "odm/"};
+ static const char* partitions[] = {"odm/", "oem/", "product/", "vendor/"};
for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
size_t plen = strlen(partitions[i]);
if (len <= plen) continue;
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index 6d9141a..9cc0c9b 100644
--- a/libmemunreachable/README.md
+++ b/libmemunreachable/README.md
@@ -44,7 +44,7 @@
### C++ interface ###
-####`bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)`####
+#### `bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)` ####
Updates an `UnreachableMemoryInfo` object with information on leaks, including details on up to `limit` leaks. Returns true if leak detection succeeded.
#### `std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)` ####
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index 406e208..bce308b 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -111,7 +111,7 @@
struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO image */
- uint64_t recovery_dtbo_offset; /* physical load addr */
+ uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
uint32_t header_size;
} __attribute__((packed));
@@ -138,11 +138,14 @@
* 1. kernel and ramdisk are required (size != 0)
* 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
* 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second, recovery_dtbo) at
+ * 4. load each element (kernel, ramdisk, second) at
* the specified physical address (kernel_addr, etc)
- * 5. prepare tags at tag_addr. kernel_args[] is
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery dtbo and
+ * apply the correct set of overlays on the base device tree depending on the
+ * hardware/product revision.
+ * 6. prepare tags at tag_addr. kernel_args[] is
* appended to the kernel commandline in the tags.
- * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 7. if second_size != 0: jump to second_addr
+ * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 8. if second_size != 0: jump to second_addr
* else: jump to kernel_addr
*/
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 51e3f9e..a0b1996 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,6 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+dir.system = /product/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index ab03755..db65c14 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,6 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+dir.system = /product/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/