Merge "Revert "SocketListener: use poll() instead of select()""
diff --git a/adb/Android.bp b/adb/Android.bp
index 1f41e4f..553473f 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -96,6 +96,7 @@
"adb_io.cpp",
"adb_listeners.cpp",
"adb_trace.cpp",
+ "adb_unique_fd.cpp",
"adb_utils.cpp",
"fdevent.cpp",
"services.cpp",
@@ -276,6 +277,7 @@
cc_library_static {
name: "libadbd",
defaults: ["adb_defaults"],
+ recovery_available: true,
// libminadbd wants both, for some reason.
compile_multilib: "both",
@@ -302,6 +304,7 @@
// adbd must be static, as it is copied into the recovery image.
static_executable: true,
+ recovery_available: true,
srcs: [
"daemon/main.cpp",
diff --git a/adb/adb_unique_fd.cpp b/adb/adb_unique_fd.cpp
new file mode 100644
index 0000000..2079be1
--- /dev/null
+++ b/adb/adb_unique_fd.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "adb_unique_fd.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "sysdeps.h"
+
+#if !defined(_WIN32)
+bool Pipe(unique_fd* read, unique_fd* write, int flags) {
+ int pipefd[2];
+#if !defined(__APPLE__)
+ if (pipe2(pipefd, flags) != 0) {
+ return false;
+ }
+#else
+ // Darwin doesn't have pipe2. Implement it ourselves.
+ if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
+ errno = EINVAL;
+ return false;
+ }
+
+ if (pipe(pipefd) != 0) {
+ return false;
+ }
+
+ if (flags & O_CLOEXEC) {
+ if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
+ fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+ adb_close(pipefd[0]);
+ adb_close(pipefd[1]);
+ return false;
+ }
+ }
+
+ if (flags & O_NONBLOCK) {
+ if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
+ fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+ adb_close(pipefd[0]);
+ adb_close(pipefd[1]);
+ return false;
+ }
+ }
+#endif
+
+ read->reset(pipefd[0]);
+ write->reset(pipefd[1]);
+ return true;
+}
+#endif
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index 7d2354d..be63262 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -16,6 +16,7 @@
#pragma once
+#include <errno.h>
#include <unistd.h>
#include <android-base/unique_fd.h>
@@ -28,40 +29,5 @@
using unique_fd = android::base::unique_fd_impl<AdbCloser>;
#if !defined(_WIN32)
-inline bool Pipe(unique_fd* read, unique_fd* write, int flags = 0) {
- int pipefd[2];
-#if !defined(__APPLE__)
- if (pipe2(pipefd, flags) != 0) {
- return false;
- }
-#else
- // Darwin doesn't have pipe2. Implement it ourselves.
- if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
- errno = EINVAL;
- return false;
- }
-
- if (pipe(pipefd) != 0) {
- return false;
- }
-
- if (flags & O_CLOEXEC) {
- if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
- fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
- PLOG(FATAL) << "failed to set FD_CLOEXEC on newly created pipe";
- }
- }
-
- if (flags & O_NONBLOCK) {
- if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
- fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
- PLOG(FATAL) << "failed to set O_NONBLOCK on newly created pipe";
- }
- }
-#endif
-
- read->reset(pipefd[0]);
- write->reset(pipefd[1]);
- return true;
-}
+bool Pipe(unique_fd* read, unique_fd* write, int flags = 0);
#endif
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/base/Android.bp b/base/Android.bp
index 47b29c6..3d80d97 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -48,6 +48,7 @@
"file.cpp",
"logging.cpp",
"parsenetaddress.cpp",
+ "properties.cpp",
"quick_exit.cpp",
"stringprintf.cpp",
"strings.cpp",
@@ -58,9 +59,6 @@
shared_libs: ["liblog"],
target: {
android: {
- srcs: [
- "properties.cpp",
- ],
sanitize: {
misc_undefined: ["integer"],
},
@@ -95,6 +93,7 @@
name: "libbase",
defaults: ["libbase_defaults"],
vendor_available: true,
+ recovery_available: true,
host_supported: true,
vndk: {
enabled: true,
@@ -129,6 +128,7 @@
"parsedouble_test.cpp",
"parseint_test.cpp",
"parsenetaddress_test.cpp",
+ "properties_test.cpp",
"quick_exit_test.cpp",
"scopeguard_test.cpp",
"stringprintf_test.cpp",
@@ -138,7 +138,6 @@
],
target: {
android: {
- srcs: ["properties_test.cpp"],
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 041586c..3a05143 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -19,10 +19,6 @@
#include <sys/cdefs.h>
-#if !defined(__BIONIC__)
-#error Only bionic supports system properties.
-#endif
-
#include <chrono>
#include <limits>
#include <string>
@@ -62,14 +58,18 @@
// Waits for the system property `key` to have the value `expected_value`.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
+#if defined(__BIONIC__)
bool WaitForProperty(const std::string& key, const std::string& expected_value,
std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
+#endif
// Waits for the system property `key` to be created.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
+#if defined(__BIONIC__)
bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
std::chrono::milliseconds::max());
+#endif
} // namespace base
} // namespace android
diff --git a/base/properties.cpp b/base/properties.cpp
index 6cf43f9..d5a5918 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-
#include "android-base/properties.h"
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/system_properties.h>
#include <sys/_system_properties.h>
+#endif
#include <algorithm>
#include <chrono>
#include <limits>
+#include <map>
#include <string>
#include <android-base/parseint.h>
@@ -31,24 +33,6 @@
namespace android {
namespace base {
-std::string GetProperty(const std::string& key, const std::string& default_value) {
- const prop_info* pi = __system_property_find(key.c_str());
- if (pi == nullptr) return default_value;
-
- std::string property_value;
- __system_property_read_callback(pi,
- [](void* cookie, const char*, const char* value, unsigned) {
- auto property_value = reinterpret_cast<std::string*>(cookie);
- *property_value = value;
- },
- &property_value);
-
- // If the property exists but is empty, also return the default value.
- // Since we can't remove system properties, "empty" is traditionally
- // the same as "missing" (this was true for cutils' property_get).
- return property_value.empty() ? default_value : property_value;
-}
-
bool GetBoolProperty(const std::string& key, bool default_value) {
std::string value = GetProperty(key, "");
if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") {
@@ -85,10 +69,43 @@
template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
+#if !defined(__BIONIC__)
+static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
+static int __system_property_set(const char* key, const char* value) {
+ g_properties[key] = value;
+ return 0;
+}
+#endif
+
+std::string GetProperty(const std::string& key, const std::string& default_value) {
+ std::string property_value;
+#if defined(__BIONIC__)
+ const prop_info* pi = __system_property_find(key.c_str());
+ if (pi == nullptr) return default_value;
+
+ __system_property_read_callback(pi,
+ [](void* cookie, const char*, const char* value, unsigned) {
+ auto property_value = reinterpret_cast<std::string*>(cookie);
+ *property_value = value;
+ },
+ &property_value);
+#else
+ auto it = g_properties.find(key);
+ if (it == g_properties.end()) return default_value;
+ property_value = it->second;
+#endif
+ // If the property exists but is empty, also return the default value.
+ // Since we can't remove system properties, "empty" is traditionally
+ // the same as "missing" (this was true for cutils' property_get).
+ return property_value.empty() ? default_value : property_value;
+}
+
bool SetProperty(const std::string& key, const std::string& value) {
return (__system_property_set(key.c_str(), value.c_str()) == 0);
}
+#if defined(__BIONIC__)
+
struct WaitForPropertyData {
bool done;
const std::string* expected_value;
@@ -175,5 +192,7 @@
return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
}
+#endif
+
} // namespace base
} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index de5f3dc..e7d4880 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -23,7 +23,9 @@
#include <string>
#include <thread>
-using namespace std::chrono_literals;
+#if !defined(_WIN32)
+using namespace std::literals;
+#endif
TEST(properties, smoke) {
android::base::SetProperty("debug.libbase.property_test", "hello");
@@ -126,6 +128,7 @@
TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
TEST(properties, WaitForProperty) {
+#if defined(__BIONIC__)
std::atomic<bool> flag{false};
std::thread thread([&]() {
std::this_thread::sleep_for(100ms);
@@ -138,9 +141,13 @@
flag = true;
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForProperty_timeout) {
+#if defined(__BIONIC__)
auto t0 = std::chrono::steady_clock::now();
ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
200ms));
@@ -149,9 +156,13 @@
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
// Upper bounds on timing are inherently flaky, but let's try...
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForProperty_MaxTimeout) {
+#if defined(__BIONIC__)
std::atomic<bool> flag{false};
std::thread thread([&]() {
android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -165,9 +176,13 @@
// Test that this does not immediately return false due to overflow issues with the timeout.
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForProperty_NegativeTimeout) {
+#if defined(__BIONIC__)
std::atomic<bool> flag{false};
std::thread thread([&]() {
android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -181,9 +196,13 @@
// Assert that this immediately returns with a negative timeout
ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForPropertyCreation) {
+#if defined(__BIONIC__)
std::thread thread([&]() {
std::this_thread::sleep_for(100ms);
android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
@@ -192,9 +211,13 @@
ASSERT_TRUE(android::base::WaitForPropertyCreation(
"debug.libbase.WaitForPropertyCreation_test", 1s));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForPropertyCreation_timeout) {
+#if defined(__BIONIC__)
auto t0 = std::chrono::steady_clock::now();
ASSERT_FALSE(android::base::WaitForPropertyCreation(
"debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
@@ -203,4 +226,7 @@
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
// Upper bounds on timing are inherently flaky, but let's try...
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 01b8948..8ed92a6 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -445,6 +445,10 @@
*hw_reset* ) var="hard,hw_reset" ;;
*usb* ) var="cold,charger" ;;
*rtc* ) var="cold,rtc" ;;
+ *2sec_reboot* ) var="cold,rtc,2sec" ;;
+ *wdt_by_pass_pwk* ) var="warm" ;;
+ wdt ) var="reboot" ;;
+ *tool_by_pass_pwk* ) var="reboot,tool" ;;
*bootloader* ) var="bootloader" ;;
* ) var="reboot" ;;
esac
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1b13b21..d5fb047 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -201,7 +201,7 @@
{"cold", 56},
{"hard", 57},
{"warm", 58},
- // {"recovery", 59}, // Duplicate of enum 3 above. Immediate reuse possible.
+ {"reboot,kernel_power_off_charging__reboot_system", 59}, // Can not happen
{"thermal-shutdown", 60},
{"shutdown,thermal", 61},
{"shutdown,battery", 62},
@@ -228,7 +228,7 @@
{"2sec_reboot", 83},
{"reboot,by_key", 84},
{"reboot,longkey", 85},
- {"reboot,2sec", 86},
+ {"reboot,2sec", 86}, // Deprecate in two years, replaced with cold,rtc,2sec
{"shutdown,thermal,battery", 87},
{"reboot,its_just_so_hard", 88}, // produced by boot_reason_test
{"reboot,Its Just So Hard", 89}, // produced by boot_reason_test
@@ -306,6 +306,10 @@
{"kernel_panic,sysrq,livelock,alarm", 161}, // llkd
{"kernel_panic,sysrq,livelock,driver", 162}, // llkd
{"kernel_panic,sysrq,livelock,zombie", 163}, // llkd
+ {"kernel_panic,modem", 164},
+ {"kernel_panic,adsp", 165},
+ {"kernel_panic,dsps", 166},
+ {"kernel_panic,wcnss", 167},
};
// Converts a string value representing the reason the system booted to an
@@ -702,6 +706,10 @@
{"Corrupt kernel stack", "stack"},
{"low stack detected", "stack"},
{"corrupted stack end", "stack"},
+ {"subsys-restart: Resetting the SoC - modem crashed.", "modem"},
+ {"subsys-restart: Resetting the SoC - adsp crashed.", "adsp"},
+ {"subsys-restart: Resetting the SoC - dsps crashed.", "dsps"},
+ {"subsys-restart: Resetting the SoC - wcnss crashed.", "wcnss"},
};
ret = "kernel_panic";
@@ -782,7 +790,10 @@
{"hard,hw_reset", "hw_reset"},
{"cold,charger", "usb"},
{"cold,rtc", "rtc"},
- {"reboot,2sec", "2sec_reboot"},
+ {"cold,rtc,2sec", "2sec_reboot"},
+ {"!warm", "wdt_by_pass_pwk"}, // change flavour of blunt
+ {"!reboot", "^wdt$"}, // change flavour of blunt
+ {"reboot,tool", "tool_by_pass_pwk"},
{"bootloader", ""},
};
@@ -842,6 +853,10 @@
ret = "reboot," + subReason; // legitimize unknown reasons
}
}
+ // Some bootloaders shutdown results record in last kernel message.
+ if (!strcmp(ret.c_str(), "reboot,kernel_power_off_charging__reboot_system")) {
+ ret = "shutdown";
+ }
}
// Check for kernel panics, allowed to override reboot command.
@@ -1114,6 +1129,13 @@
return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
}
+void SetSystemBootReason() {
+ const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
+ const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
+ // Record the scrubbed system_boot_reason to the property
+ SetProperty(system_reboot_reason_property, system_boot_reason);
+}
+
// Records several metrics related to the time it takes to boot the device,
// including disambiguating boot time on encrypted or non-encrypted devices.
void RecordBootComplete() {
@@ -1193,12 +1215,10 @@
boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
// Log the scrubbed system_boot_reason.
- const std::string system_reason(BootReasonStrToReason(reason));
+ const std::string system_reason(GetProperty(system_reboot_reason_property));
int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
- // Record the scrubbed system_boot_reason to the property
- SetProperty(system_reboot_reason_property, system_reason);
if (reason == "") {
SetProperty(bootloader_reboot_reason_property, system_reason);
}
@@ -1264,20 +1284,22 @@
int option_index = 0;
static const char value_str[] = "value";
+ static const char system_boot_reason_str[] = "set_system_boot_reason";
static const char boot_complete_str[] = "record_boot_complete";
static const char boot_reason_str[] = "record_boot_reason";
static const char factory_reset_str[] = "record_time_since_factory_reset";
static const struct option long_options[] = {
// clang-format off
- { "help", no_argument, NULL, 'h' },
- { "log", no_argument, NULL, 'l' },
- { "print", no_argument, NULL, 'p' },
- { "record", required_argument, NULL, 'r' },
- { value_str, required_argument, NULL, 0 },
- { boot_complete_str, no_argument, NULL, 0 },
- { boot_reason_str, no_argument, NULL, 0 },
- { factory_reset_str, no_argument, NULL, 0 },
- { NULL, 0, NULL, 0 }
+ { "help", no_argument, NULL, 'h' },
+ { "log", no_argument, NULL, 'l' },
+ { "print", no_argument, NULL, 'p' },
+ { "record", required_argument, NULL, 'r' },
+ { value_str, required_argument, NULL, 0 },
+ { system_boot_reason_str, no_argument, NULL, 0 },
+ { boot_complete_str, no_argument, NULL, 0 },
+ { boot_reason_str, no_argument, NULL, 0 },
+ { factory_reset_str, no_argument, NULL, 0 },
+ { NULL, 0, NULL, 0 }
// clang-format on
};
@@ -1293,6 +1315,8 @@
// |optarg| is an external variable set by getopt representing
// the option argument.
value = optarg;
+ } else if (option_name == system_boot_reason_str) {
+ SetSystemBootReason();
} else if (option_name == boot_complete_str) {
RecordBootComplete();
} else if (option_name == boot_reason_str) {
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f06a38f..1300a27 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -68,8 +68,9 @@
# Record boot complete metrics.
on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
+ # Converts bootloader boot reason to system boot reason
# Record boot_complete and related stats (decryption, etc).
# Record the boot reason.
# Record time since factory reset.
# Log all boot events.
- exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
+ exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7c28b28..0b13662 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -17,6 +17,7 @@
cc_library_headers {
name: "libdebuggerd_common_headers",
export_include_dirs: ["common/include"],
+ recovery_available: true,
}
cc_library_shared {
@@ -67,6 +68,7 @@
cc_library_static {
name: "libdebuggerd_handler_core",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: ["handler/debuggerd_handler.cpp"],
header_libs: [
@@ -88,6 +90,7 @@
cc_library_static {
name: "libdebuggerd_handler",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: ["handler/debuggerd_fallback_nop.cpp"],
whole_static_libs: [
@@ -143,6 +146,7 @@
cc_library_static {
name: "libdebuggerd",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: [
"libdebuggerd/backtrace.cpp",
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/demangle/Android.bp b/demangle/Android.bp
index cf6abfd..fd79cf8 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -36,6 +36,7 @@
name: "libdemangle",
defaults: ["libdemangle_defaults"],
vendor_available: true,
+ recovery_available: true,
srcs: [
"Demangler.cpp",
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index a7ecf37..6bee28c 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -2,6 +2,7 @@
name: "libdiagnose_usb",
cflags: ["-Wall", "-Wextra", "-Werror"],
host_supported: true,
+ recovery_available: true,
target: {
windows: {
enabled: true,
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 6493262..5aa87d9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -316,7 +316,8 @@
static int show_help() {
// clang-format off
fprintf(stdout,
-/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
+// 1 2 3 4 5 6 7 8
+// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
"usage: fastboot [OPTION...] COMMAND...\n"
"\n"
"flashing:\n"
@@ -324,8 +325,8 @@
" flashall Flash all partitions from $ANDROID_PRODUCT_OUT.\n"
" On A/B devices, flashed slot is set as active.\n"
" Secondary images may be flashed to inactive slot.\n"
- " flash PARTITION [FILENAME]\n"
- " Flash given partition only.\n"
+ " flash PARTITION [FILENAME] Flash given partition, using the image from\n"
+ " $ANDROID_PRODUCT_OUT if no filename is given.\n"
"\n"
"basics:\n"
" devices [-l] List devices in bootloader (-l: with device paths).\n"
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 05dba15..bc3b04b 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -33,6 +33,7 @@
cc_library_static {
name: "libfs_mgr",
defaults: ["fs_mgr_defaults"],
+ recovery_available: true,
export_include_dirs: ["include"],
include_dirs: ["system/vold"],
srcs: [
@@ -79,6 +80,7 @@
cc_library_static {
name: "libfstab",
vendor_available: true,
+ recovery_available: true,
defaults: ["fs_mgr_defaults"],
srcs: [
"fs_mgr_fstab.cpp",
diff --git a/init/Android.bp b/init/Android.bp
index 63f3fca..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",
@@ -232,8 +233,11 @@
"action_parser.cpp",
"capabilities.cpp",
"descriptors.cpp",
+ "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",
@@ -246,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.cpp b/init/action.cpp
index f782b51..11335ca 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,16 +18,11 @@
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include "util.h"
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
using android::base::Join;
namespace android {
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index a2c9671..8a4b518 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,16 +16,11 @@
#include "action_parser.h"
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include "stable_properties.h"
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#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 4451ac8..2352fc7 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -16,33 +16,25 @@
#include "host_init_stubs.h"
+#include <android-base/properties.h>
+
// unistd.h
int setgroups(size_t __size, const gid_t* __list) {
return 0;
}
namespace android {
-namespace base {
-
-std::string GetProperty(const std::string&, const std::string& default_value) {
- return default_value;
-}
-
-bool GetBoolProperty(const std::string&, bool default_value) {
- return default_value;
-}
-
-} // namespace base
-} // namespace android
-
-namespace android {
namespace init {
// init.h
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_stubs.h b/init/host_init_stubs.h
index bb241af..f0e1f07 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -29,16 +29,6 @@
// unistd.h
int setgroups(size_t __size, const gid_t* __list);
-// android-base/properties.h
-namespace android {
-namespace base {
-
-std::string GetProperty(const std::string& key, const std::string& default_value);
-bool GetBoolProperty(const std::string& key, bool default_value);
-
-} // namespace base
-} // namespace android
-
namespace android {
namespace init {
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 fd9a90c..82648d9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -553,6 +553,33 @@
}
}
+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") {
+ 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 "
+ << android::base::Join(keycodes, ' ') << ": " << result.error();
+ }
+ }
+ }
+ if (!found) {
+ LOG(ERROR) << "Service for keychord " << android::base::Join(keycodes, ' ') << " not found";
+ }
+}
+
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
@@ -730,9 +757,13 @@
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
+ Keychords keychords;
am.QueueBuiltinAction(
- [&epoll](const BuiltinArguments& args) -> Result<Success> {
- KeychordInit(&epoll);
+ [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
+ for (const auto& svc : ServiceList::GetInstance()) {
+ keychords.Register(svc->keycodes());
+ }
+ keychords.Start(&epoll, HandleKeychord);
return Success();
},
"KeychordInit");
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 418cdeb..1af06dd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -33,150 +33,117 @@
#include <vector>
#include <android-base/logging.h>
-#include <android-base/properties.h>
-
-#include "init.h"
-#include "service.h"
namespace android {
namespace init {
-namespace {
+Keychords::Keychords() : epoll_(nullptr), inotify_fd_(-1) {}
-int keychords_count;
-Epoll* epoll;
-
-struct KeychordEntry {
- const std::vector<int> keycodes;
- bool notified;
- int id;
-
- KeychordEntry(const std::vector<int>& keycodes, int id)
- : keycodes(keycodes), notified(false), id(id) {}
-};
-
-std::vector<KeychordEntry> keychord_entries;
-
-// Bit management
-class KeychordMask {
- private:
- typedef unsigned int mask_t;
- std::vector<mask_t> bits;
- static constexpr size_t bits_per_byte = 8;
-
- public:
- explicit KeychordMask(size_t bit = 0) : bits((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
-
- void SetBit(size_t bit, bool value = true) {
- auto idx = bit / (bits_per_byte * sizeof(mask_t));
- if (idx >= bits.size()) return;
- if (value) {
- bits[idx] |= mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t)));
- } else {
- bits[idx] &= ~(mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
- }
+Keychords::~Keychords() noexcept {
+ if (inotify_fd_ >= 0) {
+ epoll_->UnregisterHandler(inotify_fd_);
+ ::close(inotify_fd_);
}
+ while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
+}
- bool GetBit(size_t bit) const {
- auto idx = bit / (bits_per_byte * sizeof(mask_t));
- return bits[idx] & (mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
- }
+Keychords::Mask::Mask(size_t bit) : bits_((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
- size_t bytesize() const { return bits.size() * sizeof(mask_t); }
- void* data() { return bits.data(); }
- size_t size() const { return bits.size() * sizeof(mask_t) * bits_per_byte; }
- void resize(size_t bit) {
- auto idx = bit / (bits_per_byte * sizeof(mask_t));
- if (idx >= bits.size()) {
- bits.resize(idx + 1, 0);
- }
- }
-
- operator bool() const {
- for (size_t i = 0; i < bits.size(); ++i) {
- if (bits[i]) return true;
- }
- return false;
- }
-
- KeychordMask operator&(const KeychordMask& rval) const {
- auto len = std::min(bits.size(), rval.bits.size());
- KeychordMask ret;
- ret.bits.resize(len);
- for (size_t i = 0; i < len; ++i) {
- ret.bits[i] = bits[i] & rval.bits[i];
- }
- return ret;
- }
-
- void operator|=(const KeychordMask& rval) {
- size_t len = rval.bits.size();
- bits.resize(len);
- for (size_t i = 0; i < len; ++i) {
- bits[i] |= rval.bits[i];
- }
- }
-};
-
-KeychordMask keychord_current;
-
-constexpr char kDevicePath[] = "/dev/input";
-
-std::map<std::string, int> keychord_registration;
-
-void HandleKeychord(int id) {
- // 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 (auto result = svc->Start(); !result) {
- LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord " << id
- << ": " << result.error();
- }
- } else {
- LOG(ERROR) << "Service for keychord " << id << " not found";
- }
+void Keychords::Mask::SetBit(size_t bit, bool value) {
+ auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+ if (idx >= bits_.size()) return;
+ if (value) {
+ bits_[idx] |= mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t)));
} else {
- LOG(WARNING) << "Not starting service for keychord " << id << " because ADB is disabled";
+ bits_[idx] &= ~(mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
}
}
-void KeychordLambdaCheck() {
- for (auto& e : keychord_entries) {
- bool found = true;
- for (auto& code : e.keycodes) {
- if (!keychord_current.GetBit(code)) {
- e.notified = false;
+bool Keychords::Mask::GetBit(size_t bit) const {
+ auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+ return bits_[idx] & (mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
+}
+
+size_t Keychords::Mask::bytesize() const {
+ return bits_.size() * sizeof(mask_t);
+}
+
+void* Keychords::Mask::data() {
+ return bits_.data();
+}
+
+size_t Keychords::Mask::size() const {
+ return bits_.size() * sizeof(mask_t) * kBitsPerByte;
+}
+
+void Keychords::Mask::resize(size_t bit) {
+ auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+ if (idx >= bits_.size()) {
+ bits_.resize(idx + 1, 0);
+ }
+}
+
+Keychords::Mask::operator bool() const {
+ for (size_t i = 0; i < bits_.size(); ++i) {
+ if (bits_[i]) return true;
+ }
+ return false;
+}
+
+Keychords::Mask Keychords::Mask::operator&(const Keychords::Mask& rval) const {
+ auto len = std::min(bits_.size(), rval.bits_.size());
+ Keychords::Mask ret;
+ ret.bits_.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ ret.bits_[i] = bits_[i] & rval.bits_[i];
+ }
+ return ret;
+}
+
+void Keychords::Mask::operator|=(const Keychords::Mask& rval) {
+ auto len = rval.bits_.size();
+ bits_.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ bits_[i] |= rval.bits_[i];
+ }
+}
+
+Keychords::Entry::Entry() : notified(false) {}
+
+void Keychords::LambdaCheck() {
+ for (auto& [keycodes, entry] : entries_) {
+ auto found = true;
+ for (auto& code : keycodes) {
+ if (!current_.GetBit(code)) {
+ entry.notified = false;
found = false;
break;
}
}
if (!found) continue;
- if (e.notified) continue;
- e.notified = true;
- HandleKeychord(e.id);
+ if (entry.notified) continue;
+ entry.notified = true;
+ handler_(keycodes);
}
}
-void KeychordLambdaHandler(int fd) {
+void Keychords::LambdaHandler(int fd) {
input_event event;
auto res = TEMP_FAILURE_RETRY(::read(fd, &event, sizeof(event)));
if ((res != sizeof(event)) || (event.type != EV_KEY)) return;
- keychord_current.SetBit(event.code, event.value);
- KeychordLambdaCheck();
+ current_.SetBit(event.code, event.value);
+ LambdaCheck();
}
-bool KeychordGeteventEnable(int fd) {
- static bool EviocsmaskSupported = true;
-
+bool Keychords::GeteventEnable(int fd) {
// Make sure it is an event channel, should pass this ioctl call
int version;
if (::ioctl(fd, EVIOCGVERSION, &version)) return false;
+#ifdef EVIOCSMASK
+ static auto EviocsmaskSupported = true;
if (EviocsmaskSupported) {
- KeychordMask mask(EV_KEY);
+ Keychords::Mask mask(EV_KEY);
mask.SetBit(EV_KEY);
input_mask msg = {};
msg.type = EV_SYN;
@@ -187,21 +154,23 @@
EviocsmaskSupported = false;
}
}
+#endif
- KeychordMask mask;
- for (auto& e : keychord_entries) {
- for (auto& code : e.keycodes) {
+ Keychords::Mask mask;
+ for (auto& [keycodes, entry] : entries_) {
+ for (auto& code : keycodes) {
mask.resize(code);
mask.SetBit(code);
}
}
- keychord_current.resize(mask.size());
- KeychordMask available(mask.size());
+ current_.resize(mask.size());
+ Keychords::Mask available(mask.size());
auto res = ::ioctl(fd, EVIOCGBIT(EV_KEY, available.bytesize()), available.data());
if (res == -1) return false;
if (!(available & mask)) return false;
+#ifdef EVIOCSMASK
if (EviocsmaskSupported) {
input_mask msg = {};
msg.type = EV_KEY;
@@ -209,46 +178,45 @@
msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
::ioctl(fd, EVIOCSMASK, &msg);
}
+#endif
- KeychordMask set(mask.size());
+ Keychords::Mask set(mask.size());
res = ::ioctl(fd, EVIOCGKEY(res), set.data());
if (res > 0) {
- keychord_current |= mask & available & set;
- KeychordLambdaCheck();
+ current_ |= mask & available & set;
+ LambdaCheck();
}
- epoll->RegisterHandler(fd, [fd]() { KeychordLambdaHandler(fd); });
+ epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
return true;
}
-void GeteventOpenDevice(const std::string& device) {
- if (keychord_registration.count(device)) return;
+void Keychords::GeteventOpenDevice(const std::string& device) {
+ if (registration_.count(device)) return;
auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
if (fd == -1) {
PLOG(ERROR) << "Can not open " << device;
return;
}
- if (!KeychordGeteventEnable(fd)) {
+ if (!GeteventEnable(fd)) {
::close(fd);
} else {
- keychord_registration.emplace(device, fd);
+ registration_.emplace(device, fd);
}
}
-void GeteventCloseDevice(const std::string& device) {
- auto it = keychord_registration.find(device);
- if (it == keychord_registration.end()) return;
+void Keychords::GeteventCloseDevice(const std::string& device) {
+ auto it = registration_.find(device);
+ if (it == registration_.end()) return;
auto fd = (*it).second;
- epoll->UnregisterHandler(fd);
- keychord_registration.erase(it);
+ epoll_->UnregisterHandler(fd);
+ registration_.erase(it);
::close(fd);
}
-int inotify_fd = -1;
+void Keychords::InotifyHandler() {
+ unsigned char buf[512]; // History shows 32-64 bytes typical
-void InotifyHandler() {
- unsigned char buf[512];
-
- auto res = TEMP_FAILURE_RETRY(::read(inotify_fd, buf, sizeof(buf)));
+ auto res = TEMP_FAILURE_RETRY(::read(inotify_fd_, buf, sizeof(buf)));
if (res < 0) {
PLOG(WARNING) << "could not get event";
return;
@@ -274,14 +242,15 @@
}
}
-void GeteventOpenDevice() {
- inotify_fd = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
- if (inotify_fd < 0) {
+void Keychords::GeteventOpenDevice() {
+ inotify_fd_ = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+ if (inotify_fd_ < 0) {
PLOG(WARNING) << "Could not instantiate inotify for " << kDevicePath;
- } else if (::inotify_add_watch(inotify_fd, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) < 0) {
+ } else if (::inotify_add_watch(inotify_fd_, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) <
+ 0) {
PLOG(WARNING) << "Could not add watch for " << kDevicePath;
- ::close(inotify_fd);
- inotify_fd = -1;
+ ::close(inotify_fd_);
+ inotify_fd_ = -1;
}
std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
@@ -296,27 +265,20 @@
}
}
- if (inotify_fd >= 0) epoll->RegisterHandler(inotify_fd, InotifyHandler);
+ if (inotify_fd_ >= 0) {
+ epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+ }
}
-void AddServiceKeycodes(Service* svc) {
- if (svc->keycodes().empty()) return;
- for (auto& code : svc->keycodes()) {
- if ((code < 0) || (code >= KEY_MAX)) return;
- }
- ++keychords_count;
- keychord_entries.emplace_back(KeychordEntry(svc->keycodes(), keychords_count));
- svc->set_keychord_id(keychords_count);
+void Keychords::Register(const std::vector<int>& keycodes) {
+ if (keycodes.empty()) return;
+ entries_.try_emplace(keycodes, Entry());
}
-} // namespace
-
-void KeychordInit(Epoll* init_epoll) {
- epoll = init_epoll;
- for (const auto& service : ServiceList::GetInstance()) {
- AddServiceKeycodes(service.get());
- }
- if (keychords_count) GeteventOpenDevice();
+void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
+ epoll_ = epoll;
+ handler_ = handler;
+ if (entries_.size()) GeteventOpenDevice();
}
} // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index f3aecbb..00ed205 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,12 +17,81 @@
#ifndef _INIT_KEYCHORDS_H_
#define _INIT_KEYCHORDS_H_
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
#include "epoll.h"
namespace android {
namespace init {
-void KeychordInit(Epoll* init_epoll);
+class Keychords {
+ public:
+ Keychords();
+ Keychords(const Keychords&) = delete;
+ Keychords(Keychords&&) = delete;
+ Keychords& operator=(const Keychords&) = delete;
+ Keychords& operator=(Keychords&&) = delete;
+ ~Keychords() noexcept;
+
+ void Register(const std::vector<int>& keycodes);
+ void Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler);
+
+ private:
+ // Bit management
+ class Mask {
+ public:
+ explicit Mask(size_t bit = 0);
+
+ void SetBit(size_t bit, bool value = true);
+ bool GetBit(size_t bit) const;
+
+ size_t bytesize() const;
+ void* data();
+ size_t size() const;
+ void resize(size_t bit);
+
+ operator bool() const;
+ Mask operator&(const Mask& rval) const;
+ void operator|=(const Mask& rval);
+
+ private:
+ typedef unsigned int mask_t;
+ static constexpr size_t kBitsPerByte = 8;
+
+ std::vector<mask_t> bits_;
+ };
+
+ struct Entry {
+ Entry();
+
+ bool notified;
+ };
+
+ static constexpr char kDevicePath[] = "/dev/input";
+
+ void LambdaCheck();
+ void LambdaHandler(int fd);
+ void InotifyHandler();
+
+ bool GeteventEnable(int fd);
+ void GeteventOpenDevice(const std::string& device);
+ void GeteventOpenDevice();
+ void GeteventCloseDevice(const std::string& device);
+
+ Epoll* epoll_;
+ std::function<void(const std::vector<int>&)> handler_;
+
+ std::map<std::string, int> registration_;
+
+ std::map<const std::vector<int>, Entry> entries_;
+
+ Mask current_;
+
+ int inotify_fd_;
+};
} // namespace init
} // namespace android
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/property_service.cpp b/init/property_service.cpp
index 741fde0..d1c427d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -95,6 +95,11 @@
void CreateSerializedPropertyInfo();
+struct PropertyAuditData {
+ const ucred* cr;
+ const char* name;
+};
+
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
@@ -111,7 +116,7 @@
return false;
}
- property_audit_data audit_data;
+ PropertyAuditData audit_data;
audit_data.name = name.c_str();
audit_data.cr = &cr;
@@ -393,6 +398,35 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
};
+bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr) {
+ // We check the legacy method first but these properties are dontaudit, so we only log an audit
+ // if the newer method fails as well. We only do this with the legacy ctl. properties.
+ if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") {
+ // The legacy permissions model is that ctl. properties have their name ctl.<action> and
+ // their value is the name of the service to apply that action to. Permissions for these
+ // actions are based on the service, so we must create a fake name of ctl.<service> to
+ // check permissions.
+ auto control_string_legacy = "ctl." + value;
+ const char* target_context_legacy = nullptr;
+ const char* type_legacy = nullptr;
+ property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,
+ &type_legacy);
+
+ if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {
+ return true;
+ }
+ }
+
+ auto control_string_full = name + "$" + value;
+ const char* target_context_full = nullptr;
+ const char* type_full = nullptr;
+ property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,
+ &type_full);
+
+ return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);
+}
+
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error) {
@@ -402,15 +436,9 @@
}
if (StartsWith(name, "ctl.")) {
- // ctl. properties have their name ctl.<action> and their value is the name of the service
- // to apply that action to. Permissions for these actions are based on the service, so we
- // must create a fake name of ctl.<service> to check permissions.
- auto control_string = "ctl." + value;
- const char* target_context = nullptr;
- const char* type = nullptr;
- property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
- if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
- *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
+ if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
+ *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
+ value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
@@ -742,7 +770,7 @@
}
static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
- property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+ auto* d = reinterpret_cast<PropertyAuditData*>(data);
if (!d || !d->name || !d->cr) {
LOG(ERROR) << "AuditCallback invoked with null data arguments!";
diff --git a/init/property_service.h b/init/property_service.h
index 4a354c2..cacd987 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -26,11 +26,6 @@
namespace android {
namespace init {
-struct property_audit_data {
- const ucred* cr;
- const char* name;
-};
-
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
diff --git a/init/service.cpp b/init/service.cpp
index 0e08d9b..565cae7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/input.h>
#include <linux/securebits.h>
#include <sched.h>
#include <sys/mount.h>
@@ -32,6 +33,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -46,8 +48,6 @@
#if defined(__ANDROID__)
#include <sys/system_properties.h>
-#include <android-base/properties.h>
-
#include "init.h"
#include "property_service.h"
#else
@@ -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),
@@ -544,10 +543,13 @@
Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
for (std::size_t i = 1; i < args.size(); i++) {
int code;
- if (ParseInt(args[i], &code)) {
- keycodes_.emplace_back(code);
+ if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+ for (auto& key : keycodes_) {
+ if (key == code) return Error() << "duplicate keycode: " << args[i];
+ }
+ keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
} else {
- LOG(WARNING) << "ignoring invalid keycode: " << args[i];
+ return Error() << "invalid keycode: " << args[i];
}
}
return Success();
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/init/util.cpp b/init/util.cpp
index 4455b2e..5f2b87d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,6 +33,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -43,8 +44,6 @@
#include "reboot.h"
#if defined(__ANDROID__)
-#include <android-base/properties.h>
-
#include "selinux.h"
#else
#include "host_init_stubs.h"
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 8a2afea..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,6 +27,7 @@
name: "libasyncio",
defaults: ["libasyncio_defaults"],
vendor_available: true,
+ recovery_available: true,
host_supported: true,
srcs: [
"AsyncIO.cpp",
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 0f93dd0..b4bf35f 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -58,6 +58,7 @@
cc_library {
name: "libbacktrace",
vendor_available: false,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -102,8 +103,6 @@
include_dirs: [
"art/runtime",
],
-
- header_libs: ["jni_headers"],
},
android: {
static_libs: ["libasync_safe"],
@@ -112,6 +111,10 @@
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
exclude_shared_libs: ["libdexfile"],
},
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ exclude_shared_libs: ["libdexfile"],
+ },
},
whole_static_libs: ["libdemangle"],
}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 1e3d379..f78a31f 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -46,6 +46,7 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <cutils/atomic.h>
#include <cutils/threads.h>
@@ -1186,49 +1187,45 @@
ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
}
-static const char* CopySharedLibrary() {
-#if defined(__LP64__)
- const char* lib_name = "lib64";
-#else
- const char* lib_name = "lib";
-#endif
+static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+ std::string system_dir;
#if defined(__BIONIC__)
- const char* tmp_so_name = "/data/local/tmp/libbacktrace_test.so";
- std::string cp_cmd = android::base::StringPrintf("cp /system/%s/libbacktrace_test.so %s",
- lib_name, tmp_so_name);
+ system_dir = "/system/lib";
#else
- const char* tmp_so_name = "/tmp/libbacktrace_test.so";
- if (getenv("ANDROID_HOST_OUT") == NULL) {
- fprintf(stderr, "ANDROID_HOST_OUT not set, make sure you run lunch.");
- return nullptr;
- }
- std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s",
- getenv("ANDROID_HOST_OUT"), lib_name,
- tmp_so_name);
+ const char* host_out_env = getenv("ANDROID_HOST_OUT");
+ ASSERT_TRUE(host_out_env != nullptr);
+ system_dir = std::string(host_out_env) + "/lib";
#endif
- // Copy the shared so to a tempory directory.
- system(cp_cmd.c_str());
+#if defined(__LP64__)
+ system_dir += "64";
+#endif
- return tmp_so_name;
+ *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
+ std::string cp_cmd =
+ android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+
+ // Copy the shared so to a tempory directory.
+ ASSERT_EQ(0, system(cp_cmd.c_str()));
}
TEST(libbacktrace, check_unreadable_elf_local) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
struct stat buf;
- ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
uint64_t map_size = buf.st_size;
- int fd = open(tmp_so_name, O_RDONLY);
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
ASSERT_TRUE(fd != -1);
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
ASSERT_TRUE(map != MAP_FAILED);
close(fd);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
std::vector<std::string> found_functions;
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
@@ -1256,32 +1253,33 @@
}
TEST(libbacktrace, check_unreadable_elf_remote) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
g_ready = 0;
struct stat buf;
- ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
uint64_t map_size = buf.st_size;
pid_t pid;
if ((pid = fork()) == 0) {
- int fd = open(tmp_so_name, O_RDONLY);
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
if (fd == -1) {
- fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name, strerror(errno));
- unlink(tmp_so_name);
+ fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name.c_str(), strerror(errno));
+ unlink(tmp_so_name.c_str());
exit(0);
}
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) {
fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
- unlink(tmp_so_name);
+ unlink(tmp_so_name.c_str());
exit(0);
}
close(fd);
- if (unlink(tmp_so_name) == -1) {
+ if (unlink(tmp_so_name.c_str()) == -1) {
fprintf(stderr, "Failed to unlink: %s\n", strerror(errno));
exit(0);
}
@@ -1394,11 +1392,13 @@
typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
TEST(libbacktrace, unwind_through_unreadable_elf_local) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
- void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
ASSERT_TRUE(lib_handle != nullptr);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
test_func_t test_func;
test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1411,11 +1411,13 @@
}
TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
- void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
ASSERT_TRUE(lib_handle != nullptr);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
test_func_t test_func;
test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1444,7 +1446,8 @@
size_t frame_num;
if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
- &frame_num)) {
+ &frame_num) &&
+ frame_num != 0) {
VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uint64_t>(test_func), frame_num);
done = true;
}
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 47de12a..e47560f 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,7 @@
cc_library {
name: "libcrypto_utils",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
},
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/libkeyutils/Android.bp b/libkeyutils/Android.bp
index f3593ff..b388e95 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -2,6 +2,7 @@
name: "libkeyutils",
cflags: ["-Werror"],
defaults: ["linux_bionic_supported"],
+ recovery_available: true,
export_include_dirs: ["include/"],
local_include_dirs: ["include/"],
srcs: ["keyutils.cpp"],
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 1bd796a..4a165a0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -83,8 +83,8 @@
},
android_arm: {
// TODO: This is to work around b/24465209. Remove after root cause is fixed
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
- use_clang_lld: false,
},
windows: {
srcs: ["uio.c"],
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 444a5ac..443c3ea 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -27,6 +27,7 @@
// Test the APIs in this standalone include file
#include <log/log_read.h>
// Do not use anything in log/log_time.h despite side effects of the above.
+#include <private/android_logger.h>
TEST(liblog, __android_log_write__android_logger_list_read) {
#ifdef __ANDROID__
@@ -105,7 +106,10 @@
// framework (b/68266385).
EXPECT_LE( // boolean 1 or 0 depending on expected content or empty
!!((strcmp("crash", name) != 0) &&
- ((strcmp("kernel", name) != 0) || __android_log_is_debuggable()) &&
+ ((strcmp("kernel", name) != 0) ||
+ __android_logger_property_get_bool(
+ "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG |
+ BOOL_DEFAULT_FLAG_SVELTE)) &&
(strcmp("stats", name) != 0)),
android_logger_get_log_readable_size(logger));
} else {
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/libnativeloader/OWNERS b/libnativeloader/OWNERS
index f2cc942..5c28a9f 100644
--- a/libnativeloader/OWNERS
+++ b/libnativeloader/OWNERS
@@ -1 +1,2 @@
dimitry@google.com
+jiyong@google.com
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 6dfa697..1cebb5d 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -39,12 +39,14 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <private/android_filesystem_config.h>
#include <processgroup/processgroup.h>
+using android::base::GetBoolProperty;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
@@ -62,12 +64,20 @@
static const std::string& GetCgroupRootPath() {
static std::string cgroup_root_path;
std::call_once(init_path_flag, [&]() {
- // Check if mem cgroup is mounted, only then check for write-access to avoid
- // SELinux denials
+ // low-ram devices use per-app memcg by default, unlike high-end ones
+ bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+ bool per_app_memcg =
+ GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+ if (per_app_memcg) {
+ // Check if mem cgroup is mounted, only then check for
+ // write-access to avoid SELinux denials
cgroup_root_path =
- (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH
- : MEM_CGROUP_PATH);
- });
+ (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
+ ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
+ } else {
+ cgroup_root_path = ACCT_CGROUP_PATH;
+ }
+ });
return cgroup_root_path;
}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index d776b3d..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -27,6 +27,7 @@
name: "libprocinfo",
defaults: ["libprocinfo_defaults"],
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
},
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index b894656..c7c089f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -3,6 +3,7 @@
cc_library {
name: "libsparse",
host_supported: true,
+ recovery_available: true,
unique_host_soname: true,
srcs: [
"backed_block.c",
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 3fae5e6..c95563d 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -40,13 +40,6 @@
}
cc_test {
- name: "sync_test",
- defaults: ["libsync_defaults"],
- gtest: false,
- srcs: ["sync_test.c"],
-}
-
-cc_test {
name: "sync-unit-tests",
shared_libs: ["libsync"],
srcs: ["tests/sync_test.cpp"],
diff --git a/libsync/OWNERS b/libsync/OWNERS
new file mode 100644
index 0000000..dc61733
--- /dev/null
+++ b/libsync/OWNERS
@@ -0,0 +1,3 @@
+ghackmann@google.com
+jessehall@google.com
+marissaw@google.com
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
deleted file mode 100644
index f1ffdcf..0000000
--- a/libsync/sync_test.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * sync_test.c
- *
- * Copyright 2012 Google, Inc
- *
- * 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 <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <android/sync.h>
-#include "sw_sync.h"
-
-pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-struct sync_thread_data {
- int thread_no;
- int fd[2];
-};
-
-void *sync_thread(void *data)
-{
- struct sync_thread_data *sync_data = data;
- struct sync_fence_info_data *info;
- int err;
- int i;
-
- for (i = 0; i < 2; i++) {
- err = sync_wait(sync_data->fd[i], 10000);
-
- pthread_mutex_lock(&printf_mutex);
- if (err < 0) {
- printf("thread %d wait %d failed: %s\n", sync_data->thread_no,
- i, strerror(errno));
- } else {
- printf("thread %d wait %d done\n", sync_data->thread_no, i);
- }
- info = sync_fence_info(sync_data->fd[i]);
- if (info) {
- struct sync_pt_info *pt_info = NULL;
- printf(" fence %s %d\n", info->name, info->status);
-
- while ((pt_info = sync_pt_info(info, pt_info))) {
- int ts_sec = pt_info->timestamp_ns / 1000000000LL;
- int ts_usec = (pt_info->timestamp_ns % 1000000000LL) / 1000LL;
- printf(" pt %s %s %d %d.%06d", pt_info->obj_name,
- pt_info->driver_name, pt_info->status,
- ts_sec, ts_usec);
- if (!strcmp(pt_info->driver_name, "sw_sync"))
- printf(" val=%d\n", *(uint32_t *)pt_info->driver_data);
- else
- printf("\n");
- }
- sync_fence_info_free(info);
- }
- pthread_mutex_unlock(&printf_mutex);
- }
-
- return NULL;
-}
-
-int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
-{
- struct sync_thread_data sync_data[4];
- pthread_t threads[4];
- int sync_timeline_fd;
- int i, j;
- char str[256];
-
- sync_timeline_fd = sw_sync_timeline_create();
- if (sync_timeline_fd < 0) {
- perror("can't create sw_sync_timeline:");
- return 1;
- }
-
- for (i = 0; i < 3; i++) {
- sync_data[i].thread_no = i;
-
- for (j = 0; j < 2; j++) {
- unsigned val = i + j * 3 + 1;
- snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
- int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
- if (fd < 0) {
- printf("can't create sync pt %d: %s", val, strerror(errno));
- return 1;
- }
- sync_data[i].fd[j] = fd;
- printf("sync_data[%d].fd[%d] = %d;\n", i, j, fd);
-
- }
- }
-
- sync_data[3].thread_no = 3;
- for (j = 0; j < 2; j++) {
- snprintf(str, sizeof(str), "merged_fence%d", j);
- sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
- if (sync_data[3].fd[j] < 0) {
- printf("can't merge sync pts %d and %d: %s\n",
- sync_data[0].fd[j], sync_data[1].fd[j], strerror(errno));
- return 1;
- }
- }
-
- for (i = 0; i < 4; i++)
- pthread_create(&threads[i], NULL, sync_thread, &sync_data[i]);
-
-
- for (i = 0; i < 3; i++) {
- int err;
- printf("press enter to inc to %d\n", i+1);
- fgets(str, sizeof(str), stdin);
- err = sw_sync_timeline_inc(sync_timeline_fd, 1);
- if (err < 0) {
- perror("can't increment sync obj:");
- return 1;
- }
- }
-
- printf("press enter to close sync_timeline\n");
- fgets(str, sizeof(str), stdin);
-
- close(sync_timeline_fd);
-
- printf("press enter to end test\n");
- fgets(str, sizeof(str), stdin);
-
- for (i = 0; i < 3; i++) {
- void *val;
- pthread_join(threads[i], &val);
- }
-
- return 0;
-}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index a6bf730..26be64d 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -38,6 +38,7 @@
cc_library {
name: "libunwindstack",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -62,6 +63,7 @@
"MapInfo.cpp",
"Maps.cpp",
"Memory.cpp",
+ "LocalUnwinder.cpp",
"Regs.cpp",
"RegsArm.cpp",
"RegsArm64.cpp",
@@ -93,6 +95,14 @@
],
exclude_shared_libs: ["libdexfile"],
},
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ exclude_srcs: [
+ "DexFile.cpp",
+ "DexFiles.cpp",
+ ],
+ exclude_shared_libs: ["libdexfile"],
+ },
},
arch: {
@@ -125,6 +135,21 @@
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
+cc_test_library {
+ name: "libunwindstack_local",
+ defaults: ["libunwindstack_flags"],
+ srcs: ["tests/TestLocal.cpp"],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libunwindstack",
+ ],
+}
+
cc_test {
name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
@@ -151,6 +176,7 @@
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
"tests/JitDebugTest.cpp",
+ "tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 0000000..952b332
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+bool LocalUnwinder::Init() {
+ pthread_rwlock_init(&maps_rwlock_, nullptr);
+
+ // Create the maps.
+ maps_.reset(new unwindstack::LocalUpdatableMaps());
+ if (!maps_->Parse()) {
+ maps_.reset();
+ return false;
+ }
+
+ process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
+
+ return true;
+}
+
+bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
+ for (const std::string& skip_library : skip_libraries_) {
+ if (skip_library == map_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
+ pthread_rwlock_rdlock(&maps_rwlock_);
+ MapInfo* map_info = maps_->Find(pc);
+ pthread_rwlock_unlock(&maps_rwlock_);
+
+ if (map_info == nullptr) {
+ pthread_rwlock_wrlock(&maps_rwlock_);
+ // This is guaranteed not to invalidate any previous MapInfo objects so
+ // we don't need to worry about any MapInfo* values already in use.
+ if (maps_->Reparse()) {
+ map_info = maps_->Find(pc);
+ }
+ pthread_rwlock_unlock(&maps_rwlock_);
+ }
+
+ return map_info;
+}
+
+bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ unwindstack::RegsGetLocal(regs.get());
+
+ size_t num_frames = 0;
+ bool adjust_pc = false;
+ while (true) {
+ uint64_t cur_pc = regs->pc();
+ uint64_t cur_sp = regs->sp();
+
+ MapInfo* map_info = GetMapInfo(cur_pc);
+ if (map_info == nullptr) {
+ break;
+ }
+
+ Elf* elf = map_info->GetElf(process_memory_, true);
+ uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
+ uint64_t step_pc = rel_pc;
+ uint64_t pc_adjustment;
+ if (adjust_pc) {
+ pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
+ } else {
+ pc_adjustment = 0;
+ }
+ step_pc -= pc_adjustment;
+ // Skip any locations that are within this library.
+ if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
+ // Add frame information.
+ std::string func_name;
+ uint64_t func_offset;
+ if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
+ func_name, func_offset);
+ } else {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
+ }
+ num_frames++;
+ }
+ if (!elf->valid()) {
+ break;
+ }
+ if (frame_info->size() == max_frames) {
+ break;
+ }
+
+ adjust_pc = true;
+ bool finished;
+ if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) {
+ break;
+ }
+ // pc and sp are the same, terminate the unwind.
+ if (cur_pc == regs->pc() && cur_sp == regs->sp()) {
+ break;
+ }
+ }
+ return num_frames != 0;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index bb682ea..e676a5a 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -105,4 +105,83 @@
return "/proc/" + std::to_string(pid_) + "/maps";
}
+const std::string LocalUpdatableMaps::GetMapsFile() const {
+ return "/proc/self/maps";
+}
+
+bool LocalUpdatableMaps::Reparse() {
+ // New maps will be added at the end without deleting the old ones.
+ size_t last_map_idx = maps_.size();
+ if (!Parse()) {
+ // Delete any maps added by the Parse call.
+ for (size_t i = last_map_idx; i < maps_.size(); i++) {
+ delete maps_[i];
+ }
+ maps_.resize(last_map_idx);
+ return false;
+ }
+
+ size_t total_entries = maps_.size();
+ size_t search_map_idx = 0;
+ for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
+ MapInfo* new_map_info = maps_[new_map_idx];
+ uint64_t start = new_map_info->start;
+ uint64_t end = new_map_info->end;
+ uint64_t flags = new_map_info->flags;
+ std::string* name = &new_map_info->name;
+ for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
+ MapInfo* info = maps_[old_map_idx];
+ if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
+ // No need to check
+ search_map_idx = old_map_idx + 1;
+ delete new_map_info;
+ maps_[new_map_idx] = nullptr;
+ total_entries--;
+ break;
+ } else if (info->start > start) {
+ // Stop, there isn't going to be a match.
+ search_map_idx = old_map_idx;
+ break;
+ }
+
+ // Never delete these maps, they may be in use. The assumption is
+ // that there will only every be a handfull of these so waiting
+ // to destroy them is not too expensive.
+ saved_maps_.push_back(info);
+ maps_[old_map_idx] = nullptr;
+ total_entries--;
+ }
+ if (search_map_idx >= last_map_idx) {
+ break;
+ }
+ }
+
+ // Now move out any of the maps that never were found.
+ for (size_t i = search_map_idx; i < last_map_idx; i++) {
+ saved_maps_.push_back(maps_[i]);
+ maps_[i] = nullptr;
+ total_entries--;
+ }
+
+ // Sort all of the values such that the nullptrs wind up at the end, then
+ // resize them away.
+ std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+ if (a == nullptr) {
+ return false;
+ } else if (b == nullptr) {
+ return true;
+ }
+ return a->start < b->start;
+ });
+ maps_.resize(total_entries);
+
+ return true;
+}
+
+LocalUpdatableMaps::~LocalUpdatableMaps() {
+ for (auto map_info : saved_maps_) {
+ delete map_info;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
new file mode 100644
index 0000000..80bb53e
--- /dev/null
+++ b/libunwindstack/include/unwindstack/LocalUnwinder.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+struct MapInfo;
+
+struct LocalFrameData {
+ LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
+ uint64_t function_offset)
+ : map_info(map_info),
+ pc(pc),
+ rel_pc(rel_pc),
+ function_name(function_name),
+ function_offset(function_offset) {}
+
+ MapInfo* map_info;
+ uint64_t pc;
+ uint64_t rel_pc;
+ std::string function_name;
+ uint64_t function_offset;
+};
+
+// This is a specialized class that should only be used for doing local unwinds.
+// The Unwind call can be made as multiple times on the same object, and it can
+// be called by multiple threads at the same time.
+// It is designed to be used in debugging circumstances to get a stack trace
+// as fast as possible.
+class LocalUnwinder {
+ public:
+ LocalUnwinder() = default;
+ LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
+ ~LocalUnwinder() = default;
+
+ bool Init();
+
+ bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
+
+ bool ShouldSkipLibrary(const std::string& map_name);
+
+ MapInfo* GetMapInfo(uint64_t pc);
+
+ ErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ private:
+ pthread_rwlock_t maps_rwlock_;
+ std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
+ std::shared_ptr<Memory> process_memory_;
+ std::vector<std::string> skip_libraries_;
+ ErrorData last_error_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 74e5c47..67fbed2 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -87,6 +87,19 @@
virtual ~LocalMaps() = default;
};
+class LocalUpdatableMaps : public Maps {
+ public:
+ LocalUpdatableMaps() : Maps() {}
+ virtual ~LocalUpdatableMaps();
+
+ bool Reparse();
+
+ const std::string GetMapsFile() const override;
+
+ private:
+ std::vector<MapInfo*> saved_maps_;
+};
+
class BufferMaps : public Maps {
public:
BufferMaps(const char* buffer) : buffer_(buffer) {}
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
new file mode 100644
index 0000000..56a18cd
--- /dev/null
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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 <dlfcn.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/LocalUnwinder.h>
+
+namespace unwindstack {
+
+static std::vector<LocalFrameData>* g_frame_info;
+static LocalUnwinder* g_unwinder;
+
+extern "C" void SignalLocalInnerFunction() {
+ g_unwinder->Unwind(g_frame_info, 256);
+}
+
+extern "C" void SignalLocalMiddleFunction() {
+ SignalLocalInnerFunction();
+}
+
+extern "C" void SignalLocalOuterFunction() {
+ SignalLocalMiddleFunction();
+}
+
+static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
+ SignalLocalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names,
+ const std::vector<LocalFrameData>& frame_info) {
+ std::string unwind;
+ size_t i = 0;
+ for (const auto& frame : frame_info) {
+ unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
+ frame.pc, frame.rel_pc);
+ if (frame.map_info != nullptr) {
+ if (!frame.map_info->name.empty()) {
+ unwind += " " + frame.map_info->name;
+ } else {
+ unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start,
+ frame.map_info->end);
+ }
+ if (frame.map_info->offset != 0) {
+ unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset);
+ }
+ }
+ if (!frame.function_name.empty()) {
+ unwind += " " + frame.function_name;
+ if (frame.function_offset != 0) {
+ unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+ }
+ }
+ unwind += '\n';
+ }
+
+ return std::string(
+ "Unwind completed without finding all frames\n"
+ " Looking for function: ") +
+ function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ std::vector<LocalFrameData> frame_info;
+ g_frame_info = &frame_info;
+ g_unwinder = unwinder;
+ std::vector<const char*> expected_function_names;
+
+ if (unwind_through_signal) {
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalLocalCallerHandler;
+ act.sa_flags = SA_RESTART | SA_ONSTACK;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+ raise(SIGUSR1);
+
+ ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction",
+ "LocalInnerFunction", "SignalLocalOuterFunction",
+ "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
+ } else {
+ ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
+ }
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalInnerFunction(unwinder, unwind_through_signal);
+}
+
+extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalMiddleFunction(unwinder, unwind_through_signal);
+}
+
+class LocalUnwinderTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ unwinder_.reset(new LocalUnwinder);
+ ASSERT_TRUE(unwinder_->Init());
+ }
+
+ std::unique_ptr<LocalUnwinder> unwinder_;
+};
+
+TEST_F(LocalUnwinderTest, local) {
+ LocalOuterFunction(unwinder_.get(), false);
+}
+
+TEST_F(LocalUnwinderTest, local_signal) {
+ LocalOuterFunction(unwinder_.get(), true);
+}
+
+TEST_F(LocalUnwinderTest, local_multiple) {
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+}
+
+// This test verifies that doing an unwind before and after a dlopen
+// works. It's verifying that the maps read during the first unwind
+// do not cause a problem when doing the unwind using the code in
+// the dlopen'd code.
+TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
+ // Prime the maps data.
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ std::string testlib(testing::internal::GetArgvs()[0]);
+ auto const value = testlib.find_last_of('/');
+ if (value == std::string::npos) {
+ testlib = "../";
+ } else {
+ testlib = testlib.substr(0, value + 1) + "../";
+ }
+ testlib += "libunwindstack_local.so";
+
+ void* handle = dlopen(testlib.c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr);
+
+ void (*unwind_function)(void*, void*) =
+ reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
+ ASSERT_TRUE(unwind_function != nullptr);
+
+ std::vector<LocalFrameData> frame_info;
+ unwind_function(unwinder_.get(), &frame_info);
+
+ ASSERT_EQ(0, dlclose(handle));
+
+ std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
+ "TestlibLevel3", "TestlibLevel4"};
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
new file mode 100644
index 0000000..fa0baff
--- /dev/null
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <unwindstack/LocalUnwinder.h>
+
+#include <vector>
+
+extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
+ unwindstack::LocalUnwinder* unwinder =
+ reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
+ std::vector<unwindstack::LocalFrameData>* frame_info =
+ reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
+ unwinder->Unwind(frame_info, 256);
+}
+
+extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
+ TestlibLevel4(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
+ TestlibLevel3(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
+ TestlibLevel2(unwinder_data, frame_data);
+}
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 242cc6a..83695bb 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -106,15 +106,12 @@
Unwinder unwinder(512, maps, regs, process_memory);
unwinder.Unwind();
- std::string expected_function = expected_function_names.back();
- expected_function_names.pop_back();
for (auto& frame : unwinder.frames()) {
- if (frame.function_name == expected_function) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
if (expected_function_names.empty()) {
break;
}
- expected_function = expected_function_names.back();
- expected_function_names.pop_back();
}
}
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index d4ba4f4..c378646 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -12,6 +12,7 @@
cc_library {
name: "liblogwrap",
defaults: ["logwrapper_defaults"],
+ recovery_available: true,
srcs: ["logwrap.c"],
shared_libs: [
"libcutils",
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/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index ac20d05..fda9af0 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -45,6 +45,22 @@
f.write(pack(str(pad) + 'x'))
+def get_number_of_pages(image_size, page_size):
+ """calculates the number of pages required for the image"""
+ return (image_size + page_size - 1) / page_size
+
+
+def get_recovery_dtbo_offset(args):
+ """calculates the offset of recovery_dtbo image in the boot image"""
+ num_header_pages = 1 # header occupies a page
+ num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
+ num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk), args.pagesize)
+ num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
+ dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
+ num_ramdisk_pages + num_second_pages)
+ return dtbo_offset
+
+
def write_header(args):
BOOT_MAGIC = 'ANDROID!'.encode()
args.output.write(pack('8s', BOOT_MAGIC))
@@ -76,9 +92,12 @@
args.output.write(pack('1024s', args.cmdline[512:].encode()))
if args.header_version > 0:
- args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes
- args.output.write(pack('Q', args.base + args.recovery_dtbo_offset)) # physical load addr
- args.output.write(pack('I', args.output.tell() + 4)) # size of boot header
+ args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes
+ if args.recovery_dtbo:
+ args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
+ else:
+ args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
+ args.output.write(pack('I', args.output.tell() + 4)) # size of boot header
pad_file(args.output, args.pagesize)
return img_id
@@ -150,8 +169,6 @@
parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
default=0x00f00000)
- parser.add_argument('--recovery_dtbo_offset', help='recovery dtbo offset', type=parse_int,
- default=0x0f000000)
parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
default=0)
parser.add_argument('--os_patch_level', help='operating system patch level',
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg
index 8e42ec0..c37acd5 100755
--- a/mkbootimg/unpack_bootimg
+++ b/mkbootimg/unpack_bootimg
@@ -76,8 +76,8 @@
if version > 0:
recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
print('recovery dtbo size: %s' % recovery_dtbo_size)
- recovery_dtbo_address = unpack('Q', args.boot_img.read(8))[0]
- print('recovery dtbo load address: %s' % recovery_dtbo_address)
+ recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
+ print('recovery dtbo offset: %s' % recovery_dtbo_offset)
boot_header_size = unpack('I', args.boot_img.read(4))[0]
print('boot header size: %s' % boot_header_size)
else:
@@ -95,16 +95,13 @@
) # header + kernel
image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
- num_second_pages = get_number_of_pages(second_size, page_size)
second_offset = page_size * (
num_header_pages + num_kernel_pages + num_ramdisk_pages
) # header + kernel + ramdisk
image_info_list.append((second_offset, second_size, 'second'))
if recovery_dtbo_size > 0:
- dtbo_offset = page_size * (num_header_pages + num_kernel_pages +
- num_ramdisk_pages + num_second_pages)
- image_info_list.append((dtbo_offset, recovery_dtbo_size,
+ image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
'recovery_dtbo'))
for image_info in image_info_list:
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
index 93c347b..c6bda4a 100644
--- a/qemu_pipe/Android.bp
+++ b/qemu_pipe/Android.bp
@@ -3,6 +3,7 @@
cc_library_static {
name: "libqemu_pipe",
vendor_available: true,
+ recovery_available: true,
sanitize: {
misc_undefined: ["integer"],
},
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/