Merge changes I49c9f064,I946e8564
* changes:
llkd: Add cma_alloc stack symbol checking
llkd: Add __get_user_page stack symbol checking
diff --git a/adb/Android.bp b/adb/Android.bp
index 3685687..bccc71a 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -142,6 +142,8 @@
"client/fastdeploycallbacks.cpp",
],
+ generated_headers: ["platform_tools_version"],
+
target: {
linux: {
srcs: ["client/usb_linux.cpp"],
@@ -172,12 +174,6 @@
"libdiagnose_usb",
"libmdnssd",
"libusb",
- "libandroidfw",
- "libziparchive",
- "libz",
- "libutils",
- "liblog",
- "libcutils",
],
}
@@ -260,12 +256,6 @@
"liblog",
"libmdnssd",
"libusb",
- "libandroidfw",
- "libziparchive",
- "libz",
- "libutils",
- "liblog",
- "libcutils",
],
stl: "libc++_static",
@@ -275,6 +265,10 @@
// will violate ODR
shared_libs: [],
+ required: [
+ "deploypatchgenerator",
+ ],
+
target: {
darwin: {
cflags: [
@@ -311,6 +305,8 @@
"daemon/include",
],
+ generated_headers: ["platform_tools_version"],
+
static_libs: [
"libdiagnose_usb",
"libqemu_pipe",
@@ -353,10 +349,6 @@
"libavb_user",
"libdiagnose_usb",
"libqemu_pipe",
-
- // `daemon/shell_service.cpp` uses selinux_android_setcon(), which is not exposed by
- // libselinux.
- "libselinux",
],
shared_libs: [
@@ -371,6 +363,7 @@
"libfs_mgr",
"liblog",
"libmdnssd",
+ "libselinux",
],
}
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 8e028f4..62e8908 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -45,6 +45,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <build/version.h>
+#include <platform_tools_version.h>
#include "adb_auth.h"
#include "adb_io.h"
@@ -65,10 +66,11 @@
// Don't change the format of this --- it's parsed by ddmlib.
return android::base::StringPrintf(
"Android Debug Bridge version %d.%d.%d\n"
- "Version %s\n"
+ "Version %s-%s\n"
"Installed as %s\n",
ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
- android::build::GetBuildNumber().c_str(), android::base::GetExecutablePath().c_str());
+ PLATFORM_TOOLS_VERSION, android::build::GetBuildNumber().c_str(),
+ android::base::GetExecutablePath().c_str());
}
void fatal(const char *fmt, ...) {
@@ -631,11 +633,11 @@
fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
fprintf(stderr, "Server had pid: %d\n", pid);
- unique_fd fd(adb_open(GetLogFilePath().c_str(), O_RDONLY));
+ android::base::unique_fd fd(unix_open(GetLogFilePath().c_str(), O_RDONLY));
if (fd == -1) return;
// Let's not show more than 128KiB of log...
- adb_lseek(fd, -128 * 1024, SEEK_END);
+ unix_lseek(fd, -128 * 1024, SEEK_END);
std::string content;
if (!android::base::ReadFdToString(fd, &content)) return;
@@ -825,7 +827,7 @@
memcmp(temp, expected, expected_length) == 0) {
got_ack = true;
} else {
- ReportServerStartupFailure(GetProcessId(process_handle.get()));
+ ReportServerStartupFailure(pinfo.dwProcessId);
return -1;
}
} else {
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index ffac315..35017f0 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -49,9 +49,9 @@
#if defined(_WIN32)
-constexpr char kNullFileName[] = "NUL";
+static constexpr char kNullFileName[] = "NUL";
#else
-constexpr char kNullFileName[] = "/dev/null";
+static constexpr char kNullFileName[] = "/dev/null";
#endif
void close_stdin() {
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f764a0e..f6ce8e2 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -24,7 +24,7 @@
#include <android-base/macros.h>
-int syntax_error(const char*, ...);
+int syntax_error(const char*, ...) __attribute__((__format__(__printf__, 1, 2)));
void close_stdin();
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 1959258..eda4b77 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -185,6 +185,11 @@
return false;
}
+ // The server might send OKAY, so consume that.
+ char buf[4];
+ ReadFdExactly(fd, buf, 4);
+ // Now that no more data is expected, wait for socket orderly shutdown or error, indicating
+ // server death.
ReadOrderlyShutdown(fd);
return true;
}
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 95574ed..4cbc45a 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -40,6 +40,8 @@
#include <android-base/strings.h>
#include <android-base/test_utils.h>
+static constexpr int kFastDeployMinApi = 24;
+
static bool _use_legacy_install() {
FeatureSet features;
std::string error;
@@ -130,7 +132,7 @@
}
static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
- bool use_localagent, const char* adb_path) {
+ bool use_localagent) {
printf("Performing Streamed Install\n");
// The last argument must be the APK file
@@ -152,8 +154,7 @@
printf("failed to extract metadata %d\n", metadata_len);
return 1;
} else {
- int create_patch_result = create_patch(file, metadataTmpFile.path, patchTmpFile.path,
- use_localagent, adb_path);
+ int create_patch_result = create_patch(file, metadataTmpFile.path, patchTmpFile.path);
if (create_patch_result != 0) {
printf("Patch creation failure, error code: %d\n", create_patch_result);
result = create_patch_result;
@@ -176,7 +177,9 @@
}
cleanup_streamed_apk:
- delete_device_patch_file(file);
+ if (use_fastdeploy == true) {
+ delete_device_patch_file(file);
+ }
return result;
} else {
struct stat sb;
@@ -226,8 +229,8 @@
}
}
-static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy, bool use_localagent,
- const char* adb_path) {
+static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
+ bool use_localagent) {
static const char* const DATA_DEST = "/data/local/tmp/%s";
static const char* const SD_DEST = "/sdcard/tmp/%s";
const char* where = DATA_DEST;
@@ -257,10 +260,10 @@
std::string apk_dest =
android::base::StringPrintf(where, android::base::Basename(argv[last_apk]).c_str());
- TemporaryFile metadataTmpFile;
- TemporaryFile patchTmpFile;
-
if (use_fastdeploy == true) {
+ TemporaryFile metadataTmpFile;
+ TemporaryFile patchTmpFile;
+
FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
int metadata_len = extract_metadata(apk_file[0], metadataFile);
fclose(metadataFile);
@@ -269,8 +272,8 @@
printf("failed to extract metadata %d\n", metadata_len);
return 1;
} else {
- int create_patch_result = create_patch(apk_file[0], metadataTmpFile.path,
- patchTmpFile.path, use_localagent, adb_path);
+ int create_patch_result =
+ create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
if (create_patch_result != 0) {
printf("Patch creation failure, error code: %d\n", create_patch_result);
result = create_patch_result;
@@ -293,7 +296,9 @@
result = pm_command(argc, argv);
cleanup_apk:
- delete_device_patch_file(apk_file[0]);
+ if (use_fastdeploy == true) {
+ delete_device_patch_file(apk_file[0]);
+ }
delete_device_file(apk_dest);
return result;
}
@@ -327,9 +332,6 @@
} else if (!strcmp(argv[i], "--no-fastdeploy")) {
processedArgIndicies.push_back(i);
use_fastdeploy = false;
- } else if (!strcmp(argv[i], "-f")) {
- processedArgIndicies.push_back(i);
- use_fastdeploy = true;
} else if (!strcmp(argv[i], "--force-agent")) {
processedArgIndicies.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateAlways;
@@ -380,27 +382,18 @@
}
}
- std::string adb_path = android::base::GetExecutablePath();
-
- if (adb_path.length() == 0) {
- return 1;
- }
if (use_fastdeploy == true) {
- bool agent_up_to_date =
- update_agent(agent_update_strategy, use_localagent, adb_path.c_str());
- if (agent_up_to_date == false) {
- printf("Failed to update agent, exiting\n");
- return 1;
- }
+ fastdeploy_set_local_agent(use_localagent);
+ update_agent(agent_update_strategy);
}
switch (installMode) {
case INSTALL_PUSH:
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
- use_fastdeploy, use_localagent, adb_path.c_str());
+ use_fastdeploy, use_localagent);
case INSTALL_STREAM:
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
- use_fastdeploy, use_localagent, adb_path.c_str());
+ use_fastdeploy, use_localagent);
case INSTALL_DEFAULT:
default:
return 1;
@@ -415,7 +408,8 @@
for (int i = argc - 1; i >= 0; i--) {
const char* file = argv[i];
- if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+ if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+ android::base::EndsWithIgnoreCase(file, ".dm")) {
struct stat sb;
if (stat(file, &sb) != -1) total_size += sb.st_size;
first_apk = i;
@@ -477,9 +471,9 @@
}
std::string cmd =
- android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
+ android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
- session_id, i, android::base::Basename(file).c_str());
+ session_id, android::base::Basename(file).c_str());
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
@@ -536,19 +530,3 @@
std::string cmd = "rm -f " + escape_arg(filename);
return send_shell_command(cmd);
}
-
-int delete_host_file(const std::string& filename) {
-#ifdef _WIN32
- BOOL delete_return = DeleteFileA(filename.c_str());
- if (delete_return != 0) {
- return 0;
- } else {
- DWORD last_error = GetLastError();
- printf("Error [%ld] deleting: %s\n", last_error, filename.c_str());
- return delete_return;
- }
-#else
- std::string cmd = "rm -f " + escape_arg(filename);
- return system(cmd.c_str());
-#endif
-}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index b55ae95..e38e305 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -130,7 +130,7 @@
" pull [-a] REMOTE... LOCAL\n"
" copy files/dirs from device\n"
" -a: preserve file timestamp and mode\n"
- " sync [all|data|odm|oem|product|system|vendor]\n"
+ " sync [all|data|odm|oem|product_services|product|system|vendor]\n"
" sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
" -l: list but don't copy\n"
"\n"
@@ -158,7 +158,7 @@
" --instant: cause the app to be installed as an ephemeral install app\n"
" --no-streaming: always push APK to device and invoke Package Manager as separate steps\n"
" --streaming: force streaming APK directly into Package Manager\n"
- " -f/--fastdeploy: use fast deploy (only valid with -r)\n"
+ " --fastdeploy: use fast deploy (only valid with -r)\n"
" --no-fastdeploy: prevent use of fast deploy (only valid with -r)\n"
" --force-agent: force update of deployment agent when using fast deploy\n"
" --date-check-agent: update deployment agent when local version is newer and using fast deploy\n"
@@ -354,8 +354,7 @@
}
void copy_to_file(int inFd, int outFd) {
- constexpr size_t BUFSIZE = 32 * 1024;
- std::vector<char> buf(BUFSIZE);
+ std::vector<char> buf(32 * 1024);
int len;
long total = 0;
int old_stdin_mode = -1;
@@ -367,9 +366,9 @@
while (true) {
if (inFd == STDIN_FILENO) {
- len = unix_read(inFd, buf.data(), BUFSIZE);
+ len = unix_read(inFd, buf.data(), buf.size());
} else {
- len = adb_read(inFd, buf.data(), BUFSIZE);
+ len = adb_read(inFd, buf.data(), buf.size());
}
if (len == 0) {
D("copy_to_file() : read 0 bytes; exiting");
@@ -842,14 +841,19 @@
return -1;
}
- std::string service = android::base::StringPrintf(
- "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
+ std::string service =
+ android::base::StringPrintf("sideload-host:%" PRId64 ":%d",
+ static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
std::string error;
unique_fd device_fd(adb_connect(service, &error));
if (device_fd < 0) {
- // Try falling back to the older (<= K) sideload method. Maybe this
- // is an older device that doesn't support sideload-host.
fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+
+ // If this is a small enough package, maybe this is an older device that doesn't
+ // support sideload-host. Try falling back to the older (<= K) sideload method.
+ if (sb.st_size > INT_MAX) {
+ return -1;
+ }
fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
}
@@ -859,7 +863,7 @@
char buf[SIDELOAD_HOST_BLOCK_SIZE];
- size_t xfer = 0;
+ int64_t xfer = 0;
int last_percent = -1;
while (true) {
if (!ReadFdExactly(device_fd, buf, 8)) {
@@ -875,20 +879,22 @@
return 0;
}
- int block = strtol(buf, nullptr, 10);
-
- size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
- if (offset >= static_cast<size_t>(sb.st_size)) {
- fprintf(stderr, "adb: failed to read block %d past end\n", block);
+ int64_t block = strtoll(buf, nullptr, 10);
+ int64_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
+ if (offset >= static_cast<int64_t>(sb.st_size)) {
+ fprintf(stderr,
+ "adb: failed to read block %" PRId64 " at offset %" PRId64 ", past end %" PRId64
+ "\n",
+ block, offset, static_cast<int64_t>(sb.st_size));
return -1;
}
size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
- if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<size_t>(sb.st_size)) {
+ if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<int64_t>(sb.st_size)) {
to_write = sb.st_size - offset;
}
- if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
+ if (adb_lseek(package_fd, offset, SEEK_SET) != offset) {
fprintf(stderr, "adb: failed to seek to package block: %s\n", strerror(errno));
return -1;
}
@@ -1713,7 +1719,8 @@
}
if (src.empty()) src = "all";
- std::vector<std::string> partitions{"data", "odm", "oem", "product", "system", "vendor"};
+ std::vector<std::string> partitions{"data", "odm", "oem", "product", "product_services",
+ "system", "vendor"};
bool found = false;
for (const auto& partition : partitions) {
if (src == "all" || src == partition) {
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index fda3889..d3f35c8 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -14,26 +14,29 @@
* limitations under the License.
*/
-#include <androidfw/ResourceTypes.h>
-#include <androidfw/ZipFileRO.h>
#include <libgen.h>
#include <algorithm>
+#include <array>
+#include "android-base/file.h"
+#include "android-base/strings.h"
#include "client/file_sync_client.h"
#include "commandline.h"
#include "fastdeploy.h"
#include "fastdeploycallbacks.h"
#include "utils/String16.h"
-const long kRequiredAgentVersion = 0x00000001;
+static constexpr long kRequiredAgentVersion = 0x00000001;
-const char* kDeviceAgentPath = "/data/local/tmp/";
+static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
+
+static bool g_use_localagent = false;
long get_agent_version() {
std::vector<char> versionOutputBuffer;
std::vector<char> versionErrorBuffer;
- int statusCode = capture_shell_command("/data/local/tmp/deployagent.sh version",
+ int statusCode = capture_shell_command("/data/local/tmp/deployagent version",
&versionOutputBuffer, &versionErrorBuffer);
long version = -1;
@@ -58,75 +61,62 @@
return api_level;
}
-// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
-static bool get_agent_component_host_path(bool use_localagent, const char* adb_path,
- const char* local_path, const char* sdk_path,
- std::string* output_path) {
- std::string mutable_adb_path = adb_path;
- const char* adb_dir = dirname(&mutable_adb_path[0]);
- if (adb_dir == nullptr) {
- return false;
- }
-
- if (use_localagent) {
- const char* product_out = getenv("ANDROID_PRODUCT_OUT");
- if (product_out == nullptr) {
- return false;
- }
- *output_path = android::base::StringPrintf("%s%s", product_out, local_path);
- return true;
- } else {
- *output_path = android::base::StringPrintf("%s%s", adb_dir, sdk_path);
- return true;
- }
- return false;
+void fastdeploy_set_local_agent(bool use_localagent) {
+ g_use_localagent = use_localagent;
}
-static bool deploy_agent(bool checkTimeStamps, bool use_localagent, const char* adb_path) {
+// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
+static std::string get_agent_component_host_path(const char* local_path, const char* sdk_path) {
+ std::string adb_dir = android::base::GetExecutableDirectory();
+ if (adb_dir.empty()) {
+ fatal("Could not determine location of adb!");
+ }
+
+ if (g_use_localagent) {
+ const char* product_out = getenv("ANDROID_PRODUCT_OUT");
+ if (product_out == nullptr) {
+ fatal("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined", local_path);
+ }
+ return android::base::StringPrintf("%s%s", product_out, local_path);
+ } else {
+ return adb_dir + sdk_path;
+ }
+}
+
+static bool deploy_agent(bool checkTimeStamps) {
std::vector<const char*> srcs;
-
- std::string agent_jar_path;
- if (get_agent_component_host_path(use_localagent, adb_path, "/system/framework/deployagent.jar",
- "/deployagent.jar", &agent_jar_path)) {
- srcs.push_back(agent_jar_path.c_str());
- } else {
- return false;
- }
-
- std::string agent_sh_path;
- if (get_agent_component_host_path(use_localagent, adb_path, "/system/bin/deployagent.sh",
- "/deployagent.sh", &agent_sh_path)) {
- srcs.push_back(agent_sh_path.c_str());
- } else {
- return false;
- }
+ std::string jar_path =
+ get_agent_component_host_path("/system/framework/deployagent.jar", "/deployagent.jar");
+ std::string script_path =
+ get_agent_component_host_path("/system/bin/deployagent", "/deployagent");
+ srcs.push_back(jar_path.c_str());
+ srcs.push_back(script_path.c_str());
if (do_sync_push(srcs, kDeviceAgentPath, checkTimeStamps)) {
// on windows the shell script might have lost execute permission
// so need to set this explicitly
- const char* kChmodCommandPattern = "chmod 777 %sdeployagent.sh";
+ const char* kChmodCommandPattern = "chmod 777 %sdeployagent";
std::string chmodCommand =
android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
int ret = send_shell_command(chmodCommand);
- return (ret == 0);
+ if (ret != 0) {
+ fatal("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
+ }
} else {
- return false;
+ fatal("Error pushing agent files to device");
}
+
+ return true;
}
-bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy, bool use_localagent,
- const char* adb_path) {
+void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy) {
long agent_version = get_agent_version();
switch (agentUpdateStrategy) {
case FastDeploy_AgentUpdateAlways:
- if (deploy_agent(false, use_localagent, adb_path) == false) {
- return false;
- }
+ deploy_agent(false);
break;
case FastDeploy_AgentUpdateNewerTimeStamp:
- if (deploy_agent(true, use_localagent, adb_path) == false) {
- return false;
- }
+ deploy_agent(true);
break;
case FastDeploy_AgentUpdateDifferentVersion:
if (agent_version != kRequiredAgentVersion) {
@@ -136,112 +126,74 @@
printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
agent_version, kRequiredAgentVersion);
}
- if (deploy_agent(false, use_localagent, adb_path) == false) {
- return false;
- }
+ deploy_agent(false);
}
break;
}
agent_version = get_agent_version();
- return (agent_version == kRequiredAgentVersion);
+ if (agent_version != kRequiredAgentVersion) {
+ fatal("After update agent version remains incorrect! Expected %ld but version is %ld",
+ kRequiredAgentVersion, agent_version);
+ }
}
-static std::string get_string_from_utf16(const char16_t* input, int input_len) {
- ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
- if (utf8_length <= 0) {
- return {};
+static std::string get_aapt2_path() {
+ if (g_use_localagent) {
+ // This should never happen on a Windows machine
+ const char* host_out = getenv("ANDROID_HOST_OUT");
+ if (host_out == nullptr) {
+ fatal("Could not locate aapt2 because $ANDROID_HOST_OUT is not defined");
+ }
+ return android::base::StringPrintf("%s/bin/aapt2", host_out);
}
- std::string utf8;
- utf8.resize(utf8_length);
- utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
- return utf8;
+ std::string adb_dir = android::base::GetExecutableDirectory();
+ if (adb_dir.empty()) {
+ fatal("Could not locate aapt2");
+ }
+ return adb_dir + "/aapt2";
+}
+
+static int system_capture(const char* cmd, std::string& output) {
+ FILE* pipe = popen(cmd, "re");
+ int fd = -1;
+
+ if (pipe != nullptr) {
+ fd = fileno(pipe);
+ }
+
+ if (fd == -1) {
+ fatal_errno("Could not create pipe for process '%s'", cmd);
+ }
+
+ if (!android::base::ReadFdToString(fd, &output)) {
+ fatal_errno("Error reading from process '%s'", cmd);
+ }
+
+ return pclose(pipe);
}
// output is required to point to a valid output string (non-null)
-static bool get_packagename_from_apk(const char* apkPath, std::string* output) {
- using namespace android;
+static std::string get_packagename_from_apk(const char* apkPath) {
+ const char* kAapt2DumpNameCommandPattern = R"(%s dump packagename "%s")";
+ std::string aapt2_path_string = get_aapt2_path();
+ std::string getPackagenameCommand = android::base::StringPrintf(
+ kAapt2DumpNameCommandPattern, aapt2_path_string.c_str(), apkPath);
- ZipFileRO* zipFile = ZipFileRO::open(apkPath);
- if (zipFile == nullptr) {
- return false;
+ std::string package_name;
+ int exit_code = system_capture(getPackagenameCommand.c_str(), package_name);
+ if (exit_code != 0) {
+ fatal("Error executing '%s' exitcode: %d", getPackagenameCommand.c_str(), exit_code);
}
- ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
- if (entry == nullptr) {
- return false;
- }
-
- uint32_t manifest_len = 0;
- if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
- return false;
- }
-
- std::vector<char> manifest_data(manifest_len);
- if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
- return false;
- }
-
- ResXMLTree tree;
- status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
- if (setto_status != NO_ERROR) {
- return false;
- }
-
- ResXMLParser::event_code_t code;
- while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT &&
- code != ResXMLParser::END_DOCUMENT) {
- switch (code) {
- case ResXMLParser::START_TAG: {
- size_t element_name_length;
- const char16_t* element_name = tree.getElementName(&element_name_length);
- if (element_name == nullptr) {
- continue;
- }
-
- std::u16string element_name_string(element_name, element_name_length);
- if (element_name_string == u"manifest") {
- for (int i = 0; i < (int)tree.getAttributeCount(); i++) {
- size_t attribute_name_length;
- const char16_t* attribute_name_text =
- tree.getAttributeName(i, &attribute_name_length);
- if (attribute_name_text == nullptr) {
- continue;
- }
- std::u16string attribute_name_string(attribute_name_text,
- attribute_name_length);
-
- if (attribute_name_string == u"package") {
- size_t attribute_value_length;
- const char16_t* attribute_value_text =
- tree.getAttributeStringValue(i, &attribute_value_length);
- if (attribute_value_text == nullptr) {
- continue;
- }
- *output = get_string_from_utf16(attribute_value_text,
- attribute_value_length);
- return true;
- }
- }
- }
- break;
- }
- default:
- break;
- }
- }
-
- return false;
+ // strip any line end characters from the output
+ return android::base::Trim(package_name);
}
int extract_metadata(const char* apkPath, FILE* outputFp) {
- std::string packageName;
- if (get_packagename_from_apk(apkPath, &packageName) == false) {
- return -1;
- }
-
- const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent.sh extract %s";
+ std::string packageName = get_packagename_from_apk(apkPath);
+ const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent extract %s";
std::string extractCommand =
android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
@@ -257,64 +209,43 @@
return ret;
}
-// output is required to point to a valid output string (non-null)
-static bool patch_generator_command(bool use_localagent, const char* adb_path,
- std::string* output) {
- if (use_localagent) {
+static std::string get_patch_generator_command() {
+ if (g_use_localagent) {
// This should never happen on a Windows machine
- const char* kGeneratorCommandPattern = "java -jar %s/framework/deploypatchgenerator.jar";
const char* host_out = getenv("ANDROID_HOST_OUT");
if (host_out == nullptr) {
- return false;
+ fatal("Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT is not "
+ "defined");
}
- *output = android::base::StringPrintf(kGeneratorCommandPattern, host_out, host_out);
- return true;
- } else {
- const char* kGeneratorCommandPattern = R"(java -jar "%s/deploypatchgenerator.jar")";
- std::string mutable_adb_path = adb_path;
- const char* adb_dir = dirname(&mutable_adb_path[0]);
- if (adb_dir == nullptr) {
- return false;
- }
-
- *output = android::base::StringPrintf(kGeneratorCommandPattern, adb_dir, adb_dir);
- return true;
+ return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
+ host_out);
}
- return false;
+
+ std::string adb_dir = android::base::GetExecutableDirectory();
+ if (adb_dir.empty()) {
+ fatal("Could not locate deploypatchgenerator.jar");
+ }
+ return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
+ adb_dir.c_str());
}
-int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath,
- bool use_localagent, const char* adb_path) {
- const char* kGeneratePatchCommandPattern = R"(%s "%s" "%s" > "%s")";
- std::string patch_generator_command_string;
- if (patch_generator_command(use_localagent, adb_path, &patch_generator_command_string) ==
- false) {
- return 1;
- }
+int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
std::string generatePatchCommand = android::base::StringPrintf(
- kGeneratePatchCommandPattern, patch_generator_command_string.c_str(), apkPath,
- metadataPath, patchPath);
+ R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
+ patchPath);
return system(generatePatchCommand.c_str());
}
std::string get_patch_path(const char* apkPath) {
- std::string packageName;
- if (get_packagename_from_apk(apkPath, &packageName) == false) {
- return "";
- }
+ std::string packageName = get_packagename_from_apk(apkPath);
std::string patchDevicePath =
android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
return patchDevicePath;
}
int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
- const std::string kAgentApplyCommandPattern =
- "/data/local/tmp/deployagent.sh apply %s %s -o %s";
-
- std::string packageName;
- if (get_packagename_from_apk(apkPath, &packageName) == false) {
- return -1;
- }
+ const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -o %s";
+ std::string packageName = get_packagename_from_apk(apkPath);
std::string patchDevicePath = get_patch_path(apkPath);
std::vector<const char*> srcs = {patchPath};
@@ -327,18 +258,13 @@
std::string applyPatchCommand =
android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
patchDevicePath.c_str(), outputPath);
+
return send_shell_command(applyPatchCommand);
}
int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
- const std::string kAgentApplyCommandPattern =
- "/data/local/tmp/deployagent.sh apply %s %s -pm %s";
-
- std::string packageName;
- if (get_packagename_from_apk(apkPath, &packageName) == false) {
- return -1;
- }
-
+ const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -pm %s";
+ std::string packageName = get_packagename_from_apk(apkPath);
std::vector<const char*> srcs;
std::string patchDevicePath =
android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
index d8acd30..e5e7663 100644
--- a/adb/client/fastdeploy.h
+++ b/adb/client/fastdeploy.h
@@ -18,20 +18,17 @@
#include "adb.h"
-typedef enum EFastDeploy_AgentUpdateStrategy {
+enum FastDeploy_AgentUpdateStrategy {
FastDeploy_AgentUpdateAlways,
FastDeploy_AgentUpdateNewerTimeStamp,
FastDeploy_AgentUpdateDifferentVersion
-} FastDeploy_AgentUpdateStrategy;
+};
-static constexpr int kFastDeployMinApi = 24;
-
+void fastdeploy_set_local_agent(bool use_localagent);
int get_device_api_level();
-bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy, bool use_localagent,
- const char* adb_path);
+void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy);
int extract_metadata(const char* apkPath, FILE* outputFp);
-int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath,
- bool use_localagent, const char* adb_path);
+int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath);
int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
std::string get_patch_path(const char* apkPath);
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 095ad98..a7e454d 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -56,15 +56,6 @@
LOG(INFO) << adb_version();
}
-#if defined(_WIN32)
-static BOOL WINAPI ctrlc_handler(DWORD type) {
- // TODO: Consider trying to kill a starting up adb server (if we're in
- // launch_server) by calling GenerateConsoleCtrlEvent().
- exit(STATUS_CONTROL_C_EXIT);
- return TRUE;
-}
-#endif
-
void adb_server_cleanup() {
// Upon exit, we want to clean up in the following order:
// 1. close_smartsockets, so that we don't get any new clients
@@ -97,12 +88,16 @@
}
}
- SetConsoleCtrlHandler(ctrlc_handler, TRUE);
-#else
+ // TODO: On Ctrl-C, consider trying to kill a starting up adb server (if we're in
+ // launch_server) by calling GenerateConsoleCtrlEvent().
+
+ // On Windows, SIGBREAK is when Ctrl-Break is pressed or the console window is closed. It should
+ // act like Ctrl-C.
+ signal(SIGBREAK, [](int) { raise(SIGINT); });
+#endif
signal(SIGINT, [](int) {
fdevent_run_on_main_thread([]() { exit(0); });
});
-#endif
char* leak = getenv("ADB_LEAK");
if (leak && strcmp(leak, "1") == 0) {
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index ed5f944..ae02525 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -35,6 +35,7 @@
#include <string>
#include <vector>
+#include <android-base/file.h>
#include <android-base/properties.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
@@ -47,6 +48,8 @@
#include "adb_utils.h"
#include "set_verity_enable_state_service.h"
+using android::base::Realpath;
+
// Returns the last device used to mount a directory in /proc/mounts.
// This will find overlayfs entry where upperdir=lowerdir, to make sure
// remount is associated with the correct directory.
@@ -55,9 +58,15 @@
std::string mnt_fsname;
if (!fp) return mnt_fsname;
+ // dir might be a symlink, e.g., /product -> /system/product in GSI.
+ std::string canonical_path;
+ if (!Realpath(dir, &canonical_path)) {
+ PLOG(ERROR) << "Realpath failed: " << dir;
+ }
+
mntent* e;
while ((e = getmntent(fp.get())) != nullptr) {
- if (strcmp(dir, e->mnt_dir) == 0) {
+ if (canonical_path == e->mnt_dir) {
mnt_fsname = e->mnt_fsname;
}
}
@@ -146,6 +155,10 @@
return true;
}
bool is_root = strcmp(dir, "/") == 0;
+ if (is_root && !find_mount("/system", false).empty()) {
+ dir = "/system";
+ is_root = false;
+ }
std::string dev = find_mount(dir, is_root);
// Even if the device for the root is not found, we still try to remount it
// as rw. This typically only happens when running Android in a container:
@@ -209,24 +222,26 @@
bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
- 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");
- }
+ std::vector<std::string> partitions{"/", "/odm", "/oem", "/product_services",
+ "/product", "/vendor"};
bool verity_enabled = (system_verified || vendor_verified);
// If we can use overlayfs, lets get it in place first
// before we struggle with determining deduplication operations.
- if (!verity_enabled && fs_mgr_overlayfs_setup() && fs_mgr_overlayfs_mount_all()) {
- WriteFdExactly(fd.get(), "overlayfs mounted\n");
+ if (!verity_enabled && fs_mgr_overlayfs_setup()) {
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ fs_mgr_free_fstab);
+ if (fs_mgr_overlayfs_mount_all(fstab.get())) {
+ WriteFdExactly(fd.get(), "overlayfs mounted\n");
+ }
}
// Find partitions that are deduplicated, and can be un-deduplicated.
std::set<std::string> dedup;
- for (const auto& partition : partitions) {
+ for (const auto& part : partitions) {
+ auto partition = part;
+ if ((part == "/") && !find_mount("/system", false).empty()) partition = "/system";
std::string dev = find_mount(partition.c_str(), partition == "/");
if (dev.empty() || !fs_mgr_has_shared_blocks(partition, dev)) {
continue;
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 8417690..2bac486 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -103,7 +103,9 @@
if (reboot_arg.empty()) reboot_arg = "adb";
std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
- if (reboot_arg == "fastboot" && access("/dev/socket/recovery", F_OK) == 0) {
+ if (reboot_arg == "fastboot" &&
+ android::base::GetBoolProperty("ro.boot.logical_partitions", false) &&
+ access("/dev/socket/recovery", F_OK) == 0) {
LOG(INFO) << "Recovery specific reboot fastboot";
/*
* The socket is created to allow switching between recovery and
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 30f4730..400b12f 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -14,24 +14,17 @@
// limitations under the License.
//
-java_library {
+java_binary {
name: "deployagent",
sdk_version: "24",
srcs: ["deployagent/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
static_libs: ["apkzlib_zip"],
+ wrapper: "deployagent/deployagent.sh",
proto: {
type: "lite",
}
}
-cc_prebuilt_binary {
- name: "deployagent.sh",
-
- srcs: ["deployagent/deployagent.sh"],
- required: ["deployagent"],
- device_supported: true,
-}
-
java_binary_host {
name: "deploypatchgenerator",
srcs: ["deploypatchgenerator/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index dee87bd..98a73eb 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -147,24 +147,34 @@
return fde;
}
-void fdevent_destroy(fdevent* fde) {
+unique_fd fdevent_release(fdevent* fde) {
check_main_thread();
- if (fde == nullptr) return;
+ if (!fde) {
+ return {};
+ }
+
if (!(fde->state & FDE_CREATED)) {
LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
}
+ unique_fd result = std::move(fde->fd);
if (fde->state & FDE_ACTIVE) {
- g_poll_node_map.erase(fde->fd.get());
+ g_poll_node_map.erase(result.get());
+
if (fde->state & FDE_PENDING) {
g_pending_list.remove(fde);
}
- fde->fd.reset();
fde->state = 0;
fde->events = 0;
}
delete fde;
+ return result;
+}
+
+void fdevent_destroy(fdevent* fde) {
+ // Release, and then let unique_fd's destructor cleanup.
+ fdevent_release(fde);
}
static void fdevent_update(fdevent* fde, unsigned events) {
diff --git a/adb/fdevent.h b/adb/fdevent.h
index d501b86..df2339a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -50,11 +50,12 @@
*/
fdevent *fdevent_create(int fd, fd_func func, void *arg);
-/* Uninitialize and deallocate an fdevent object that was
-** created by fdevent_create()
-*/
+// Deallocate an fdevent object that was created by fdevent_create.
void fdevent_destroy(fdevent *fde);
+// fdevent_destroy, except releasing the file descriptor previously owned by the fdevent.
+unique_fd fdevent_release(fdevent* fde);
+
/* Change which events should cause notifications
*/
void fdevent_set(fdevent *fde, unsigned events);
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 0c2e45c..b8d7e06 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -52,10 +52,11 @@
#include <fcntl.h>
#include <io.h>
#include <process.h>
+#include <stdint.h>
#include <sys/stat.h>
#include <utime.h>
-#include <winsock2.h>
#include <windows.h>
+#include <winsock2.h>
#include <ws2tcpip.h>
#include <memory> // unique_ptr
@@ -72,12 +73,7 @@
return c == '\\' || c == '/';
}
-static __inline__ int adb_thread_setname(const std::string& name) {
- // TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
- // the thread name in Windows. Unfortunately, it only works during debugging, but
- // our build process doesn't generate PDB files needed for debugging.
- return 0;
-}
+extern int adb_thread_setname(const std::string& name);
static __inline__ void close_on_exec(int fd)
{
@@ -97,7 +93,7 @@
extern int adb_creat(const char* path, int mode);
extern int adb_read(int fd, void* buf, int len);
extern int adb_write(int fd, const void* buf, int len);
-extern int adb_lseek(int fd, int pos, int where);
+extern int64_t adb_lseek(int fd, int64_t pos, int where);
extern int adb_shutdown(int fd, int direction = SHUT_RDWR);
extern int adb_close(int fd);
extern int adb_register_socket(SOCKET s);
@@ -129,6 +125,13 @@
#undef write
#define write ___xxx_write
+// See the comments for the !defined(_WIN32) version of unix_lseek().
+static __inline__ int unix_lseek(int fd, int pos, int where) {
+ return lseek(fd, pos, where);
+}
+#undef lseek
+#define lseek ___xxx_lseek
+
// See the comments for the !defined(_WIN32) version of adb_open_mode().
static __inline__ int adb_open_mode(const char* path, int options, int mode)
{
@@ -313,25 +316,24 @@
#else /* !_WIN32 a.k.a. Unix */
-#include <cutils/sockets.h>
#include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include <pthread.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdarg.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <string>
+#include <cutils/sockets.h>
+
#define OS_PATH_SEPARATORS "/"
#define OS_PATH_SEPARATOR '/'
#define OS_PATH_SEPARATOR_STR "/"
@@ -441,12 +443,15 @@
#undef write
#define write ___xxx_write
-static __inline__ int adb_lseek(int fd, int pos, int where)
-{
+static __inline__ int64_t adb_lseek(int fd, int64_t pos, int where) {
+#if defined(__APPLE__)
return lseek(fd, pos, where);
+#else
+ return lseek64(fd, pos, where);
+#endif
}
-#undef lseek
-#define lseek ___xxx_lseek
+#undef lseek
+#define lseek ___xxx_lseek
static __inline__ int adb_unlink(const char* path)
{
@@ -523,6 +528,7 @@
// via _setmode()).
#define unix_read adb_read
#define unix_write adb_write
+#define unix_lseek adb_lseek
#define unix_close adb_close
static __inline__ int adb_thread_setname(const std::string& name) {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index c94d13f..8784757 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -52,12 +52,11 @@
typedef const struct FHClassRec_* FHClass;
typedef struct FHRec_* FH;
-typedef struct EventHookRec_* EventHook;
typedef struct FHClassRec_ {
void (*_fh_init)(FH);
int (*_fh_close)(FH);
- int (*_fh_lseek)(FH, int, int);
+ int64_t (*_fh_lseek)(FH, int64_t, int);
int (*_fh_read)(FH, void*, int);
int (*_fh_write)(FH, const void*, int);
int (*_fh_writev)(FH, const adb_iovec*, int);
@@ -65,7 +64,7 @@
static void _fh_file_init(FH);
static int _fh_file_close(FH);
-static int _fh_file_lseek(FH, int, int);
+static int64_t _fh_file_lseek(FH, int64_t, int);
static int _fh_file_read(FH, void*, int);
static int _fh_file_write(FH, const void*, int);
static int _fh_file_writev(FH, const adb_iovec*, int);
@@ -81,7 +80,7 @@
static void _fh_socket_init(FH);
static int _fh_socket_close(FH);
-static int _fh_socket_lseek(FH, int, int);
+static int64_t _fh_socket_lseek(FH, int64_t, int);
static int _fh_socket_read(FH, void*, int);
static int _fh_socket_write(FH, const void*, int);
static int _fh_socket_writev(FH, const adb_iovec*, int);
@@ -318,10 +317,8 @@
return wrote_bytes;
}
-static int _fh_file_lseek(FH f, int pos, int origin) {
+static int64_t _fh_file_lseek(FH f, int64_t pos, int origin) {
DWORD method;
- DWORD result;
-
switch (origin) {
case SEEK_SET:
method = FILE_BEGIN;
@@ -337,14 +334,13 @@
return -1;
}
- result = SetFilePointer(f->fh_handle, pos, nullptr, method);
- if (result == INVALID_SET_FILE_POINTER) {
+ LARGE_INTEGER li = {.QuadPart = pos};
+ if (!SetFilePointerEx(f->fh_handle, li, &li, method)) {
errno = EIO;
return -1;
- } else {
- f->eof = 0;
}
- return (int)result;
+ f->eof = 0;
+ return li.QuadPart;
}
/**************************************************************************/
@@ -491,14 +487,12 @@
return f->clazz->_fh_writev(f, iov, iovcnt);
}
-int adb_lseek(int fd, int pos, int where) {
+int64_t adb_lseek(int fd, int64_t pos, int where) {
FH f = _fh_from_int(fd, __func__);
-
if (!f) {
errno = EBADF;
return -1;
}
-
return f->clazz->_fh_lseek(f, pos, where);
}
@@ -644,7 +638,7 @@
return 0;
}
-static int _fh_socket_lseek(FH f, int pos, int origin) {
+static int64_t _fh_socket_lseek(FH f, int64_t pos, int origin) {
errno = EPIPE;
return -1;
}
@@ -2742,3 +2736,26 @@
return buf;
}
+
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
+
+// Based on PlatformThread::SetName() from
+// https://cs.chromium.org/chromium/src/base/threading/platform_thread_win.cc
+int adb_thread_setname(const std::string& name) {
+ // The SetThreadDescription API works even if no debugger is attached.
+ auto set_thread_description_func = reinterpret_cast<SetThreadDescription>(
+ ::GetProcAddress(::GetModuleHandleW(L"Kernel32.dll"), "SetThreadDescription"));
+ if (set_thread_description_func) {
+ std::wstring name_wide;
+ if (!android::base::UTF8ToWide(name.c_str(), &name_wide)) {
+ return errno;
+ }
+ set_thread_description_func(::GetCurrentThread(), name_wide.c_str());
+ }
+
+ // Don't use the thread naming SEH exception because we're compiled with -fno-exceptions.
+ // https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code?view=vs-2017
+
+ return 0;
+}
diff --git a/adb/test_adb.py b/adb/test_adb.py
index cde2b22..7e73818 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -490,6 +490,58 @@
self.assertEqual(_devices(server_port), [])
+@unittest.skipUnless(sys.platform == "win32", "requires Windows")
+class PowerTest(unittest.TestCase):
+ def test_resume_usb_kick(self):
+ """Resuming from sleep/hibernate should kick USB devices."""
+ try:
+ usb_serial = subprocess.check_output(["adb", "-d", "get-serialno"]).strip()
+ except subprocess.CalledProcessError:
+ # If there are multiple USB devices, we don't have a way to check whether the selected
+ # device is USB.
+ raise unittest.SkipTest('requires single USB device')
+
+ try:
+ serial = subprocess.check_output(["adb", "get-serialno"]).strip()
+ except subprocess.CalledProcessError:
+ # Did you forget to select a device with $ANDROID_SERIAL?
+ raise unittest.SkipTest('requires $ANDROID_SERIAL set to a USB device')
+
+ # Test only works with USB devices because adb _power_notification_thread does not kick
+ # non-USB devices on resume event.
+ if serial != usb_serial:
+ raise unittest.SkipTest('requires USB device')
+
+ # Run an adb shell command in the background that takes a while to complete.
+ proc = subprocess.Popen(['adb', 'shell', 'sleep', '5'])
+
+ # Wait for startup of adb server's _power_notification_thread.
+ time.sleep(0.1)
+
+ # Simulate resuming from sleep/hibernation by sending Windows message.
+ import ctypes
+ from ctypes import wintypes
+ HWND_BROADCAST = 0xffff
+ WM_POWERBROADCAST = 0x218
+ PBT_APMRESUMEAUTOMATIC = 0x12
+
+ PostMessageW = ctypes.windll.user32.PostMessageW
+ PostMessageW.restype = wintypes.BOOL
+ PostMessageW.argtypes = (wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)
+ result = PostMessageW(HWND_BROADCAST, WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC, 0)
+ if not result:
+ raise ctypes.WinError()
+
+ # Wait for connection to adb shell to be broken by _power_notification_thread detecting the
+ # Windows message.
+ start = time.time()
+ proc.wait()
+ end = time.time()
+
+ # If the power event was detected, the adb shell command should be broken very quickly.
+ self.assertLess(end - start, 2)
+
+
def main():
"""Main entrypoint."""
random.seed(0)
diff --git a/adb/test_device.py b/adb/test_device.py
old mode 100644
new mode 100755
index 20f224a..c3166ff
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -751,7 +751,7 @@
shutil.rmtree(host_dir)
def test_push_empty(self):
- """Push a directory containing an empty directory to the device."""
+ """Push an empty directory to the device."""
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
self.device.shell(['mkdir', self.DEVICE_TEMP_DIR])
@@ -767,9 +767,10 @@
self.device.push(empty_dir_path, self.DEVICE_TEMP_DIR)
- test_empty_cmd = ['[', '-d',
- os.path.join(self.DEVICE_TEMP_DIR, 'empty')]
+ remote_path = os.path.join(self.DEVICE_TEMP_DIR, "empty")
+ test_empty_cmd = ["[", "-d", remote_path, "]"]
rc, _, _ = self.device.shell_nocheck(test_empty_cmd)
+
self.assertEqual(rc, 0)
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
finally:
@@ -1334,6 +1335,206 @@
self.device.forward_remove("tcp:{}".format(local_port))
+if sys.platform == "win32":
+ # From https://stackoverflow.com/a/38749458
+ import os
+ import contextlib
+ import msvcrt
+ import ctypes
+ from ctypes import wintypes
+
+ kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
+
+ GENERIC_READ = 0x80000000
+ GENERIC_WRITE = 0x40000000
+ FILE_SHARE_READ = 1
+ FILE_SHARE_WRITE = 2
+ CONSOLE_TEXTMODE_BUFFER = 1
+ INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
+ STD_OUTPUT_HANDLE = wintypes.DWORD(-11)
+ STD_ERROR_HANDLE = wintypes.DWORD(-12)
+
+ def _check_zero(result, func, args):
+ if not result:
+ raise ctypes.WinError(ctypes.get_last_error())
+ return args
+
+ def _check_invalid(result, func, args):
+ if result == INVALID_HANDLE_VALUE:
+ raise ctypes.WinError(ctypes.get_last_error())
+ return args
+
+ if not hasattr(wintypes, 'LPDWORD'): # Python 2
+ wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD)
+ wintypes.PSMALL_RECT = ctypes.POINTER(wintypes.SMALL_RECT)
+
+ class COORD(ctypes.Structure):
+ _fields_ = (('X', wintypes.SHORT),
+ ('Y', wintypes.SHORT))
+
+ class CONSOLE_SCREEN_BUFFER_INFOEX(ctypes.Structure):
+ _fields_ = (('cbSize', wintypes.ULONG),
+ ('dwSize', COORD),
+ ('dwCursorPosition', COORD),
+ ('wAttributes', wintypes.WORD),
+ ('srWindow', wintypes.SMALL_RECT),
+ ('dwMaximumWindowSize', COORD),
+ ('wPopupAttributes', wintypes.WORD),
+ ('bFullscreenSupported', wintypes.BOOL),
+ ('ColorTable', wintypes.DWORD * 16))
+ def __init__(self, *args, **kwds):
+ super(CONSOLE_SCREEN_BUFFER_INFOEX, self).__init__(
+ *args, **kwds)
+ self.cbSize = ctypes.sizeof(self)
+
+ PCONSOLE_SCREEN_BUFFER_INFOEX = ctypes.POINTER(
+ CONSOLE_SCREEN_BUFFER_INFOEX)
+ LPSECURITY_ATTRIBUTES = wintypes.LPVOID
+
+ kernel32.GetStdHandle.errcheck = _check_invalid
+ kernel32.GetStdHandle.restype = wintypes.HANDLE
+ kernel32.GetStdHandle.argtypes = (
+ wintypes.DWORD,) # _In_ nStdHandle
+
+ kernel32.CreateConsoleScreenBuffer.errcheck = _check_invalid
+ kernel32.CreateConsoleScreenBuffer.restype = wintypes.HANDLE
+ kernel32.CreateConsoleScreenBuffer.argtypes = (
+ wintypes.DWORD, # _In_ dwDesiredAccess
+ wintypes.DWORD, # _In_ dwShareMode
+ LPSECURITY_ATTRIBUTES, # _In_opt_ lpSecurityAttributes
+ wintypes.DWORD, # _In_ dwFlags
+ wintypes.LPVOID) # _Reserved_ lpScreenBufferData
+
+ kernel32.GetConsoleScreenBufferInfoEx.errcheck = _check_zero
+ kernel32.GetConsoleScreenBufferInfoEx.argtypes = (
+ wintypes.HANDLE, # _In_ hConsoleOutput
+ PCONSOLE_SCREEN_BUFFER_INFOEX) # _Out_ lpConsoleScreenBufferInfo
+
+ kernel32.SetConsoleScreenBufferInfoEx.errcheck = _check_zero
+ kernel32.SetConsoleScreenBufferInfoEx.argtypes = (
+ wintypes.HANDLE, # _In_ hConsoleOutput
+ PCONSOLE_SCREEN_BUFFER_INFOEX) # _In_ lpConsoleScreenBufferInfo
+
+ kernel32.SetConsoleWindowInfo.errcheck = _check_zero
+ kernel32.SetConsoleWindowInfo.argtypes = (
+ wintypes.HANDLE, # _In_ hConsoleOutput
+ wintypes.BOOL, # _In_ bAbsolute
+ wintypes.PSMALL_RECT) # _In_ lpConsoleWindow
+
+ kernel32.FillConsoleOutputCharacterW.errcheck = _check_zero
+ kernel32.FillConsoleOutputCharacterW.argtypes = (
+ wintypes.HANDLE, # _In_ hConsoleOutput
+ wintypes.WCHAR, # _In_ cCharacter
+ wintypes.DWORD, # _In_ nLength
+ COORD, # _In_ dwWriteCoord
+ wintypes.LPDWORD) # _Out_ lpNumberOfCharsWritten
+
+ kernel32.ReadConsoleOutputCharacterW.errcheck = _check_zero
+ kernel32.ReadConsoleOutputCharacterW.argtypes = (
+ wintypes.HANDLE, # _In_ hConsoleOutput
+ wintypes.LPWSTR, # _Out_ lpCharacter
+ wintypes.DWORD, # _In_ nLength
+ COORD, # _In_ dwReadCoord
+ wintypes.LPDWORD) # _Out_ lpNumberOfCharsRead
+
+ @contextlib.contextmanager
+ def allocate_console():
+ allocated = kernel32.AllocConsole()
+ try:
+ yield allocated
+ finally:
+ if allocated:
+ kernel32.FreeConsole()
+
+ @contextlib.contextmanager
+ def console_screen(ncols=None, nrows=None):
+ info = CONSOLE_SCREEN_BUFFER_INFOEX()
+ new_info = CONSOLE_SCREEN_BUFFER_INFOEX()
+ nwritten = (wintypes.DWORD * 1)()
+ hStdOut = kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
+ kernel32.GetConsoleScreenBufferInfoEx(
+ hStdOut, ctypes.byref(info))
+ if ncols is None:
+ ncols = info.dwSize.X
+ if nrows is None:
+ nrows = info.dwSize.Y
+ elif nrows > 9999:
+ raise ValueError('nrows must be 9999 or less')
+ fd_screen = None
+ hScreen = kernel32.CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ None, CONSOLE_TEXTMODE_BUFFER, None)
+ try:
+ fd_screen = msvcrt.open_osfhandle(
+ hScreen, os.O_RDWR | os.O_BINARY)
+ kernel32.GetConsoleScreenBufferInfoEx(
+ hScreen, ctypes.byref(new_info))
+ new_info.dwSize = COORD(ncols, nrows)
+ new_info.srWindow = wintypes.SMALL_RECT(
+ Left=0, Top=0, Right=(ncols - 1),
+ Bottom=(info.srWindow.Bottom - info.srWindow.Top))
+ kernel32.SetConsoleScreenBufferInfoEx(
+ hScreen, ctypes.byref(new_info))
+ kernel32.SetConsoleWindowInfo(hScreen, True,
+ ctypes.byref(new_info.srWindow))
+ kernel32.FillConsoleOutputCharacterW(
+ hScreen, u'\0', ncols * nrows, COORD(0,0), nwritten)
+ kernel32.SetConsoleActiveScreenBuffer(hScreen)
+ try:
+ yield fd_screen
+ finally:
+ kernel32.SetConsoleScreenBufferInfoEx(
+ hStdOut, ctypes.byref(info))
+ kernel32.SetConsoleWindowInfo(hStdOut, True,
+ ctypes.byref(info.srWindow))
+ kernel32.SetConsoleActiveScreenBuffer(hStdOut)
+ finally:
+ if fd_screen is not None:
+ os.close(fd_screen)
+ else:
+ kernel32.CloseHandle(hScreen)
+
+ def read_screen(fd):
+ hScreen = msvcrt.get_osfhandle(fd)
+ csbi = CONSOLE_SCREEN_BUFFER_INFOEX()
+ kernel32.GetConsoleScreenBufferInfoEx(
+ hScreen, ctypes.byref(csbi))
+ ncols = csbi.dwSize.X
+ pos = csbi.dwCursorPosition
+ length = ncols * pos.Y + pos.X + 1
+ buf = (ctypes.c_wchar * length)()
+ n = (wintypes.DWORD * 1)()
+ kernel32.ReadConsoleOutputCharacterW(
+ hScreen, buf, length, COORD(0,0), n)
+ lines = [buf[i:i+ncols].rstrip(u'\0')
+ for i in range(0, n[0], ncols)]
+ return u'\n'.join(lines)
+
+@unittest.skipUnless(sys.platform == "win32", "requires Windows")
+class WindowsConsoleTest(DeviceTest):
+ def test_unicode_output(self):
+ """Test Unicode command line parameters and Unicode console window output.
+
+ Bug: https://issuetracker.google.com/issues/111972753
+ """
+ # If we don't have a console window, allocate one. This isn't necessary if we're already
+ # being run from a console window, which is typical.
+ with allocate_console() as allocated_console:
+ # Create a temporary console buffer and switch to it. We could also pass a parameter of
+ # ncols=len(unicode_string), but it causes the window to flash as it is resized and
+ # likely unnecessary given the typical console window size.
+ with console_screen(nrows=1000) as screen:
+ unicode_string = u'로보카 폴리'
+ # Run adb and allow it to detect that stdout is a console, not a pipe, by using
+ # device.shell_popen() which does not use a pipe, unlike device.shell().
+ process = self.device.shell_popen(['echo', '"' + unicode_string + '"'])
+ process.wait()
+ # Read what was written by adb to the temporary console buffer.
+ console_output = read_screen(screen)
+ self.assertEqual(unicode_string, console_output)
+
+
def main():
random.seed(0)
if len(adb.get_devices()) > 0:
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 239403a..95df490 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -1040,12 +1040,6 @@
return max_payload;
}
-namespace {
-
-constexpr char kFeatureStringDelimiter = ',';
-
-} // namespace
-
const FeatureSet& supported_features() {
// Local static allocation to avoid global non-POD variables.
static const FeatureSet* features = new FeatureSet{
@@ -1059,7 +1053,7 @@
}
std::string FeatureSetToString(const FeatureSet& features) {
- return android::base::Join(features, kFeatureStringDelimiter);
+ return android::base::Join(features, ',');
}
FeatureSet StringToFeatureSet(const std::string& features_string) {
@@ -1067,7 +1061,7 @@
return FeatureSet();
}
- auto names = android::base::Split(features_string, {kFeatureStringDelimiter});
+ auto names = android::base::Split(features_string, ",");
return FeatureSet(names.begin(), names.end());
}
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 4fd483b..8353d89 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -187,8 +187,8 @@
}
// Retry the disconnected local port for 60 times, and sleep 1 second between two retries.
-constexpr uint32_t LOCAL_PORT_RETRY_COUNT = 60;
-constexpr auto LOCAL_PORT_RETRY_INTERVAL = 1s;
+static constexpr uint32_t LOCAL_PORT_RETRY_COUNT = 60;
+static constexpr auto LOCAL_PORT_RETRY_INTERVAL = 1s;
struct RetryPort {
int port;
diff --git a/adb/types.h b/adb/types.h
index a3e5d48..1f7008e 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -42,14 +42,14 @@
}
Block(const Block& copy) = delete;
- Block(Block&& move) {
+ Block(Block&& move) noexcept {
std::swap(data_, move.data_);
std::swap(capacity_, move.capacity_);
std::swap(size_, move.size_);
}
Block& operator=(const Block& copy) = delete;
- Block& operator=(Block&& move) {
+ Block& operator=(Block&& move) noexcept {
clear();
std::swap(data_, move.data_);
@@ -147,12 +147,10 @@
}
IOVector(const IOVector& copy) = delete;
- IOVector(IOVector&& move) : IOVector() {
- *this = std::move(move);
- }
+ IOVector(IOVector&& move) noexcept : IOVector() { *this = std::move(move); }
IOVector& operator=(const IOVector& copy) = delete;
- IOVector& operator=(IOVector&& move) {
+ IOVector& operator=(IOVector&& move) noexcept {
chain_ = std::move(move.chain_);
chain_length_ = move.chain_length_;
begin_offset_ = move.begin_offset_;
diff --git a/base/Android.bp b/base/Android.bp
index 3d80d97..daa820a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -56,6 +56,7 @@
"test_utils.cpp",
],
+ cppflags: ["-Wexit-time-destructors"],
shared_libs: ["liblog"],
target: {
android: {
@@ -68,13 +69,11 @@
srcs: [
"errors_unix.cpp",
],
- cppflags: ["-Wexit-time-destructors"],
},
darwin: {
srcs: [
"errors_unix.cpp",
],
- cppflags: ["-Wexit-time-destructors"],
},
linux_bionic: {
enabled: true,
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 49cc0c9..1748665 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -170,7 +170,9 @@
//
// In either case this macro has no effect on runtime behavior and performance
// of code.
+#ifndef FALLTHROUGH_INTENDED
#define FALLTHROUGH_INTENDED [[clang::fallthrough]] // NOLINT
+#endif
// Current ABI string
#if defined(__arm__)
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index e6a9d10..5a224d6 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -28,7 +28,7 @@
public:
ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
- ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+ ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
that.active_ = false;
}
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 9e2ea97..2abe68e 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -62,16 +62,18 @@
CapturedStdFd(int std_fd);
~CapturedStdFd();
- int fd() const;
std::string str();
- private:
- void Init();
+ void Start();
+ void Stop();
void Reset();
+ private:
+ int fd() const;
+
TemporaryFile temp_file_;
int std_fd_;
- int old_fd_;
+ int old_fd_ = -1;
DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
};
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index c6936f1..cd2dc04 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -22,6 +22,7 @@
#include <sys/socket.h>
#endif
+#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
@@ -89,8 +90,8 @@
explicit unique_fd_impl(int fd) { reset(fd); }
~unique_fd_impl() { reset(); }
- unique_fd_impl(unique_fd_impl&& other) { reset(other.release()); }
- unique_fd_impl& operator=(unique_fd_impl&& s) {
+ unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
+ unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
int fd = s.fd_;
s.fd_ = -1;
reset(fd, &s);
@@ -199,6 +200,17 @@
return Socketpair(AF_UNIX, type, 0, left, right);
}
+// Using fdopen with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline FILE* Fdopen(unique_fd&& ufd, const char* mode) {
+ int fd = ufd.release();
+ FILE* file = fdopen(fd, mode);
+ if (!file) {
+ close(fd);
+ }
+ return file;
+}
+
#endif // !defined(_WIN32)
} // namespace base
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 75b4ea0..3113fb4 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -206,10 +206,8 @@
}
#endif
-static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
+static void CheckMessage(const std::string& output, android::base::LogSeverity severity,
const char* expected, const char* expected_tag = nullptr) {
- std::string output = cap.str();
-
// We can't usefully check the output of any of these on Windows because we
// don't have std::regex, but we can at least make sure we printed at least as
// many characters are in the log message.
@@ -231,20 +229,28 @@
#endif
}
+static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
+ const char* expected, const char* expected_tag = nullptr) {
+ cap.Stop();
+ std::string output = cap.str();
+ return CheckMessage(output, severity, expected, expected_tag);
+}
-#define CHECK_LOG_STREAM_DISABLED(severity) \
- { \
+#define CHECK_LOG_STREAM_DISABLED(severity) \
+ { \
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG_STREAM(severity) << "foo bar"; \
- ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
- } \
- { \
+ CapturedStderr cap1; \
+ LOG_STREAM(severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ } \
+ { \
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG_STREAM(::android::base::severity) << "foo bar"; \
- ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
- } \
+ CapturedStderr cap1; \
+ LOG_STREAM(::android::base::severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ }
#define CHECK_LOG_STREAM_ENABLED(severity) \
{ \
@@ -265,7 +271,7 @@
}
TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
- CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
}
TEST(logging, LOG_STREAM_ERROR_disabled) {
@@ -273,7 +279,7 @@
}
TEST(logging, LOG_STREAM_ERROR_enabled) {
- CHECK_LOG_STREAM_ENABLED(ERROR);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(ERROR));
}
TEST(logging, LOG_STREAM_WARNING_disabled) {
@@ -281,7 +287,7 @@
}
TEST(logging, LOG_STREAM_WARNING_enabled) {
- CHECK_LOG_STREAM_ENABLED(WARNING);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(WARNING));
}
TEST(logging, LOG_STREAM_INFO_disabled) {
@@ -289,7 +295,7 @@
}
TEST(logging, LOG_STREAM_INFO_enabled) {
- CHECK_LOG_STREAM_ENABLED(INFO);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(INFO));
}
TEST(logging, LOG_STREAM_DEBUG_disabled) {
@@ -297,7 +303,7 @@
}
TEST(logging, LOG_STREAM_DEBUG_enabled) {
- CHECK_LOG_STREAM_ENABLED(DEBUG);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(DEBUG));
}
TEST(logging, LOG_STREAM_VERBOSE_disabled) {
@@ -305,26 +311,27 @@
}
TEST(logging, LOG_STREAM_VERBOSE_enabled) {
- CHECK_LOG_STREAM_ENABLED(VERBOSE);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(VERBOSE));
}
#undef CHECK_LOG_STREAM_DISABLED
#undef CHECK_LOG_STREAM_ENABLED
-
-#define CHECK_LOG_DISABLED(severity) \
- { \
+#define CHECK_LOG_DISABLED(severity) \
+ { \
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG(severity) << "foo bar"; \
- ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
- } \
- { \
+ CapturedStderr cap1; \
+ LOG(severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ } \
+ { \
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- LOG(::android::base::severity) << "foo bar"; \
- ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
- } \
+ CapturedStderr cap1; \
+ LOG(::android::base::severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ }
#define CHECK_LOG_ENABLED(severity) \
{ \
@@ -350,7 +357,7 @@
}
TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
- CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
}
TEST(logging, LOG_ERROR_disabled) {
@@ -358,7 +365,7 @@
}
TEST(logging, LOG_ERROR_enabled) {
- CHECK_LOG_ENABLED(ERROR);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(ERROR));
}
TEST(logging, LOG_WARNING_disabled) {
@@ -366,7 +373,7 @@
}
TEST(logging, LOG_WARNING_enabled) {
- CHECK_LOG_ENABLED(WARNING);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(WARNING));
}
TEST(logging, LOG_INFO_disabled) {
@@ -374,7 +381,7 @@
}
TEST(logging, LOG_INFO_enabled) {
- CHECK_LOG_ENABLED(INFO);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(INFO));
}
TEST(logging, LOG_DEBUG_disabled) {
@@ -382,7 +389,7 @@
}
TEST(logging, LOG_DEBUG_enabled) {
- CHECK_LOG_ENABLED(DEBUG);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(DEBUG));
}
TEST(logging, LOG_VERBOSE_disabled) {
@@ -390,28 +397,28 @@
}
TEST(logging, LOG_VERBOSE_enabled) {
- CHECK_LOG_ENABLED(VERBOSE);
+ ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(VERBOSE));
}
#undef CHECK_LOG_DISABLED
#undef CHECK_LOG_ENABLED
-
TEST(logging, LOG_complex_param) {
-#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info) \
- { \
- android::base::ScopedLogSeverity sls( \
- (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING); \
- CapturedStderr cap; \
- LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING) \
- << "foobar"; \
- if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) { \
- CheckMessage(cap, \
- (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
- "foobar"); \
- } else { \
- ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_CUR)); \
- } \
+#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info) \
+ { \
+ android::base::ScopedLogSeverity sls( \
+ (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING); \
+ CapturedStderr cap; \
+ LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING) \
+ << "foobar"; \
+ if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) { \
+ ASSERT_NO_FATAL_FAILURE(CheckMessage( \
+ cap, (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
+ "foobar")); \
+ } else { \
+ cap.Stop(); \
+ ASSERT_EQ("", cap.str()); \
+ } \
}
CHECK_LOG_COMBINATION(false,false);
@@ -429,7 +436,7 @@
LOG(INFO) << (errno = 67890);
EXPECT_EQ(12345, errno) << "errno was not restored";
- CheckMessage(cap, android::base::INFO, "67890");
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
}
TEST(logging, PLOG_does_not_clobber_errno) {
@@ -438,7 +445,7 @@
PLOG(INFO) << (errno = 67890);
EXPECT_EQ(12345, errno) << "errno was not restored";
- CheckMessage(cap, android::base::INFO, "67890");
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
}
TEST(logging, LOG_does_not_have_dangling_if) {
@@ -464,19 +471,21 @@
EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
}
-#define CHECK_PLOG_DISABLED(severity) \
- { \
+#define CHECK_PLOG_DISABLED(severity) \
+ { \
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- PLOG(severity) << "foo bar"; \
- ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
- } \
- { \
+ CapturedStderr cap1; \
+ PLOG(severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ } \
+ { \
android::base::ScopedLogSeverity sls1(android::base::FATAL); \
- CapturedStderr cap1; \
- PLOG(severity) << "foo bar"; \
- ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
- } \
+ CapturedStderr cap1; \
+ PLOG(severity) << "foo bar"; \
+ cap1.Stop(); \
+ ASSERT_EQ("", cap1.str()); \
+ }
#define CHECK_PLOG_ENABLED(severity) \
{ \
@@ -504,7 +513,7 @@
}
TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
- CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT);
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
}
TEST(logging, PLOG_ERROR_disabled) {
@@ -512,7 +521,7 @@
}
TEST(logging, PLOG_ERROR_enabled) {
- CHECK_PLOG_ENABLED(ERROR);
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(ERROR));
}
TEST(logging, PLOG_WARNING_disabled) {
@@ -520,7 +529,7 @@
}
TEST(logging, PLOG_WARNING_enabled) {
- CHECK_PLOG_ENABLED(WARNING);
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(WARNING));
}
TEST(logging, PLOG_INFO_disabled) {
@@ -528,7 +537,7 @@
}
TEST(logging, PLOG_INFO_enabled) {
- CHECK_PLOG_ENABLED(INFO);
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(INFO));
}
TEST(logging, PLOG_DEBUG_disabled) {
@@ -536,7 +545,7 @@
}
TEST(logging, PLOG_DEBUG_enabled) {
- CHECK_PLOG_ENABLED(DEBUG);
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(DEBUG));
}
TEST(logging, PLOG_VERBOSE_disabled) {
@@ -544,7 +553,7 @@
}
TEST(logging, PLOG_VERBOSE_enabled) {
- CHECK_PLOG_ENABLED(VERBOSE);
+ ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(VERBOSE));
}
#undef CHECK_PLOG_DISABLED
@@ -557,7 +566,7 @@
CapturedStderr cap;
errno = ENOENT;
UNIMPLEMENTED(ERROR);
- CheckMessage(cap, android::base::ERROR, expected.c_str());
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::ERROR, expected.c_str()));
}
static void NoopAborter(const char* msg ATTRIBUTE_UNUSED) {
@@ -565,17 +574,19 @@
}
TEST(logging, LOG_FATAL_NOOP_ABORTER) {
+ CapturedStderr cap;
{
android::base::SetAborter(NoopAborter);
android::base::ScopedLogSeverity sls(android::base::ERROR);
- CapturedStderr cap;
LOG(FATAL) << "foobar";
- CheckMessage(cap, android::base::FATAL, "foobar");
- CheckMessage(cap, android::base::ERROR, "called noop");
+ cap.Stop();
android::base::SetAborter(android::base::DefaultAborter);
}
+ std::string output = cap.str();
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::FATAL, "foobar"));
+ ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::ERROR, "called noop"));
ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
}
@@ -619,25 +630,21 @@
LOG(INFO) << expected_msg;
android::base::SetDefaultTag(old_default_tag);
}
- CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag);
+ ASSERT_NO_FATAL_FAILURE(
+ CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag));
}
TEST(logging, StdioLogger) {
- std::string err_str;
- std::string out_str;
- {
- CapturedStderr cap_err;
- CapturedStdout cap_out;
- android::base::SetLogger(android::base::StdioLogger);
- LOG(INFO) << "out";
- LOG(ERROR) << "err";
- err_str = cap_err.str();
- out_str = cap_out.str();
- }
+ CapturedStderr cap_err;
+ CapturedStdout cap_out;
+ android::base::SetLogger(android::base::StdioLogger);
+ LOG(INFO) << "out";
+ LOG(ERROR) << "err";
+ cap_err.Stop();
+ cap_out.Stop();
// For INFO we expect just the literal "out\n".
- ASSERT_EQ("out\n", out_str) << out_str;
+ ASSERT_EQ("out\n", cap_out.str());
// Whereas ERROR logging includes the program name.
- ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", err_str)
- << err_str;
+ ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 5096369..4d9466b 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -126,11 +126,13 @@
}
CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
- Init();
+ Start();
}
CapturedStdFd::~CapturedStdFd() {
- Reset();
+ if (old_fd_ != -1) {
+ Stop();
+ }
}
int CapturedStdFd::fd() const {
@@ -144,19 +146,28 @@
return result;
}
-void CapturedStdFd::Init() {
+void CapturedStdFd::Reset() {
+ // Do not reset while capturing.
+ CHECK_EQ(-1, old_fd_);
+ CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
+ CHECK_EQ(0, ftruncate(fd(), 0));
+}
+
+void CapturedStdFd::Start() {
#if defined(_WIN32)
// On Windows, stderr is often buffered, so make sure it is unbuffered so
// that we can immediately read back what was written to stderr.
- if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
+ if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, nullptr, _IONBF, 0));
#endif
old_fd_ = dup(std_fd_);
CHECK_NE(-1, old_fd_);
CHECK_NE(-1, dup2(fd(), std_fd_));
}
-void CapturedStdFd::Reset() {
+void CapturedStdFd::Stop() {
+ CHECK_NE(-1, old_fd_);
CHECK_NE(-1, dup2(old_fd_, std_fd_));
- CHECK_EQ(0, close(old_fd_));
+ close(old_fd_);
+ old_fd_ = -1;
// Note: cannot restore prior setvbuf() setting.
}
diff --git a/base/test_utils_test.cpp b/base/test_utils_test.cpp
index 597271a..15a79dd 100644
--- a/base/test_utils_test.cpp
+++ b/base/test_utils_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <stdio.h>
+
#include "android-base/test_utils.h"
#include <gtest/gtest-spi.h>
@@ -42,5 +44,43 @@
EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
}
+TEST(TestUtilsTest, CaptureStdout_smoke) {
+ CapturedStdout cap;
+ printf("This should be captured.\n");
+ cap.Stop();
+ printf("This will not be captured.\n");
+ ASSERT_EQ("This should be captured.\n", cap.str());
+
+ cap.Start();
+ printf("And this text should be captured too.\n");
+ cap.Stop();
+ ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
+
+ printf("Still not going to be captured.\n");
+ cap.Reset();
+ cap.Start();
+ printf("Only this will be captured.\n");
+ ASSERT_EQ("Only this will be captured.\n", cap.str());
+}
+
+TEST(TestUtilsTest, CaptureStderr_smoke) {
+ CapturedStderr cap;
+ fprintf(stderr, "This should be captured.\n");
+ cap.Stop();
+ fprintf(stderr, "This will not be captured.\n");
+ ASSERT_EQ("This should be captured.\n", cap.str());
+
+ cap.Start();
+ fprintf(stderr, "And this text should be captured too.\n");
+ cap.Stop();
+ ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
+
+ fprintf(stderr, "Still not going to be captured.\n");
+ cap.Reset();
+ cap.Start();
+ fprintf(stderr, "Only this will be captured.\n");
+ ASSERT_EQ("Only this will be captured.\n", cap.str());
+}
+
} // namespace base
} // namespace android
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index c17e00f..6700b6c 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -236,7 +236,7 @@
{"reboot,rescueparty", 90},
{"charge", 91},
{"oem_tz_crash", 92},
- {"uvlo", 93},
+ {"uvlo", 93}, // aliasReasons converts to reboot,undervoltage
{"oem_ps_hold", 94},
{"abnormal_reset", 95},
{"oemerr_unknown", 96},
@@ -248,9 +248,9 @@
{"watchdog_nonsec", 102},
{"watchdog_apps_bark", 103},
{"reboot_dmverity_corrupted", 104},
- {"reboot_smpl", 105},
+ {"reboot_smpl", 105}, // aliasReasons converts to reboot,powerloss
{"watchdog_sdi_apps_reset", 106},
- {"smpl", 107},
+ {"smpl", 107}, // aliasReasons converts to reboot,powerloss
{"oem_modem_failed_to_powerup", 108},
{"reboot_normal", 109},
{"oem_lpass_cfg", 110},
@@ -262,8 +262,8 @@
{"oem_rpm_undef_error", 116},
{"oem_crash_on_the_lk", 117},
{"oem_rpm_reset", 118},
- {"REUSE1", 119}, // Former dupe, can be re-used
- {"REUSE2", 120}, // Former dupe, can be re-used
+ {"reboot,powerloss", 119},
+ {"reboot,undervoltage", 120},
{"factory_cable", 121},
{"oem_ar6320_failed_to_powerup", 122},
{"watchdog_rpm_bite", 123},
@@ -840,6 +840,8 @@
{"reboot,tool", "tool_by_pass_pwk"},
{"!reboot,longkey", "reboot_longkey"},
{"!reboot,longkey", "kpdpwr"},
+ {"!reboot,undervoltage", "uvlo"},
+ {"!reboot,powerloss", "smpl"},
{"bootloader", ""},
};
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index cb7cbbe..77f3515 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -143,12 +143,16 @@
ssize_t rc =
TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
- LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+ LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
+ << "timeout reached?";
+ return false;
+ } else if (rc == -1) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned";
return false;
} else if (rc != sizeof(response)) {
- LOG(ERROR)
- << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
- << sizeof(response) << ", received " << rc;
+ LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
+ "reading initial response: expected "
+ << sizeof(response) << ", received " << rc;
return false;
}
@@ -164,12 +168,16 @@
rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
- LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+ LOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned: "
+ "timeout reached?";
+ return false;
+ } else if (rc == -1) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned";
return false;
} else if (rc != sizeof(response)) {
- LOG(ERROR)
- << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
- << sizeof(response) << ", received " << rc;
+ LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
+ "reading confirmation response: expected "
+ << sizeof(response) << ", received " << rc;
return false;
}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 93f7572..577e336 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -34,6 +34,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -282,6 +283,7 @@
switch (crash_info->header.version) {
case 2:
*fdsan_table_address = crash_info->data.v2.fdsan_table_address;
+ FALLTHROUGH_INTENDED;
case 1:
*abort_msg_address = crash_info->data.v1.abort_msg_address;
*siginfo = crash_info->data.v1.siginfo;
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f0fe1d0..f0bdfbf 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -224,7 +224,7 @@
// Prefixes.
if (!strncmp(arg, "wait-", strlen("wait-"))) {
char buf[1];
- TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
+ UNUSED(TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf))));
return do_action(arg + strlen("wait-"));
} else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
errno = 0;
@@ -258,10 +258,14 @@
__assert("some_file.c", 123, "false");
} else if (!strcasecmp(arg, "assert2")) {
__assert2("some_file.c", 123, "some_function", "false");
+#if !defined(__clang_analyzer__)
} else if (!strcasecmp(arg, "fortify")) {
+ // FORTIFY is disabled when running clang-tidy and other tools, so this
+ // shouldn't depend on internal implementation details of it.
char buf[10];
__read_chk(-1, buf, 32, 10);
while (true) pause();
+#endif
} else if (!strcasecmp(arg, "fdsan_file")) {
FILE* f = fopen("/dev/null", "r");
close(fileno(f));
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e2ea480..bea8b43 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -37,6 +37,7 @@
#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
@@ -587,9 +588,28 @@
"/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy";
static pid_t seccomp_fork_impl(void (*prejail)()) {
- unique_fd policy_fd(open(kDebuggerdSeccompPolicy, O_RDONLY | O_CLOEXEC));
- if (policy_fd == -1) {
- LOG(FATAL) << "failed to open policy " << kDebuggerdSeccompPolicy;
+ std::string policy;
+ if (!android::base::ReadFileToString(kDebuggerdSeccompPolicy, &policy)) {
+ PLOG(FATAL) << "failed to read policy file";
+ }
+
+ // Allow a bunch of syscalls used by the tests.
+ policy += "\nclone: 1";
+ policy += "\nsigaltstack: 1";
+ policy += "\nnanosleep: 1";
+
+ FILE* tmp_file = tmpfile();
+ if (!tmp_file) {
+ PLOG(FATAL) << "tmpfile failed";
+ }
+
+ unique_fd tmp_fd(dup(fileno(tmp_file)));
+ if (!android::base::WriteStringToFd(policy, tmp_fd.get())) {
+ PLOG(FATAL) << "failed to write policy to tmpfile";
+ }
+
+ if (lseek(tmp_fd.get(), 0, SEEK_SET) != 0) {
+ PLOG(FATAL) << "failed to seek tmp_fd";
}
ScopedMinijail jail{minijail_new()};
@@ -600,7 +620,7 @@
minijail_no_new_privs(jail.get());
minijail_log_seccomp_filter_failures(jail.get());
minijail_use_seccomp_filter(jail.get());
- minijail_parse_seccomp_filters_from_fd(jail.get(), policy_fd.release());
+ minijail_parse_seccomp_filters_from_fd(jail.get(), tmp_fd.release());
pid_t result = fork();
if (result == -1) {
@@ -735,6 +755,16 @@
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
}
+extern "C" void foo() {
+ LOG(INFO) << "foo";
+ std::this_thread::sleep_for(1s);
+}
+
+extern "C" void bar() {
+ LOG(INFO) << "bar";
+ std::this_thread::sleep_for(1s);
+}
+
TEST_F(CrasherTest, seccomp_backtrace) {
int intercept_result;
unique_fd output_fd;
@@ -742,6 +772,11 @@
static const auto dump_type = kDebuggerdNativeBacktrace;
StartProcess(
[]() {
+ std::thread a(foo);
+ std::thread b(bar);
+
+ std::this_thread::sleep_for(100ms);
+
raise_debugger_signal(dump_type);
_exit(0);
},
@@ -756,6 +791,8 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+ ASSERT_BACKTRACE_FRAME(result, "foo");
+ ASSERT_BACKTRACE_FRAME(result, "bar");
}
TEST_F(CrasherTest, seccomp_crash_logcat) {
@@ -1017,3 +1054,42 @@
ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
ASSERT_STREQ("any", outbuf);
}
+
+TEST(tombstoned, interceptless_backtrace) {
+ // Generate 50 backtraces, and then check to see that we haven't created 50 new tombstones.
+ auto get_tombstone_timestamps = []() -> std::map<int, time_t> {
+ std::map<int, time_t> result;
+ for (int i = 0; i < 99; ++i) {
+ std::string path = android::base::StringPrintf("/data/tombstones/tombstone_%02d", i);
+ struct stat st;
+ if (stat(path.c_str(), &st) == 0) {
+ result[i] = st.st_mtim.tv_sec;
+ }
+ }
+ return result;
+ };
+
+ auto before = get_tombstone_timestamps();
+ for (int i = 0; i < 50; ++i) {
+ raise_debugger_signal(kDebuggerdNativeBacktrace);
+ }
+ auto after = get_tombstone_timestamps();
+
+ int diff = 0;
+ for (int i = 0; i < 99; ++i) {
+ if (after.count(i) == 0) {
+ continue;
+ }
+ if (before.count(i) == 0) {
+ ++diff;
+ continue;
+ }
+ if (before[i] != after[i]) {
+ ++diff;
+ }
+ }
+
+ // We can't be sure that nothing's crash looping in the background.
+ // This should be good enough, though...
+ ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?";
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 079a574..ed7423b 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -47,6 +47,7 @@
#include <unwindstack/Regs.h>
#include "debuggerd/handler.h"
+#include "handler/fallback.h"
#include "tombstoned/tombstoned.h"
#include "util.h"
@@ -187,7 +188,7 @@
static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
- if (info->si_value.sival_int == ~0) {
+ if (info->si_value.sival_ptr == kDebuggerdFallbackSivalPtrRequestDump) {
// Asked to dump by the original signal recipient.
uint64_t val = trace_output.load();
auto [tid, fd] = unpack_thread_fd(val);
@@ -259,7 +260,7 @@
siginfo_t siginfo = {};
siginfo.si_code = SI_QUEUE;
- siginfo.si_value.sival_int = ~0;
+ siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump;
siginfo.si_pid = getpid();
siginfo.si_uid = getuid();
@@ -331,7 +332,7 @@
extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
void* abort_message) {
- if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_int != 0) {
+ if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_ptr != nullptr) {
return trace_handler(info, ucontext);
} else {
return crash_handler(info, ucontext, abort_message);
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 15557b6..a064ca0 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -58,6 +58,8 @@
#include "dump_type.h"
#include "protocol.h"
+#include "handler/fallback.h"
+
using android::base::Pipe;
// We muck with our fds in a 'thread' that doesn't share the same fd table.
@@ -473,13 +475,15 @@
}
void* abort_message = nullptr;
+ uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
if (signal_number == DEBUGGER_SIGNAL) {
if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
// Allow for the abort message to be explicitly specified via the sigqueue value.
// Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
- uintptr_t value = reinterpret_cast<uintptr_t>(info->si_ptr);
- abort_message = reinterpret_cast<void*>(value & ~1);
- info->si_ptr = reinterpret_cast<void*>(value & 1);
+ if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) {
+ abort_message = reinterpret_cast<void*>(si_val & ~1);
+ info->si_ptr = reinterpret_cast<void*>(si_val & 1);
+ }
}
} else {
if (g_callbacks.get_abort_message) {
@@ -492,7 +496,8 @@
// of a specific thread. It is possible that the prctl call might return 1,
// then return 0 in subsequent calls, so check the sival_int to determine if
// the fallback handler should be called first.
- if (info->si_value.sival_int == ~0 || prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+ if (si_val == kDebuggerdFallbackSivalUintptrRequestDump ||
+ prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
// This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
// you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
// ANR trace.
diff --git a/libbacktrace/GetPss.h b/debuggerd/handler/fallback.h
similarity index 63%
rename from libbacktrace/GetPss.h
rename to debuggerd/handler/fallback.h
index 787c33d..597f582 100644
--- a/libbacktrace/GetPss.h
+++ b/debuggerd/handler/fallback.h
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright 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
+ * 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,
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#ifndef _LIBBACKTRACE_GET_PSS_H
-#define _LIBBACKTRACE_GET_PSS_H
+#pragma once
-size_t GetPssBytes();
+#include <stdint.h>
-#endif // _LIBBACKTRACE_GET_PSS_H
+static void* const kDebuggerdFallbackSivalPtrRequestDump = reinterpret_cast<void*>(~0UL);
+static const uintptr_t kDebuggerdFallbackSivalUintptrRequestDump = ~0UL;
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 15ae406..ad92067 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -212,8 +212,13 @@
bool intercepted =
intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd);
if (!intercepted) {
- std::tie(crash->crash_tombstone_path, output_fd) = CrashQueue::for_crash(crash)->get_output();
- crash->crash_tombstone_fd.reset(dup(output_fd.get()));
+ if (crash->crash_type == kDebuggerdNativeBacktrace) {
+ // Don't generate tombstones for native backtrace requests.
+ output_fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
+ } else {
+ std::tie(crash->crash_tombstone_path, output_fd) = CrashQueue::for_crash(crash)->get_output();
+ crash->crash_tombstone_fd.reset(dup(output_fd.get()));
+ }
}
TombstonedCrashPacket response = {
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index f32b5d5..50d18ed 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -21,67 +21,66 @@
compile_multilib: "first",
srcs: [
- "bootimg_utils.cpp",
- "fs.cpp",
- "socket.cpp",
- "tcp.cpp",
- "udp.cpp",
- "util.cpp",
- "fastboot_driver.cpp",
+ "bootimg_utils.cpp",
+ "fs.cpp",
+ "socket.cpp",
+ "tcp.cpp",
+ "udp.cpp",
+ "util.cpp",
+ "fastboot_driver.cpp",
],
static_libs: [
- "libziparchive",
- "libsparse",
- "libutils",
- "liblog",
- "libz",
- "libdiagnose_usb",
- "libbase",
- "libcutils",
- "libgtest",
- "libgtest_main",
- "libbase",
- "libadb_host"
+ "libziparchive",
+ "libsparse",
+ "libutils",
+ "liblog",
+ "libz",
+ "libdiagnose_usb",
+ "libbase",
+ "libcutils",
+ "libgtest",
+ "libgtest_main",
+ "libbase",
+ "libadb_host",
],
header_libs: [
- "bootimg_headers"
+ "bootimg_headers",
],
export_header_lib_headers: [
- "bootimg_headers"
+ "bootimg_headers",
],
-
target: {
- linux: {
- srcs: ["usb_linux.cpp"],
- },
+ linux: {
+ srcs: ["usb_linux.cpp"],
+ },
- darwin: {
- srcs: ["usb_osx.cpp"],
+ darwin: {
+ srcs: ["usb_osx.cpp"],
- host_ldlibs: [
- "-framework CoreFoundation",
- "-framework IOKit",
- ],
- },
+ host_ldlibs: [
+ "-framework CoreFoundation",
+ "-framework IOKit",
+ ],
+ },
- windows: {
- srcs: ["usb_windows.cpp"],
+ windows: {
+ srcs: ["usb_windows.cpp"],
- host_ldlibs: [
- "-lws2_32",
- ],
- },
+ host_ldlibs: [
+ "-lws2_32",
+ ],
+ },
},
cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wunreachable-code",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wunreachable-code",
],
export_include_dirs: ["."],
@@ -122,6 +121,8 @@
shared_libs: [
"android.hardware.boot@1.0",
+ "android.hardware.fastboot@1.0",
+ "android.hardware.health@2.0",
"libadbd",
"libasyncio",
"libbase",
@@ -139,5 +140,159 @@
"libutils",
],
+ static_libs: [
+ "libhealthhalutils",
+ ],
+
cpp_std: "c++17",
}
+
+cc_defaults {
+ name: "fastboot_host_defaults",
+
+ use_version_lib: true,
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wunreachable-code",
+ ],
+
+ target: {
+ darwin: {
+ cflags: ["-Wno-unused-parameter"],
+ host_ldlibs: [
+ "-lpthread",
+ "-framework CoreFoundation",
+ "-framework IOKit",
+ ],
+ },
+ windows: {
+ enabled: true,
+
+ host_ldlibs: ["-lws2_32"],
+ },
+ },
+
+ stl: "libc++_static",
+
+ // Don't add anything here, we don't want additional shared dependencies
+ // on the host fastboot tool, and shared libraries that link against libc++
+ // will violate ODR.
+ shared_libs: [],
+
+ header_libs: ["bootimg_headers"],
+ static_libs: [
+ "libziparchive",
+ "libsparse",
+ "libutils",
+ "liblog",
+ "libz",
+ "libdiagnose_usb",
+ "libbase",
+ "libcutils",
+ "libgtest_host",
+ ],
+}
+
+//
+// Build host libfastboot.
+//
+
+cc_library_host_static {
+ name: "libfastboot",
+ defaults: ["fastboot_host_defaults"],
+
+ cpp_std: "c++17",
+ srcs: [
+ "bootimg_utils.cpp",
+ "fastboot.cpp",
+ "fs.cpp",
+ "socket.cpp",
+ "tcp.cpp",
+ "udp.cpp",
+ "util.cpp",
+ "fastboot_driver.cpp",
+ ],
+
+ // Only version the final binaries
+ use_version_lib: false,
+ static_libs: ["libbuildversion"],
+
+ generated_headers: ["platform_tools_version"],
+
+ target: {
+ windows: {
+ srcs: ["usb_windows.cpp"],
+
+ include_dirs: ["development/host/windows/usb/api"],
+ },
+ darwin: {
+ srcs: ["usb_osx.cpp"],
+ },
+ linux_glibc: {
+ srcs: ["usb_linux.cpp"],
+ },
+ },
+}
+
+//
+// Build host fastboot / fastboot.exe
+//
+
+cc_binary_host {
+ name: "fastboot",
+ defaults: ["fastboot_host_defaults"],
+
+ srcs: ["main.cpp"],
+ static_libs: ["libfastboot"],
+
+ required: [
+ "mke2fs",
+ "make_f2fs",
+ ],
+
+ target: {
+ not_windows: {
+ required: [
+ "e2fsdroid",
+ "mke2fs.conf",
+ "sload_f2fs",
+ ],
+ },
+ windows: {
+ required: ["AdbWinUsbApi"],
+ shared_libs: ["AdbWinApi"],
+ },
+ },
+}
+
+//
+// Build host fastboot_test.
+//
+
+cc_test_host {
+ name: "fastboot_test",
+ defaults: ["fastboot_host_defaults"],
+
+ srcs: [
+ "fastboot_test.cpp",
+ "socket_mock.cpp",
+ "socket_test.cpp",
+ "tcp_test.cpp",
+ "udp_test.cpp",
+ ],
+
+ static_libs: ["libfastboot"],
+
+ target: {
+ windows: {
+ shared_libs: ["AdbWinApi"],
+ },
+ windows_x86_64: {
+ // Avoid trying to build for win64
+ enabled: false,
+ },
+ },
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 7da0a9f..e4c1317 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -14,89 +14,6 @@
LOCAL_PATH:= $(call my-dir)
-include $(LOCAL_PATH)/../platform_tools_tool_version.mk
-
-fastboot_cflags := -Wall -Wextra -Werror -Wunreachable-code
-fastboot_cflags += -DFASTBOOT_VERSION="\"$(tool_version)\""
-fastboot_cflags_darwin := -Wno-unused-parameter
-fastboot_ldlibs_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-fastboot_ldlibs_windows := -lws2_32
-# Don't add anything here, we don't want additional shared dependencies
-# on the host fastboot tool, and shared libraries that link against libc++
-# will violate ODR.
-fastboot_shared_libs :=
-fastboot_static_libs := \
- libziparchive \
- libsparse \
- libutils \
- liblog \
- libz \
- libdiagnose_usb \
- libbase \
- libcutils \
- libgtest_host \
-
-fastboot_stl := libc++_static
-
-#
-# Build host libfastboot.
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libfastboot
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_SRC_FILES := \
- bootimg_utils.cpp \
- engine.cpp \
- fastboot.cpp \
- fs.cpp \
- socket.cpp \
- tcp.cpp \
- udp.cpp \
- util.cpp \
- fastboot_driver.cpp \
-
-LOCAL_SRC_FILES_darwin := usb_osx.cpp
-LOCAL_SRC_FILES_linux := usb_linux.cpp
-LOCAL_SRC_FILES_windows := usb_windows.cpp
-
-LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
-LOCAL_CFLAGS := $(fastboot_cflags)
-LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
-LOCAL_CPP_STD := c++17
-LOCAL_CXX_STL := $(fastboot_stl)
-LOCAL_HEADER_LIBRARIES := bootimg_headers
-LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
-LOCAL_LDLIBS_windows := $(fastboot_ldlibs_windows)
-LOCAL_SHARED_LIBRARIES := $(fastboot_shared_libs)
-LOCAL_STATIC_LIBRARIES := $(fastboot_static_libs)
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-#
-# Build host fastboot / fastboot.exe
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := fastboot
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_CFLAGS := $(fastboot_cflags)
-LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
-LOCAL_CXX_STL := $(fastboot_stl)
-LOCAL_HEADER_LIBRARIES := bootimg_headers
-LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
-LOCAL_LDLIBS_windows := $(fastboot_ldlibs_windows)
-LOCAL_REQUIRED_MODULES := mke2fs make_f2fs
-LOCAL_REQUIRED_MODULES_darwin := e2fsdroid mke2fs.conf sload_f2fs
-LOCAL_REQUIRED_MODULES_linux := e2fsdroid mke2fs.conf sload_f2fs
-LOCAL_REQUIRED_MODULES_windows := AdbWinUsbApi
-LOCAL_SRC_FILES := main.cpp
-LOCAL_SHARED_LIBRARIES := $(fastboot_shared_libs)
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-LOCAL_STATIC_LIBRARIES := libfastboot $(fastboot_static_libs)
-include $(BUILD_HOST_EXECUTABLE)
-
#
# Package fastboot-related executables.
#
@@ -111,30 +28,3 @@
$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
endif
my_dist_files :=
-
-#
-# Build host fastboot_test.
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := fastboot_test
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_MODULE_HOST_CROSS_ARCH := x86 # Avoid trying to build for win64.
-
-LOCAL_SRC_FILES := \
- fastboot_test.cpp \
- socket_mock.cpp \
- socket_test.cpp \
- tcp_test.cpp \
- udp_test.cpp \
-
-LOCAL_CFLAGS := $(fastboot_cflags)
-LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
-LOCAL_CXX_STL := $(fastboot_stl)
-LOCAL_HEADER_LIBRARIES := bootimg_headers
-LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
-LOCAL_LDLIBS_windows := $(fastboot_ldlibs_windows)
-LOCAL_SHARED_LIBRARIES := $(fastboot_shared_libs)
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-LOCAL_STATIC_LIBRARIES := libfastboot $(fastboot_static_libs)
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index 1152007..e433787 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -39,27 +39,27 @@
strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
}
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
- void* second, int64_t second_size, size_t base,
- const boot_img_hdr_v1& src, int64_t* bootimg_size) {
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+ const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+ std::vector<char>* out) {
const size_t page_mask = src.page_size - 1;
int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);
- int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
- int64_t ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
- int64_t second_actual = (second_size + page_mask) & (~page_mask);
+ int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
+ int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);
+ int64_t second_actual = (second.size() + page_mask) & (~page_mask);
- *bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+ int64_t bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+ out->resize(bootimg_size);
- boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(calloc(*bootimg_size, 1));
- if (hdr == nullptr) die("couldn't allocate boot image: %" PRId64 " bytes", *bootimg_size);
+ boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(out->data());
*hdr = src;
memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
- hdr->kernel_size = kernel_size;
- hdr->ramdisk_size = ramdisk_size;
- hdr->second_size = second_size;
+ hdr->kernel_size = kernel.size();
+ hdr->ramdisk_size = ramdisk.size();
+ hdr->second_size = second.size();
hdr->kernel_addr += base;
hdr->ramdisk_addr += base;
@@ -70,8 +70,9 @@
hdr->header_size = sizeof(boot_img_hdr_v1);
}
- memcpy(hdr->magic + hdr->page_size, kernel, kernel_size);
- memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk, ramdisk_size);
- memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second, second_size);
+ memcpy(hdr->magic + hdr->page_size, kernel.data(), kernel.size());
+ memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk.data(), ramdisk.size());
+ memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second.data(),
+ second.size());
return hdr;
}
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index fe805b0..a4e8870 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -33,8 +33,9 @@
#include <sys/types.h>
#include <string>
+#include <vector>
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
- void* second, int64_t second_size, size_t base,
- const boot_img_hdr_v1& src, int64_t* bootimg_size);
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+ const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+ std::vector<char>* out);
void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline);
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 063cd40..705da33 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -32,6 +32,7 @@
#define FB_CMD_DELETE_PARTITION "delete-logical-partition"
#define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
#define FB_CMD_UPDATE_SUPER "update-super"
+#define FB_CMD_OEM "oem"
#define RESPONSE_OKAY "OKAY"
#define RESPONSE_FAIL "FAIL"
@@ -53,7 +54,13 @@
#define FB_VAR_HAS_SLOT "has-slot"
#define FB_VAR_SLOT_COUNT "slot-count"
#define FB_VAR_PARTITION_SIZE "partition-size"
+#define FB_VAR_PARTITION_TYPE "partition-type"
#define FB_VAR_SLOT_SUCCESSFUL "slot-successful"
#define FB_VAR_SLOT_UNBOOTABLE "slot-unbootable"
#define FB_VAR_IS_LOGICAL "is-logical"
#define FB_VAR_IS_USERSPACE "is-userspace"
+#define FB_VAR_HW_REVISION "hw-revision"
+#define FB_VAR_VARIANT "variant"
+#define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
+#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
+#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 1e853bf..3f663ef 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -27,6 +27,7 @@
#include <android-base/unique_fd.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/wipe.h>
+#include <fs_mgr.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <uuid/uuid.h>
@@ -40,36 +41,90 @@
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::CommandResult;
using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::Result;
+using ::android::hardware::fastboot::V1_0::Status;
+
using namespace android::fs_mgr;
+struct VariableHandlers {
+ // Callback to retrieve the value of a single variable.
+ std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)> get;
+ // Callback to retrieve all possible argument combinations, for getvar all.
+ std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;
+};
+
+static void GetAllVars(FastbootDevice* device, const std::string& name,
+ const VariableHandlers& handlers) {
+ if (!handlers.get_all_args) {
+ std::string message;
+ if (!handlers.get(device, std::vector<std::string>(), &message)) {
+ return;
+ }
+ device->WriteInfo(android::base::StringPrintf("%s:%s", name.c_str(), message.c_str()));
+ return;
+ }
+
+ auto all_args = handlers.get_all_args(device);
+ for (const auto& args : all_args) {
+ std::string message;
+ if (!handlers.get(device, args, &message)) {
+ continue;
+ }
+ std::string arg_string = android::base::Join(args, ":");
+ device->WriteInfo(android::base::StringPrintf("%s:%s:%s", name.c_str(), arg_string.c_str(),
+ message.c_str()));
+ }
+}
+
bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
- using VariableHandler = std::function<bool(FastbootDevice*, const std::vector<std::string>&)>;
- const std::unordered_map<std::string, VariableHandler> kVariableMap = {
- {FB_VAR_VERSION, GetVersion},
- {FB_VAR_VERSION_BOOTLOADER, GetBootloaderVersion},
- {FB_VAR_VERSION_BASEBAND, GetBasebandVersion},
- {FB_VAR_PRODUCT, GetProduct},
- {FB_VAR_SERIALNO, GetSerial},
- {FB_VAR_SECURE, GetSecure},
- {FB_VAR_UNLOCKED, GetUnlocked},
- {FB_VAR_MAX_DOWNLOAD_SIZE, GetMaxDownloadSize},
- {FB_VAR_CURRENT_SLOT, ::GetCurrentSlot},
- {FB_VAR_SLOT_COUNT, GetSlotCount},
- {FB_VAR_HAS_SLOT, GetHasSlot},
- {FB_VAR_SLOT_SUCCESSFUL, GetSlotSuccessful},
- {FB_VAR_SLOT_UNBOOTABLE, GetSlotUnbootable},
- {FB_VAR_PARTITION_SIZE, GetPartitionSize},
- {FB_VAR_IS_LOGICAL, GetPartitionIsLogical},
- {FB_VAR_IS_USERSPACE, GetIsUserspace}};
+ const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
+ {FB_VAR_VERSION, {GetVersion, nullptr}},
+ {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
+ {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+ {FB_VAR_PRODUCT, {GetProduct, nullptr}},
+ {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+ {FB_VAR_VARIANT, {GetVariant, nullptr}},
+ {FB_VAR_SECURE, {GetSecure, nullptr}},
+ {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
+ {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
+ {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
+ {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
+ {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
+ {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
+ {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
+ {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
+ {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
+ {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+ {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
+ {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}}};
+
+ if (args.size() < 2) {
+ return device->WriteFail("Missing argument");
+ }
+
+ // Special case: return all variables that we can.
+ if (args[1] == "all") {
+ for (const auto& [name, handlers] : kVariableMap) {
+ GetAllVars(device, name, handlers);
+ }
+ return device->WriteOkay("");
+ }
// args[0] is command name, args[1] is variable.
auto found_variable = kVariableMap.find(args[1]);
if (found_variable == kVariableMap.end()) {
- return device->WriteStatus(FastbootResult::FAIL, "Unknown variable");
+ return device->WriteFail("Unknown variable");
}
+ std::string message;
std::vector<std::string> getvar_args(args.begin() + 2, args.end());
- return found_variable->second(device, getvar_args);
+ if (!found_variable->second.get(device, getvar_args, &message)) {
+ return device->WriteFail(message);
+ }
+ return device->WriteOkay(message);
}
bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) {
@@ -86,6 +141,24 @@
return device->WriteStatus(FastbootResult::FAIL, "Erasing failed");
}
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+ auto fastboot_hal = device->fastboot_hal();
+ if (!fastboot_hal) {
+ return device->WriteStatus(FastbootResult::FAIL, "Unable to open fastboot HAL");
+ }
+
+ Result ret;
+ auto ret_val = fastboot_hal->doOemCommand(args[0], [&](Result result) { ret = result; });
+ if (!ret_val.isOk()) {
+ return device->WriteStatus(FastbootResult::FAIL, "Unable to do OEM command");
+ }
+ if (ret.status != Status::SUCCESS) {
+ return device->WriteStatus(FastbootResult::FAIL, ret.message);
+ }
+
+ return device->WriteStatus(FastbootResult::OKAY, ret.message);
+}
+
bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
if (args.size() < 2) {
return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
@@ -112,6 +185,12 @@
if (args.size() < 2) {
return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
}
+
+ if (GetDeviceLockStatus()) {
+ return device->WriteStatus(FastbootResult::FAIL,
+ "Flashing is not allowed on locked devices");
+ }
+
int ret = Flash(device, args[1]);
if (ret < 0) {
return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
@@ -233,7 +312,7 @@
};
PartitionBuilder::PartitionBuilder(FastbootDevice* device) {
- auto super_device = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+ auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name());
if (!super_device) {
return;
}
@@ -257,6 +336,10 @@
return device->WriteFail("Invalid partition name and size");
}
+ if (GetDeviceLockStatus()) {
+ return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+ }
+
uint64_t partition_size;
std::string partition_name = args[1];
if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
@@ -272,13 +355,7 @@
return device->WriteFail("Partition already exists");
}
- // Make a random UUID, since they're not currently used.
- uuid_t uuid;
- char uuid_str[37];
- uuid_generate_random(uuid);
- uuid_unparse(uuid, uuid_str);
-
- Partition* partition = builder->AddPartition(partition_name, uuid_str, 0);
+ Partition* partition = builder->AddPartition(partition_name, 0);
if (!partition) {
return device->WriteFail("Failed to add partition");
}
@@ -297,6 +374,10 @@
return device->WriteFail("Invalid partition name and size");
}
+ if (GetDeviceLockStatus()) {
+ return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+ }
+
PartitionBuilder builder(device);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
@@ -313,6 +394,10 @@
return device->WriteFail("Invalid partition name and size");
}
+ if (GetDeviceLockStatus()) {
+ return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+ }
+
uint64_t partition_size;
std::string partition_name = args[1];
if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
@@ -341,6 +426,11 @@
if (args.size() < 2) {
return device->WriteFail("Invalid arguments");
}
+
+ if (GetDeviceLockStatus()) {
+ return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
+ }
+
bool wipe = (args.size() >= 3 && args[2] == "wipe");
return UpdateSuper(device, args[1], wipe);
}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index 4778d23..9df43a9 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -45,3 +45,4 @@
bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 6ed6d32..b843c05 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -19,6 +19,8 @@
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <healthhalutils/HealthHalUtils.h>
#include <algorithm>
@@ -29,6 +31,9 @@
using ::android::hardware::hidl_string;
using ::android::hardware::boot::V1_0::IBootControl;
using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::IFastboot;
+using ::android::hardware::health::V2_0::get_health_service;
+
namespace sph = std::placeholders;
FastbootDevice::FastbootDevice()
@@ -47,9 +52,12 @@
{FB_CMD_DELETE_PARTITION, DeletePartitionHandler},
{FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
{FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
+ {FB_CMD_OEM, OemCmdHandler},
}),
transport_(std::make_unique<ClientUsbTransport>()),
- boot_control_hal_(IBootControl::getService()) {}
+ boot_control_hal_(IBootControl::getService()),
+ health_hal_(get_health_service()),
+ fastboot_hal_(IFastboot::getService()) {}
FastbootDevice::~FastbootDevice() {
CloseDevice();
@@ -118,10 +126,20 @@
command[bytes_read] = '\0';
LOG(INFO) << "Fastboot command: " << command;
- auto args = android::base::Split(command, ":");
- auto found_command = kCommandMap.find(args[0]);
+
+ std::vector<std::string> args;
+ std::string cmd_name;
+ if (android::base::StartsWith(command, "oem ")) {
+ args = {command};
+ cmd_name = "oem";
+ } else {
+ args = android::base::Split(command, ":");
+ cmd_name = args[0];
+ }
+
+ auto found_command = kCommandMap.find(cmd_name);
if (found_command == kCommandMap.end()) {
- WriteStatus(FastbootResult::FAIL, "Unrecognized command");
+ WriteStatus(FastbootResult::FAIL, "Unrecognized command " + args[0]);
continue;
}
if (!found_command->second(this, args)) {
@@ -137,3 +155,7 @@
bool FastbootDevice::WriteFail(const std::string& message) {
return WriteStatus(FastbootResult::FAIL, message);
}
+
+bool FastbootDevice::WriteInfo(const std::string& message) {
+ return WriteStatus(FastbootResult::INFO, message);
+}
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index addc2ef..2eb7177 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -23,6 +23,8 @@
#include <vector>
#include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/fastboot/1.0/IFastboot.h>
+#include <android/hardware/health/2.0/IHealth.h>
#include "commands.h"
#include "transport.h"
@@ -39,20 +41,27 @@
bool HandleData(bool read, std::vector<char>* data);
std::string GetCurrentSlot();
- // Shortcuts for writing OKAY and FAIL status results.
+ // Shortcuts for writing status results.
bool WriteOkay(const std::string& message);
bool WriteFail(const std::string& message);
+ bool WriteInfo(const std::string& message);
std::vector<char>& download_data() { return download_data_; }
Transport* get_transport() { return transport_.get(); }
android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
return boot_control_hal_;
}
+ android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
+ return fastboot_hal_;
+ }
+ android::sp<android::hardware::health::V2_0::IHealth> health_hal() { return health_hal_; }
private:
const std::unordered_map<std::string, CommandHandler> kCommandMap;
std::unique_ptr<Transport> transport_;
android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+ android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
+ android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
std::vector<char> download_data_;
};
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index e3efbcb..4fc3d1d 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -72,7 +72,7 @@
}
int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
- struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false);
+ struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, true);
if (!file) {
return -ENOENT;
}
@@ -146,8 +146,7 @@
if (builder->FindPartition(name)) {
continue;
}
- std::string guid = GetPartitionGuid(partition);
- if (!builder->AddPartition(name, guid, partition.attributes)) {
+ if (!builder->AddPartition(name, partition.attributes)) {
return device->WriteFail("Unable to add partition: " + name);
}
}
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index ec84576..528abec 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -16,13 +16,21 @@
#include "utility.h"
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
#include <android-base/logging.h>
+#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <liblp/liblp.h>
#include "fastboot_device.h"
using namespace android::fs_mgr;
+using namespace std::chrono_literals;
using android::base::unique_fd;
using android::hardware::boot::V1_0::Slot;
@@ -37,17 +45,17 @@
static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
PartitionHandle* handle) {
- std::optional<std::string> path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+ std::optional<std::string> path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
if (!path) {
return false;
}
uint32_t slot_number = SlotNumberForSlotSuffix(slot);
std::string dm_path;
- if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, &dm_path)) {
+ if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, 5s, &dm_path)) {
LOG(ERROR) << "Could not map partition: " << name;
return false;
}
- auto closer = [name]() -> void { DestroyLogicalPartition(name); };
+ auto closer = [name]() -> void { DestroyLogicalPartition(name, 5s); };
*handle = PartitionHandle(dm_path, std::move(closer));
return true;
}
@@ -75,7 +83,7 @@
std::optional<std::string> FindPhysicalPartition(const std::string& name) {
std::string path = "/dev/block/by-name/" + name;
- if (access(path.c_str(), R_OK | W_OK) < 0) {
+ if (access(path.c_str(), W_OK) < 0) {
return {};
}
return path;
@@ -93,7 +101,7 @@
bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
bool* is_zero_length) {
- auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+ auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
if (!path) {
return false;
}
@@ -123,3 +131,39 @@
*number = slot[0] - 'a';
return true;
}
+
+std::vector<std::string> ListPartitions(FastbootDevice* device) {
+ std::vector<std::string> partitions;
+
+ // First get physical partitions.
+ struct dirent* de;
+ std::unique_ptr<DIR, decltype(&closedir)> by_name(opendir("/dev/block/by-name"), closedir);
+ while ((de = readdir(by_name.get())) != nullptr) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+ continue;
+ }
+ struct stat s;
+ std::string path = "/dev/block/by-name/" + std::string(de->d_name);
+ if (!stat(path.c_str(), &s) && S_ISBLK(s.st_mode)) {
+ partitions.emplace_back(de->d_name);
+ }
+ }
+
+ // Next get logical partitions.
+ if (auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name())) {
+ uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+ if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
+ for (const auto& partition : metadata->partitions) {
+ std::string partition_name = GetPartitionName(partition);
+ partitions.emplace_back(partition_name);
+ }
+ }
+ }
+ return partitions;
+}
+
+bool GetDeviceLockStatus() {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 0931fc3..bb08f72 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -56,5 +56,6 @@
bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
bool* is_zero_length = nullptr);
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
-
bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
+std::vector<std::string> ListPartitions(FastbootDevice* device);
+bool GetDeviceLockStatus();
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 65cfea3..2de79b1 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -24,6 +24,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <ext4_utils/ext4_utils.h>
+#include <healthhalutils/HealthHalUtils.h>
#include "fastboot_device.h"
#include "flashing.h"
@@ -31,145 +32,382 @@
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::FileSystemType;
+using ::android::hardware::fastboot::V1_0::Result;
+using ::android::hardware::fastboot::V1_0::Status;
constexpr int kMaxDownloadSizeDefault = 0x20000000;
constexpr char kFastbootProtocolVersion[] = "0.4";
-bool GetVersion(FastbootDevice* device, const std::vector<std::string>& /* args */) {
- return device->WriteOkay(kFastbootProtocolVersion);
+bool GetVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = kFastbootProtocolVersion;
+ return true;
}
-bool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& /* args */) {
- return device->WriteOkay(android::base::GetProperty("ro.bootloader", ""));
+bool GetBootloaderVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.bootloader", "");
+ return true;
}
-bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& /* args */) {
- return device->WriteOkay(android::base::GetProperty("ro.build.expect.baseband", ""));
+bool GetBasebandVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.build.expect.baseband", "");
+ return true;
}
-bool GetProduct(FastbootDevice* device, const std::vector<std::string>& /* args */) {
- return device->WriteOkay(android::base::GetProperty("ro.product.device", ""));
+bool GetProduct(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.product.device", "");
+ return true;
}
-bool GetSerial(FastbootDevice* device, const std::vector<std::string>& /* args */) {
- return device->WriteOkay(android::base::GetProperty("ro.serialno", ""));
+bool GetSerial(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.serialno", "");
+ return true;
}
-bool GetSecure(FastbootDevice* device, const std::vector<std::string>& /* args */) {
- return device->WriteOkay(android::base::GetBoolProperty("ro.secure", "") ? "yes" : "no");
+bool GetSecure(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetBoolProperty("ro.secure", "") ? "yes" : "no";
+ return true;
}
-bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+bool GetVariant(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ auto fastboot_hal = device->fastboot_hal();
+ if (!fastboot_hal) {
+ *message = "Fastboot HAL not found";
+ return false;
+ }
+
+ Result ret;
+ auto ret_val = fastboot_hal->getVariant([&](std::string device_variant, Result result) {
+ *message = device_variant;
+ ret = result;
+ });
+ if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+ *message = "Unable to get device variant";
+ return false;
+ }
+
+ return true;
+}
+
+bool GetBatteryVoltageHelper(FastbootDevice* device, int32_t* battery_voltage) {
+ using android::hardware::health::V2_0::HealthInfo;
+ using android::hardware::health::V2_0::Result;
+
+ auto health_hal = device->health_hal();
+ if (!health_hal) {
+ return false;
+ }
+
+ Result ret;
+ auto ret_val = health_hal->getHealthInfo([&](Result result, HealthInfo info) {
+ *battery_voltage = info.legacy.batteryVoltage;
+ ret = result;
+ });
+ if (!ret_val.isOk() || (ret != Result::SUCCESS)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ int32_t battery_voltage = 0;
+ if (!GetBatteryVoltageHelper(device, &battery_voltage)) {
+ *message = "Unable to read battery voltage";
+ return false;
+ }
+
+ auto fastboot_hal = device->fastboot_hal();
+ if (!fastboot_hal) {
+ *message = "Fastboot HAL not found";
+ return false;
+ }
+
+ Result ret;
+ auto ret_val = fastboot_hal->getBatteryVoltageFlashingThreshold(
+ [&](int32_t voltage_threshold, Result result) {
+ *message = battery_voltage >= voltage_threshold ? "yes" : "no";
+ ret = result;
+ });
+
+ if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+ *message = "Unable to get battery voltage flashing threshold";
+ return false;
+ }
+
+ return true;
+}
+
+bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ auto fastboot_hal = device->fastboot_hal();
+ if (!fastboot_hal) {
+ *message = "Fastboot HAL not found";
+ return false;
+ }
+
+ Result ret;
+ auto ret_val =
+ fastboot_hal->getOffModeChargeState([&](bool off_mode_charging_state, Result result) {
+ *message = off_mode_charging_state ? "1" : "0";
+ ret = result;
+ });
+ if (!ret_val.isOk() || (ret.status != Status::SUCCESS)) {
+ *message = "Unable to get off mode charge state";
+ return false;
+ }
+
+ return true;
+}
+
+bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ int32_t battery_voltage = 0;
+ if (GetBatteryVoltageHelper(device, &battery_voltage)) {
+ *message = std::to_string(battery_voltage);
+ return true;
+ }
+ *message = "Unable to get battery voltage";
+ return false;
+}
+
+bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
std::string suffix = device->GetCurrentSlot();
- std::string slot = suffix.size() == 2 ? suffix.substr(1) : suffix;
- return device->WriteOkay(slot);
+ *message = suffix.size() == 2 ? suffix.substr(1) : suffix;
+ return true;
}
-bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
auto boot_control_hal = device->boot_control_hal();
if (!boot_control_hal) {
- return "0";
+ *message = "0";
+ } else {
+ *message = std::to_string(boot_control_hal->getNumberSlots());
}
- return device->WriteOkay(std::to_string(boot_control_hal->getNumberSlots()));
+ return true;
}
-bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message) {
if (args.empty()) {
- return device->WriteFail("Missing argument");
+ *message = "Missing argument";
+ return false;
}
Slot slot;
if (!GetSlotNumber(args[0], &slot)) {
- return device->WriteFail("Invalid slot");
+ *message = "Invalid slot";
+ return false;
}
auto boot_control_hal = device->boot_control_hal();
if (!boot_control_hal) {
- return device->WriteFail("Device has no slots");
+ *message = "Device has no slots";
+ return false;
}
if (boot_control_hal->isSlotMarkedSuccessful(slot) != BoolResult::TRUE) {
- return device->WriteOkay("no");
+ *message = "no";
+ } else {
+ *message = "yes";
}
- return device->WriteOkay("yes");
+ return true;
}
-bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message) {
if (args.empty()) {
- return device->WriteFail("Missing argument");
+ *message = "Missing argument";
+ return false;
}
Slot slot;
if (!GetSlotNumber(args[0], &slot)) {
- return device->WriteFail("Invalid slot");
+ *message = "Invalid slot";
+ return false;
}
auto boot_control_hal = device->boot_control_hal();
if (!boot_control_hal) {
- return device->WriteFail("Device has no slots");
+ *message = "Device has no slots";
+ return false;
}
if (boot_control_hal->isSlotBootable(slot) != BoolResult::TRUE) {
- return device->WriteOkay("yes");
+ *message = "yes";
+ } else {
+ *message = "no";
}
- return device->WriteOkay("no");
+ return true;
}
-bool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& /* args */) {
- return device->WriteOkay(std::to_string(kMaxDownloadSizeDefault));
+bool GetMaxDownloadSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::StringPrintf("0x%X", kMaxDownloadSizeDefault);
+ return true;
}
-bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& /* args */) {
- return device->WriteOkay("yes");
+bool GetUnlocked(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = GetDeviceLockStatus() ? "no" : "yes";
+ return true;
}
-bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message) {
if (args.empty()) {
- return device->WriteFail("Missing argument");
+ *message = "Missing argument";
+ return false;
}
std::string slot_suffix = device->GetCurrentSlot();
if (slot_suffix.empty()) {
- return device->WriteOkay("no");
+ *message = "no";
+ return true;
}
std::string partition_name = args[0] + slot_suffix;
if (FindPhysicalPartition(partition_name) ||
LogicalPartitionExists(partition_name, slot_suffix)) {
- return device->WriteOkay("yes");
+ *message = "yes";
+ } else {
+ *message = "no";
}
- return device->WriteOkay("no");
+ return true;
}
-bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message) {
if (args.size() < 1) {
- return device->WriteFail("Missing argument");
+ *message = "Missing argument";
+ return false;
}
// Zero-length partitions cannot be created through device-mapper, so we
// special case them here.
bool is_zero_length;
if (LogicalPartitionExists(args[0], device->GetCurrentSlot(), &is_zero_length) &&
is_zero_length) {
- return device->WriteOkay("0");
+ *message = "0";
+ return true;
}
// Otherwise, open the partition as normal.
PartitionHandle handle;
if (!OpenPartition(device, args[0], &handle)) {
- return device->WriteFail("Could not open partition");
+ *message = "Could not open partition";
+ return false;
}
uint64_t size = get_block_device_size(handle.fd());
- return device->WriteOkay(android::base::StringPrintf("%" PRIX64, size));
+ *message = android::base::StringPrintf("0x%" PRIX64, size);
+ return true;
}
-bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetPartitionType(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message) {
if (args.size() < 1) {
- return device->WriteFail("Missing argument");
+ *message = "Missing argument";
+ return false;
+ }
+ std::string partition_name = args[0];
+ auto fastboot_hal = device->fastboot_hal();
+ if (!fastboot_hal) {
+ *message = "Fastboot HAL not found";
+ return false;
+ }
+
+ FileSystemType type;
+ Result ret;
+ auto ret_val =
+ fastboot_hal->getPartitionType(args[0], [&](FileSystemType fs_type, Result result) {
+ type = fs_type;
+ ret = result;
+ });
+ if (!ret_val.isOk() || (ret.status != Status::SUCCESS)) {
+ *message = "Unable to retrieve partition type";
+ } else {
+ switch (type) {
+ case FileSystemType::RAW:
+ *message = "raw";
+ return true;
+ case FileSystemType::EXT4:
+ *message = "ext4";
+ return true;
+ case FileSystemType::F2FS:
+ *message = "f2fs";
+ return true;
+ default:
+ *message = "Unknown file system type";
+ }
+ }
+
+ return false;
+}
+
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message) {
+ if (args.size() < 1) {
+ *message = "Missing argument";
+ return false;
}
// Note: if a partition name is in both the GPT and the super partition, we
// return "true", to be consistent with prefering to flash logical partitions
// over physical ones.
std::string partition_name = args[0];
if (LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
- return device->WriteOkay("yes");
+ *message = "yes";
+ return true;
}
if (FindPhysicalPartition(partition_name)) {
- return device->WriteOkay("no");
+ *message = "no";
+ return true;
}
- return device->WriteFail("Partition not found");
+ *message = "Partition not found";
+ return false;
}
-bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& /* args */) {
- return device->WriteOkay("yes");
+bool GetIsUserspace(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = "yes";
+ return true;
+}
+
+std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device) {
+ std::vector<std::vector<std::string>> args;
+ auto partitions = ListPartitions(device);
+ for (const auto& partition : partitions) {
+ args.emplace_back(std::initializer_list<std::string>{partition});
+ }
+ return args;
+}
+
+std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device) {
+ auto partitions = ListPartitions(device);
+
+ std::string slot_suffix = device->GetCurrentSlot();
+ if (!slot_suffix.empty()) {
+ auto names = std::move(partitions);
+ for (const auto& name : names) {
+ std::string slotless_name = name;
+ if (android::base::EndsWith(name, "_a") || android::base::EndsWith(name, "_b")) {
+ slotless_name = name.substr(0, name.rfind("_"));
+ }
+ if (std::find(partitions.begin(), partitions.end(), slotless_name) ==
+ partitions.end()) {
+ partitions.emplace_back(slotless_name);
+ }
+ }
+ }
+
+ std::vector<std::vector<std::string>> args;
+ for (const auto& partition : partitions) {
+ args.emplace_back(std::initializer_list<std::string>{partition});
+ }
+ return args;
+}
+
+bool GetHardwareRevision(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ *message = android::base::GetProperty("ro.revision", "");
+ return true;
}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 554a080..59b71e8 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -21,19 +21,45 @@
class FastbootDevice;
-bool GetVersion(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetProduct(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSerial(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSecure(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetVersion(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetProduct(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetSerial(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetSecure(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetPartitionType(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetHardwareRevision(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetVariant(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
+
+// Helpers for getvar all.
+std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
+std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
deleted file mode 100644
index 6a52b12..0000000
--- a/fastboot/engine.cpp
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Copyright (C) 2008 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 "engine.h"
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <vector>
-
-#include <android-base/stringprintf.h>
-
-#include "constants.h"
-#include "transport.h"
-
-enum Op {
- OP_DOWNLOAD,
- OP_COMMAND,
- OP_QUERY,
- OP_NOTICE,
- OP_DOWNLOAD_SPARSE,
- OP_WAIT_FOR_DISCONNECT,
- OP_DOWNLOAD_FD,
- OP_UPLOAD,
-};
-
-struct Action {
- Action(Op op, const std::string& cmd) : op(op), cmd(cmd) {}
-
- Op op;
- std::string cmd;
- std::string msg;
-
- std::string product;
-
- void* data = nullptr;
- // The protocol only supports 32-bit sizes, so you'll have to break
- // anything larger into multiple chunks.
- uint32_t size = 0;
-
- int fd = -1;
-
- int (*func)(Action& a, int status, const char* resp) = nullptr;
-
- double start = -1;
-};
-
-static std::vector<std::unique_ptr<Action>> action_list;
-static fastboot::FastBootDriver* fb = nullptr;
-
-static constexpr char kStatusFormat[] = "%-50s ";
-
-void fb_init(fastboot::FastBootDriver& fbi) {
- fb = &fbi;
- auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
- fb->SetInfoCallback(cb);
-}
-
-void fb_reinit(Transport* transport) {
- fb->set_transport(transport);
-}
-
-const std::string fb_get_error() {
- return fb->Error();
-}
-
-bool fb_getvar(const std::string& key, std::string* value) {
- return !fb->GetVar(key, value);
-}
-
-static int cb_default(Action& a, int status, const char* resp) {
- if (status) {
- fprintf(stderr,"FAILED (%s)\n", resp);
- } else {
- double split = now();
- fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
- a.start = split;
- }
- return status;
-}
-
-static Action& queue_action(Op op, const std::string& cmd) {
- std::unique_ptr<Action> a{new Action(op, cmd)};
- a->func = cb_default;
-
- action_list.push_back(std::move(a));
- return *action_list.back();
-}
-
-void fb_set_active(const std::string& slot) {
- Action& a = queue_action(OP_COMMAND, FB_CMD_SET_ACTIVE ":" + slot);
- a.msg = "Setting current slot to '" + slot + "'";
-}
-
-void fb_queue_erase(const std::string& partition) {
- Action& a = queue_action(OP_COMMAND, FB_CMD_ERASE ":" + partition);
- a.msg = "Erasing '" + partition + "'";
-}
-
-void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz) {
- Action& a = queue_action(OP_DOWNLOAD_FD, "");
- a.fd = fd;
- a.size = sz;
- a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
-
- Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
- b.msg = "Writing '" + partition + "'";
-}
-
-void fb_queue_flash(const std::string& partition, void* data, uint32_t sz) {
- Action& a = queue_action(OP_DOWNLOAD, "");
- a.data = data;
- a.size = sz;
- a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
-
- Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
- b.msg = "Writing '" + partition + "'";
-}
-
-void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
- size_t current, size_t total) {
- Action& a = queue_action(OP_DOWNLOAD_SPARSE, "");
- a.data = s;
- a.size = 0;
- a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(),
- current, total, sz / 1024);
-
- Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
- b.msg = android::base::StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current,
- total);
-}
-
-void fb_queue_create_partition(const std::string& partition, const std::string& size) {
- Action& a = queue_action(OP_COMMAND, FB_CMD_CREATE_PARTITION ":" + partition + ":" + size);
- a.msg = "Creating '" + partition + "'";
-}
-
-void fb_queue_delete_partition(const std::string& partition) {
- Action& a = queue_action(OP_COMMAND, FB_CMD_DELETE_PARTITION ":" + partition);
- a.msg = "Deleting '" + partition + "'";
-}
-
-void fb_queue_resize_partition(const std::string& partition, const std::string& size) {
- Action& a = queue_action(OP_COMMAND, FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size);
- a.msg = "Resizing '" + partition + "'";
-}
-
-static int match(const char* str, const char** value, unsigned count) {
- unsigned n;
-
- for (n = 0; n < count; n++) {
- const char *val = value[n];
- int len = strlen(val);
- int match;
-
- if ((len > 1) && (val[len-1] == '*')) {
- len--;
- match = !strncmp(val, str, len);
- } else {
- match = !strcmp(val, str);
- }
-
- if (match) return 1;
- }
-
- return 0;
-}
-
-static int cb_check(Action& a, int status, const char* resp, int invert) {
- const char** value = reinterpret_cast<const char**>(a.data);
- unsigned count = a.size;
- unsigned n;
-
- if (status) {
- fprintf(stderr,"FAILED (%s)\n", resp);
- return status;
- }
-
- if (!a.product.empty()) {
- if (a.product != cur_product) {
- double split = now();
- fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product,
- a.product.c_str(), (split - a.start));
- a.start = split;
- return 0;
- }
- }
-
- int yes = match(resp, value, count);
- if (invert) yes = !yes;
-
- if (yes) {
- double split = now();
- fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
- a.start = split;
- return 0;
- }
-
- fprintf(stderr, "FAILED\n\n");
- fprintf(stderr, "Device %s is '%s'.\n", a.cmd.c_str() + 7, resp);
- fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", value[0]);
- for (n = 1; n < count; n++) {
- fprintf(stderr, " or '%s'", value[n]);
- }
- fprintf(stderr, ".\n\n");
- return -1;
-}
-
-static int cb_require(Action& a, int status, const char* resp) {
- return cb_check(a, status, resp, 0);
-}
-
-static int cb_reject(Action& a, int status, const char* resp) {
- return cb_check(a, status, resp, 1);
-}
-
-void fb_queue_require(const std::string& product, const std::string& var, bool invert,
- size_t nvalues, const char** values) {
- Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
- a.product = product;
- a.data = values;
- a.size = nvalues;
- a.msg = "Checking " + var;
- a.func = invert ? cb_reject : cb_require;
- if (a.data == nullptr) die("out of memory");
-}
-
-static int cb_display(Action& a, int status, const char* resp) {
- if (status) {
- fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
- return status;
- }
- fprintf(stderr, "%s: %s\n", static_cast<const char*>(a.data), resp);
- free(static_cast<char*>(a.data));
- return 0;
-}
-
-void fb_queue_display(const std::string& label, const std::string& var) {
- Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
- a.data = xstrdup(label.c_str());
- a.func = cb_display;
-}
-
-static int cb_save(Action& a, int status, const char* resp) {
- if (status) {
- fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
- return status;
- }
- strncpy(reinterpret_cast<char*>(a.data), resp, a.size);
- return 0;
-}
-
-void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) {
- Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
- a.data = dest;
- a.size = dest_size;
- a.func = cb_save;
-}
-
-static int cb_do_nothing(Action&, int, const char*) {
- fprintf(stderr, "\n");
- return 0;
-}
-
-void fb_queue_reboot() {
- Action& a = queue_action(OP_COMMAND, FB_CMD_REBOOT);
- a.func = cb_do_nothing;
- a.msg = "Rebooting";
-}
-
-void fb_queue_command(const std::string& cmd, const std::string& msg) {
- Action& a = queue_action(OP_COMMAND, cmd);
- a.msg = msg;
-}
-
-void fb_queue_download(const std::string& name, void* data, uint32_t size) {
- Action& a = queue_action(OP_DOWNLOAD, "");
- a.data = data;
- a.size = size;
- a.msg = "Downloading '" + name + "'";
-}
-
-void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz) {
- Action& a = queue_action(OP_DOWNLOAD_FD, "");
- a.fd = fd;
- a.size = sz;
- a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", name.c_str(), sz / 1024);
-}
-
-void fb_queue_upload(const std::string& outfile) {
- Action& a = queue_action(OP_UPLOAD, "");
- a.data = xstrdup(outfile.c_str());
- a.msg = "Uploading '" + outfile + "'";
-}
-
-void fb_queue_notice(const std::string& notice) {
- Action& a = queue_action(OP_NOTICE, "");
- a.msg = notice;
-}
-
-void fb_queue_wait_for_disconnect() {
- queue_action(OP_WAIT_FOR_DISCONNECT, "");
-}
-
-int64_t fb_execute_queue() {
- int64_t status = 0;
- for (auto& a : action_list) {
- a->start = now();
- if (!a->msg.empty()) {
- fprintf(stderr, kStatusFormat, a->msg.c_str());
- verbose("\n");
- }
- if (a->op == OP_DOWNLOAD) {
- char* cbuf = static_cast<char*>(a->data);
- status = fb->Download(cbuf, a->size);
- status = a->func(*a, status, status ? fb_get_error().c_str() : "");
- if (status) break;
- } else if (a->op == OP_DOWNLOAD_FD) {
- status = fb->Download(a->fd, a->size);
- status = a->func(*a, status, status ? fb_get_error().c_str() : "");
- if (status) break;
- } else if (a->op == OP_COMMAND) {
- status = fb->RawCommand(a->cmd);
- status = a->func(*a, status, status ? fb_get_error().c_str() : "");
- if (status) break;
- } else if (a->op == OP_QUERY) {
- std::string resp;
- status = fb->RawCommand(a->cmd, &resp);
- status = a->func(*a, status, status ? fb_get_error().c_str() : resp.c_str());
- if (status) break;
- } else if (a->op == OP_NOTICE) {
- // We already showed the notice because it's in `Action::msg`.
- fprintf(stderr, "\n");
- } else if (a->op == OP_DOWNLOAD_SPARSE) {
- status = fb->Download(reinterpret_cast<sparse_file*>(a->data));
- status = a->func(*a, status, status ? fb_get_error().c_str() : "");
- if (status) break;
- } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
- fb->WaitForDisconnect();
- } else if (a->op == OP_UPLOAD) {
- status = fb->Upload(reinterpret_cast<const char*>(a->data));
- status = a->func(*a, status, status ? fb_get_error().c_str() : "");
- } else {
- die("unknown action: %d", a->op);
- }
- }
- action_list.clear();
- return status;
-}
-
-bool fb_reboot_to_userspace() {
- // First ensure that the queue is flushed.
- fb_execute_queue();
-
- fprintf(stderr, kStatusFormat, "Rebooting to userspace fastboot");
- verbose("\n");
-
- if (fb->RebootTo("fastboot") != fastboot::RetCode::SUCCESS) {
- fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
- return false;
- }
- fprintf(stderr, "OKAY\n");
-
- fb->set_transport(nullptr);
- return true;
-}
diff --git a/fastboot/engine.h b/fastboot/engine.h
deleted file mode 100644
index f098ca7..0000000
--- a/fastboot/engine.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#pragma once
-
-#include <inttypes.h>
-#include <stdlib.h>
-
-#include <string>
-
-#include <bootimg.h>
-#include "fastboot_driver.h"
-#include "util.h"
-
-#include "constants.h"
-
-class Transport;
-struct sparse_file;
-
-const std::string fb_get_error();
-
-//#define FB_COMMAND_SZ (fastboot::FB_COMMAND_SZ)
-//#define FB_RESPONSE_SZ (fastboot::FB_RESPONSE_SZ)
-
-/* engine.c - high level command queue engine */
-
-void fb_init(fastboot::FastBootDriver& fbi);
-void fb_reinit(Transport* transport);
-
-bool fb_getvar(const std::string& key, std::string* value);
-void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
-void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz);
-void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
- size_t current, size_t total);
-void fb_queue_erase(const std::string& partition);
-void fb_queue_format(const std::string& partition, int skip_if_not_supported, int32_t max_chunk_sz);
-void fb_queue_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
- const char** values);
-void fb_queue_display(const std::string& label, const std::string& var);
-void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size);
-void fb_queue_reboot(void);
-void fb_queue_command(const std::string& cmd, const std::string& msg);
-void fb_queue_download(const std::string& name, void* data, uint32_t size);
-void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz);
-void fb_queue_upload(const std::string& outfile);
-void fb_queue_notice(const std::string& notice);
-void fb_queue_wait_for_disconnect(void);
-void fb_queue_create_partition(const std::string& partition, const std::string& size);
-void fb_queue_delete_partition(const std::string& partition);
-void fb_queue_resize_partition(const std::string& partition, const std::string& size);
-int64_t fb_execute_queue();
-void fb_set_active(const std::string& slot);
-bool fb_reboot_to_userspace();
-
-/* Current product */
-extern char cur_product[FB_RESPONSE_SZ + 1];
-
-class FastBootTool {
- public:
- int Main(int argc, char* argv[]);
-
- void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
- void ParseOsVersion(boot_img_hdr_v1*, const char*);
-};
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 293ff00..5962650 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -26,6 +26,8 @@
* SUCH DAMAGE.
*/
+#include "fastboot.h"
+
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -43,6 +45,7 @@
#include <chrono>
#include <functional>
+#include <regex>
#include <thread>
#include <utility>
#include <vector>
@@ -55,26 +58,30 @@
#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
+#include <build/version.h>
+#include <platform_tools_version.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
#include "bootimg_utils.h"
#include "diagnose_usb.h"
-#include "engine.h"
+#include "fastboot_driver.h"
#include "fs.h"
#include "tcp.h"
#include "transport.h"
#include "udp.h"
#include "usb.h"
+#include "util.h"
+using android::base::ReadFully;
+using android::base::Split;
+using android::base::Trim;
using android::base::unique_fd;
#ifndef O_BINARY
#define O_BINARY 0
#endif
-char cur_product[FB_RESPONSE_SZ + 1];
-
static const char* serial = nullptr;
static bool g_long_listing = false;
@@ -94,6 +101,8 @@
static const std::string convert_fbe_marker_filename("convert_fbe");
+fastboot::FastBootDriver* fb = nullptr;
+
enum fb_buffer_type {
FB_BUFFER_FD,
FB_BUFFER_SPARSE,
@@ -146,6 +155,11 @@
{ nullptr, "system_other.img", "system.sig", "system", true, ImageType::Normal },
{ "userdata", "userdata.img", "userdata.sig", "userdata", true, ImageType::Extra },
{ "vbmeta", "vbmeta.img", "vbmeta.sig", "vbmeta", true, ImageType::BootCritical },
+ { "vbmeta_mainline",
+ "vbmeta_mainline.img",
+ "vbmeta_mainline.sig",
+ "vbmeta_mainline",
+ true, ImageType::BootCritical },
{ "vendor", "vendor.img", "vendor.sig", "vendor", true, ImageType::Normal },
{ nullptr, "vendor_other.img", "vendor.sig", "vendor", true, ImageType::Normal },
// clang-format on
@@ -170,40 +184,46 @@
return "";
}
+double last_start_time;
+
+static void Status(const std::string& message) {
+ static constexpr char kStatusFormat[] = "%-50s ";
+ fprintf(stderr, kStatusFormat, message.c_str());
+ last_start_time = now();
+}
+
+static void Epilog(int status) {
+ if (status) {
+ fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+ die("Command failed");
+ } else {
+ double split = now();
+ fprintf(stderr, "OKAY [%7.3fs]\n", (split - last_start_time));
+ }
+}
+
+static void InfoMessage(const std::string& info) {
+ fprintf(stderr, "(bootloader) %s\n", info.c_str());
+}
+
static int64_t get_file_size(int fd) {
struct stat sb;
- return fstat(fd, &sb) == -1 ? -1 : sb.st_size;
+ if (fstat(fd, &sb) == -1) {
+ die("could not get file size");
+ }
+ return sb.st_size;
}
-static void* load_fd(int fd, int64_t* sz) {
- int errno_tmp;
- char* data = nullptr;
+bool ReadFileToVector(const std::string& file, std::vector<char>* out) {
+ out->clear();
- *sz = get_file_size(fd);
- if (*sz < 0) {
- goto oops;
+ unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)));
+ if (fd == -1) {
+ return false;
}
- data = (char*) malloc(*sz);
- if (data == nullptr) goto oops;
-
- if(read(fd, data, *sz) != *sz) goto oops;
- close(fd);
-
- return data;
-
-oops:
- errno_tmp = errno;
- close(fd);
- if(data != 0) free(data);
- errno = errno_tmp;
- return 0;
-}
-
-static void* load_file(const std::string& path, int64_t* sz) {
- int fd = open(path.c_str(), O_RDONLY | O_BINARY);
- if (fd == -1) return nullptr;
- return load_fd(fd, sz);
+ out->resize(get_file_size(fd));
+ return ReadFully(fd, out->data(), out->size());
}
static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
@@ -411,70 +431,71 @@
return 0;
}
-static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
- const std::string& second_stage, int64_t* sz) {
- int64_t ksize;
- void* kdata = load_file(kernel.c_str(), &ksize);
- if (kdata == nullptr) die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+static std::vector<char> LoadBootableImage(const std::string& kernel, const std::string& ramdisk,
+ const std::string& second_stage) {
+ std::vector<char> kernel_data;
+ if (!ReadFileToVector(kernel, &kernel_data)) {
+ die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+ }
// Is this actually a boot image?
- if (ksize < static_cast<int64_t>(sizeof(boot_img_hdr_v1))) {
+ if (kernel_data.size() < sizeof(boot_img_hdr_v1)) {
die("cannot load '%s': too short", kernel.c_str());
}
- if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+ if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
if (!g_cmdline.empty()) {
- bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kdata), g_cmdline);
+ bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kernel_data.data()), g_cmdline);
}
if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
- *sz = ksize;
- return kdata;
+ return kernel_data;
}
- void* rdata = nullptr;
- int64_t rsize = 0;
+ std::vector<char> ramdisk_data;
if (!ramdisk.empty()) {
- rdata = load_file(ramdisk.c_str(), &rsize);
- if (rdata == nullptr) die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+ if (!ReadFileToVector(ramdisk, &ramdisk_data)) {
+ die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+ }
}
- void* sdata = nullptr;
- int64_t ssize = 0;
+ std::vector<char> second_stage_data;
if (!second_stage.empty()) {
- sdata = load_file(second_stage.c_str(), &ssize);
- if (sdata == nullptr) die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+ if (!ReadFileToVector(second_stage, &second_stage_data)) {
+ die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+ }
}
-
fprintf(stderr,"creating boot image...\n");
- boot_img_hdr_v1* bdata = mkbootimg(kdata, ksize, rdata, rsize, sdata, ssize,
- g_base_addr, g_boot_img_hdr, sz);
- if (bdata == nullptr) die("failed to create boot.img");
- if (!g_cmdline.empty()) bootimg_set_cmdline(bdata, g_cmdline);
- fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", *sz);
+ std::vector<char> out;
+ boot_img_hdr_v1* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
+ g_base_addr, g_boot_img_hdr, &out);
- return bdata;
+ if (!g_cmdline.empty()) bootimg_set_cmdline(boot_image_data, g_cmdline);
+ fprintf(stderr, "creating boot image - %zu bytes\n", out.size());
+
+ return out;
}
-static void* unzip_to_memory(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
- ZipString zip_entry_name(entry_name);
+static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
+ std::vector<char>* out) {
+ ZipString zip_entry_name(entry_name.c_str());
ZipEntry zip_entry;
if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
- fprintf(stderr, "archive does not contain '%s'\n", entry_name);
- return nullptr;
+ fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
+ return false;
}
- *sz = zip_entry.uncompressed_length;
+ out->resize(zip_entry.uncompressed_length);
- fprintf(stderr, "extracting %s (%" PRId64 " MB) to RAM...\n", entry_name, *sz / 1024 / 1024);
- uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
- if (data == nullptr) die("failed to allocate %" PRId64 " bytes for '%s'", *sz, entry_name);
+ fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
+ out->size() / 1024 / 1024);
- int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
- if (error != 0) die("failed to extract '%s': %s", entry_name, ErrorCodeString(error));
+ int error = ExtractToMemory(zip, &zip_entry, reinterpret_cast<uint8_t*>(out->data()),
+ out->size());
+ if (error != 0) die("failed to extract '%s': %s", entry_name.c_str(), ErrorCodeString(error));
- return data;
+ return true;
}
#if defined(_WIN32)
@@ -595,120 +616,168 @@
return fd.release();
}
-static char* strip(char* s) {
- while (*s && isspace(*s)) s++;
+static void CheckRequirement(const std::string& cur_product, const std::string& var,
+ const std::string& product, bool invert,
+ const std::vector<std::string>& options) {
+ Status("Checking '" + var + "'");
- int n = strlen(s);
- while (n-- > 0) {
- if (!isspace(s[n])) break;
- s[n] = 0;
+ double start = now();
+
+ if (!product.empty()) {
+ if (product != cur_product) {
+ double split = now();
+ fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n",
+ cur_product.c_str(), product.c_str(), (split - start));
+ return;
+ }
}
- return s;
+
+ std::string var_value;
+ if (fb->GetVar(var, &var_value) != fastboot::SUCCESS) {
+ fprintf(stderr, "FAILED\n\n");
+ fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(),
+ fb->Error().c_str());
+ die("requirements not met!");
+ }
+
+ bool match = false;
+ for (const auto& option : options) {
+ if (option == var_value || (option.back() == '*' &&
+ !var_value.compare(0, option.length() - 1, option, 0,
+ option.length() - 1))) {
+ match = true;
+ break;
+ }
+ }
+
+ if (invert) {
+ match = !match;
+ }
+
+ if (match) {
+ double split = now();
+ fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
+ return;
+ }
+
+ fprintf(stderr, "FAILED\n\n");
+ fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
+ fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", options[0].c_str());
+ for (auto it = std::next(options.begin()); it != options.end(); ++it) {
+ fprintf(stderr, " or '%s'", it->c_str());
+ }
+ fprintf(stderr, ".\n\n");
+ die("requirements not met!");
}
-#define MAX_OPTIONS 32
-static void check_requirement(char* line) {
- char *val[MAX_OPTIONS];
- unsigned count;
- char *x;
- int invert = 0;
-
+bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+ bool* invert, std::vector<std::string>* options) {
// "require product=alpha|beta|gamma"
// "require version-bootloader=1234"
// "require-for-product:gamma version-bootloader=istanbul|constantinople"
// "require partition-exists=vendor"
+ *product = "";
+ *invert = false;
- char* name = line;
- const char* product = "";
- if (!strncmp(name, "reject ", 7)) {
- name += 7;
- invert = 1;
- } else if (!strncmp(name, "require ", 8)) {
- name += 8;
- invert = 0;
- } else if (!strncmp(name, "require-for-product:", 20)) {
- // Get the product and point name past it
- product = name + 20;
- name = strchr(name, ' ');
- if (!name) die("android-info.txt syntax error: %s", line);
- *name = 0;
- name += 1;
- invert = 0;
+ auto require_reject_regex = std::regex{"(require\\s+|reject\\s+)?\\s*(\\S+)\\s*=\\s*(.*)"};
+ auto require_product_regex =
+ std::regex{"require-for-product:\\s*(\\S+)\\s+(\\S+)\\s*=\\s*(.*)"};
+ std::smatch match_results;
+
+ if (std::regex_match(line, match_results, require_reject_regex)) {
+ *invert = Trim(match_results[1]) == "reject";
+ } else if (std::regex_match(line, match_results, require_product_regex)) {
+ *product = match_results[1];
+ } else {
+ return false;
}
- x = strchr(name, '=');
- if (x == 0) return;
- *x = 0;
- val[0] = x + 1;
+ *name = match_results[2];
+ // Work around an unfortunate name mismatch.
+ if (*name == "board") {
+ *name = "product";
+ }
- name = strip(name);
+ auto raw_options = Split(match_results[3], "|");
+ for (const auto& option : raw_options) {
+ auto trimmed_option = Trim(option);
+ options->emplace_back(trimmed_option);
+ }
- // "require partition-exists=x" is a special case, added because of the trouble we had when
- // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
- // missing out new partitions. A device with new partitions can use "partition-exists" to
- // override the fields `optional_if_no_image` in the `images` array.
- if (!strcmp(name, "partition-exists")) {
- const char* partition_name = val[0];
- std::string has_slot;
- if (!fb_getvar(std::string("has-slot:") + partition_name, &has_slot) ||
- (has_slot != "yes" && has_slot != "no")) {
- die("device doesn't have required partition %s!", partition_name);
+ return true;
+}
+
+// "require partition-exists=x" is a special case, added because of the trouble we had when
+// Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
+// missing out new partitions. A device with new partitions can use "partition-exists" to
+// override the fields `optional_if_no_image` in the `images` array.
+static void HandlePartitionExists(const std::vector<std::string>& options) {
+ const std::string& partition_name = options[0];
+ std::string has_slot;
+ if (fb->GetVar("has-slot:" + partition_name, &has_slot) != fastboot::SUCCESS ||
+ (has_slot != "yes" && has_slot != "no")) {
+ die("device doesn't have required partition %s!", partition_name.c_str());
+ }
+ bool known_partition = false;
+ for (size_t i = 0; i < arraysize(images); ++i) {
+ if (images[i].nickname && images[i].nickname == partition_name) {
+ images[i].optional_if_no_image = false;
+ known_partition = true;
}
- bool known_partition = false;
- for (size_t i = 0; i < arraysize(images); ++i) {
- if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
- images[i].optional_if_no_image = false;
- known_partition = true;
- }
+ }
+ if (!known_partition) {
+ die("device requires partition %s which is not known to this version of fastboot",
+ partition_name.c_str());
+ }
+}
+
+static void CheckRequirements(const std::string& data) {
+ std::string cur_product;
+ if (fb->GetVar("product", &cur_product) != fastboot::SUCCESS) {
+ fprintf(stderr, "getvar:product FAILED (%s)\n", fb->Error().c_str());
+ }
+
+ auto lines = Split(data, "\n");
+ for (const auto& line : lines) {
+ if (line.empty()) {
+ continue;
}
- if (!known_partition) {
- die("device requires partition %s which is not known to this version of fastboot",
- partition_name);
+
+ std::string name;
+ std::string product;
+ bool invert;
+ std::vector<std::string> options;
+
+ if (!ParseRequirementLine(line, &name, &product, &invert, &options)) {
+ fprintf(stderr, "android-info.txt syntax error: %s\n", line.c_str());
+ continue;
}
+ if (name == "partition-exists") {
+ HandlePartitionExists(options);
+ } else {
+ CheckRequirement(cur_product, name, product, invert, options);
+ }
+ }
+}
+
+static void DisplayVarOrError(const std::string& label, const std::string& var) {
+ std::string value;
+
+ if (fb->GetVar(var, &value) != fastboot::SUCCESS) {
+ Status("getvar:" + var);
+ fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
return;
}
-
- for(count = 1; count < MAX_OPTIONS; count++) {
- x = strchr(val[count - 1],'|');
- if (x == 0) break;
- *x = 0;
- val[count] = x + 1;
- }
-
- // Work around an unfortunate name mismatch.
- const char* var = name;
- if (!strcmp(name, "board")) var = "product";
-
- const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
- if (out == nullptr) die("out of memory");
-
- for (size_t i = 0; i < count; ++i) {
- out[i] = xstrdup(strip(val[i]));
- }
-
- fb_queue_require(product, var, invert, count, out);
+ fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
}
-static void check_requirements(char* data, int64_t sz) {
- char* s = data;
- while (sz-- > 0) {
- if (*s == '\n') {
- *s++ = 0;
- check_requirement(data);
- data = s;
- } else {
- s++;
- }
- }
- if (fb_execute_queue()) die("requirements not met!");
-}
+static void DumpInfo() {
+ fprintf(stderr, "--------------------------------------------\n");
+ DisplayVarOrError("Bootloader Version...", "version-bootloader");
+ DisplayVarOrError("Baseband Version.....", "version-baseband");
+ DisplayVarOrError("Serial Number........", "serialno");
+ fprintf(stderr, "--------------------------------------------\n");
-static void queue_info_dump() {
- fb_queue_notice("--------------------------------------------");
- fb_queue_display("Bootloader Version...", "version-bootloader");
- fb_queue_display("Baseband Version.....", "version-baseband");
- fb_queue_display("Serial Number........", "serialno");
- fb_queue_notice("--------------------------------------------");
}
static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
@@ -733,7 +802,7 @@
static int64_t get_target_sparse_limit() {
std::string max_download_size;
- if (!fb_getvar("max-download-size", &max_download_size) ||
+ if (fb->GetVar("max-download-size", &max_download_size) != fastboot::SUCCESS ||
max_download_size.empty()) {
verbose("target didn't report max-download-size");
return 0;
@@ -881,12 +950,12 @@
for (size_t i = 0; i < sparse_files.size(); ++i) {
const auto& pair = sparse_files[i];
- fb_queue_flash_sparse(partition, pair.first, pair.second, i + 1, sparse_files.size());
+ fb->FlashPartition(partition, pair.first, pair.second, i + 1, sparse_files.size());
}
break;
}
case FB_BUFFER_FD:
- fb_queue_flash_fd(partition, buf->fd, buf->sz);
+ fb->FlashPartition(partition, buf->fd, buf->sz);
break;
default:
die("unknown buffer type: %d", buf->type);
@@ -895,14 +964,15 @@
static std::string get_current_slot() {
std::string current_slot;
- if (!fb_getvar("current-slot", ¤t_slot)) return "";
+ if (fb->GetVar("current-slot", ¤t_slot) != fastboot::SUCCESS) return "";
return current_slot;
}
static int get_slot_count() {
std::string var;
int count = 0;
- if (!fb_getvar("slot-count", &var) || !android::base::ParseInt(var, &count)) {
+ if (fb->GetVar("slot-count", &var) != fastboot::SUCCESS ||
+ !android::base::ParseInt(var, &count)) {
return 0;
}
return count;
@@ -977,7 +1047,7 @@
std::string has_slot;
std::string current_slot;
- if (!fb_getvar("has-slot:" + part, &has_slot)) {
+ if (fb->GetVar("has-slot:" + part, &has_slot) != fastboot::SUCCESS) {
/* If has-slot is not supported, the answer is no. */
has_slot = "no";
}
@@ -1010,7 +1080,7 @@
std::string has_slot;
if (slot == "all") {
- if (!fb_getvar("has-slot:" + part, &has_slot)) {
+ if (fb->GetVar("has-slot:" + part, &has_slot) != fastboot::SUCCESS) {
die("Could not check if partition %s has slot %s", part.c_str(), slot.c_str());
}
if (has_slot == "yes") {
@@ -1040,25 +1110,25 @@
if (!supports_AB()) return;
if (slot_override != "") {
- fb_set_active(slot_override);
+ fb->SetActive(slot_override);
} else {
std::string current_slot = get_current_slot();
if (current_slot != "") {
- fb_set_active(current_slot);
+ fb->SetActive(current_slot);
}
}
}
static bool is_userspace_fastboot() {
std::string value;
- return fb_getvar("is-userspace", &value) && value == "yes";
+ return fb->GetVar("is-userspace", &value) == fastboot::SUCCESS && value == "yes";
}
static bool if_partition_exists(const std::string& partition, const std::string& slot) {
std::string has_slot;
std::string partition_name = partition;
- if (fb_getvar("has-slot:" + partition, &has_slot) && has_slot == "yes") {
+ if (fb->GetVar("has-slot:" + partition, &has_slot) == fastboot::SUCCESS && has_slot == "yes") {
if (slot == "") {
std::string current_slot = get_current_slot();
if (current_slot == "") {
@@ -1070,28 +1140,29 @@
}
}
std::string partition_size;
- return fb_getvar("partition-size:" + partition_name, &partition_size);
+ return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
}
static bool is_logical(const std::string& partition) {
std::string value;
- return fb_getvar("is-logical:" + partition, &value) && value == "yes";
+ return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
}
static void reboot_to_userspace_fastboot() {
- if (!fb_reboot_to_userspace()) {
- die("Must reboot to userspace fastboot to flash logical partitions");
- }
+ fb->RebootTo("fastboot");
+
+ auto* old_transport = fb->set_transport(nullptr);
+ delete old_transport;
// Give the current connection time to close.
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
- fb_reinit(open_device());
+ fb->set_transport(open_device());
}
class ImageSource {
public:
- virtual void* ReadFile(const std::string& name, int64_t* size) const = 0;
+ virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
virtual int OpenFile(const std::string& name) const = 0;
};
@@ -1127,6 +1198,7 @@
}
void FlashAllTool::Flash() {
+ DumpInfo();
CheckRequirements();
DetermineSecondarySlot();
CollectImages();
@@ -1143,7 +1215,7 @@
for (const auto& [image, slot] : os_images_) {
auto resize_partition = [](const std::string& partition) -> void {
if (is_logical(partition)) {
- fb_queue_resize_partition(partition, "0");
+ fb->ResizePartition(partition, "0");
}
};
do_for_partitions(image->part_name, slot, resize_partition, false);
@@ -1160,12 +1232,11 @@
}
void FlashAllTool::CheckRequirements() {
- int64_t sz;
- void* data = source_.ReadFile("android-info.txt", &sz);
- if (data == nullptr) {
+ std::vector<char> contents;
+ if (!source_.ReadFile("android-info.txt", &contents)) {
die("could not read android-info.txt");
}
- check_requirements(reinterpret_cast<char*>(data), sz);
+ ::CheckRequirements({contents.data(), contents.size()});
}
void FlashAllTool::DetermineSecondarySlot() {
@@ -1218,15 +1289,14 @@
void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
auto flash = [&, this](const std::string& partition_name) {
- int64_t sz;
- void* data = source_.ReadFile(image.sig_name, &sz);
- if (data) {
- fb_queue_download("signature", data, sz);
- fb_queue_command("signature", "installing signature");
+ std::vector<char> signature_data;
+ if (source_.ReadFile(image.sig_name, &signature_data)) {
+ fb->Download("signature", signature_data);
+ fb->RawCommand("signature", "installing signature");
}
if (is_logical(partition_name)) {
- fb_queue_resize_partition(partition_name, std::to_string(buf->image_size));
+ fb->ResizePartition(partition_name, std::to_string(buf->image_size));
}
flash_buf(partition_name.c_str(), buf);
};
@@ -1245,31 +1315,27 @@
if (!is_userspace_fastboot()) {
reboot_to_userspace_fastboot();
}
- fb_queue_download_fd("super", fd, get_file_size(fd));
+ fb->Download("super", fd, get_file_size(fd));
std::string command = "update-super:super";
if (wipe_) {
command += ":wipe";
}
- fb_queue_command(command, "Updating super partition");
-
- // We need these commands to have finished before proceeding, since
- // otherwise "getvar is-logical" may not return a correct answer below.
- fb_execute_queue();
+ fb->RawCommand(command, "Updating super partition");
}
class ZipImageSource final : public ImageSource {
public:
explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
- void* ReadFile(const std::string& name, int64_t* size) const override;
+ bool ReadFile(const std::string& name, std::vector<char>* out) const override;
int OpenFile(const std::string& name) const override;
private:
ZipArchiveHandle zip_;
};
-void* ZipImageSource::ReadFile(const std::string& name, int64_t* size) const {
- return unzip_to_memory(zip_, name.c_str(), size);
+bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
+ return UnzipToMemory(zip_, name, out);
}
int ZipImageSource::OpenFile(const std::string& name) const {
@@ -1277,10 +1343,6 @@
}
static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
- queue_info_dump();
-
- fb_queue_query_save("product", cur_product, sizeof(cur_product));
-
ZipArchiveHandle zip;
int error = OpenArchive(filename, &zip);
if (error != 0) {
@@ -1295,16 +1357,16 @@
class LocalImageSource final : public ImageSource {
public:
- void* ReadFile(const std::string& name, int64_t* size) const override;
+ bool ReadFile(const std::string& name, std::vector<char>* out) const override;
int OpenFile(const std::string& name) const override;
};
-void* LocalImageSource::ReadFile(const std::string& name, int64_t* size) const {
+bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
auto path = find_item_given_name(name);
if (path.empty()) {
- return nullptr;
+ return false;
}
- return load_file(path.c_str(), size);
+ return ReadFileToVector(path, out);
}
int LocalImageSource::OpenFile(const std::string& name) const {
@@ -1313,11 +1375,6 @@
}
static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
- std::string fname;
- queue_info_dump();
-
- fb_queue_query_save("product", cur_product, sizeof(cur_product));
-
FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
tool.Flash();
}
@@ -1336,7 +1393,7 @@
while (!args->empty()) {
command += " " + next_arg(args);
}
- fb_queue_command(command, "");
+ fb->RawCommand(command, "");
}
static std::string fb_fix_numeric_var(std::string var) {
@@ -1350,7 +1407,7 @@
static unsigned fb_get_flash_block_size(std::string name) {
std::string sizeString;
- if (!fb_getvar(name, &sizeString) || sizeString.empty()) {
+ if (fb->GetVar(name, &sizeString) != fastboot::SUCCESS || sizeString.empty()) {
// This device does not report flash block sizes, so return 0.
return 0;
}
@@ -1388,7 +1445,7 @@
limit = sparse_limit;
}
- if (!fb_getvar("partition-type:" + partition, &partition_type)) {
+ if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
errMsg = "Can't determine partition type.\n";
goto failed;
}
@@ -1400,7 +1457,7 @@
partition_type = type_override;
}
- if (!fb_getvar("partition-size:" + partition, &partition_size)) {
+ if (fb->GetVar("partition-size:" + partition, &partition_size) != fastboot::SUCCESS) {
errMsg = "Unable to get partition size\n";
goto failed;
}
@@ -1458,7 +1515,7 @@
fprintf(stderr, "Erase successful, but not automatically formatting.\n");
if (errMsg) fprintf(stderr, "%s", errMsg);
}
- fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
+ fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
}
int FastBootTool::Main(int argc, char* argv[]) {
@@ -1471,8 +1528,6 @@
bool wants_set_active = false;
bool skip_secondary = false;
bool set_fbe_marker = false;
- void *data;
- int64_t sz;
int longindex;
std::string slot_override;
std::string next_active;
@@ -1548,7 +1603,7 @@
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
} else if (name == "version") {
- fprintf(stdout, "fastboot version %s\n", FASTBOOT_VERSION);
+ fprintf(stdout, "fastboot version %s-%s\n", PLATFORM_TOOLS_VERSION, android::build::GetBuildNumber().c_str());
fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
return 0;
#if !defined(_WIN32)
@@ -1610,8 +1665,13 @@
if (transport == nullptr) {
return 1;
}
- fastboot::FastBootDriver fb(transport);
- fb_init(fb);
+ fastboot::DriverCallbacks driver_callbacks = {
+ .prolog = Status,
+ .epilog = Epilog,
+ .info = InfoMessage,
+ };
+ fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
+ fb = &fastboot_driver;
const double start = now();
@@ -1622,7 +1682,7 @@
if (next_active == "") {
if (slot_override == "") {
std::string current_slot;
- if (fb_getvar("current-slot", ¤t_slot)) {
+ if (fb->GetVar("current-slot", ¤t_slot) == fastboot::SUCCESS) {
next_active = verify_slot(current_slot, false);
} else {
wants_set_active = false;
@@ -1639,19 +1699,18 @@
if (command == "getvar") {
std::string variable = next_arg(&args);
- fb_queue_display(variable, variable);
+ DisplayVarOrError(variable, variable);
} else if (command == "erase") {
std::string partition = next_arg(&args);
auto erase = [&](const std::string& partition) {
std::string partition_type;
- if (fb_getvar(std::string("partition-type:") + partition,
- &partition_type) &&
+ if (fb->GetVar("partition-type:" + partition, &partition_type) == fastboot::SUCCESS &&
fs_get_generator(partition_type) != nullptr) {
fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
partition_type.c_str());
}
- fb_queue_erase(partition);
+ fb->Erase(partition);
};
do_for_partitions(partition, slot_override, erase, true);
} else if (android::base::StartsWith(command, "format")) {
@@ -1676,11 +1735,13 @@
do_for_partitions(partition.c_str(), slot_override, format, true);
} else if (command == "signature") {
std::string filename = next_arg(&args);
- data = load_file(filename.c_str(), &sz);
- if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
- if (sz != 256) die("signature must be 256 bytes (got %" PRId64 ")", sz);
- fb_queue_download("signature", data, sz);
- fb_queue_command("signature", "installing signature");
+ std::vector<char> data;
+ if (!ReadFileToVector(filename, &data)) {
+ die("could not load '%s': %s", filename.c_str(), strerror(errno));
+ }
+ if (data.size() != 256) die("signature must be 256 bytes (got %zu)", data.size());
+ fb->Download("signature", data);
+ fb->RawCommand("signature", "installing signature");
} else if (command == "reboot") {
wants_reboot = true;
@@ -1708,7 +1769,7 @@
} else if (command == "reboot-fastboot") {
wants_reboot_fastboot = true;
} else if (command == "continue") {
- fb_queue_command("continue", "resuming boot");
+ fb->Continue();
} else if (command == "boot") {
std::string kernel = next_arg(&args);
std::string ramdisk;
@@ -1716,9 +1777,9 @@
std::string second_stage;
if (!args.empty()) second_stage = next_arg(&args);
- data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
- fb_queue_download("boot.img", data, sz);
- fb_queue_command("boot", "booting");
+ auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+ fb->Download("boot.img", data);
+ fb->Boot();
} else if (command == "flash") {
std::string pname = next_arg(&args);
@@ -1742,9 +1803,9 @@
std::string second_stage;
if (!args.empty()) second_stage = next_arg(&args);
- data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
- auto flashraw = [&](const std::string& partition) {
- fb_queue_flash(partition, data, sz);
+ auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+ auto flashraw = [&data](const std::string& partition) {
+ fb->FlashPartition(partition, data);
};
do_for_partitions(partition, slot_override, flashraw, true);
} else if (command == "flashall") {
@@ -1768,7 +1829,7 @@
wants_reboot = true;
} else if (command == "set_active") {
std::string slot = verify_slot(next_arg(&args), false);
- fb_set_active(slot);
+ fb->SetActive(slot);
} else if (command == "stage") {
std::string filename = next_arg(&args);
@@ -1776,10 +1837,10 @@
if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
die("cannot load '%s'", filename.c_str());
}
- fb_queue_download_fd(filename, buf.fd, buf.sz);
+ fb->Download(filename, buf.fd, buf.sz);
} else if (command == "get_staged") {
std::string filename = next_arg(&args);
- fb_queue_upload(filename);
+ fb->Upload(filename);
} else if (command == "oem") {
do_oem_command("oem", &args);
} else if (command == "flashing") {
@@ -1796,14 +1857,14 @@
} else if (command == "create-logical-partition") {
std::string partition = next_arg(&args);
std::string size = next_arg(&args);
- fb_queue_create_partition(partition, size);
+ fb->CreatePartition(partition, size);
} else if (command == "delete-logical-partition") {
std::string partition = next_arg(&args);
- fb_queue_delete_partition(partition);
+ fb->DeletePartition(partition);
} else if (command == "resize-logical-partition") {
std::string partition = next_arg(&args);
std::string size = next_arg(&args);
- fb_queue_resize_partition(partition, size);
+ fb->ResizePartition(partition, size);
} else {
syntax_error("unknown command %s", command.c_str());
}
@@ -1813,9 +1874,11 @@
std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
for (const auto& partition : partitions) {
std::string partition_type;
- if (!fb_getvar(std::string{"partition-type:"} + partition, &partition_type)) continue;
+ if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
+ continue;
+ }
if (partition_type.empty()) continue;
- fb_queue_erase(partition);
+ fb->Erase(partition);
if (partition == "userdata" && set_fbe_marker) {
fprintf(stderr, "setting FBE marker on initial userdata...\n");
std::string initial_userdata_dir = create_fbemarker_tmpdir();
@@ -1827,25 +1890,28 @@
}
}
if (wants_set_active) {
- fb_set_active(next_active);
+ fb->SetActive(next_active);
}
if (wants_reboot && !skip_reboot) {
- fb_queue_reboot();
- fb_queue_wait_for_disconnect();
+ fb->Reboot();
+ fb->WaitForDisconnect();
} else if (wants_reboot_bootloader) {
- fb_queue_command("reboot-bootloader", "rebooting into bootloader");
- fb_queue_wait_for_disconnect();
+ fb->RebootTo("bootloader");
+ fb->WaitForDisconnect();
} else if (wants_reboot_recovery) {
- fb_queue_command("reboot-recovery", "rebooting into recovery");
- fb_queue_wait_for_disconnect();
+ fb->RebootTo("recovery");
+ fb->WaitForDisconnect();
} else if (wants_reboot_fastboot) {
- fb_queue_command("reboot-fastboot", "rebooting into fastboot");
- fb_queue_wait_for_disconnect();
+ fb->RebootTo("fastboot");
+ fb->WaitForDisconnect();
}
- int status = fb_execute_queue() ? EXIT_FAILURE : EXIT_SUCCESS;
fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
- return status;
+
+ auto* old_transport = fb->set_transport(nullptr);
+ delete old_transport;
+
+ return 0;
}
void FastBootTool::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
new file mode 100644
index 0000000..9f18253
--- /dev/null
+++ b/fastboot/fastboot.h
@@ -0,0 +1,37 @@
+/*
+ * 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 <bootimg.h>
+
+class FastBootTool {
+ public:
+ int Main(int argc, char* argv[]);
+
+ void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);
+ void ParseOsVersion(boot_img_hdr_v1*, const char*);
+};
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index ceee066..b1f3bc9 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -25,6 +25,7 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+
#include "fastboot_driver.h"
#include <errno.h>
@@ -44,44 +45,56 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <utils/FileMap.h>
-#include "fastboot_driver.h"
+
+#include "constants.h"
#include "transport.h"
+using android::base::StringPrintf;
+
namespace fastboot {
/*************************** PUBLIC *******************************/
-FastBootDriver::FastBootDriver(Transport* transport, std::function<void(std::string&)> info,
+FastBootDriver::FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks,
bool no_checks)
- : transport_(transport) {
- info_cb_ = info;
- disable_checks_ = no_checks;
-}
+ : transport_(transport),
+ prolog_(std::move(driver_callbacks.prolog)),
+ epilog_(std::move(driver_callbacks.epilog)),
+ info_(std::move(driver_callbacks.info)),
+ disable_checks_(no_checks) {}
FastBootDriver::~FastBootDriver() {
- set_transport(nullptr);
}
RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
- return RawCommand(Commands::BOOT, response, info);
+ return RawCommand(FB_CMD_BOOT, "Booting", response, info);
}
RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
- return RawCommand(Commands::CONTINUE, response, info);
+ return RawCommand(FB_CMD_CONTINUE, "Resuming boot", response, info);
}
-RetCode FastBootDriver::Erase(const std::string& part, std::string* response,
- std::vector<std::string>* info) {
- return RawCommand(Commands::ERASE + part, response, info);
+RetCode FastBootDriver::CreatePartition(const std::string& partition, const std::string& size) {
+ return RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size,
+ "Creating '" + partition + "'");
}
-RetCode FastBootDriver::Flash(const std::string& part, std::string* response,
+RetCode FastBootDriver::DeletePartition(const std::string& partition) {
+ return RawCommand(FB_CMD_DELETE_PARTITION ":" + partition, "Deleting '" + partition + "'");
+}
+
+RetCode FastBootDriver::Erase(const std::string& partition, std::string* response,
std::vector<std::string>* info) {
- return RawCommand(Commands::FLASH + part, response, info);
+ return RawCommand(FB_CMD_ERASE ":" + partition, "Erasing '" + partition + "'", response, info);
+}
+
+RetCode FastBootDriver::Flash(const std::string& partition, std::string* response,
+ std::vector<std::string>* info) {
+ return RawCommand(FB_CMD_FLASH ":" + partition, "Writing '" + partition + "'", response, info);
}
RetCode FastBootDriver::GetVar(const std::string& key, std::string* val,
std::vector<std::string>* info) {
- return RawCommand(Commands::GET_VAR + key, val, info);
+ return RawCommand(FB_CMD_GETVAR ":" + key, val, info);
}
RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
@@ -90,44 +103,52 @@
}
RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
- return RawCommand(Commands::REBOOT, response, info);
+ return RawCommand(FB_CMD_REBOOT, "Rebooting", response, info);
}
RetCode FastBootDriver::RebootTo(std::string target, std::string* response,
std::vector<std::string>* info) {
- return RawCommand("reboot-" + target, response, info);
+ return RawCommand("reboot-" + target, "Rebooting into " + target, response, info);
}
-RetCode FastBootDriver::SetActive(const std::string& part, std::string* response,
+RetCode FastBootDriver::ResizePartition(const std::string& partition, const std::string& size) {
+ return RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size,
+ "Resizing '" + partition + "'");
+}
+
+RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,
std::vector<std::string>* info) {
- return RawCommand(Commands::SET_ACTIVE + part, response, info);
+ return RawCommand(FB_CMD_SET_ACTIVE ":" + slot, "Setting current slot to '" + slot + "'",
+ response, info);
}
-RetCode FastBootDriver::FlashPartition(const std::string& part, const std::vector<char>& data) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition,
+ const std::vector<char>& data) {
RetCode ret;
- if ((ret = Download(data))) {
+ if ((ret = Download(partition, data))) {
return ret;
}
- return RawCommand(Commands::FLASH + part);
+ return Flash(partition);
}
-RetCode FastBootDriver::FlashPartition(const std::string& part, int fd, uint32_t sz) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition, int fd, uint32_t size) {
RetCode ret;
- if ((ret = Download(fd, sz))) {
+ if ((ret = Download(partition, fd, size))) {
return ret;
}
- return RawCommand(Commands::FLASH + part);
+ return Flash(partition);
}
-RetCode FastBootDriver::FlashPartition(const std::string& part, sparse_file* s) {
+RetCode FastBootDriver::FlashPartition(const std::string& partition, sparse_file* s, uint32_t size,
+ size_t current, size_t total) {
RetCode ret;
- if ((ret = Download(s))) {
+ if ((ret = Download(partition, s, size, current, total, false))) {
return ret;
}
- return RawCommand(Commands::FLASH + part);
+ return Flash(partition);
}
-RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts) {
+RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions) {
std::vector<std::string> all;
RetCode ret;
if ((ret = GetVarAll(&all))) {
@@ -141,33 +162,19 @@
if (std::regex_match(s, sm, reg)) {
std::string m1(sm[1]);
std::string m2(sm[2]);
- uint32_t tmp = strtol(m2.c_str(), 0, 16);
- parts->push_back(std::make_tuple(m1, tmp));
+ uint64_t tmp = strtoll(m2.c_str(), 0, 16);
+ partitions->push_back(std::make_tuple(m1, tmp));
}
}
return SUCCESS;
}
-RetCode FastBootDriver::Require(const std::string& var, const std::vector<std::string>& allowed,
- bool* reqmet, bool invert) {
- *reqmet = invert;
- RetCode ret;
- std::string response;
- if ((ret = GetVar(var, &response))) {
- return ret;
- }
-
- // Now check if we have a match
- for (const auto s : allowed) {
- // If it ends in *, and starting substring match
- if (response == s || (s.length() && s.back() == '*' &&
- !response.compare(0, s.length() - 1, s, 0, s.length() - 1))) {
- *reqmet = !invert;
- break;
- }
- }
-
- return SUCCESS;
+RetCode FastBootDriver::Download(const std::string& name, int fd, size_t size,
+ std::string* response, std::vector<std::string>* info) {
+ prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), size / 1024));
+ auto result = Download(fd, size, response, info);
+ epilog_(result);
+ return result;
}
RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
@@ -193,26 +200,29 @@
return HandleResponse(response, info);
}
-RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
- std::vector<std::string>* info) {
- return Download(buf.data(), buf.size(), response, info);
+RetCode FastBootDriver::Download(const std::string& name, const std::vector<char>& buf,
+ std::string* response, std::vector<std::string>* info) {
+ prolog_(StringPrintf("Sending '%s' (%zu KB)", name.c_str(), buf.size() / 1024));
+ auto result = Download(buf, response, info);
+ epilog_(result);
+ return result;
}
-RetCode FastBootDriver::Download(const char* buf, uint32_t size, std::string* response,
+RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
std::vector<std::string>* info) {
RetCode ret;
error_ = "";
- if ((size == 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+ if ((buf.size() == 0 || buf.size() > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
error_ = "Buffer is too large or 0 bytes";
return BAD_ARG;
}
- if ((ret = DownloadCommand(size, response, info))) {
+ if ((ret = DownloadCommand(buf.size(), response, info))) {
return ret;
}
// Write the buffer
- if ((ret = SendBuffer(buf, size))) {
+ if ((ret = SendBuffer(buf))) {
return ret;
}
@@ -220,6 +230,16 @@
return HandleResponse(response, info);
}
+RetCode FastBootDriver::Download(const std::string& partition, struct sparse_file* s, uint32_t size,
+ size_t current, size_t total, bool use_crc, std::string* response,
+ std::vector<std::string>* info) {
+ prolog_(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
+ size / 1024));
+ auto result = Download(s, use_crc, response, info);
+ epilog_(result);
+ return result;
+}
+
RetCode FastBootDriver::Download(sparse_file* s, bool use_crc, std::string* response,
std::vector<std::string>* info) {
error_ = "";
@@ -262,9 +282,17 @@
RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,
std::vector<std::string>* info) {
+ prolog_("Uploading '" + outfile + "'");
+ auto result = UploadInner(outfile, response, info);
+ epilog_(result);
+ return result;
+}
+
+RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
+ std::vector<std::string>* info) {
RetCode ret;
int dsize;
- if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize))) {
+ if ((ret = RawCommand(FB_CMD_UPLOAD, response, info, &dsize))) {
error_ = "Upload request failed: " + error_;
return ret;
}
@@ -298,8 +326,8 @@
}
// Helpers
-void FastBootDriver::SetInfoCallback(std::function<void(std::string&)> info) {
- info_cb_ = info;
+void FastBootDriver::SetInfoCallback(std::function<void(const std::string&)> info) {
+ info_ = info;
}
const std::string FastBootDriver::RCString(RetCode rc) {
@@ -336,6 +364,15 @@
}
/****************************** PROTECTED *************************************/
+RetCode FastBootDriver::RawCommand(const std::string& cmd, const std::string& message,
+ std::string* response, std::vector<std::string>* info,
+ int* dsize) {
+ prolog_(message);
+ auto result = RawCommand(cmd, response, info, dsize);
+ epilog_(result);
+ return result;
+}
+
RetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,
std::vector<std::string>* info, int* dsize) {
error_ = ""; // Clear any pending error
@@ -355,7 +392,7 @@
RetCode FastBootDriver::DownloadCommand(uint32_t size, std::string* response,
std::vector<std::string>* info) {
- std::string cmd(android::base::StringPrintf("%s%08" PRIx32, Commands::DOWNLOAD.c_str(), size));
+ std::string cmd(android::base::StringPrintf("%s:%08" PRIx32, FB_CMD_DOWNLOAD, size));
RetCode ret;
if ((ret = RawCommand(cmd, response, info))) {
return ret;
@@ -388,7 +425,7 @@
std::string input(status);
if (android::base::StartsWith(input, "INFO")) {
std::string tmp = input.substr(strlen("INFO"));
- info_cb_(tmp);
+ info_(tmp);
add_info(std::move(tmp));
} else if (android::base::StartsWith(input, "OKAY")) {
set_response(input.substr(strlen("OKAY")));
@@ -421,16 +458,6 @@
return android::base::StringPrintf("%s (%s)", msg.c_str(), strerror(errno));
}
-const std::string FastBootDriver::Commands::BOOT = "boot";
-const std::string FastBootDriver::Commands::CONTINUE = "continue";
-const std::string FastBootDriver::Commands::DOWNLOAD = "download:";
-const std::string FastBootDriver::Commands::ERASE = "erase:";
-const std::string FastBootDriver::Commands::FLASH = "flash:";
-const std::string FastBootDriver::Commands::GET_VAR = "getvar:";
-const std::string FastBootDriver::Commands::REBOOT = "reboot";
-const std::string FastBootDriver::Commands::SET_ACTIVE = "set_active:";
-const std::string FastBootDriver::Commands::UPLOAD = "upload";
-
/******************************* PRIVATE **************************************/
RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
@@ -537,12 +564,9 @@
return 0;
}
-void FastBootDriver::set_transport(Transport* transport) {
- if (transport_) {
- transport_->Close();
- delete transport_;
- }
- transport_ = transport;
+Transport* FastBootDriver::set_transport(Transport* transport) {
+ std::swap(transport_, transport);
+ return transport;
}
} // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 4647945..62bbe52 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -55,6 +55,12 @@
TIMEOUT,
};
+struct DriverCallbacks {
+ std::function<void(const std::string&)> prolog = [](const std::string&) {};
+ std::function<void(int)> epilog = [](int) {};
+ std::function<void(const std::string&)> info = [](const std::string&) {};
+};
+
class FastBootDriver {
friend class FastBootTest;
@@ -63,25 +69,30 @@
static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
- FastBootDriver(Transport* transport,
- std::function<void(std::string&)> info = [](std::string&) {},
+ FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks = {},
bool no_checks = false);
~FastBootDriver();
RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+ RetCode CreatePartition(const std::string& partition, const std::string& size);
+ RetCode DeletePartition(const std::string& partition);
+ RetCode Download(const std::string& name, int fd, size_t size, std::string* response = nullptr,
+ std::vector<std::string>* info = nullptr);
RetCode Download(int fd, size_t size, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
+ RetCode Download(const std::string& name, const std::vector<char>& buf,
+ std::string* response = nullptr, std::vector<std::string>* info = nullptr);
RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
- // This will be removed after fastboot is modified to use a vector
- RetCode Download(const char* buf, uint32_t size, std::string* response = nullptr,
+ RetCode Download(const std::string& partition, struct sparse_file* s, uint32_t sz,
+ size_t current, size_t total, bool use_crc, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
- RetCode Erase(const std::string& part, std::string* response = nullptr,
+ RetCode Erase(const std::string& partition, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
- RetCode Flash(const std::string& part, std::string* response = nullptr,
+ RetCode Flash(const std::string& partition, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode GetVar(const std::string& key, std::string* val,
std::vector<std::string>* info = nullptr);
@@ -89,31 +100,36 @@
RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
RetCode RebootTo(std::string target, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
- RetCode SetActive(const std::string& part, std::string* response = nullptr,
+ RetCode ResizePartition(const std::string& partition, const std::string& size);
+ RetCode SetActive(const std::string& slot, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode Upload(const std::string& outfile, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
/* HIGHER LEVEL COMMANDS -- Composed of the commands above */
- RetCode FlashPartition(const std::string& part, const std::vector<char>& data);
- RetCode FlashPartition(const std::string& part, int fd, uint32_t sz);
- RetCode FlashPartition(const std::string& part, sparse_file* s);
+ RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
+ RetCode FlashPartition(const std::string& partition, int fd, uint32_t sz);
+ RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,
+ size_t current, size_t total);
- RetCode Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts);
+ RetCode Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions);
RetCode Require(const std::string& var, const std::vector<std::string>& allowed, bool* reqmet,
bool invert = false);
/* HELPERS */
- void SetInfoCallback(std::function<void(std::string&)> info);
+ void SetInfoCallback(std::function<void(const std::string&)> info);
static const std::string RCString(RetCode rc);
std::string Error();
RetCode WaitForDisconnect();
- // Note: changing the transport will close and delete the existing one.
- void set_transport(Transport* transport);
+ // Note: set_transport will return the previous transport.
+ Transport* set_transport(Transport* transport);
Transport* transport() const { return transport_; }
- // This is temporarily public for engine.cpp
+ RetCode RawCommand(const std::string& cmd, const std::string& message,
+ std::string* response = nullptr, std::vector<std::string>* info = nullptr,
+ int* dsize = nullptr);
+
RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,
std::vector<std::string>* info = nullptr, int* dsize = nullptr);
@@ -125,19 +141,6 @@
std::string ErrnoStr(const std::string& msg);
- // More like a namespace...
- struct Commands {
- static const std::string BOOT;
- static const std::string CONTINUE;
- static const std::string DOWNLOAD;
- static const std::string ERASE;
- static const std::string FLASH;
- static const std::string GET_VAR;
- static const std::string REBOOT;
- static const std::string SET_ACTIVE;
- static const std::string UPLOAD;
- };
-
Transport* transport_;
private:
@@ -148,10 +151,15 @@
RetCode ReadBuffer(std::vector<char>& buf);
RetCode ReadBuffer(void* buf, size_t size);
+ RetCode UploadInner(const std::string& outfile, std::string* response = nullptr,
+ std::vector<std::string>* info = nullptr);
+
int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);
std::string error_;
- std::function<void(std::string&)> info_cb_;
+ std::function<void(const std::string&)> prolog_;
+ std::function<void(int)> epilog_;
+ std::function<void(const std::string&)> info_;
bool disable_checks_;
};
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 43201fa..9c3ab6e 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "engine.h"
+#include "fastboot.h"
#include <gtest/gtest.h>
@@ -59,3 +59,145 @@
EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.128.3"), "bad OS version");
EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.2.128"), "bad OS version");
}
+
+extern bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+ bool* invert, std::vector<std::string>* options);
+
+static void ParseRequirementLineTest(const std::string& line, const std::string& expected_name,
+ const std::string& expected_product, bool expected_invert,
+ const std::vector<std::string>& expected_options) {
+ std::string name;
+ std::string product;
+ bool invert;
+ std::vector<std::string> options;
+
+ EXPECT_TRUE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+
+ EXPECT_EQ(expected_name, name) << line;
+ EXPECT_EQ(expected_product, product) << line;
+ EXPECT_EQ(expected_invert, invert) << line;
+ EXPECT_EQ(expected_options, options) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineSuccesses) {
+ // Examples provided in the code + slight variations.
+ ParseRequirementLineTest("require product=alpha", "product", "", false, {"alpha"});
+ ParseRequirementLineTest("require product=alpha|beta|gamma", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("require version-bootloader=1234", "version-bootloader", "", false,
+ {"1234"});
+ ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul",
+ "version-bootloader", "gamma", false, {"istanbul"});
+ ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul|constantinople",
+ "version-bootloader", "gamma", false, {"istanbul", "constantinople"});
+ ParseRequirementLineTest("require partition-exists=vendor", "partition-exists", "", false,
+ {"vendor"});
+ ParseRequirementLineTest("reject product=alpha", "product", "", true, {"alpha"});
+ ParseRequirementLineTest("reject product=alpha|beta|gamma", "product", "", true,
+ {"alpha", "beta", "gamma"});
+
+ // Without any prefix, assume 'require'
+ ParseRequirementLineTest("product=alpha|beta|gamma", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ // Including if the variable name is otherwise a prefix keyword
+ ParseRequirementLineTest("require = alpha", "require", "", false, {"alpha"});
+ ParseRequirementLineTest("reject = alpha", "reject", "", false, {"alpha"});
+ ParseRequirementLineTest("require-for-product:gamma = alpha", "require-for-product:gamma", "",
+ false, {"alpha"});
+
+ // Extra spaces are allowed.
+ ParseRequirementLineTest("require product=alpha|beta|gamma", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("require product =alpha|beta|gamma", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("require product= alpha|beta|gamma", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("require product = alpha|beta|gamma", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("require product=alpha |beta|gamma", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("require product=alpha| beta|gamma", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("require product=alpha | beta|gamma", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("require product=alpha|beta|gamma ", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("product = alpha | beta | gamma ", "product", "", false,
+ {"alpha", "beta", "gamma"});
+ ParseRequirementLineTest("require-for-product: gamma version-bootloader=istanbul",
+ "version-bootloader", "gamma", false, {"istanbul"});
+
+ // Extraneous ending | is okay, implies accepting an empty string.
+ ParseRequirementLineTest("require product=alpha|", "product", "", false, {"alpha", ""});
+ ParseRequirementLineTest("require product=alpha|beta|gamma|", "product", "", false,
+ {"alpha", "beta", "gamma", ""});
+
+ // Accept empty options, double ||, etc, implies accepting an empty string.
+ ParseRequirementLineTest("require product=alpha||beta| |gamma", "product", "", false,
+ {"alpha", "", "beta", "", "gamma"});
+ ParseRequirementLineTest("require product=alpha||beta|gamma", "product", "", false,
+ {"alpha", "", "beta", "gamma"});
+ ParseRequirementLineTest("require product=alpha|beta| |gamma", "product", "", false,
+ {"alpha", "beta", "", "gamma"});
+ ParseRequirementLineTest("require product=alpha||", "product", "", false, {"alpha", "", ""});
+ ParseRequirementLineTest("require product=alpha|| ", "product", "", false, {"alpha", "", ""});
+ ParseRequirementLineTest("require product=alpha| ", "product", "", false, {"alpha", ""});
+ ParseRequirementLineTest("require product=alpha|beta| ", "product", "", false,
+ {"alpha", "beta", ""});
+
+ // No option string is also treating as accepting an empty string.
+ ParseRequirementLineTest("require =", "require", "", false, {""});
+ ParseRequirementLineTest("require = |", "require", "", false, {"", ""});
+ ParseRequirementLineTest("reject =", "reject", "", false, {""});
+ ParseRequirementLineTest("reject = |", "reject", "", false, {"", ""});
+ ParseRequirementLineTest("require-for-product: =", "require-for-product:", "", false, {""});
+ ParseRequirementLineTest("require-for-product: = | ", "require-for-product:", "", false,
+ {"", ""});
+ ParseRequirementLineTest("require product=", "product", "", false, {""});
+ ParseRequirementLineTest("require product = ", "product", "", false, {""});
+ ParseRequirementLineTest("require product = | ", "product", "", false, {"", ""});
+ ParseRequirementLineTest("reject product=", "product", "", true, {""});
+ ParseRequirementLineTest("reject product = ", "product", "", true, {""});
+ ParseRequirementLineTest("reject product = | ", "product", "", true, {"", ""});
+ ParseRequirementLineTest("require-for-product:gamma product=", "product", "gamma", false, {""});
+ ParseRequirementLineTest("require-for-product:gamma product = ", "product", "gamma", false,
+ {""});
+ ParseRequirementLineTest("require-for-product:gamma product = |", "product", "gamma", false,
+ {"", ""});
+
+ // Check for board -> product substitution.
+ ParseRequirementLineTest("require board=alpha", "product", "", false, {"alpha"});
+ ParseRequirementLineTest("board=alpha", "product", "", false, {"alpha"});
+}
+
+static void ParseRequirementLineTestMalformed(const std::string& line) {
+ std::string name;
+ std::string product;
+ bool invert;
+ std::vector<std::string> options;
+
+ EXPECT_FALSE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineMalformed) {
+ ParseRequirementLineTestMalformed("nothing");
+ ParseRequirementLineTestMalformed("");
+ ParseRequirementLineTestMalformed("=");
+ ParseRequirementLineTestMalformed("|");
+
+ ParseRequirementLineTestMalformed("require");
+ ParseRequirementLineTestMalformed("require ");
+ ParseRequirementLineTestMalformed("reject");
+ ParseRequirementLineTestMalformed("reject ");
+ ParseRequirementLineTestMalformed("require-for-product:");
+ ParseRequirementLineTestMalformed("require-for-product: ");
+
+ ParseRequirementLineTestMalformed("require product");
+ ParseRequirementLineTestMalformed("reject product");
+
+ ParseRequirementLineTestMalformed("require-for-product:gamma");
+ ParseRequirementLineTestMalformed("require-for-product:gamma product");
+
+ // No spaces allowed before between require-for-product and :.
+ ParseRequirementLineTestMalformed("require-for-product :");
+}
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 0a87598..c18af1d 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -121,8 +121,7 @@
} else {
ASSERT_EQ(device_path, cb_scratch); // The path can not change
}
- fb = std::unique_ptr<FastBootDriver>(
- new FastBootDriver(transport.get(), [](std::string&) {}, true));
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
}
void FastBootTest::TearDown() {
@@ -133,7 +132,6 @@
fb.reset();
if (transport) {
- transport->Close();
transport.reset();
}
@@ -188,7 +186,6 @@
ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
<< "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
fb.reset();
- transport->Close();
transport.reset();
printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
while (UsbStillAvailible())
@@ -206,8 +203,7 @@
putchar('.');
}
device_path = cb_scratch;
- fb = std::unique_ptr<FastBootDriver>(
- new FastBootDriver(transport.get(), [](std::string&) {}, true));
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
if (assert_change) {
ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
ASSERT_EQ(resp, unlock ? "yes" : "no")
@@ -249,7 +245,6 @@
}
if (transport) {
- transport->Close();
transport.reset();
}
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 1d30f8b..90a2e74 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -304,13 +304,13 @@
}
TEST_F(Conformance, PartitionInfo) {
- std::vector<std::tuple<std::string, uint32_t>> parts;
+ std::vector<std::tuple<std::string, uint64_t>> parts;
EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
EXPECT_GT(parts.size(), 0)
<< "getvar:all did not report any partition-size: through INFO responses";
std::set<std::string> allowed{"ext4", "f2fs", "raw"};
for (const auto p : parts) {
- EXPECT_GT(std::get<1>(p), 0);
+ EXPECT_GE(std::get<1>(p), 0);
std::string part(std::get<0>(p));
std::set<std::string> allowed{"ext4", "f2fs", "raw"};
std::string resp;
@@ -340,7 +340,7 @@
// Can't run out of alphabet letters...
ASSERT_LE(num_slots, 26) << "What?! You can't have more than 26 slots";
- std::vector<std::tuple<std::string, uint32_t>> parts;
+ std::vector<std::tuple<std::string, uint64_t>> parts;
EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
std::map<std::string, std::set<char>> part_slots;
@@ -441,9 +441,6 @@
ASSERT_TRUE(*sparse) << "Sparse file creation failed on: " << bs;
EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
- EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
- << "Download sparse with crc failed: " << sparse.Rep();
- EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
}
}
@@ -462,9 +459,6 @@
<< "Adding data failed to sparse file: " << sparse.Rep();
EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
- EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
- << "Download sparse with crc failed: " << sparse.Rep();
- EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
}
}
@@ -474,9 +468,6 @@
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
- EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
- << "Download sparse with crc failed: " << sparse.Rep();
- EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
}
TEST_F(Conformance, SparseDownload1) {
@@ -487,9 +478,6 @@
<< "Adding data failed to sparse file: " << sparse.Rep();
EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
- EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
- << "Download sparse with crc failed: " << sparse.Rep();
- EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
}
TEST_F(Conformance, SparseDownload2) {
@@ -503,9 +491,6 @@
<< "Adding data failed to sparse file: " << sparse.Rep();
EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
- EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
- << "Download sparse with crc failed: " << sparse.Rep();
- EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
}
TEST_F(Conformance, SparseDownload3) {
@@ -537,9 +522,6 @@
}
EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
- EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
- << "Download sparse with crc failed: " << sparse.Rep();
- EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
}
TEST_F(Conformance, SparseVersionCheck) {
@@ -559,25 +541,6 @@
}
}
-TEST_F(Conformance, SparseCRCCheck) {
- SparseWrapper sparse(4096, 4096);
- ASSERT_TRUE(*sparse) << "Sparse image creation failed";
- std::vector<char> buf = RandomBuf(4096);
- ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
- << "Adding data failed to sparse file: " << sparse.Rep();
- ASSERT_TRUE(SparseToBuf(*sparse, &buf, true)) << "Sparse buffer creation failed";
- // Flip a bit in the crc
- buf.back() = buf.back() ^ 0x01;
- ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
- ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
- printf("%02x\n", (unsigned char)buf.back());
- // It can either reject this download or reject it during flash
- if (HandleResponse() != DEVICE_FAIL) {
- EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
- << "Flashing an invalid sparse version should fail " << sparse.Rep();
- }
-}
-
TEST_F(UnlockPermissions, Download) {
std::vector<char> buf{'a', 'o', 's', 'p'};
EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download 4-byte payload failed";
@@ -587,14 +550,14 @@
std::vector<char> buf{'a', 'o', 's', 'p'};
EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download failed in unlocked mode";
;
- std::vector<std::tuple<std::string, uint32_t>> parts;
+ std::vector<std::tuple<std::string, uint64_t>> parts;
EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in unlocked mode";
}
TEST_F(LockPermissions, DownloadFlash) {
std::vector<char> buf{'a', 'o', 's', 'p'};
EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download failed in locked mode";
- std::vector<std::tuple<std::string, uint32_t>> parts;
+ std::vector<std::tuple<std::string, uint64_t>> parts;
EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in locked mode";
std::string resp;
for (const auto tup : parts) {
@@ -607,7 +570,7 @@
}
TEST_F(LockPermissions, Erase) {
- std::vector<std::tuple<std::string, uint32_t>> parts;
+ std::vector<std::tuple<std::string, uint64_t>> parts;
EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
std::string resp;
for (const auto tup : parts) {
@@ -619,7 +582,7 @@
}
TEST_F(LockPermissions, SetActive) {
- std::vector<std::tuple<std::string, uint32_t>> parts;
+ std::vector<std::tuple<std::string, uint64_t>> parts;
EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
std::string resp;
@@ -916,7 +879,7 @@
TEST_P(AnyPartition, ReportedGetVarAll) {
// As long as the partition is reported in INFO, it would be tested by generic Conformance
- std::vector<std::tuple<std::string, uint32_t>> parts;
+ std::vector<std::tuple<std::string, uint64_t>> parts;
ASSERT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
const std::string name = GetParam().first;
if (GetParam().second.slots) {
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp b/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
index ee510e9..7c595f4 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
+++ b/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
@@ -12,6 +12,10 @@
const int serial_fd)
: transport_(std::move(transport)), serial_fd_(serial_fd) {}
+UsbTransportSniffer::~UsbTransportSniffer() {
+ Close();
+}
+
ssize_t UsbTransportSniffer::Read(void* data, size_t len) {
ProcessSerial();
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h b/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
index 693f042..8119aea 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
+++ b/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
@@ -68,10 +68,11 @@
};
UsbTransportSniffer(std::unique_ptr<UsbTransport> transport, const int serial_fd = 0);
+ ~UsbTransportSniffer() override;
virtual ssize_t Read(void* data, size_t len) override;
virtual ssize_t Write(const void* data, size_t len) override;
- virtual int Close() override;
+ virtual int Close() override final; // note usage in destructor
virtual int Reset() override;
const std::vector<Event> Transfers();
diff --git a/fastboot/main.cpp b/fastboot/main.cpp
index c3683f7..35f4218 100644
--- a/fastboot/main.cpp
+++ b/fastboot/main.cpp
@@ -26,7 +26,7 @@
* SUCH DAMAGE.
*/
-#include "engine.h"
+#include "fastboot.h"
int main(int argc, char* argv[]) {
FastBootTool fb;
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 9b779dd..6363aa5 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -95,7 +95,7 @@
public:
explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
: handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
- ~LinuxUsbTransport() override = default;
+ ~LinuxUsbTransport() override;
ssize_t Read(void* data, size_t len) override;
ssize_t Write(const void* data, size_t len) override;
@@ -387,6 +387,10 @@
return usb;
}
+LinuxUsbTransport::~LinuxUsbTransport() {
+ Close();
+}
+
ssize_t LinuxUsbTransport::Write(const void* _data, size_t len)
{
unsigned char *data = (unsigned char*) _data;
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 4d48f6e..ed02c4a 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -70,7 +70,7 @@
// A timeout of 0 is blocking
OsxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
: handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
- ~OsxUsbTransport() override = default;
+ ~OsxUsbTransport() override;
ssize_t Read(void* data, size_t len) override;
ssize_t Write(const void* data, size_t len) override;
@@ -471,6 +471,10 @@
return new OsxUsbTransport(std::move(handle), timeout_ms);
}
+OsxUsbTransport::~OsxUsbTransport() {
+ Close();
+}
+
int OsxUsbTransport::Close() {
/* TODO: Something better here? */
return 0;
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 8c60a71..b00edb3 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -69,7 +69,7 @@
class WindowsUsbTransport : public UsbTransport {
public:
WindowsUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
- ~WindowsUsbTransport() override = default;
+ ~WindowsUsbTransport() override;
ssize_t Read(void* data, size_t len) override;
ssize_t Write(const void* data, size_t len) override;
@@ -250,6 +250,10 @@
}
}
+WindowsUsbTransport::~WindowsUsbTransport() {
+ Close();
+}
+
int WindowsUsbTransport::Close() {
DBG("usb_close\n");
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 8f6e52a..d02b37f 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -69,9 +69,3 @@
}
fprintf(stderr, "\n");
}
-
-char* xstrdup(const char* s) {
- char* result = strdup(s);
- if (!result) die("out of memory");
- return result;
-}
diff --git a/fastboot/util.h b/fastboot/util.h
index 9033f93..2535414 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -9,7 +9,6 @@
/* util stuff */
double now();
-char* xstrdup(const char*);
void set_verbose();
// These printf-like functions are implemented in terms of vsnprintf, so they
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index e3c2c2b..3ab9732 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -55,6 +55,7 @@
#include <ext4_utils/wipe.h>
#include <fs_mgr_overlayfs.h>
#include <libdm/dm.h>
+#include <liblp/metadata_format.h>
#include <linux/fs.h>
#include <linux/loop.h>
#include <linux/magic.h>
@@ -102,12 +103,16 @@
// TODO: switch to inotify()
bool fs_mgr_wait_for_file(const std::string& filename,
- const std::chrono::milliseconds relative_timeout) {
+ const std::chrono::milliseconds relative_timeout,
+ FileWaitMode file_wait_mode) {
auto start_time = std::chrono::steady_clock::now();
while (true) {
- if (!access(filename.c_str(), F_OK) || errno != ENOENT) {
- return true;
+ int rv = access(filename.c_str(), F_OK);
+ if (file_wait_mode == FileWaitMode::Exists) {
+ if (!rv || errno != ENOENT) return true;
+ } else if (file_wait_mode == FileWaitMode::DoesNotExist) {
+ if (rv && errno == ENOENT) return true;
}
std::this_thread::sleep_for(50ms);
@@ -525,8 +530,18 @@
errno = 0;
ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
save_errno = errno;
- PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
- << ",type=" << rec->fs_type << ")=" << ret;
+ const char* target_missing = "";
+ const char* source_missing = "";
+ if (save_errno == ENOENT) {
+ if (access(target, F_OK)) {
+ target_missing = "(missing)";
+ } else if (access(source, F_OK)) {
+ source_missing = "(missing)";
+ }
+ errno = save_errno;
+ }
+ PINFO << __FUNCTION__ << "(source=" << source << source_missing << ",target=" << target
+ << target_missing << ",type=" << rec->fs_type << ")=" << ret;
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
fs_mgr_set_blk_ro(source);
}
@@ -565,8 +580,7 @@
* -1 on failure with errno set to match the 1st mount failure.
* 0 on success.
*/
-static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
-{
+static int mount_with_alternatives(fstab* fstab, int start_idx, int* end_idx, int* attempted_idx) {
int i;
int mount_errno = 0;
int mounted = 0;
@@ -801,6 +815,23 @@
return true;
}
+static bool call_vdc_ret(const std::vector<std::string>& args, int* ret) {
+ std::vector<char const*> argv;
+ argv.emplace_back("/system/bin/vdc");
+ for (auto& arg : args) {
+ argv.emplace_back(arg.c_str());
+ }
+ LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
+ int err = android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), ret, false, true);
+ if (err != 0) {
+ LOG(ERROR) << "vdc call failed with error code: " << err;
+ return false;
+ }
+ LOG(DEBUG) << "vdc finished successfully";
+ *ret = WEXITSTATUS(*ret);
+ return true;
+}
+
bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
// Logical partitions are specified with a named partition rather than a
// block device, so if the block device is a path, then it has already
@@ -819,19 +850,70 @@
return true;
}
+bool fs_mgr_update_checkpoint_partition(struct fstab_rec* rec) {
+ if (fs_mgr_is_checkpoint_fs(rec)) {
+ if (!strcmp(rec->fs_type, "f2fs")) {
+ std::string opts(rec->fs_options);
+
+ opts += ",checkpoint=disable";
+ free(rec->fs_options);
+ rec->fs_options = strdup(opts.c_str());
+ } else {
+ LERROR << rec->fs_type << " does not implement checkpoints.";
+ }
+ } else if (fs_mgr_is_checkpoint_blk(rec)) {
+ call_vdc({"checkpoint", "restoreCheckpoint", rec->blk_device});
+
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(rec->blk_device, O_RDONLY | O_CLOEXEC)));
+ if (!fd) {
+ PERROR << "Cannot open device " << rec->blk_device;
+ return false;
+ }
+
+ uint64_t size = get_block_device_size(fd) / 512;
+ if (!size) {
+ PERROR << "Cannot get device size";
+ return false;
+ }
+
+ android::dm::DmTable table;
+ if (!table.AddTarget(
+ std::make_unique<android::dm::DmTargetBow>(0, size, rec->blk_device))) {
+ LERROR << "Failed to add Bow target";
+ return false;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.CreateDevice("bow", table)) {
+ PERROR << "Failed to create bow device";
+ return false;
+ }
+
+ std::string name;
+ if (!dm.GetDmDevicePathByName("bow", &name)) {
+ PERROR << "Failed to get bow device name";
+ return false;
+ }
+
+ rec->blk_device = strdup(name.c_str());
+ }
+ return true;
+}
+
/* When multiple fstab records share the same mount_point, it will
* try to mount each one in turn, and ignore any duplicates after a
* first successful mount.
* Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
*/
-int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
-{
+int fs_mgr_mount_all(fstab* fstab, int mount_mode) {
int i = 0;
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
int error_count = 0;
int mret = -1;
int mount_errno = 0;
int attempted_idx = -1;
+ int need_checkpoint = -1;
FsManagerAvbUniquePtr avb_handle(nullptr);
if (!fstab) {
@@ -878,6 +960,18 @@
}
}
+ if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
+ if (need_checkpoint == -1 &&
+ !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
+ LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+ need_checkpoint = 0;
+ }
+ if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
+ LERROR << "Could not set up checkpoint partition, skipping!";
+ continue;
+ }
+ }
+
if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
!fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
@@ -1041,7 +1135,7 @@
}
#if ALLOW_ADBD_DISABLE_VERITY == 1 // "userdebug" build
- fs_mgr_overlayfs_mount_all();
+ fs_mgr_overlayfs_mount_all(fstab);
#endif
if (error_count) {
@@ -1077,9 +1171,8 @@
* If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
* in turn, and stop on 1st success, or no more match.
*/
-int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
- char *tmp_mount_point)
-{
+static int fs_mgr_do_mount_helper(fstab* fstab, const char* n_name, char* n_blk_device,
+ char* tmp_mount_point, int need_checkpoint) {
int i = 0;
int mount_errors = 0;
int first_mount_errno = 0;
@@ -1112,6 +1205,18 @@
}
}
+ if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
+ if (need_checkpoint == -1 &&
+ !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
+ LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+ need_checkpoint = 0;
+ }
+ if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
+ LERROR << "Could not set up checkpoint partition, skipping!";
+ continue;
+ }
+ }
+
/* First check the filesystem if requested */
if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
LERROR << "Skipping mounting '" << n_blk_device << "'";
@@ -1181,6 +1286,15 @@
return FS_MGR_DOMNT_FAILED;
}
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
+ return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+}
+
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+ bool needs_cp) {
+ return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_cp);
+}
+
/*
* mount a tmpfs filesystem at the given point.
* return 0 on success, non-zero on failure.
@@ -1203,8 +1317,7 @@
/* This must be called after mount_all, because the mkswap command needs to be
* available.
*/
-int fs_mgr_swapon_all(struct fstab *fstab)
-{
+int fs_mgr_swapon_all(fstab* fstab) {
int i = 0;
int flags = 0;
int err = 0;
@@ -1231,29 +1344,25 @@
* on a system (all the memory comes from the same pool) so
* we can assume the device number is 0.
*/
- FILE *zram_fp;
- FILE *zram_mcs_fp;
-
if (fstab->recs[i].max_comp_streams >= 0) {
- zram_mcs_fp = fopen(ZRAM_CONF_MCS, "r+");
- if (zram_mcs_fp == NULL) {
- LERROR << "Unable to open zram conf comp device "
- << ZRAM_CONF_MCS;
- ret = -1;
- continue;
- }
- fprintf(zram_mcs_fp, "%d\n", fstab->recs[i].max_comp_streams);
- fclose(zram_mcs_fp);
+ auto zram_mcs_fp = std::unique_ptr<FILE, decltype(&fclose)>{
+ fopen(ZRAM_CONF_MCS, "re"), fclose};
+ if (zram_mcs_fp == NULL) {
+ LERROR << "Unable to open zram conf comp device " << ZRAM_CONF_MCS;
+ ret = -1;
+ continue;
+ }
+ fprintf(zram_mcs_fp.get(), "%d\n", fstab->recs[i].max_comp_streams);
}
- zram_fp = fopen(ZRAM_CONF_DEV, "r+");
+ auto zram_fp =
+ std::unique_ptr<FILE, decltype(&fclose)>{fopen(ZRAM_CONF_DEV, "re+"), fclose};
if (zram_fp == NULL) {
LERROR << "Unable to open zram conf device " << ZRAM_CONF_DEV;
ret = -1;
continue;
}
- fprintf(zram_fp, "%u\n", fstab->recs[i].zram_size);
- fclose(zram_fp);
+ fprintf(zram_fp.get(), "%u\n", fstab->recs[i].zram_size);
}
if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
@@ -1294,7 +1403,7 @@
return ret;
}
-struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab) {
+struct fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab) {
int i;
if (!fstab) {
@@ -1318,7 +1427,7 @@
*
* real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
*/
-void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
+void fs_mgr_get_crypt_info(fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
struct fstab_rec const* rec = fs_mgr_get_crypt_entry(fstab);
if (key_loc) {
if (rec) {
@@ -1432,3 +1541,7 @@
return true;
}
+
+std::string fs_mgr_get_super_partition_name(int /* slot */) {
+ return LP_METADATA_DEFAULT_PARTITION_NAME;
+}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index a91e92e..804069a 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -81,7 +81,7 @@
static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
const LpMetadataPartition& partition, bool force_writable,
- std::string* path) {
+ const std::chrono::milliseconds& timeout_ms, std::string* path) {
DeviceMapper& dm = DeviceMapper::Instance();
DmTable table;
@@ -98,6 +98,13 @@
if (!dm.GetDmDevicePathByName(name, path)) {
return false;
}
+ if (timeout_ms > std::chrono::milliseconds::zero()) {
+ if (!fs_mgr_wait_for_file(*path, timeout_ms, FileWaitMode::Exists)) {
+ DestroyLogicalPartition(name, {});
+ LERROR << "Timed out waiting for device path: " << *path;
+ return false;
+ }
+ }
LINFO << "Created logical partition " << name << " on device " << *path;
return true;
}
@@ -115,7 +122,7 @@
continue;
}
std::string path;
- if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, &path)) {
+ if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, {}, &path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
@@ -125,7 +132,7 @@
bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
const std::string& partition_name, bool force_writable,
- std::string* path) {
+ const std::chrono::milliseconds& timeout_ms, std::string* path) {
auto metadata = ReadMetadata(block_device.c_str(), metadata_slot);
if (!metadata) {
LOG(ERROR) << "Could not read partition table.";
@@ -134,18 +141,26 @@
for (const auto& partition : metadata->partitions) {
if (GetPartitionName(partition) == partition_name) {
return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable,
- path);
+ timeout_ms, path);
}
}
LERROR << "Could not find any partition with name: " << partition_name;
return false;
}
-bool DestroyLogicalPartition(const std::string& name) {
+bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
DeviceMapper& dm = DeviceMapper::Instance();
+ std::string path;
+ if (timeout_ms > std::chrono::milliseconds::zero()) {
+ dm.GetDmDevicePathByName(name, &path);
+ }
if (!dm.DeleteDevice(name)) {
return false;
}
+ if (!path.empty() && !fs_mgr_wait_for_file(path, timeout_ms, FileWaitMode::DoesNotExist)) {
+ LERROR << "Timed out waiting for device path to unlink: " << path;
+ return false;
+ }
LINFO << "Unmapped logical partition " << name;
return true;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f87a3b1..fc3a05c 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -80,37 +80,39 @@
};
static struct flag_list fs_mgr_flags[] = {
- {"wait", MF_WAIT},
- {"check", MF_CHECK},
- {"encryptable=", MF_CRYPT},
- {"forceencrypt=", MF_FORCECRYPT},
- {"fileencryption=", MF_FILEENCRYPTION},
- {"forcefdeorfbe=", MF_FORCEFDEORFBE},
- {"keydirectory=", MF_KEYDIRECTORY},
- {"nonremovable", MF_NONREMOVABLE},
- {"voldmanaged=", MF_VOLDMANAGED},
- {"length=", MF_LENGTH},
- {"recoveryonly", MF_RECOVERYONLY},
- {"swapprio=", MF_SWAPPRIO},
- {"zramsize=", MF_ZRAMSIZE},
- {"max_comp_streams=", MF_MAX_COMP_STREAMS},
- {"verifyatboot", MF_VERIFYATBOOT},
- {"verify", MF_VERIFY},
- {"avb", MF_AVB},
- {"noemulatedsd", MF_NOEMULATEDSD},
- {"notrim", MF_NOTRIM},
- {"formattable", MF_FORMATTABLE},
- {"slotselect", MF_SLOTSELECT},
- {"nofail", MF_NOFAIL},
- {"latemount", MF_LATEMOUNT},
- {"reservedsize=", MF_RESERVEDSIZE},
- {"quota", MF_QUOTA},
- {"eraseblk=", MF_ERASEBLKSIZE},
- {"logicalblk=", MF_LOGICALBLKSIZE},
- {"sysfs_path=", MF_SYSFS},
- {"defaults", 0},
- {"logical", MF_LOGICAL},
- {0, 0},
+ {"wait", MF_WAIT},
+ {"check", MF_CHECK},
+ {"encryptable=", MF_CRYPT},
+ {"forceencrypt=", MF_FORCECRYPT},
+ {"fileencryption=", MF_FILEENCRYPTION},
+ {"forcefdeorfbe=", MF_FORCEFDEORFBE},
+ {"keydirectory=", MF_KEYDIRECTORY},
+ {"nonremovable", MF_NONREMOVABLE},
+ {"voldmanaged=", MF_VOLDMANAGED},
+ {"length=", MF_LENGTH},
+ {"recoveryonly", MF_RECOVERYONLY},
+ {"swapprio=", MF_SWAPPRIO},
+ {"zramsize=", MF_ZRAMSIZE},
+ {"max_comp_streams=", MF_MAX_COMP_STREAMS},
+ {"verifyatboot", MF_VERIFYATBOOT},
+ {"verify", MF_VERIFY},
+ {"avb", MF_AVB},
+ {"noemulatedsd", MF_NOEMULATEDSD},
+ {"notrim", MF_NOTRIM},
+ {"formattable", MF_FORMATTABLE},
+ {"slotselect", MF_SLOTSELECT},
+ {"nofail", MF_NOFAIL},
+ {"latemount", MF_LATEMOUNT},
+ {"reservedsize=", MF_RESERVEDSIZE},
+ {"quota", MF_QUOTA},
+ {"eraseblk=", MF_ERASEBLKSIZE},
+ {"logicalblk=", MF_LOGICALBLKSIZE},
+ {"sysfs_path=", MF_SYSFS},
+ {"defaults", 0},
+ {"logical", MF_LOGICAL},
+ {"checkpoint=block", MF_CHECKPOINT_BLK},
+ {"checkpoint=fs", MF_CHECKPOINT_FS},
+ {0, 0},
};
#define EM_AES_256_XTS 1
@@ -730,21 +732,19 @@
struct fstab *fs_mgr_read_fstab(const char *fstab_path)
{
- FILE *fstab_file;
struct fstab *fstab;
- fstab_file = fopen(fstab_path, "r");
+ auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fstab_path, "re"), fclose};
if (!fstab_file) {
PERROR << __FUNCTION__<< "(): cannot open file: '" << fstab_path << "'";
return nullptr;
}
- fstab = fs_mgr_read_fstab_file(fstab_file, !strcmp("/proc/mounts", fstab_path));
+ fstab = fs_mgr_read_fstab_file(fstab_file.get(), !strcmp("/proc/mounts", fstab_path));
if (!fstab) {
LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
}
- fclose(fstab_file);
return fstab;
}
@@ -1004,3 +1004,15 @@
int fs_mgr_is_logical(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_LOGICAL;
}
+
+int fs_mgr_is_checkpoint(const struct fstab_rec* fstab) {
+ return fstab->fs_mgr_flags & (MF_CHECKPOINT_FS | MF_CHECKPOINT_BLK);
+}
+
+int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab) {
+ return fstab->fs_mgr_flags & MF_CHECKPOINT_FS;
+}
+
+int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab) {
+ return fstab->fs_mgr_flags & MF_CHECKPOINT_BLK;
+}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 720dcfd..1edd573 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -49,10 +49,14 @@
#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
-bool fs_mgr_overlayfs_mount_all() {
+bool fs_mgr_overlayfs_mount_all(const fstab*) {
return false;
}
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab*) {
+ return {};
+}
+
bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change) {
if (change) *change = false;
return false;
@@ -109,9 +113,9 @@
struct statvfs vst;
if (statvfs(mount_point, &vst)) return true;
- static constexpr int percent = 1; // 1%
+ static constexpr int kPercentThreshold = 1; // 1%
- return (vst.f_bfree >= (vst.f_blocks * percent / 100));
+ return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
}
bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
@@ -123,32 +127,32 @@
!fs_mgr_filesystem_has_space(fsrec->mount_point);
}
-constexpr char upper_name[] = "upper";
-constexpr char work_name[] = "work";
+const auto kUpperName = "upper"s;
+const auto kWorkName = "work"s;
+const auto kOverlayTopDir = "/overlay"s;
std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
if (!fs_mgr_is_dir(mount_point)) return "";
- auto dir = kOverlayMountPoint + "/overlay/" + android::base::Basename(mount_point) + "/";
- auto upper = dir + upper_name;
+ auto dir =
+ kOverlayMountPoint + kOverlayTopDir + "/" + android::base::Basename(mount_point) + "/";
+ auto upper = dir + kUpperName;
if (!fs_mgr_is_dir(upper)) return "";
- auto work = dir + work_name;
+ auto work = dir + kWorkName;
if (!fs_mgr_is_dir(work)) return "";
if (!fs_mgr_dir_is_writable(work)) return "";
return dir;
}
-constexpr char lowerdir_option[] = "lowerdir=";
-constexpr char upperdir_option[] = "upperdir=";
+const auto kLowerdirOption = "lowerdir="s;
+const auto kUpperdirOption = "upperdir="s;
// default options for mount_point, returns empty string for none available.
std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
if (candidate.empty()) return "";
- auto context = fs_mgr_get_context(mount_point);
- if (!context.empty()) context = ",rootcontext="s + context;
- return "override_creds=off,"s + lowerdir_option + mount_point + "," + upperdir_option +
- candidate + upper_name + ",workdir=" + candidate + work_name + context;
+ return "override_creds=off,"s + kLowerdirOption + mount_point + "," + kUpperdirOption +
+ candidate + kUpperName + ",workdir=" + candidate + kWorkName;
}
bool fs_mgr_system_root_image(const fstab* fstab) {
@@ -192,6 +196,40 @@
return overlayfs_in_kernel;
}
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
+ fs_mgr_free_fstab);
+ if (!fstab) return false;
+ const auto lowerdir = kLowerdirOption + mount_point;
+ for (auto i = 0; i < fstab->num_entries; ++i) {
+ const auto fsrec = &fstab->recs[i];
+ const auto fs_type = fsrec->fs_type;
+ if (!fs_type) continue;
+ if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
+ auto fsrec_mount_point = fsrec->mount_point;
+ if (!fsrec_mount_point) continue;
+ if (mount_point != fsrec_mount_point) continue;
+ const auto fs_options = fsrec->fs_options;
+ if (!fs_options) continue;
+ const auto options = android::base::Split(fs_options, ",");
+ for (const auto& opt : options) {
+ if (opt == lowerdir) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool fs_mgr_overlayfs_verity_enabled(const std::string& basename_mount_point) {
+ auto found = false;
+ fs_mgr_update_verity_state(
+ [&basename_mount_point, &found](fstab_rec*, const char* mount_point, int, int) {
+ if (mount_point && (basename_mount_point == mount_point)) found = true;
+ });
+ return found;
+}
+
bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
if (!fsrec) return false;
@@ -215,14 +253,7 @@
if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
- // Verity enabled?
- const auto basename_mount_point(android::base::Basename(fsrec_mount_point));
- auto found = false;
- fs_mgr_update_verity_state(
- [&basename_mount_point, &found](fstab_rec*, const char* mount_point, int, int) {
- if (mount_point && (basename_mount_point == mount_point)) found = true;
- });
- return !found;
+ return !fs_mgr_overlayfs_verity_enabled(android::base::Basename(fsrec_mount_point));
}
bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr) {
@@ -233,7 +264,7 @@
errno = save_errno;
return true;
}
- PERROR << "overlayfs open " << path;
+ PERROR << "opendir " << path;
return false;
}
dirent* entry;
@@ -251,7 +282,7 @@
if (change) *change = true;
} else {
ret = false;
- PERROR << "overlayfs rmdir " << file;
+ PERROR << "rmdir " << file;
}
continue;
}
@@ -259,39 +290,61 @@
if (change) *change = true;
} else {
ret = false;
- PERROR << "overlayfs rm " << file;
+ PERROR << "rm " << file;
}
}
return ret;
}
-constexpr char overlayfs_file_context[] = "u:object_r:overlayfs_file:s0";
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
+
+bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
+ auto ret = true;
+ auto top = dir + kOverlayTopDir;
+ if (setfscreatecon(kOverlayfsFileContext)) {
+ ret = false;
+ PERROR << "setfscreatecon " << kOverlayfsFileContext;
+ }
+ auto save_errno = errno;
+ if (!mkdir(top.c_str(), 0755)) {
+ if (change) *change = true;
+ } else if (errno != EEXIST) {
+ ret = false;
+ PERROR << "mkdir " << top;
+ } else {
+ errno = save_errno;
+ }
+ setfscreatecon(nullptr);
+
+ if (overlay) *overlay = std::move(top);
+ return ret;
+}
bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
bool* change) {
auto ret = true;
- auto fsrec_mount_point = overlay + android::base::Basename(mount_point) + "/";
+ auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
- if (setfscreatecon(overlayfs_file_context)) {
+ if (setfscreatecon(kOverlayfsFileContext)) {
ret = false;
- PERROR << "overlayfs setfscreatecon " << overlayfs_file_context;
+ PERROR << "setfscreatecon " << kOverlayfsFileContext;
}
auto save_errno = errno;
if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
if (change) *change = true;
} else if (errno != EEXIST) {
ret = false;
- PERROR << "overlayfs mkdir " << fsrec_mount_point;
+ PERROR << "mkdir " << fsrec_mount_point;
} else {
errno = save_errno;
}
save_errno = errno;
- if (!mkdir((fsrec_mount_point + work_name).c_str(), 0755)) {
+ if (!mkdir((fsrec_mount_point + kWorkName).c_str(), 0755)) {
if (change) *change = true;
} else if (errno != EEXIST) {
ret = false;
- PERROR << "overlayfs mkdir " << fsrec_mount_point << work_name;
+ PERROR << "mkdir " << fsrec_mount_point << kWorkName;
} else {
errno = save_errno;
}
@@ -300,15 +353,15 @@
auto new_context = fs_mgr_get_context(mount_point);
if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
ret = false;
- PERROR << "overlayfs setfscreatecon " << new_context;
+ PERROR << "setfscreatecon " << new_context;
}
- auto upper = fsrec_mount_point + upper_name;
+ auto upper = fsrec_mount_point + kUpperName;
save_errno = errno;
if (!mkdir(upper.c_str(), 0755)) {
if (change) *change = true;
} else if (errno != EEXIST) {
ret = false;
- PERROR << "overlayfs mkdir " << upper;
+ PERROR << "mkdir " << upper;
} else {
errno = save_errno;
}
@@ -317,6 +370,50 @@
return ret;
}
+bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
+ bool* change) {
+ const auto top = overlay + kOverlayTopDir;
+ auto save_errno = errno;
+ auto missing = access(top.c_str(), F_OK);
+ errno = save_errno;
+ if (missing) return false;
+
+ const auto oldpath = top + (mount_point.empty() ? "" : ("/"s + mount_point));
+ const auto newpath = oldpath + ".teardown";
+ auto ret = fs_mgr_rm_all(newpath);
+ save_errno = errno;
+ if (!rename(oldpath.c_str(), newpath.c_str())) {
+ if (change) *change = true;
+ } else if (errno != ENOENT) {
+ ret = false;
+ PERROR << "mv " << oldpath << " " << newpath;
+ } else {
+ errno = save_errno;
+ }
+ ret &= fs_mgr_rm_all(newpath, change);
+ save_errno = errno;
+ if (!rmdir(newpath.c_str())) {
+ if (change) *change = true;
+ } else if (errno != ENOENT) {
+ ret = false;
+ PERROR << "rmdir " << newpath;
+ } else {
+ errno = save_errno;
+ }
+ if (!mount_point.empty()) {
+ save_errno = errno;
+ if (!rmdir(top.c_str())) {
+ if (change) *change = true;
+ } else if ((errno != ENOENT) && (errno != ENOTEMPTY)) {
+ ret = false;
+ PERROR << "rmdir " << top;
+ } else {
+ errno = save_errno;
+ }
+ }
+ return ret;
+}
+
bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
auto options = fs_mgr_get_overlayfs_options(mount_point);
if (options.empty()) return false;
@@ -325,7 +422,7 @@
auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
const auto opt_list = android::base::Split(options, ",");
for (const auto opt : opt_list) {
- if (android::base::StartsWith(opt, upperdir_option)) {
+ if (android::base::StartsWith(opt, kUpperdirOption)) {
report = report + "," + opt;
break;
}
@@ -343,31 +440,6 @@
}
}
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
- std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(
- fs_mgr_read_fstab("/proc/mounts"), fs_mgr_free_fstab);
- if (!fstab) return false;
- const auto lowerdir = std::string(lowerdir_option) + mount_point;
- for (auto i = 0; i < fstab->num_entries; ++i) {
- const auto fsrec = &fstab->recs[i];
- const auto fs_type = fsrec->fs_type;
- if (!fs_type) continue;
- if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
- auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point) continue;
- if (mount_point != fsrec_mount_point) continue;
- const auto fs_options = fsrec->fs_options;
- if (!fs_options) continue;
- const auto options = android::base::Split(fs_options, ",");
- for (const auto opt : options) {
- if (opt == lowerdir) {
- return true;
- }
- }
- }
- return false;
-}
-
std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
const char* mount_point = nullptr) {
std::vector<std::string> mounts;
@@ -393,27 +465,38 @@
}
if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
}
+ // if not itemized /system or /, system as root, fake up
+ // fs_mgr_wants_overlayfs evaluation of /system as candidate.
+
+ if ((std::find(mounts.begin(), mounts.end(), "/system") == mounts.end()) &&
+ !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/") &&
+ !fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/system") &&
+ !fs_mgr_overlayfs_verity_enabled("system")) {
+ mounts.emplace_back("/system");
+ }
return mounts;
}
} // namespace
-bool fs_mgr_overlayfs_mount_all() {
+bool fs_mgr_overlayfs_mount_all(const fstab* fstab) {
auto ret = false;
if (!fs_mgr_wants_overlayfs()) return ret;
- std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
if (!fstab) return ret;
- for (const auto& mount_point : fs_mgr_candidate_list(fstab.get())) {
+ for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
}
return ret;
}
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab*) {
+ return {};
+}
+
// Returns false if setup not permitted, errno set to last error.
// If something is altered, set *change.
bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
@@ -426,29 +509,19 @@
if (!fs_mgr_wants_overlayfs()) return ret;
if (!fs_mgr_boot_completed()) {
errno = EBUSY;
- PERROR << "overlayfs setup";
+ PERROR << "setup";
return ret;
}
- std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+ fs_mgr_free_fstab);
if (fstab && !fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(fstab.get(), mount_point));
if (fstab && mounts.empty()) return ret;
- if (setfscreatecon(overlayfs_file_context)) {
- PERROR << "overlayfs setfscreatecon " << overlayfs_file_context;
- }
- auto overlay = kOverlayMountPoint + "/overlay/";
- auto save_errno = errno;
- if (!mkdir(overlay.c_str(), 0755)) {
- if (change) *change = true;
- } else if (errno != EEXIST) {
- PERROR << "overlayfs mkdir " << overlay;
- } else {
- errno = save_errno;
- }
- setfscreatecon(nullptr);
+ std::string overlay;
+ ret |= fs_mgr_overlayfs_setup_dir(kOverlayMountPoint, &overlay, change);
+
if (!fstab && mount_point && fs_mgr_overlayfs_setup_one(overlay, mount_point, change)) {
ret = true;
}
@@ -462,45 +535,11 @@
// If something is altered, set *change.
bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
if (change) *change = false;
- mount_point = fs_mgr_mount_point(std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)>(
+ mount_point = fs_mgr_mount_point(std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>(
fs_mgr_read_fstab_default(), fs_mgr_free_fstab)
.get(),
mount_point);
- auto ret = true;
- const auto overlay = kOverlayMountPoint + "/overlay";
- const auto oldpath = overlay + (mount_point ?: "");
- const auto newpath = oldpath + ".teardown";
- ret &= fs_mgr_rm_all(newpath);
- auto save_errno = errno;
- if (!rename(oldpath.c_str(), newpath.c_str())) {
- if (change) *change = true;
- } else if (errno != ENOENT) {
- ret = false;
- PERROR << "overlayfs mv " << oldpath << " " << newpath;
- } else {
- errno = save_errno;
- }
- ret &= fs_mgr_rm_all(newpath, change);
- save_errno = errno;
- if (!rmdir(newpath.c_str())) {
- if (change) *change = true;
- } else if (errno != ENOENT) {
- ret = false;
- PERROR << "overlayfs rmdir " << newpath;
- } else {
- errno = save_errno;
- }
- if (mount_point) {
- save_errno = errno;
- if (!rmdir(overlay.c_str())) {
- if (change) *change = true;
- } else if ((errno != ENOENT) && (errno != ENOTEMPTY)) {
- ret = false;
- PERROR << "overlayfs rmdir " << overlay;
- } else {
- errno = save_errno;
- }
- }
+ auto ret = fs_mgr_overlayfs_teardown_one(kOverlayMountPoint, mount_point ?: "", change);
if (!fs_mgr_wants_overlayfs()) {
// After obligatory teardown to make sure everything is clean, but if
// we didn't want overlayfs in the the first place, we do not want to
@@ -511,7 +550,7 @@
// caller that there may still be more to do.
if (!fs_mgr_boot_completed()) {
errno = EBUSY;
- PERROR << "overlayfs teardown";
+ PERROR << "teardown";
ret = false;
}
return ret;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index a347faf..506e81d 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -113,15 +113,21 @@
#define MF_KEYDIRECTORY 0X4000000
#define MF_SYSFS 0X8000000
#define MF_LOGICAL 0x10000000
+#define MF_CHECKPOINT_BLK 0x20000000
+#define MF_CHECKPOINT_FS 0x40000000
// clang-format on
#define DM_BUF_SIZE 4096
using namespace std::chrono_literals;
-int fs_mgr_set_blk_ro(const char *blockdev);
+enum class FileWaitMode { Exists, DoesNotExist };
+
bool fs_mgr_wait_for_file(const std::string& filename,
- const std::chrono::milliseconds relative_timeout);
+ const std::chrono::milliseconds relative_timeout,
+ FileWaitMode wait_mode = FileWaitMode::Exists);
+
+int fs_mgr_set_blk_ro(const char* blockdev);
bool fs_mgr_update_for_slotselect(struct fstab *fstab);
bool fs_mgr_is_device_unlocked();
const std::string& get_android_dt_dir();
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5fb4ebb..2727a6d 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -89,24 +89,21 @@
{
uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
- FILE* f = fopen(path, "r");
+ auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
if (!f) {
LERROR << "Can't open " << path;
- return NULL;
+ return nullptr;
}
- if (!fread(key_data, sizeof(key_data), 1, f)) {
+ if (!fread(key_data, sizeof(key_data), 1, f.get())) {
LERROR << "Could not read key!";
- fclose(f);
- return NULL;
+ return nullptr;
}
- fclose(f);
-
- RSA* key = NULL;
+ RSA* key = nullptr;
if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
LERROR << "Could not parse key!";
- return NULL;
+ return nullptr;
}
return key;
@@ -368,7 +365,6 @@
static int metadata_find(const char *fname, const char *stag,
unsigned int slength, off64_t *offset)
{
- FILE *fp = NULL;
char tag[METADATA_TAG_MAX_LENGTH + 1];
int rc = -1;
int n;
@@ -380,75 +376,64 @@
return -1;
}
- fp = fopen(fname, "r+");
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
if (!fp) {
PERROR << "Failed to open " << fname;
- goto out;
+ return -1;
}
/* check magic */
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fread(&magic, sizeof(magic), 1, fp) != 1) {
+ if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
PERROR << "Failed to read magic from " << fname;
- goto out;
+ return -1;
}
if (magic != METADATA_MAGIC) {
magic = METADATA_MAGIC;
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fwrite(&magic, sizeof(magic), 1, fp) != 1) {
+ if (fseek(fp.get(), start, SEEK_SET) < 0 ||
+ fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
PERROR << "Failed to write magic to " << fname;
- goto out;
+ return -1;
}
- rc = metadata_add(fp, start + sizeof(magic), stag, slength, offset);
+ rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
if (rc < 0) {
PERROR << "Failed to add metadata to " << fname;
}
- goto out;
+ return rc;
}
start += sizeof(magic);
while (1) {
- n = fscanf(fp, "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n",
- tag, &length);
+ n = fscanf(fp.get(), "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n", tag, &length);
if (n == 2 && strcmp(tag, METADATA_EOD)) {
/* found a tag */
- start = ftell(fp);
+ start = ftell(fp.get());
if (!strcmp(tag, stag) && length == slength) {
*offset = start;
- rc = 0;
- goto out;
+ return 0;
}
start += length;
- if (fseek(fp, length, SEEK_CUR) < 0) {
+ if (fseek(fp.get(), length, SEEK_CUR) < 0) {
PERROR << "Failed to seek " << fname;
- goto out;
+ return -1;
}
} else {
- rc = metadata_add(fp, start, stag, slength, offset);
+ rc = metadata_add(fp.get(), start, stag, slength, offset);
if (rc < 0) {
PERROR << "Failed to write metadata to " << fname;
}
- goto out;
+ return rc;
}
- }
-
-out:
- if (fp) {
- fflush(fp);
- fclose(fp);
}
-
- return rc;
}
static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 1049fb6..a4544b2 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -23,6 +23,7 @@
#include <linux/dm-ioctl.h>
#include <functional>
+#include <string>
#include <fstab/fstab.h>
@@ -70,6 +71,8 @@
int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
char *tmp_mount_point);
+int fs_mgr_do_mount(struct fstab* fstab, const char* n_name, char* n_blk_device,
+ char* tmp_mount_point, bool need_cp);
int fs_mgr_do_mount_one(struct fstab_rec *rec);
int fs_mgr_do_tmpfs_mount(const char *n_name);
struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
@@ -87,4 +90,9 @@
#define FS_MGR_SETUP_VERITY_SUCCESS 0
int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
+// Return the name of the super partition if it exists. If a slot number is
+// specified, the super partition for the corresponding metadata slot will be
+// returned. Otherwise, it will use the current slot.
+std::string fs_mgr_get_super_partition_name(int slot = -1);
+
#endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index f15c450..08f4554 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -27,6 +27,7 @@
#include <stdint.h>
+#include <chrono>
#include <memory>
#include <string>
#include <vector>
@@ -43,12 +44,16 @@
// the partition name. On success, a path to the partition's block device is
// returned. If |force_writable| is true, the "readonly" flag will be ignored
// so the partition can be flashed.
+//
+// If |timeout_ms| is non-zero, then CreateLogicalPartition will block for the
+// given amount of time until the path returned in |path| is available.
bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
const std::string& partition_name, bool force_writable,
- std::string* path);
+ const std::chrono::milliseconds& timeout_ms, std::string* path);
-// Destroy the block device for a logical partition, by name.
-bool DestroyLogicalPartition(const std::string& name);
+// Destroy the block device for a logical partition, by name. If |timeout_ms|
+// is non-zero, then this will block until the device path has been unlinked.
+bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index ceb45de..3274e0e 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -19,8 +19,10 @@
#include <fstab/fstab.h>
#include <string>
+#include <vector>
-bool fs_mgr_overlayfs_mount_all();
+bool fs_mgr_overlayfs_mount_all(const fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(const fstab* fstab);
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
bool* change = nullptr);
bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index b1ee328..bb40511 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -86,6 +86,9 @@
int fs_mgr_is_latemount(const struct fstab_rec* fstab);
int fs_mgr_is_quota(const struct fstab_rec* fstab);
int fs_mgr_is_logical(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab);
int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
std::string fs_mgr_get_slot_suffix();
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 2b526f6..c4b57c7 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -278,7 +278,7 @@
struct dm_ioctl io;
InitIo(&io, name);
if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
- PLOG(ERROR) << "DM_DEV_STATUS failed for " << name;
+ PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
return false;
}
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index cc61917..70823c6 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -59,7 +59,8 @@
: dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
valid_ = dm_.CreateDevice(name, table);
}
- TempDevice(TempDevice&& other) : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+ TempDevice(TempDevice&& other) noexcept
+ : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
other.valid_ = false;
}
~TempDevice() {
@@ -103,7 +104,7 @@
TempDevice(const TempDevice&) = delete;
TempDevice& operator=(const TempDevice&) = delete;
- TempDevice& operator=(TempDevice&& other) {
+ TempDevice& operator=(TempDevice&& other) noexcept {
name_ = other.name_;
valid_ = other.valid_;
other.valid_ = false;
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index aab89e5..175b0f0 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -156,6 +156,20 @@
std::string target_string_;
};
+// dm-bow is the backup on write target that can provide checkpoint capability
+// for file systems that do not support checkpoints natively
+class DmTargetBow final : public DmTarget {
+ public:
+ DmTargetBow(uint64_t start, uint64_t length, const std::string& target_string)
+ : DmTarget(start, length), target_string_(target_string) {}
+
+ std::string name() const override { return "bow"; }
+ std::string GetParameterString() const override { return target_string_; }
+
+ private:
+ std::string target_string_;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 89282db..69dc065 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -35,7 +35,6 @@
"libcrypto",
"libcrypto_utils",
"libsparse",
- "libext2_uuid",
"libext4_utils",
"libz",
],
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 2015e4d..97b15bd 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -25,7 +25,6 @@
#include <algorithm>
#include <android-base/unique_fd.h>
-#include <uuid/uuid.h>
#include "liblp/liblp.h"
#include "reader.h"
@@ -79,8 +78,8 @@
out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
}
-Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
- : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
+Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes)
+ : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
size_ += extent->num_sectors() * LP_SECTOR_SIZE;
@@ -128,6 +127,17 @@
DCHECK(size_ == aligned_size);
}
+uint64_t Partition::BytesOnDisk() const {
+ uint64_t sectors = 0;
+ for (const auto& extent : extents_) {
+ if (!extent->AsLinearExtent()) {
+ continue;
+ }
+ sectors += extent->num_sectors();
+ }
+ return sectors * LP_SECTOR_SIZE;
+}
+
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
uint32_t slot_number) {
std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
@@ -175,14 +185,23 @@
header_.header_size = sizeof(header_);
header_.partitions.entry_size = sizeof(LpMetadataPartition);
header_.extents.entry_size = sizeof(LpMetadataExtent);
+ header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
}
bool MetadataBuilder::Init(const LpMetadata& metadata) {
geometry_ = metadata.geometry;
+ for (const auto& group : metadata.groups) {
+ std::string group_name = GetPartitionGroupName(group);
+ if (!AddGroup(group_name, group.maximum_size)) {
+ return false;
+ }
+ }
+
for (const auto& partition : metadata.partitions) {
- Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
- partition.attributes);
+ std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
+ Partition* builder =
+ AddPartition(GetPartitionName(partition), group_name, partition.attributes);
if (!builder) {
return false;
}
@@ -292,10 +311,27 @@
geometry_.alignment_offset = device_info_.alignment_offset;
geometry_.block_device_size = device_info_.size;
geometry_.logical_block_size = device_info.logical_block_size;
+
+ if (!AddGroup("default", 0)) {
+ return false;
+ }
return true;
}
-Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
+bool MetadataBuilder::AddGroup(const std::string& group_name, uint64_t maximum_size) {
+ if (FindGroup(group_name)) {
+ LERROR << "Group already exists: " << group_name;
+ return false;
+ }
+ groups_.push_back(std::make_unique<PartitionGroup>(group_name, maximum_size));
+ return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
+ return AddPartition(name, "default", attributes);
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
uint32_t attributes) {
if (name.empty()) {
LERROR << "Partition must have a non-empty name.";
@@ -305,7 +341,11 @@
LERROR << "Attempting to create duplication partition with name: " << name;
return nullptr;
}
- partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
+ if (!FindGroup(group_name)) {
+ LERROR << "Could not find partition group: " << group_name;
+ return nullptr;
+ }
+ partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));
return partitions_.back().get();
}
@@ -318,6 +358,26 @@
return nullptr;
}
+PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) const {
+ for (const auto& group : groups_) {
+ if (group->name() == group_name) {
+ return group.get();
+ }
+ }
+ return nullptr;
+}
+
+uint64_t MetadataBuilder::TotalSizeOfGroup(PartitionGroup* group) const {
+ uint64_t total = 0;
+ for (const auto& partition : partitions_) {
+ if (partition->group_name() != group->name()) {
+ continue;
+ }
+ total += partition->BytesOnDisk();
+ }
+ return total;
+}
+
void MetadataBuilder::RemovePartition(const std::string& name) {
for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
if ((*iter)->name() == name) {
@@ -328,8 +388,23 @@
}
bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
- // Figure out how much we need to allocate.
+ PartitionGroup* group = FindGroup(partition->group_name());
+ CHECK(group);
+
+ // Figure out how much we need to allocate, and whether our group has
+ // enough space remaining.
uint64_t space_needed = aligned_size - partition->size();
+ if (group->maximum_size() > 0) {
+ uint64_t group_size = TotalSizeOfGroup(group);
+ if (group_size >= group->maximum_size() ||
+ group->maximum_size() - group_size < space_needed) {
+ LERROR << "Partition " << partition->name() << " is part of group " << group->name()
+ << " which does not have enough space free (" << space_needed << "requested, "
+ << group_size << " used out of " << group->maximum_size();
+ return false;
+ }
+ }
+
uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
@@ -386,6 +461,7 @@
}
const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+ CHECK_NE(sectors_per_block, 0);
CHECK(sectors_needed % sectors_per_block == 0);
// Find gaps that we can use for new extents. Note we store new extents in a
@@ -440,6 +516,20 @@
metadata->header = header_;
metadata->geometry = geometry_;
+ std::map<std::string, size_t> group_indices;
+ for (const auto& group : groups_) {
+ LpMetadataPartitionGroup out = {};
+
+ if (group->name().size() > sizeof(out.name)) {
+ LERROR << "Partition group name is too long: " << group->name();
+ return nullptr;
+ }
+ strncpy(out.name, group->name().c_str(), sizeof(out.name));
+ out.maximum_size = group->maximum_size();
+
+ metadata->groups.push_back(out);
+ }
+
// Flatten the partition and extent structures into an LpMetadata, which
// makes it very easy to validate, serialize, or pass on to device-mapper.
for (const auto& partition : partitions_) {
@@ -456,12 +546,6 @@
}
strncpy(part.name, partition->name().c_str(), sizeof(part.name));
- if (uuid_parse(partition->guid().c_str(), part.guid) != 0) {
- LERROR << "Could not parse guid " << partition->guid() << " for partition "
- << partition->name();
- return nullptr;
- }
-
part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
part.num_extents = static_cast<uint32_t>(partition->extents().size());
part.attributes = partition->attributes();
@@ -474,6 +558,7 @@
metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+ metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());
return metadata;
}
@@ -481,6 +566,14 @@
return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
}
+uint64_t MetadataBuilder::UsedSpace() const {
+ uint64_t size = 0;
+ for (const auto& partition : partitions_) {
+ size += partition->size();
+ }
+ return size;
+}
+
uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
@@ -521,8 +614,10 @@
ShrinkPartition(partition, aligned_size);
}
- LINFO << "Partition " << partition->name() << " will resize from " << old_size << " bytes to "
- << aligned_size << " bytes";
+ if (partition->size() != old_size) {
+ LINFO << "Partition " << partition->name() << " will resize from " << old_size
+ << " bytes to " << aligned_size << " bytes";
+ }
return true;
}
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index da9c8f3..c916b44 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -22,16 +22,12 @@
using namespace std;
using namespace android::fs_mgr;
-static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
-static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303";
-
TEST(liblp, BuildBasic) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(partition, nullptr);
EXPECT_EQ(partition->name(), "system");
- EXPECT_EQ(partition->guid(), TEST_GUID);
EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);
EXPECT_EQ(partition->size(), 0);
EXPECT_EQ(builder->FindPartition("system"), partition);
@@ -43,7 +39,7 @@
TEST(liblp, ResizePartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 65536), true);
EXPECT_EQ(system->size(), 65536);
@@ -94,7 +90,7 @@
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
// Test that we align up to one sector.
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 10000), true);
EXPECT_EQ(system->size(), 12288);
@@ -171,9 +167,9 @@
BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
- Partition* a = builder->AddPartition("a", TEST_GUID, 0);
+ Partition* a = builder->AddPartition("a", 0);
ASSERT_NE(a, nullptr);
- Partition* b = builder->AddPartition("b", TEST_GUID2, 0);
+ Partition* b = builder->AddPartition("b", 0);
ASSERT_NE(b, nullptr);
// Add a bunch of small extents to each, interleaving.
@@ -202,21 +198,35 @@
}
TEST(liblp, UseAllDiskSpace) {
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- EXPECT_EQ(builder->AllocatableSpace(), 1036288);
+ static constexpr uint64_t total = 1024 * 1024;
+ static constexpr uint64_t metadata = 1024;
+ static constexpr uint64_t slots = 2;
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(total, metadata, slots);
+ // We reserve a geometry block (4KB) plus space for each copy of the
+ // maximum size of a metadata blob. Then, we double that space since
+ // we store a backup copy of everything.
+ static constexpr uint64_t geometry = 4 * 1024;
+ static constexpr uint64_t allocatable = total - (metadata * slots + geometry) * 2;
+ EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+ EXPECT_EQ(builder->UsedSpace(), 0);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
- EXPECT_EQ(builder->ResizePartition(system, 1036288), true);
- EXPECT_EQ(system->size(), 1036288);
- EXPECT_EQ(builder->ResizePartition(system, 1036289), false);
+ EXPECT_EQ(builder->ResizePartition(system, allocatable), true);
+ EXPECT_EQ(system->size(), allocatable);
+ EXPECT_EQ(builder->UsedSpace(), allocatable);
+ EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+ EXPECT_EQ(builder->ResizePartition(system, allocatable + 1), false);
+ EXPECT_EQ(system->size(), allocatable);
+ EXPECT_EQ(builder->UsedSpace(), allocatable);
+ EXPECT_EQ(builder->AllocatableSpace(), allocatable);
}
TEST(liblp, BuildComplex) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
- Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
ASSERT_NE(vendor, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -249,15 +259,15 @@
TEST(liblp, AddInvalidPartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(partition, nullptr);
// Duplicate name.
- partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
EXPECT_EQ(partition, nullptr);
// Empty name.
- partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY);
+ partition = builder->AddPartition("", LP_PARTITION_ATTR_READONLY);
EXPECT_EQ(partition, nullptr);
}
@@ -268,8 +278,8 @@
unique_ptr<MetadataBuilder> builder =
MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
- Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
ASSERT_NE(vendor, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -308,7 +318,6 @@
for (const auto& partition : exported->partitions) {
Partition* original = builder->FindPartition(GetPartitionName(partition));
ASSERT_NE(original, nullptr);
- EXPECT_EQ(original->guid(), GetPartitionGuid(partition));
for (size_t i = 0; i < partition.num_extents; i++) {
const auto& extent = exported->extents[partition.first_extent_index + i];
LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();
@@ -323,8 +332,8 @@
TEST(liblp, BuilderImport) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
- Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system, nullptr);
ASSERT_NE(vendor, nullptr);
EXPECT_EQ(builder->ResizePartition(system, 65536), true);
@@ -343,11 +352,9 @@
EXPECT_EQ(system->size(), 98304);
ASSERT_EQ(system->extents().size(), 2);
- EXPECT_EQ(system->guid(), TEST_GUID);
EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);
EXPECT_EQ(vendor->size(), 32768);
ASSERT_EQ(vendor->extents().size(), 1);
- EXPECT_EQ(vendor->guid(), TEST_GUID2);
EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);
LinearExtent* system1 = system->extents()[0]->AsLinearExtent();
@@ -364,17 +371,7 @@
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
- Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY);
- EXPECT_NE(system, nullptr);
-
- unique_ptr<LpMetadata> exported = builder->Export();
- EXPECT_EQ(exported, nullptr);
-}
-
-TEST(liblp, ExportInvalidGuid) {
- unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
-
- Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY);
+ Partition* system = builder->AddPartition(name + name, LP_PARTITION_ATTR_READONLY);
EXPECT_NE(system, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
@@ -469,7 +466,7 @@
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
- Partition* partition = builder->AddPartition("system", TEST_GUID, 0);
+ Partition* partition = builder->AddPartition("system", 0);
ASSERT_NE(partition, nullptr);
ASSERT_TRUE(builder->ResizePartition(partition, 512));
EXPECT_EQ(partition->size(), 4096);
@@ -481,3 +478,28 @@
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
ASSERT_EQ(builder, nullptr);
}
+
+TEST(liblp, HasDefaultGroup) {
+ BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+
+ EXPECT_FALSE(builder->AddGroup("default", 0));
+}
+
+TEST(liblp, GroupSizeLimits) {
+ BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("google", 16384));
+
+ Partition* partition = builder->AddPartition("system", "google", 0);
+ ASSERT_NE(partition, nullptr);
+ EXPECT_TRUE(builder->ResizePartition(partition, 8192));
+ EXPECT_EQ(partition->size(), 8192);
+ EXPECT_TRUE(builder->ResizePartition(partition, 16384));
+ EXPECT_EQ(partition->size(), 16384);
+ EXPECT_FALSE(builder->ResizePartition(partition, 32768));
+ EXPECT_EQ(partition->size(), 16384);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index a35cf8e..a6044d0 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -95,11 +95,24 @@
void AddTo(LpMetadata* out) const override;
};
+class PartitionGroup final {
+ public:
+ explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
+ : name_(name), maximum_size_(maximum_size) {}
+
+ const std::string& name() const { return name_; }
+ uint64_t maximum_size() const { return maximum_size_; }
+
+ private:
+ std::string name_;
+ uint64_t maximum_size_;
+};
+
class Partition final {
friend class MetadataBuilder;
public:
- Partition(const std::string& name, const std::string& guid, uint32_t attributes);
+ Partition(const std::string& name, const std::string& group_name, uint32_t attributes);
// Add a raw extent.
void AddExtent(std::unique_ptr<Extent>&& extent);
@@ -107,9 +120,13 @@
// Remove all extents from this partition.
void RemoveExtents();
+ // Compute the size used by linear extents. This is the same as size(),
+ // but does not factor in extents which do not take up space.
+ uint64_t BytesOnDisk() const;
+
const std::string& name() const { return name_; }
+ const std::string& group_name() const { return group_name_; }
uint32_t attributes() const { return attributes_; }
- const std::string& guid() const { return guid_; }
const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
uint64_t size() const { return size_; }
@@ -117,7 +134,7 @@
void ShrinkTo(uint64_t aligned_size);
std::string name_;
- std::string guid_;
+ std::string group_name_;
std::vector<std::unique_ptr<Extent>> extents_;
uint32_t attributes_;
uint64_t size_;
@@ -156,13 +173,25 @@
return New(device_info, metadata_max_size, metadata_slot_count);
}
+ // Define a new partition group. By default there is one group called
+ // "default", with an unrestricted size. A non-zero size will restrict the
+ // total space used by all partitions in the group.
+ //
+ // This can fail and return false if the group already exists.
+ bool AddGroup(const std::string& group_name, uint64_t maximum_size);
+
// Export metadata so it can be serialized to an image, to disk, or mounted
// via device-mapper.
std::unique_ptr<LpMetadata> Export();
// Add a partition, returning a handle so it can be sized as needed. If a
// partition with the given name already exists, nullptr is returned.
- Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);
+ Partition* AddPartition(const std::string& name, const std::string& group_name,
+ uint32_t attributes);
+
+ // Same as AddPartition above, but uses the default partition group which
+ // has no size restrictions.
+ Partition* AddPartition(const std::string& name, uint32_t attributes);
// Delete a partition by name if it exists.
void RemovePartition(const std::string& name);
@@ -184,6 +213,7 @@
// Amount of space that can be allocated to logical partitions.
uint64_t AllocatableSpace() const;
+ uint64_t UsedSpace() const;
// Merge new block device information into previous values. Alignment values
// are only overwritten if the new values are non-zero.
@@ -192,15 +222,22 @@
private:
MetadataBuilder();
+ MetadataBuilder(const MetadataBuilder&) = delete;
+ MetadataBuilder(MetadataBuilder&&) = delete;
+ MetadataBuilder& operator=(const MetadataBuilder&) = delete;
+ MetadataBuilder& operator=(MetadataBuilder&&) = delete;
bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
bool Init(const LpMetadata& metadata);
bool GrowPartition(Partition* partition, uint64_t aligned_size);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
uint64_t AlignSector(uint64_t sector);
+ PartitionGroup* FindGroup(const std::string& group_name) const;
+ uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
LpMetadataGeometry geometry_;
LpMetadataHeader header_;
std::vector<std::unique_ptr<Partition>> partitions_;
+ std::vector<std::unique_ptr<PartitionGroup>> groups_;
BlockDeviceInfo device_info_;
};
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 6da24f6..5f95dca 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -36,6 +36,7 @@
LpMetadataHeader header;
std::vector<LpMetadataPartition> partitions;
std::vector<LpMetadataExtent> extents;
+ std::vector<LpMetadataPartitionGroup> groups;
};
// Place an initial partition table on the device. This will overwrite the
@@ -67,7 +68,7 @@
// Helper to extract safe C++ strings from partition info.
std::string GetPartitionName(const LpMetadataPartition& partition);
-std::string GetPartitionGuid(const LpMetadataPartition& partition);
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
// Helper to return a slot number for a slot suffix.
uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 52c80f7..7d1a2a9 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
#define LP_METADATA_HEADER_MAGIC 0x414C5030
/* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 1
+#define LP_METADATA_MAJOR_VERSION 3
#define LP_METADATA_MINOR_VERSION 0
/* Attributes for the LpMetadataPartition::attributes field.
@@ -67,7 +67,7 @@
* | Geometry Backup |
* +--------------------+
*/
-#define LP_METADATA_PARTITION_NAME "super"
+#define LP_METADATA_DEFAULT_PARTITION_NAME "super"
/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
#define LP_SECTOR_SIZE 512
@@ -216,6 +216,8 @@
LpMetadataTableDescriptor partitions;
/* 92: Extent table descriptor. */
LpMetadataTableDescriptor extents;
+ /* 104: Updateable group descriptor. */
+ LpMetadataTableDescriptor groups;
} __attribute__((packed)) LpMetadataHeader;
/* This struct defines a logical partition entry, similar to what would be
@@ -230,21 +232,21 @@
*/
char name[36];
- /* 36: Globally unique identifier (GUID) of this partition. */
- uint8_t guid[16];
-
- /* 52: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+ /* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
uint32_t attributes;
- /* 56: Index of the first extent owned by this partition. The extent will
+ /* 40: Index of the first extent owned by this partition. The extent will
* start at logical sector 0. Gaps between extents are not allowed.
*/
uint32_t first_extent_index;
- /* 60: Number of extents in the partition. Every partition must have at
+ /* 44: Number of extents in the partition. Every partition must have at
* least one extent.
*/
uint32_t num_extents;
+
+ /* 48: Group this partition belongs to. */
+ uint32_t group_index;
} __attribute__((packed)) LpMetadataPartition;
/* This extent is a dm-linear target, and the index is an index into the
@@ -271,6 +273,19 @@
uint64_t target_data;
} __attribute__((packed)) LpMetadataExtent;
+/* This struct defines an entry in the groups table. Each group has a maximum
+ * size, and partitions in a group must not exceed that size. There is always
+ * a "default" group of unlimited size, which is used when not using update
+ * groups or when using overlayfs or fastbootd.
+ */
+typedef struct LpMetadataPartitionGroup {
+ /* 0: Name of this group. Any unused characters must be 0. */
+ char name[36];
+
+ /* 36: Maximum size in bytes. If 0, the group has no maximum size. */
+ uint64_t maximum_size;
+} LpMetadataPartitionGroup;
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 329a901..01de3ac 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -37,8 +37,6 @@
static const size_t kDiskSize = 131072;
static const size_t kMetadataSize = 512;
static const size_t kMetadataSlots = 2;
-static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
-static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
// Helper function for creating an in-memory file descriptor. This lets us
// simulate read/writing logical partition metadata as if we had a block device
@@ -81,7 +79,7 @@
}
static bool AddDefaultPartitions(MetadataBuilder* builder) {
- Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_NONE);
if (!system) {
return false;
}
@@ -117,6 +115,9 @@
uint64_t size;
ASSERT_TRUE(GetDescriptorSize(fd, &size));
ASSERT_EQ(size, kDiskSize);
+
+ // Verify that we can't read unwritten metadata.
+ ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
}
// Flashing metadata should not work if the metadata was created for a larger
@@ -168,7 +169,6 @@
// Check partition tables.
ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
- EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
EXPECT_EQ(exported->partitions[0].first_extent_index,
imported->partitions[0].first_extent_index);
@@ -191,9 +191,6 @@
ASSERT_EQ(imported->partitions.size(), 1);
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
- // Verify that we can't read unwritten metadata.
- ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
-
// Change the name before writing to the next slot.
strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
@@ -331,21 +328,18 @@
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
ASSERT_NE(builder, nullptr);
- // Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
- size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
- EXPECT_LT(max_partitions, 10);
+ // Compute the maximum number of partitions we can fit in 512 bytes of
+ // metadata. By default there is the header, and one partition group.
+ static const size_t kMaxPartitionTableSize =
+ kMetadataSize - sizeof(LpMetadataHeader) - sizeof(LpMetadataPartitionGroup);
+ size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);
// Add this number of partitions.
Partition* partition = nullptr;
for (size_t i = 0; i < max_partitions; i++) {
- std::string guid = std::string(TEST_GUID) + to_string(i);
- partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
+ partition = builder->AddPartition(to_string(i), LP_PARTITION_ATTR_NONE);
ASSERT_NE(partition, nullptr);
}
- ASSERT_NE(partition, nullptr);
- // Add one extent to any partition to fill up more space - we're at 508
- // bytes after this, out of 512.
- ASSERT_TRUE(builder->ResizePartition(partition, 1024));
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
@@ -357,7 +351,7 @@
ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
// Check that adding one more partition overflows the metadata allotment.
- partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
+ partition = builder->AddPartition("final", LP_PARTITION_ATTR_NONE);
EXPECT_NE(partition, nullptr);
exported = builder->Export();
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 117f5d5..005d493 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -188,7 +188,8 @@
return false;
}
if (!ValidateTableBounds(header, header.partitions) ||
- !ValidateTableBounds(header, header.extents)) {
+ !ValidateTableBounds(header, header.extents) ||
+ !ValidateTableBounds(header, header.groups)) {
LERROR << "Logical partition metadata has invalid table bounds.";
return false;
}
@@ -202,6 +203,10 @@
LERROR << "Logical partition metadata has invalid extent table entry size.";
return false;
}
+ if (header.groups.entry_size != sizeof(LpMetadataPartitionGroup)) {
+ LERROR << "Logical partition metadata has invalid group table entry size.";
+ return false;
+ }
return true;
}
@@ -257,6 +262,10 @@
LERROR << "Logical partition has invalid extent list.";
return nullptr;
}
+ if (partition.group_index >= header.groups.num_entries) {
+ LERROR << "Logical partition has invalid group index.";
+ return nullptr;
+ }
metadata->partitions.push_back(partition);
}
@@ -269,6 +278,16 @@
metadata->extents.push_back(extent);
}
+
+ cursor = buffer.get() + header.groups.offset;
+ for (size_t i = 0; i < header.groups.num_entries; i++) {
+ LpMetadataPartitionGroup group = {};
+ memcpy(&group, cursor, sizeof(group));
+ cursor += header.groups.entry_size;
+
+ metadata->groups.push_back(group);
+ }
+
return metadata;
}
@@ -314,7 +333,7 @@
return nullptr;
}
- // Read the priamry copy, and if that fails, try the backup.
+ // Read the primary copy, and if that fails, try the backup.
std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number);
if (metadata) {
return metadata;
@@ -345,5 +364,9 @@
return NameFromFixedArray(partition.name, sizeof(partition.name));
}
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) {
+ return NameFromFixedArray(group.name, sizeof(group.name));
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index a590037..b08f96c 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -23,7 +23,6 @@
#include <android-base/file.h>
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
-#include <uuid/uuid.h>
#include "utility.h"
@@ -80,15 +79,6 @@
SHA256_Final(out, &c);
}
-std::string GetPartitionGuid(const LpMetadataPartition& partition) {
- // 32 hex characters, four hyphens. Unfortunately libext2_uuid provides no
- // macro to assist with buffer sizing.
- static const size_t kGuidLen = 36;
- char buffer[kGuidLen + 1];
- uuid_unparse_upper(partition.guid, buffer);
- return buffer;
-}
-
uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
if (suffix.empty()) {
return 0;
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index ad84b22..2415629 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -56,14 +56,17 @@
metadata.partitions.size() * sizeof(LpMetadataPartition));
std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
metadata.extents.size() * sizeof(LpMetadataExtent));
+ std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
+ metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
// Compute positions of tables.
header.partitions.offset = 0;
header.extents.offset = header.partitions.offset + partitions.size();
- header.tables_size = header.extents.offset + extents.size();
+ header.groups.offset = header.extents.offset + extents.size();
+ header.tables_size = header.groups.offset + groups.size();
// Compute payload checksum.
- std::string tables = partitions + extents;
+ std::string tables = partitions + extents + groups;
SHA256(tables.data(), tables.size(), header.tables_checksum);
// Compute header checksum.
@@ -94,7 +97,8 @@
}
// Make sure we're writing within the space reserved.
if (blob->size() > geometry.metadata_max_size) {
- LERROR << "Logical partition metadata is too large.";
+ LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
+ << geometry.metadata_max_size;
return false;
}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 8b1c55a..db01c1e 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -139,7 +139,7 @@
auto fstab = fs_mgr_read_fstab("/proc/mounts");
ASSERT_NE(fstab, nullptr);
- std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "r"),
+ std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "re"),
endmntent);
ASSERT_NE(mounts, nullptr);
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 879ba21..f78093b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -42,6 +42,7 @@
using DmTargetLinear = ::android::dm::DmTargetLinear;
using DmTargetZero = ::android::dm::DmTargetZero;
using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
+using DmTargetBow = ::android::dm::DmTargetBow;
using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
@@ -108,6 +109,13 @@
std::string block_device = NextArg();
return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
block_device);
+ } else if (target_type == "bow") {
+ if (!HasArgs(1)) {
+ std::cerr << "Expected \"bow\" <block_device>" << std::endl;
+ return nullptr;
+ }
+ std::string block_device = NextArg();
+ return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
diff --git a/healthd/Android.mk b/healthd/Android.mk
index f7214c6..80bf84a 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -43,7 +43,7 @@
AnimationParser.cpp
LOCAL_MODULE := libhealthd_charger
-LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH) \
$(LOCAL_PATH)/include
@@ -97,6 +97,7 @@
android.hardware.health@2.0 \
android.hardware.health@1.0 \
android.hardware.health@1.0-convert \
+ libbinderthreadstate \
libhidltransport \
libhidlbase \
libhwbinder_noltopgo \
diff --git a/init/Android.bp b/init/Android.bp
index a2c49d0..c793971 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -68,6 +68,7 @@
],
shared_libs: [
"libbase",
+ "libbinder",
"libbootloader_message",
"libcutils",
"libdl",
@@ -78,6 +79,7 @@
"liblog",
"liblogwrap",
"libselinux",
+ "libutils",
],
}
@@ -127,6 +129,13 @@
type: "lite",
export_proto_headers: true,
},
+
+ target: {
+ recovery: {
+ cflags: ["-DRECOVERY"],
+ exclude_shared_libs: ["libbinder", "libutils"],
+ },
+ },
}
cc_binary {
@@ -143,6 +152,12 @@
],
srcs: ["main.cpp"],
symlinks: ["ueventd"],
+ target: {
+ recovery: {
+ cflags: ["-DRECOVERY"],
+ exclude_shared_libs: ["libbinder", "libutils"],
+ },
+ },
}
// Tests
diff --git a/init/Android.mk b/init/Android.mk
index dc400ad..5554995 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -61,9 +61,9 @@
# Set up the same mount points on the ramdisk that system-as-root contains.
LOCAL_POST_INSTALL_CMD := \
mkdir -p $(TARGET_RAMDISK_OUT)/dev \
- mkdir -p $(TARGET_RAMDISK_OUT)/mnt \
- mkdir -p $(TARGET_RAMDISK_OUT)/proc \
- mkdir -p $(TARGET_RAMDISK_OUT)/sys \
+ $(TARGET_RAMDISK_OUT)/mnt \
+ $(TARGET_RAMDISK_OUT)/proc \
+ $(TARGET_RAMDISK_OUT)/sys \
LOCAL_STATIC_LIBRARIES := \
libfs_mgr \
diff --git a/init/README.md b/init/README.md
index b45da21..02a65d5 100644
--- a/init/README.md
+++ b/init/README.md
@@ -262,6 +262,13 @@
> Scheduling priority of the service process. This value has to be in range
-20 to 19. Default priority is 0. Priority is set via setpriority().
+`restart_period <seconds>`
+> If a non-oneshot service exits, it will be restarted at its start time plus
+ this period. It defaults to 5s to rate limit crashing services.
+ This can be increased for services that are meant to run periodically. For
+ example, it may be set to 3600 to indicate that the service should run every hour
+ or 86400 to indicate that the service should run every day.
+
`rlimit <resource> <cur> <max>`
> This applies the given rlimit to the service. rlimits are inherited by child
processes, so this effectively applies the given rlimit to the process tree
@@ -298,6 +305,12 @@
seclabel or computed based on the service executable file security context.
For native executables see libcutils android\_get\_control\_socket().
+`timeout_period <seconds>`
+> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
+ here, so oneshot services do not automatically restart, however all other services will.
+ This is particularly useful for creating a periodic service combined with the restart_period
+ option described above.
+
`user <username>`
> Change to 'username' before exec'ing this service.
Currently defaults to root. (??? probably should default to nobody)
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 1496935..eb86eb0 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -68,6 +68,7 @@
bool CreateLogicalPartitions();
bool MountPartition(fstab_rec* fstab_rec);
bool MountPartitions();
+ bool IsDmLinearEnabled();
bool GetBackingDmLinearDevices();
virtual ListenerAction UeventCallback(const Uevent& uevent);
@@ -82,6 +83,7 @@
std::string lp_metadata_partition_;
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
+ std::string super_partition_name_;
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
};
@@ -120,24 +122,18 @@
return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
}
-static bool inline IsRecoveryMode() {
- return access("/system/bin/recovery", F_OK) == 0;
+static bool ForceNormalBoot() {
+ static bool force_normal_boot = []() {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+ }();
+
+ return force_normal_boot;
}
-static inline bool IsDmLinearEnabled() {
- static bool checked = false;
- static bool enabled = false;
- if (checked) {
- return enabled;
- }
- import_kernel_cmdline(false,
- [](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.logical_partitions" && value == "1") {
- enabled = true;
- }
- });
- checked = true;
- return enabled;
+static bool IsRecoveryMode() {
+ return !ForceNormalBoot() && access("/system/bin/recovery", F_OK) == 0;
}
// Class Definitions
@@ -158,6 +154,8 @@
device_handler_ = std::make_unique<DeviceHandler>(
std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
std::move(boot_devices), false);
+
+ super_partition_name_ = fs_mgr_get_super_partition_name();
}
std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -188,13 +186,20 @@
return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
}
+bool FirstStageMount::IsDmLinearEnabled() {
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (fs_mgr_is_logical(fstab_rec)) return true;
+ }
+ return false;
+}
+
bool FirstStageMount::GetBackingDmLinearDevices() {
// Add any additional devices required for dm-linear mappings.
if (!IsDmLinearEnabled()) {
return true;
}
- required_devices_partition_names_.emplace(LP_METADATA_PARTITION_NAME);
+ required_devices_partition_names_.emplace(super_partition_name_);
return true;
}
@@ -260,7 +265,7 @@
if (lp_metadata_partition_.empty()) {
LOG(ERROR) << "Could not locate logical partition tables in partition "
- << LP_METADATA_PARTITION_NAME;
+ << super_partition_name_;
return false;
}
return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
@@ -273,7 +278,7 @@
auto iter = required_devices_partition_names_.find(name);
if (iter != required_devices_partition_names_.end()) {
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
- if (IsDmLinearEnabled() && name == LP_METADATA_PARTITION_NAME) {
+ if (IsDmLinearEnabled() && name == super_partition_name_) {
std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
lp_metadata_partition_ = links[0];
}
@@ -364,23 +369,35 @@
auto system_partition =
std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(),
[](const auto& rec) { return rec->mount_point == "/system"s; });
+
if (system_partition != mount_fstab_recs_.end()) {
+ if (ForceNormalBoot()) {
+ free((*system_partition)->mount_point);
+ (*system_partition)->mount_point = strdup("/system_recovery_mount");
+ }
+
if (!MountPartition(*system_partition)) {
return false;
}
- SwitchRoot("/system");
+ SwitchRoot((*system_partition)->mount_point);
mount_fstab_recs_.erase(system_partition);
}
for (auto fstab_rec : mount_fstab_recs_) {
- if (!MountPartition(fstab_rec)) {
+ if (!MountPartition(fstab_rec) && !fs_mgr_is_nofail(fstab_rec)) {
return false;
}
}
- fs_mgr_overlayfs_mount_all();
+ // heads up for instantiating required device(s) for overlayfs logic
+ const auto devices = fs_mgr_overlayfs_required_devices(device_tree_fstab_.get());
+ for (auto const& device : devices) {
+ InitMappedDevice(device);
+ }
+
+ fs_mgr_overlayfs_mount_all(device_tree_fstab_.get());
return true;
}
diff --git a/init/init.cpp b/init/init.cpp
index 16564f4..42ec88c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -19,6 +19,7 @@
#include <dirent.h>
#include <fcntl.h>
#include <pthread.h>
+#include <seccomp_policy.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
@@ -40,6 +41,11 @@
#include <cutils/android_reboot.h>
#include <keyutils.h>
#include <libavb/libavb.h>
+#include <selinux/android.h>
+
+#ifndef RECOVERY
+#include <binder/ProcessState.h>
+#endif
#include "action_parser.h"
#include "epoll.h"
@@ -181,23 +187,34 @@
}
}
-static std::optional<boot_clock::time_point> RestartProcesses() {
- std::optional<boot_clock::time_point> next_process_restart_time;
+static std::optional<boot_clock::time_point> HandleProcessActions() {
+ std::optional<boot_clock::time_point> next_process_action_time;
for (const auto& s : ServiceList::GetInstance()) {
+ if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
+ auto timeout_time = s->time_started() + *s->timeout_period();
+ if (boot_clock::now() > timeout_time) {
+ s->Timeout();
+ } else {
+ if (!next_process_action_time || timeout_time < *next_process_action_time) {
+ next_process_action_time = timeout_time;
+ }
+ }
+ }
+
if (!(s->flags() & SVC_RESTARTING)) continue;
- auto restart_time = s->time_started() + 5s;
+ auto restart_time = s->time_started() + s->restart_period();
if (boot_clock::now() > restart_time) {
if (auto result = s->Start(); !result) {
LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
}
} else {
- if (!next_process_restart_time || restart_time < *next_process_restart_time) {
- next_process_restart_time = restart_time;
+ if (!next_process_action_time || restart_time < *next_process_action_time) {
+ next_process_action_time = restart_time;
}
}
}
- return next_process_restart_time;
+ return next_process_action_time;
}
static Result<Success> DoControlStart(Service* service) {
@@ -340,12 +357,12 @@
if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
return;
}
-
- std::string value = GetProperty("ro.boot.verifiedbootstate", "");
-
- if (!value.empty()) {
- property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
- }
+ import_kernel_cmdline(
+ false, [](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.verifiedbootstate") {
+ property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
+ }
+ });
}
static void export_kernel_boot_props() {
@@ -413,6 +430,22 @@
return Success();
}
+static Result<Success> InitBinder(const BuiltinArguments& args) {
+ // init's use of binder is very limited. init cannot:
+ // - have any binder threads
+ // - receive incoming binder calls
+ // - pass local binder services to remote processes
+ // - use death recipients
+ // The main supported usecases are:
+ // - notifying other daemons (oneway calls only)
+ // - retrieving data that is necessary to boot
+ // Also, binder can't be used by recovery.
+#ifndef RECOVERY
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
+#endif
+ return Success();
+}
+
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
@@ -548,21 +581,41 @@
RebootSystem(ANDROID_RB_RESTART2, "bootloader");
}
-static void InitKernelLogging(char* argv[]) {
- // Make stdin/stdout/stderr all point to /dev/null.
- int fd = open("/sys/fs/selinux/null", O_RDWR);
- if (fd == -1) {
- int saved_errno = errno;
- android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
- errno = saved_errno;
- PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
- }
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) close(fd);
+static void GlobalSeccomp() {
+ import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
+ bool in_qemu) {
+ if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+ LOG(FATAL) << "Failed to globally enable seccomp!";
+ }
+ });
+}
- android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
+static void SetupSelinux(char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+ });
+
+ // Set up SELinux, loading the SELinux policy.
+ SelinuxSetupKernelLogging();
+ SelinuxInitialize();
+
+ // We're in the kernel domain and want to transition to the init domain. File systems that
+ // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
+ // but other file systems do. In particular, this is needed for ramdisks such as the
+ // recovery image for A/B devices.
+ if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+ PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
+ }
+
+ setenv("SELINUX_INITIALIZED", "true", 1);
+
+ const char* path = "/system/bin/init";
+ const char* args[] = {path, nullptr};
+ execv(path, const_cast<char**>(args));
+
+ // execv() only returns if an error happened, in which case we
+ // panic and never return from this function.
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
}
int main(int argc, char** argv) {
@@ -580,9 +633,17 @@
InstallRebootSignalHandlers();
}
- InitKernelLogging(argv);
+ if (getenv("SELINUX_INITIALIZED") == nullptr) {
+ SetupSelinux(argv);
+ }
+
+ // We need to set up stdin/stdout/stderr again now that we're running in init's context.
+ InitKernelLogging(argv, InitAborter);
LOG(INFO) << "init second stage started!";
+ // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+ GlobalSeccomp();
+
// Set up a session keyring that all processes will have access to. It
// will hold things like FBE encryption keys. No process should override
// its session keyring.
@@ -611,6 +672,7 @@
if (avb_version) property_set("ro.boot.avb_version", avb_version);
// Clean up our environment.
+ unsetenv("SELINUX_INITIALIZED");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
@@ -673,6 +735,9 @@
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
+ // Initialize binder before bringing up other system services
+ am.QueueBuiltinAction(InitBinder, "InitBinder");
+
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
@@ -700,12 +765,12 @@
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
- auto next_process_restart_time = RestartProcesses();
+ auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
- if (next_process_restart_time) {
+ if (next_process_action_time) {
epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
- *next_process_restart_time - boot_clock::now());
+ *next_process_action_time - boot_clock::now());
if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 466cde3..d81ca5c 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -15,7 +15,6 @@
*/
#include <paths.h>
-#include <seccomp_policy.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
@@ -30,11 +29,9 @@
#include <android-base/logging.h>
#include <cutils/android_reboot.h>
#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
#include "first_stage_mount.h"
#include "reboot_utils.h"
-#include "selinux.h"
#include "util.h"
using android::base::boot_clock;
@@ -42,15 +39,6 @@
namespace android {
namespace init {
-static void GlobalSeccomp() {
- import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
- bool in_qemu) {
- if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
- LOG(FATAL) << "Failed to globally enable seccomp!";
- }
- });
-}
-
int main(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -96,6 +84,9 @@
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
+ // These below mounts are done in first stage init so that first stage mount can mount
+ // subdirectories of /mnt/{vendor,product}/. Other mounts, not required by first stage mount,
+ // should be done in rc files.
// Mount staging areas for devices managed by vold
// See storage config details at http://source.android.com/devices/storage/
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
@@ -111,9 +102,11 @@
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
- android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
- RebootSystem(ANDROID_RB_RESTART2, "bootloader");
- });
+ // We need to set up stdin/stdout/stderr for child processes forked from first
+ // stage init as part of the mount process. This closes /dev/console if the
+ // kernel had previously opened it.
+ auto reboot_bootloader = [](const char*) { RebootSystem(ANDROID_RB_RESTART2, "bootloader"); };
+ InitKernelLogging(argv, reboot_bootloader);
if (!errors.empty()) {
for (const auto& [error_string, error_errno] : errors) {
@@ -130,22 +123,6 @@
SetInitAvbVersionInRecovery();
- // Does this need to be done in first stage init or can it be done later?
- // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
- GlobalSeccomp();
-
- // Set up SELinux, loading the SELinux policy.
- SelinuxSetupKernelLogging();
- SelinuxInitialize();
-
- // We're in the kernel domain and want to transition to the init domain when we exec second
- // stage init. File systems that store SELabels in their xattrs, such as ext4 do not need an
- // explicit restorecon here, but other file systems do. In particular, this is needed for
- // ramdisks such as the recovery image for A/B devices.
- if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
- PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
- }
-
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index c8c47a8..a3baeb1 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -47,9 +47,9 @@
public:
EventHandler();
EventHandler(const EventHandler&) = delete;
- EventHandler(EventHandler&&);
+ EventHandler(EventHandler&&) noexcept;
EventHandler& operator=(const EventHandler&) = delete;
- EventHandler& operator=(EventHandler&&);
+ EventHandler& operator=(EventHandler&&) noexcept;
~EventHandler() noexcept;
bool init();
@@ -64,11 +64,11 @@
EventHandler::EventHandler() : fd_(-1) {}
-EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+EventHandler::EventHandler(EventHandler&& rval) noexcept : fd_(rval.fd_) {
rval.fd_ = -1;
}
-EventHandler& EventHandler::operator=(EventHandler&& rval) {
+EventHandler& EventHandler::operator=(EventHandler&& rval) noexcept {
fd_ = rval.fd_;
rval.fd_ = -1;
return *this;
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
index 1e0db57..c61c210 100644
--- a/init/modalias_handler.cpp
+++ b/init/modalias_handler.cpp
@@ -50,7 +50,7 @@
}
// Key is striped module name to match names in alias file
- std::size_t start = args[0].find_last_of("/");
+ std::size_t start = args[0].find_last_of('/');
std::size_t end = args[0].find(".ko:");
if ((end - start) <= 1) return Error() << "malformed dependency line";
auto mod_name = args[0].substr(start + 1, (end - start) - 1);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 2f88121..866f40e 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -162,7 +162,7 @@
*/
static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
std::vector<MountEntry>* emulatedPartitions, bool dump) {
- std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
+ std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
if (fp == nullptr) {
PLOG(ERROR) << "Failed to open /proc/mounts";
return false;
@@ -464,6 +464,12 @@
cmd = ANDROID_RB_RESTART2;
if (cmd_params.size() >= 2) {
reboot_target = cmd_params[1];
+ // adb reboot fastboot should boot into bootloader for devices not
+ // supporting logical partitions.
+ if (reboot_target == "fastboot" &&
+ !android::base::GetBoolProperty("ro.boot.logical_partitions", false)) {
+ reboot_target = "bootloader";
+ }
// When rebooting to the bootloader notify the bootloader writing
// also the BCB.
if (reboot_target == "bootloader") {
diff --git a/init/service.cpp b/init/service.cpp
index d20e90a..a3e5953 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -627,6 +627,15 @@
return Success();
}
+Result<Success> Service::ParseRestartPeriod(const std::vector<std::string>& args) {
+ int period;
+ if (!ParseInt(args[1], &period, 5)) {
+ return Error() << "restart_period value must be an integer >= 5";
+ }
+ restart_period_ = std::chrono::seconds(period);
+ return Success();
+}
+
Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
seclabel_ = args[1];
return Success();
@@ -650,6 +659,15 @@
return Error() << "Invalid shutdown option";
}
+Result<Success> Service::ParseTimeoutPeriod(const std::vector<std::string>& args) {
+ int period;
+ if (!ParseInt(args[1], &period, 1)) {
+ return Error() << "timeout_period value must be an integer >= 1";
+ }
+ timeout_period_ = std::chrono::seconds(period);
+ return Success();
+}
+
template <typename T>
Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
@@ -757,12 +775,16 @@
{1, 1, &Service::ParseOomScoreAdjust}},
{"override", {0, 0, &Service::ParseOverride}},
{"priority", {1, 1, &Service::ParsePriority}},
+ {"restart_period",
+ {1, 1, &Service::ParseRestartPeriod}},
{"rlimit", {3, 3, &Service::ParseProcessRlimit}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
{"shutdown", {1, 1, &Service::ParseShutdown}},
{"sigstop", {0, 0, &Service::ParseSigstop}},
{"socket", {3, 6, &Service::ParseSocket}},
+ {"timeout_period",
+ {1, 1, &Service::ParseTimeoutPeriod}},
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
@@ -1022,6 +1044,18 @@
}
}
+void Service::Timeout() {
+ // All process state flags will be taken care of in Reap(), we really just want to kill the
+ // process here when it times out. Oneshot processes will transition to be disabled, and
+ // all other processes will transition to be restarting.
+ LOG(INFO) << "Service '" << name_ << "' expired its timeout of " << timeout_period_->count()
+ << " seconds and will now be killed";
+ if (pid_) {
+ KillProcessGroup(SIGKILL);
+ NotifyStateChange("stopping");
+ }
+}
+
void Service::Restart() {
if (flags_ & SVC_RUNNING) {
/* Stop, wait, then start the service. */
diff --git a/init/service.h b/init/service.h
index ea79a07..e8d5ead 100644
--- a/init/service.h
+++ b/init/service.h
@@ -21,7 +21,9 @@
#include <sys/resource.h>
#include <sys/types.h>
+#include <chrono>
#include <memory>
+#include <optional>
#include <set>
#include <string>
#include <vector>
@@ -81,6 +83,7 @@
void Reset();
void Stop();
void Terminate();
+ void Timeout();
void Restart();
void Reap(const siginfo_t& siginfo);
void DumpState() const;
@@ -117,6 +120,8 @@
bool process_cgroup_empty() const { return process_cgroup_empty_; }
unsigned long start_order() const { return start_order_; }
void set_sigstop(bool value) { sigstop_ = value; }
+ std::chrono::seconds restart_period() const { return restart_period_; }
+ std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
const std::vector<std::string>& args() const { return args_; }
private:
@@ -153,11 +158,13 @@
Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
Result<Success> ParseNamespace(const std::vector<std::string>& args);
Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
+ Result<Success> ParseRestartPeriod(const std::vector<std::string>& args);
Result<Success> ParseSeclabel(const std::vector<std::string>& args);
Result<Success> ParseSetenv(const std::vector<std::string>& args);
Result<Success> ParseShutdown(const std::vector<std::string>& args);
Result<Success> ParseSigstop(const std::vector<std::string>& args);
Result<Success> ParseSocket(const std::vector<std::string>& args);
+ Result<Success> ParseTimeoutPeriod(const std::vector<std::string>& args);
Result<Success> ParseFile(const std::vector<std::string>& args);
Result<Success> ParseUser(const std::vector<std::string>& args);
Result<Success> ParseWritepid(const std::vector<std::string>& args);
@@ -220,6 +227,9 @@
bool sigstop_ = false;
+ std::chrono::seconds restart_period_ = 5s;
+ std::optional<std::chrono::seconds> timeout_period_;
+
std::vector<std::string> args_;
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index c2a21d4..092c51c 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -62,7 +62,9 @@
Result<std::string> ReadMessage(int socket) {
char buffer[kBufferSize] = {};
auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
- if (result <= 0) {
+ if (result == 0) {
+ return Error();
+ } else if (result < 0) {
return ErrnoError();
}
return std::string(buffer, result);
@@ -175,6 +177,12 @@
auto init_message = ReadMessage(init_fd_);
if (!init_message) {
+ if (init_message.error_errno() == 0) {
+ // If the init file descriptor was closed, let's exit quietly. If
+ // this was accidental, init will restart us. If init died, this
+ // avoids calling abort(3) unnecessarily.
+ return;
+ }
LOG(FATAL) << "Could not read message from init: " << init_message.error();
}
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
index bb143f1..7e05a0a 100644
--- a/init/tokenizer.cpp
+++ b/init/tokenizer.cpp
@@ -1,5 +1,7 @@
#include "tokenizer.h"
+#include <android-base/macros.h>
+
namespace android {
namespace init {
@@ -106,6 +108,7 @@
continue;
}
x++;
+ FALLTHROUGH_INTENDED;
case '\n':
/* \ <lf> -> line continuation */
state->line++;
diff --git a/init/util.cpp b/init/util.cpp
index 105ac87..3781141 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -436,5 +436,21 @@
return true;
}
+void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function) {
+ // Make stdin/stdout/stderr all point to /dev/null.
+ int fd = open("/dev/null", O_RDWR);
+ if (fd == -1) {
+ int saved_errno = errno;
+ android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+ errno = saved_errno;
+ PLOG(FATAL) << "Couldn't open /dev/null";
+ }
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ if (fd > 2) close(fd);
+ android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 07e4864..53f4547 100644
--- a/init/util.h
+++ b/init/util.h
@@ -64,6 +64,8 @@
bool IsLegalPropertyName(const std::string& name);
+void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function);
+
} // namespace init
} // namespace android
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index a10e636..7f9a18a 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -27,15 +27,6 @@
enabled: false,
},
},
-
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
}
libbacktrace_sources = [
@@ -89,35 +80,26 @@
"libbase",
"liblog",
"libunwindstack",
- "libdexfile",
],
static_libs: [
"libprocinfo",
],
-
- // libdexfile will eventually properly export headers, for now
- // include these directly.
- include_dirs: [
- "art/runtime",
- ],
},
android: {
static_libs: ["libasync_safe"],
},
vendor: {
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
- exclude_shared_libs: ["libdexfile"],
},
recovery: {
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
- exclude_shared_libs: ["libdexfile"],
},
},
whole_static_libs: ["libdemangle"],
}
-cc_library_shared {
+cc_test_library {
name: "libbacktrace_test",
defaults: ["libbacktrace_common"],
host_supported: true,
@@ -130,6 +112,17 @@
shared_libs: [
"libunwindstack",
],
+ relative_install_path: "backtrace_test_libs",
+
+ target: {
+ linux_glibc: {
+ // This forces the creation of eh_frame with unwind information
+ // for host.
+ cflags: [
+ "-fcxx-exceptions"
+ ],
+ },
+ },
}
//-------------------------------------------------------------------------
@@ -137,12 +130,12 @@
//-------------------------------------------------------------------------
cc_test {
name: "backtrace_test",
+ isolated: true,
defaults: ["libbacktrace_common"],
host_supported: true,
srcs: [
"backtrace_offline_test.cpp",
"backtrace_test.cpp",
- "GetPss.cpp",
],
cflags: [
@@ -152,9 +145,7 @@
],
shared_libs: [
- "libbacktrace_test",
"libbacktrace",
- "libdexfile",
"libbase",
"liblog",
"libunwindstack",
@@ -162,22 +153,9 @@
group_static_libs: true,
- target: {
- android: {
- cflags: ["-DENABLE_PSS_TESTS"],
- shared_libs: [
- "libutils",
- ],
- },
- linux_glibc: {
- static_libs: ["libutils"],
- },
- },
-
- // libdexfile will eventually properly export headers, for now
- // include these directly.
- include_dirs: [
- "art/runtime",
+ // So that the dlopen can find the libbacktrace_test.so.
+ ldflags: [
+ "-Wl,--rpath,${ORIGIN}/../backtrace_test_libs",
],
test_suites: ["device-tests"],
diff --git a/libbacktrace/BacktraceTest.h b/libbacktrace/BacktraceTest.h
new file mode 100644
index 0000000..c38af04
--- /dev/null
+++ b/libbacktrace/BacktraceTest.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_TEST_H
+#define _LIBBACKTRACE_BACKTRACE_TEST_H
+
+#include <dlfcn.h>
+
+#include <gtest/gtest.h>
+
+class BacktraceTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ dl_handle_ = dlopen("libbacktrace_test.so", RTLD_NOW | RTLD_LOCAL);
+
+ test_level_one_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_one"));
+
+ test_level_two_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_two"));
+
+ test_level_three_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_three"));
+
+ test_level_four_ = reinterpret_cast<int (*)(int, int, int, int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_level_four"));
+
+ test_recursive_call_ = reinterpret_cast<int (*)(int, void (*)(void*), void*)>(
+ dlsym(dl_handle_, "test_recursive_call"));
+
+ test_get_context_and_wait_ = reinterpret_cast<void (*)(void*, volatile int*)>(
+ dlsym(dl_handle_, "test_get_context_and_wait"));
+
+ test_signal_action_ =
+ reinterpret_cast<void (*)(int, siginfo_t*, void*)>(dlsym(dl_handle_, "test_signal_action"));
+
+ test_signal_handler_ =
+ reinterpret_cast<void (*)(int)>(dlsym(dl_handle_, "test_signal_handler"));
+ }
+
+ void SetUp() override {
+ ASSERT_TRUE(dl_handle_ != nullptr);
+ ASSERT_TRUE(test_level_one_ != nullptr);
+ ASSERT_TRUE(test_level_two_ != nullptr);
+ ASSERT_TRUE(test_level_three_ != nullptr);
+ ASSERT_TRUE(test_level_four_ != nullptr);
+ ASSERT_TRUE(test_recursive_call_ != nullptr);
+ ASSERT_TRUE(test_get_context_and_wait_ != nullptr);
+ ASSERT_TRUE(test_signal_action_ != nullptr);
+ ASSERT_TRUE(test_signal_handler_ != nullptr);
+ }
+
+ public:
+ static void* dl_handle_;
+ static int (*test_level_one_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_level_two_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_level_three_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_level_four_)(int, int, int, int, void (*)(void*), void*);
+ static int (*test_recursive_call_)(int, void (*)(void*), void*);
+ static void (*test_get_context_and_wait_)(void*, volatile int*);
+ static void (*test_signal_action_)(int, siginfo_t*, void*);
+ static void (*test_signal_handler_)(int);
+};
+
+#endif // _LIBBACKTRACE_BACKTRACE_TEST_H
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
deleted file mode 100644
index 6d750ea..0000000
--- a/libbacktrace/GetPss.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-// This is an extremely simplified version of libpagemap.
-
-#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
-
-#define PAGEMAP_PRESENT(x) (_BITS(x, 63, 1))
-#define PAGEMAP_SWAPPED(x) (_BITS(x, 62, 1))
-#define PAGEMAP_SHIFT(x) (_BITS(x, 55, 6))
-#define PAGEMAP_PFN(x) (_BITS(x, 0, 55))
-#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
-#define PAGEMAP_SWAP_TYPE(x) (_BITS(x, 0, 5))
-
-static bool ReadData(int fd, off_t place, uint64_t *data) {
- if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
- return false;
- }
- if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
- return false;
- }
- return true;
-}
-
-size_t GetPssBytes() {
- FILE* maps = fopen("/proc/self/maps", "r");
- if (maps == nullptr) {
- return 0;
- }
-
- int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
- if (pagecount_fd == -1) {
- fclose(maps);
- return 0;
- }
-
- int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
- if (pagemap_fd == -1) {
- fclose(maps);
- close(pagecount_fd);
- return 0;
- }
-
- char line[4096];
- size_t total_pss = 0;
- int pagesize = getpagesize();
- while (fgets(line, sizeof(line), maps)) {
- uintptr_t start, end;
- if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
- total_pss = 0;
- break;
- }
- for (off_t page = static_cast<off_t>(start/pagesize);
- page < static_cast<off_t>(end/pagesize); page++) {
- uint64_t data;
- if (ReadData(pagemap_fd, page, &data)) {
- if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
- uint64_t count;
- if (ReadData(pagecount_fd, static_cast<off_t>(PAGEMAP_PFN(data)), &count)) {
- total_pss += (count >= 1) ? pagesize / count : 0;
- }
- }
- }
- }
- }
-
- fclose(maps);
-
- close(pagecount_fd);
- close(pagemap_fd);
-
- return total_pss;
-}
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 7d1027e..662fb99 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -37,15 +37,7 @@
#include <gtest/gtest.h>
-extern "C" {
-// Prototypes for functions in the test library.
-int test_level_one(int, int, int, int, void (*)(void*), void*);
-int test_level_two(int, int, int, int, void (*)(void*), void*);
-int test_level_three(int, int, int, int, void (*)(void*), void*);
-int test_level_four(int, int, int, int, void (*)(void*), void*);
-int test_recursive_call(int, void (*)(void*), void*);
-void test_get_context_and_wait(void* context, volatile int* exit_flag);
-}
+#include "BacktraceTest.h"
struct FunctionSymbol {
std::string name;
@@ -56,12 +48,13 @@
static std::vector<FunctionSymbol> GetFunctionSymbols() {
std::vector<FunctionSymbol> symbols = {
{"unknown_start", 0, 0},
- {"test_level_one", reinterpret_cast<uint64_t>(&test_level_one), 0},
- {"test_level_two", reinterpret_cast<uint64_t>(&test_level_two), 0},
- {"test_level_three", reinterpret_cast<uint64_t>(&test_level_three), 0},
- {"test_level_four", reinterpret_cast<uint64_t>(&test_level_four), 0},
- {"test_recursive_call", reinterpret_cast<uint64_t>(&test_recursive_call), 0},
- {"test_get_context_and_wait", reinterpret_cast<uint64_t>(&test_get_context_and_wait), 0},
+ {"test_level_one", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_one_), 0},
+ {"test_level_two", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_two_), 0},
+ {"test_level_three", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_three_), 0},
+ {"test_level_four", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_four_), 0},
+ {"test_recursive_call", reinterpret_cast<uint64_t>(&BacktraceTest::test_recursive_call_), 0},
+ {"test_get_context_and_wait",
+ reinterpret_cast<uint64_t>(&BacktraceTest::test_get_context_and_wait_), 0},
{"unknown_end", static_cast<uint64_t>(-1), static_cast<uint64_t>(-1)},
};
std::sort(
@@ -100,7 +93,7 @@
static void* OfflineThreadFunc(void* arg) {
OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
fn_arg->tid = android::base::GetThreadId();
- test_get_context_and_wait(&fn_arg->ucontext, &fn_arg->exit_flag);
+ BacktraceTest::test_get_context_and_wait_(&fn_arg->ucontext, &fn_arg->exit_flag);
return nullptr;
}
@@ -109,7 +102,7 @@
}
// This test is disable because it is for generating test data.
-TEST(libbacktrace, DISABLED_generate_offline_testdata) {
+TEST_F(BacktraceTest, DISABLED_generate_offline_testdata) {
// Create a thread to generate the needed stack and registers information.
const size_t stack_size = 16 * 1024;
void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
@@ -304,22 +297,22 @@
}
// For now, these tests can only run on the given architectures.
-TEST(libbacktrace, offline_eh_frame) {
+TEST_F(BacktraceTest, offline_eh_frame) {
BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
}
-TEST(libbacktrace, offline_debug_frame) {
+TEST_F(BacktraceTest, offline_debug_frame) {
BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
}
-TEST(libbacktrace, offline_gnu_debugdata) {
+TEST_F(BacktraceTest, offline_gnu_debugdata) {
BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
}
-TEST(libbacktrace, offline_arm_exidx) {
+TEST_F(BacktraceTest, offline_arm_exidx) {
BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
}
@@ -373,32 +366,32 @@
// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
// overlap with each other, which appears in /system/lib/libart.so.
-TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
+TEST_F(BacktraceTest, offline_unwind_mix_eh_frame_and_arm_exidx) {
LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
}
-TEST(libbacktrace, offline_debug_frame_with_load_bias) {
+TEST_F(BacktraceTest, offline_debug_frame_with_load_bias) {
LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
}
-TEST(libbacktrace, offline_try_armexidx_after_debug_frame) {
+TEST_F(BacktraceTest, offline_try_armexidx_after_debug_frame) {
LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
}
-TEST(libbacktrace, offline_cie_with_P_augmentation) {
+TEST_F(BacktraceTest, offline_cie_with_P_augmentation) {
// Make sure we can unwind through functions with CIE entry containing P augmentation, which
// makes unwinding library reading personality handler from memory. One example is
// /system/lib64/libskia.so.
LibUnwindingTest("arm64", "offline_testdata_for_libskia", "libskia.so");
}
-TEST(libbacktrace, offline_empty_eh_frame_hdr) {
+TEST_F(BacktraceTest, offline_empty_eh_frame_hdr) {
// Make sure we can unwind through libraries with empty .eh_frame_hdr section. One example is
// /vendor/lib64/egl/eglSubDriverAndroid.so.
LibUnwindingTest("arm64", "offline_testdata_for_eglSubDriverAndroid", "eglSubDriverAndroid.so");
}
-TEST(libbacktrace, offline_max_frames_limit) {
+TEST_F(BacktraceTest, offline_max_frames_limit) {
// The length of callchain can reach 256 when recording an application.
ASSERT_GE(MAX_BACKTRACE_FRAMES, 256);
}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 06a32c7..f4191b9 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <malloc.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
@@ -55,6 +56,7 @@
// For the THREAD_SIGNAL definition.
#include "BacktraceCurrent.h"
+#include "BacktraceTest.h"
#include "backtrace_testlib.h"
// Number of microseconds per milliseconds.
@@ -95,6 +97,23 @@
static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
map_create_func_t map_func = nullptr);
+void* BacktraceTest::dl_handle_;
+int (*BacktraceTest::test_level_one_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_two_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_three_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_level_four_)(int, int, int, int, void (*)(void*), void*);
+int (*BacktraceTest::test_recursive_call_)(int, void (*)(void*), void*);
+void (*BacktraceTest::test_get_context_and_wait_)(void*, volatile int*);
+void (*BacktraceTest::test_signal_action_)(int, siginfo_t*, void*);
+void (*BacktraceTest::test_signal_handler_)(int);
+
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+ static const char* initial_args[] = {"--slow_threshold_ms=8000", "--deadline_threshold_ms=15000"};
+ *args = initial_args;
+ *num_args = 2;
+ return true;
+}
+
static uint64_t NanoTime() {
struct timespec t = { 0, 0 };
clock_gettime(CLOCK_MONOTONIC, &t);
@@ -250,7 +269,7 @@
return false;
}
-TEST(libbacktrace, local_no_unwind_frames) {
+TEST_F(BacktraceTest, local_no_unwind_frames) {
// Verify that a local unwind does not include any frames within
// libunwind or libbacktrace.
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
@@ -270,7 +289,7 @@
}
}
-TEST(libbacktrace, local_unwind_frames) {
+TEST_F(BacktraceTest, local_unwind_frames) {
// Verify that a local unwind with the skip frames disabled does include
// frames within the backtrace libraries.
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
@@ -302,8 +321,8 @@
<< DumpFrames(backtrace.get());
}
-TEST(libbacktrace, local_trace) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, local_trace) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
}
static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
@@ -357,12 +376,12 @@
VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
}
-TEST(libbacktrace, local_trace_ignore_frames) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
+TEST_F(BacktraceTest, local_trace_ignore_frames) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelIgnoreFrames, nullptr), 0);
}
-TEST(libbacktrace, local_max_trace) {
- ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
+TEST_F(BacktraceTest, local_max_trace) {
+ ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxBacktrace, nullptr), 0);
}
static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
@@ -402,10 +421,10 @@
ASSERT_TRUE(verified) << "Last backtrace:\n" << last_dump;
}
-TEST(libbacktrace, ptrace_trace) {
+TEST_F(BacktraceTest, ptrace_trace) {
pid_t pid;
if ((pid = fork()) == 0) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump,
@@ -416,10 +435,10 @@
ASSERT_EQ(waitpid(pid, &status, 0), pid);
}
-TEST(libbacktrace, ptrace_max_trace) {
+TEST_F(BacktraceTest, ptrace_max_trace) {
pid_t pid;
if ((pid = fork()) == 0) {
- ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, nullptr, nullptr), 0);
+ ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, nullptr, nullptr), 0);
_exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump, Backtrace::Create,
@@ -446,10 +465,10 @@
VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
}
-TEST(libbacktrace, ptrace_ignore_frames) {
+TEST_F(BacktraceTest, ptrace_ignore_frames) {
pid_t pid;
if ((pid = fork()) == 0) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames,
@@ -462,7 +481,7 @@
// Create a process with multiple threads and dump all of the threads.
static void* PtraceThreadLevelRun(void*) {
- EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
return nullptr;
}
@@ -483,7 +502,7 @@
}
}
-TEST(libbacktrace, ptrace_threads) {
+TEST_F(BacktraceTest, ptrace_threads) {
pid_t pid;
if ((pid = fork()) == 0) {
for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
@@ -494,7 +513,7 @@
pthread_t thread;
ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, nullptr) == 0);
}
- ASSERT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, nullptr, nullptr), 0);
_exit(1);
}
@@ -532,8 +551,8 @@
VerifyLevelDump(backtrace.get());
}
-TEST(libbacktrace, thread_current_level) {
- ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_level) {
+ ASSERT_NE(test_level_one_(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
}
static void VerifyMaxThread(void*) {
@@ -545,19 +564,19 @@
VerifyMaxDump(backtrace.get());
}
-TEST(libbacktrace, thread_current_max) {
- ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, nullptr), 0);
+TEST_F(BacktraceTest, thread_current_max) {
+ ASSERT_NE(test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, VerifyMaxThread, nullptr), 0);
}
static void* ThreadLevelRun(void* data) {
thread_t* thread = reinterpret_cast<thread_t*>(data);
thread->tid = android::base::GetThreadId();
- EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
+ EXPECT_NE(BacktraceTest::test_level_one_(1, 2, 3, 4, ThreadSetState, data), 0);
return nullptr;
}
-TEST(libbacktrace, thread_level_trace) {
+TEST_F(BacktraceTest, thread_level_trace) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -607,7 +626,7 @@
EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
}
-TEST(libbacktrace, thread_ignore_frames) {
+TEST_F(BacktraceTest, thread_ignore_frames) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -644,11 +663,12 @@
thread_t* thread = reinterpret_cast<thread_t*>(data);
thread->tid = android::base::GetThreadId();
- EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
+ EXPECT_NE(BacktraceTest::test_recursive_call_(MAX_BACKTRACE_FRAMES + 10, ThreadSetState, data),
+ 0);
return nullptr;
}
-TEST(libbacktrace, thread_max_trace) {
+TEST_F(BacktraceTest, thread_max_trace) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -742,17 +762,17 @@
}
}
-TEST(libbacktrace, thread_multiple_dump) {
+TEST_F(BacktraceTest, thread_multiple_dump) {
MultipleThreadDumpTest(false);
}
-TEST(libbacktrace, thread_multiple_dump_same_map) {
+TEST_F(BacktraceTest, thread_multiple_dump_same_map) {
MultipleThreadDumpTest(true);
}
// This test is for UnwindMaps that should share the same map cursor when
// multiple maps are created for the current process at the same time.
-TEST(libbacktrace, simultaneous_maps) {
+TEST_F(BacktraceTest, simultaneous_maps) {
BacktraceMap* map1 = BacktraceMap::Create(getpid());
BacktraceMap* map2 = BacktraceMap::Create(getpid());
BacktraceMap* map3 = BacktraceMap::Create(getpid());
@@ -779,7 +799,7 @@
delete map3;
}
-TEST(libbacktrace, fillin_erases) {
+TEST_F(BacktraceTest, fillin_erases) {
BacktraceMap* back_map = BacktraceMap::Create(getpid());
backtrace_map_t map;
@@ -798,7 +818,7 @@
ASSERT_EQ("", map.name);
}
-TEST(libbacktrace, format_test) {
+TEST_F(BacktraceTest, format_test) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
@@ -969,7 +989,7 @@
ASSERT_TRUE(test_it == test_maps.end());
}
-TEST(libbacktrace, verify_map_remote) {
+TEST_F(BacktraceTest, verify_map_remote) {
pid_t pid;
CreateRemoteProcess(&pid);
@@ -1069,7 +1089,7 @@
delete[] expected;
}
-TEST(libbacktrace, thread_read) {
+TEST_F(BacktraceTest, thread_read) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -1120,7 +1140,7 @@
}
}
-TEST(libbacktrace, process_read) {
+TEST_F(BacktraceTest, process_read) {
g_ready = 0;
pid_t pid;
if ((pid = fork()) == 0) {
@@ -1187,29 +1207,23 @@
}
static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
- std::string system_dir;
-
-#if defined(__BIONIC__)
- system_dir = "/system/lib";
-#else
- const char* host_out_env = getenv("ANDROID_HOST_OUT");
- ASSERT_TRUE(host_out_env != nullptr);
- system_dir = std::string(host_out_env) + "/lib";
-#endif
-
-#if defined(__LP64__)
- system_dir += "64";
-#endif
+ std::string test_lib(testing::internal::GetArgvs()[0]);
+ auto const value = test_lib.find_last_of('/');
+ if (value == std::string::npos) {
+ test_lib = "../backtrace_test_libs/";
+ } else {
+ test_lib = test_lib.substr(0, value + 1) + "../backtrace_test_libs/";
+ }
+ test_lib += "libbacktrace_test.so";
*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);
+ std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.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) {
+TEST_F(BacktraceTest, check_unreadable_elf_local) {
TemporaryDir td;
std::string tmp_so_name;
ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1251,7 +1265,7 @@
VerifyFunctionsFound(found_functions);
}
-TEST(libbacktrace, check_unreadable_elf_remote) {
+TEST_F(BacktraceTest, check_unreadable_elf_remote) {
TemporaryDir td;
std::string tmp_so_name;
ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1390,7 +1404,7 @@
typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
-TEST(libbacktrace, unwind_through_unreadable_elf_local) {
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_local) {
TemporaryDir td;
std::string tmp_so_name;
ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1405,11 +1419,9 @@
ASSERT_NE(test_func(1, 2, 3, 4, VerifyUnreadableElfBacktrace, reinterpret_cast<void*>(test_func)),
0);
-
- ASSERT_TRUE(dlclose(lib_handle) == 0);
}
-TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
+TEST_F(BacktraceTest, unwind_through_unreadable_elf_remote) {
TemporaryDir td;
std::string tmp_so_name;
ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
@@ -1428,7 +1440,6 @@
exit(0);
}
ASSERT_TRUE(pid > 0);
- ASSERT_TRUE(dlclose(lib_handle) == 0);
uint64_t start = NanoTime();
bool done = false;
@@ -1465,7 +1476,7 @@
ASSERT_TRUE(done) << "Test function never found in unwind.";
}
-TEST(libbacktrace, unwind_thread_doesnt_exist) {
+TEST_F(BacktraceTest, unwind_thread_doesnt_exist) {
std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
ASSERT_TRUE(backtrace.get() != nullptr);
@@ -1473,18 +1484,18 @@
ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError().error_code);
}
-TEST(libbacktrace, local_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, local_get_function_name_before_unwind) {
std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
// Verify that trying to get a function name before doing an unwind works.
- uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+ uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
uint64_t offset;
ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
}
-TEST(libbacktrace, remote_get_function_name_before_unwind) {
+TEST_F(BacktraceTest, remote_get_function_name_before_unwind) {
pid_t pid;
CreateRemoteProcess(&pid);
@@ -1492,7 +1503,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
// Verify that trying to get a function name before doing an unwind works.
- uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+ uint64_t cur_func_offset = reinterpret_cast<uint64_t>(test_level_one_) + 1;
uint64_t offset;
ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
@@ -1579,7 +1590,7 @@
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(0, &offset));
- uint64_t cur_func_offset = reinterpret_cast<uint64_t>(&test_level_one) + 1;
+ uint64_t cur_func_offset = reinterpret_cast<uint64_t>(BacktraceTest::test_level_one_) + 1;
// Now verify the device map flag actually causes the function name to be empty.
backtrace->FillInMap(cur_func_offset, &map);
ASSERT_TRUE((map.flags & PROT_DEVICE_MAP) == 0);
@@ -1628,7 +1639,7 @@
ASSERT_EQ(0U, backtrace->NumFrames());
}
-TEST(libbacktrace, unwind_disallow_device_map_local) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_local) {
void* device_map;
SetupDeviceMap(&device_map);
@@ -1642,7 +1653,7 @@
munmap(device_map, DEVICE_MAP_SIZE);
}
-TEST(libbacktrace, unwind_disallow_device_map_remote) {
+TEST_F(BacktraceTest, unwind_disallow_device_map_remote) {
void* device_map;
SetupDeviceMap(&device_map);
@@ -1698,13 +1709,13 @@
pid_t pid;
if ((pid = fork()) == 0) {
if (use_action) {
- ScopedSignalHandler ssh(SIGUSR1, test_signal_action);
+ ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_action_);
- test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
} else {
- ScopedSignalHandler ssh(SIGUSR1, test_signal_handler);
+ ScopedSignalHandler ssh(SIGUSR1, BacktraceTest::test_signal_handler_);
- test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ BacktraceTest::test_level_one_(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
}
}
ASSERT_NE(-1, pid);
@@ -1805,11 +1816,11 @@
FinishRemoteProcess(pid);
}
-TEST(libbacktrace, unwind_remote_through_signal_using_handler) {
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_handler) {
UnwindThroughSignal(false, Backtrace::Create, BacktraceMap::Create);
}
-TEST(libbacktrace, unwind_remote_through_signal_using_action) {
+TEST_F(BacktraceTest, unwind_remote_through_signal_using_action) {
UnwindThroughSignal(true, Backtrace::Create, BacktraceMap::Create);
}
@@ -1822,49 +1833,41 @@
ASSERT_EQ(0U, backtrace->GetFrame(0)->num);
}
-TEST(libbacktrace, unwind_frame_skip_numbering) {
+TEST_F(BacktraceTest, unwind_frame_skip_numbering) {
TestFrameSkipNumbering(Backtrace::Create, BacktraceMap::Create);
}
-#if defined(ENABLE_PSS_TESTS)
-#include "GetPss.h"
-
#define MAX_LEAK_BYTES (32*1024UL)
static void CheckForLeak(pid_t pid, pid_t tid) {
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
- // Do a few runs to get the PSS stable.
- for (size_t i = 0; i < 100; i++) {
- Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
- ASSERT_TRUE(backtrace != nullptr);
- ASSERT_TRUE(backtrace->Unwind(0));
- VERIFY_NO_ERROR(backtrace->GetError().error_code);
- delete backtrace;
- }
- size_t stable_pss = GetPssBytes();
- ASSERT_TRUE(stable_pss != 0);
-
// Loop enough that even a small leak should be detectable.
+ size_t first_allocated_bytes = 0;
+ size_t last_allocated_bytes = 0;
for (size_t i = 0; i < 4096; i++) {
Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
VERIFY_NO_ERROR(backtrace->GetError().error_code);
delete backtrace;
- }
- size_t new_pss = GetPssBytes();
- ASSERT_TRUE(new_pss != 0);
- if (new_pss > stable_pss) {
- ASSERT_LE(new_pss - stable_pss, MAX_LEAK_BYTES);
+
+ size_t allocated_bytes = mallinfo().uordblks;
+ if (first_allocated_bytes == 0) {
+ first_allocated_bytes = allocated_bytes;
+ } else if (last_allocated_bytes > first_allocated_bytes) {
+ // Check that the memory did not increase too much over the first loop.
+ ASSERT_LE(last_allocated_bytes - first_allocated_bytes, MAX_LEAK_BYTES);
+ }
+ last_allocated_bytes = allocated_bytes;
}
}
-TEST(libbacktrace, check_for_leak_local) {
+TEST_F(BacktraceTest, check_for_leak_local) {
CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
}
-TEST(libbacktrace, check_for_leak_local_thread) {
+TEST_F(BacktraceTest, check_for_leak_local_thread) {
thread_t thread_data = { 0, 0, 0, nullptr };
pthread_t thread;
ASSERT_TRUE(pthread_create(&thread, nullptr, ThreadLevelRun, &thread_data) == 0);
@@ -1880,7 +1883,7 @@
ASSERT_TRUE(pthread_join(thread, nullptr) == 0);
}
-TEST(libbacktrace, check_for_leak_remote) {
+TEST_F(BacktraceTest, check_for_leak_remote) {
pid_t pid;
CreateRemoteProcess(&pid);
@@ -1888,4 +1891,3 @@
FinishRemoteProcess(pid);
}
-#endif
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index bcc0616..d27feb9 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-cc_library_static {
+cc_library {
name: "libgrallocusage",
vendor_available: true,
cflags: [
diff --git a/libgrallocusage/OWNERS b/libgrallocusage/OWNERS
new file mode 100644
index 0000000..154dc6d
--- /dev/null
+++ b/libgrallocusage/OWNERS
@@ -0,0 +1,3 @@
+jessehall@google.com
+olv@google.com
+stoza@google.com
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 2e2bf87..574a386 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -72,7 +72,7 @@
explicit MapString(const std::string& str)
: alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
}
- MapString(MapString&& rval)
+ MapString(MapString&& rval) noexcept
: alloc(rval.alloc), str(rval.data(), rval.length()) {
rval.alloc = NULL;
}
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index e7b1728..53653de 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -56,15 +56,24 @@
/*
* Use __VA_ARGS__ if running a static analyzer,
* to avoid warnings of unused variables in __VA_ARGS__.
- * __FAKE_USE_VA_ARGS is undefined at link time,
- * so don't link with __clang_analyzer__ defined.
+ * Use contexpr function in C++ mode, so these macros can be used
+ * in other constexpr functions without warning.
*/
#ifdef __clang_analyzer__
-extern void __fake_use_va_args(int, ...);
-#define __FAKE_USE_VA_ARGS(...) __fake_use_va_args(0, ##__VA_ARGS__)
+#ifdef __cplusplus
+extern "C++" {
+template <typename... Ts>
+constexpr int __fake_use_va_args(Ts...) {
+ return 0;
+}
+}
+#else
+extern int __fake_use_va_args(int, ...);
+#endif /* __cplusplus */
+#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__))
#else
#define __FAKE_USE_VA_ARGS(...) ((void)(0))
-#endif
+#endif /* __clang_analyzer__ */
#ifndef __predict_false
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index a8a9a12..383d0e7 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -30,6 +30,7 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#ifdef __ANDROID__ // includes sys/properties.h which does not exist outside
#include <cutils/properties.h>
@@ -130,7 +131,7 @@
static bool isLogdwActive() {
std::string logdwSignature =
- popenToString("grep /dev/socket/logdw /proc/net/unix");
+ popenToString("grep -a /dev/socket/logdw /proc/net/unix");
size_t beginning = logdwSignature.find(' ');
if (beginning == std::string::npos) return true;
beginning = logdwSignature.find(' ', beginning + 1);
@@ -144,7 +145,7 @@
end = logdwSignature.find(' ', end + 1);
if (end == std::string::npos) return true;
std::string allLogdwEndpoints = popenToString(
- "grep ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
+ "grep -a ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
" ' /proc/net/unix | " +
"sed -n 's/.* \\([0-9][0-9]*\\)$/ -> socket:[\\1]/p'");
if (allLogdwEndpoints.length() == 0) return true;
@@ -2516,7 +2517,7 @@
#endif
elem.data.string = const_cast<char*>("<unknown>");
elem.len = strlen(elem.data.string);
- /* FALLTHRU */
+ FALLTHROUGH_INTENDED;
case EVENT_TYPE_STRING:
if (elem.len <= strOutLen) {
memcpy(strOut, elem.data.string, elem.len);
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 5c7ec13..92a8325 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -52,7 +52,7 @@
allocation_bytes_(0),
roots_(allocator),
root_vals_(allocator),
- segv_handler_(allocator),
+ segv_handler_(),
walking_ptr_(0) {
valid_allocations_range_.end = 0;
valid_allocations_range_.begin = ~valid_allocations_range_.end;
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
index 69f320c..074dc48 100644
--- a/libmemunreachable/LeakFolding.cpp
+++ b/libmemunreachable/LeakFolding.cpp
@@ -57,7 +57,7 @@
}
void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
- std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_, [&](SCCInfo* scc) {
+ std::function<void(SCCInfo*)> walk([&](SCCInfo* scc) {
if (scc->accumulator != dominator) {
scc->accumulator = dominator;
dominator->cuumulative_size += scc->size;
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
index adabfd8..b9dead5 100644
--- a/libmemunreachable/ScopedPipe.h
+++ b/libmemunreachable/ScopedPipe.h
@@ -33,12 +33,12 @@
}
~ScopedPipe() { Close(); }
- ScopedPipe(ScopedPipe&& other) {
+ ScopedPipe(ScopedPipe&& other) noexcept {
SetReceiver(other.ReleaseReceiver());
SetSender(other.ReleaseSender());
}
- ScopedPipe& operator=(ScopedPipe&& other) {
+ ScopedPipe& operator=(ScopedPipe&& other) noexcept {
SetReceiver(other.ReleaseReceiver());
SetSender(other.ReleaseSender());
return *this;
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index ff53fad..9e08a8e 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -32,15 +32,14 @@
public:
using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
- explicit ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
+ explicit ScopedSignalHandler() : signal_(-1) {}
~ScopedSignalHandler() { reset(); }
template <class F>
void install(int signal, F&& f) {
if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
- handler_ = SignalFn(std::allocator_arg, allocator_,
- [=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
+ handler_ = SignalFn([=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
struct sigaction act {};
act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
@@ -68,7 +67,6 @@
private:
using SignalFn = std::function<void(int, siginfo_t*, void*)>;
DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
- Allocator<Fn> allocator_;
int signal_;
struct sigaction old_act_;
// TODO(ccross): to support multiple ScopedSignalHandlers handler_ would need
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
index 355679f..f3ab652 100644
--- a/libmemunreachable/Tarjan.h
+++ b/libmemunreachable/Tarjan.h
@@ -38,7 +38,7 @@
Node(T* ptr, Allocator<Node> allocator)
: references_in(allocator), references_out(allocator), ptr(ptr){};
- Node(Node&& rhs) = default;
+ Node(Node&& rhs) noexcept = default;
void Edge(Node<T>* ref) {
references_out.emplace(ref);
ref->references_in.emplace(this);
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index e6e17ce..1551b5b 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -54,12 +54,12 @@
// -----------------------------------------------------------------------------
cc_test {
name: "metricslogger_tests",
+ isolated: true,
defaults: ["metricslogger_defaults"],
shared_libs: [
"libbase",
"libmetricslogger_debug",
],
- static_libs: ["libBionicGtestMain"],
srcs: [
"metrics_logger_test.cpp",
],
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
index 9bfc935..28f1927 100644
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -99,7 +99,7 @@
bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename);
// Decrements the reference count on the dynamic library handler. If the reference count drops
-// to zero then the dynamic library is unloaded.
+// to zero then the dynamic library is unloaded. Returns 0 on success and non-zero on error.
int NativeBridgeUnloadLibrary(void* handle);
// Get last error message of native bridge when fail to load library or search symbol.
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 19a1783..c1d9d2a 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -47,8 +47,9 @@
bool* needs_native_bridge,
std::string* error_msg);
-__attribute__((visibility("default")))
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge);
+__attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle,
+ const bool needs_native_bridge,
+ std::string* error_msg);
#if defined(__ANDROID__)
// Look up linker namespace by class_loader. Returns nullptr if
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 67c1c10..b3e2b97 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -710,9 +710,21 @@
#endif
}
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge) {
- return needs_native_bridge ? NativeBridgeUnloadLibrary(handle) :
- dlclose(handle);
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, std::string* error_msg) {
+ bool success;
+ if (needs_native_bridge) {
+ success = (NativeBridgeUnloadLibrary(handle) == 0);
+ if (!success) {
+ *error_msg = NativeBridgeGetError();
+ }
+ } else {
+ success = (dlclose(handle) == 0);
+ if (!success) {
+ *error_msg = dlerror();
+ }
+ }
+
+ return success;
}
#if defined(__ANDROID__)
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 14883f4..8c80f6a 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -73,6 +73,7 @@
LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+LOCAL_HEADER_LIBRARIES := libbase_headers
LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
include $(BUILD_SHARED_LIBRARY)
diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp
index dcb95c5..ea9514c 100644
--- a/libpixelflinger/buffer.cpp
+++ b/libpixelflinger/buffer.cpp
@@ -18,6 +18,8 @@
#include <assert.h>
+#include <android-base/macros.h>
+
#include "buffer.h"
namespace android {
@@ -266,8 +268,11 @@
p = downshift_component(p, b, hbits, lbits, f->bh, f->bl, 0, 1, -1);
p = downshift_component(p, a, hbits, lbits, f->ah, f->al, 0, 1, -1);
switch (f->size) {
- case 1: p |= p << 8; // fallthrough
- case 2: p |= p << 16;
+ case 1:
+ p |= p << 8;
+ FALLTHROUGH_INTENDED;
+ case 2:
+ p |= p << 16;
}
return p;
}
diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp
index a55dfe3..2cbb00f 100644
--- a/libpixelflinger/codeflinger/blending.cpp
+++ b/libpixelflinger/codeflinger/blending.cpp
@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <sys/types.h>
+#include <android-base/macros.h>
#include <log/log.h>
#include "GGLAssembler.h"
@@ -301,7 +302,7 @@
return;
}
}
- // fall-through...
+ FALLTHROUGH_INTENDED;
case GGL_ONE_MINUS_DST_COLOR:
case GGL_DST_COLOR:
case GGL_ONE_MINUS_SRC_COLOR:
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
new file mode 100644
index 0000000..bfa730a
--- /dev/null
+++ b/libprocessgroup/OWNERS
@@ -0,0 +1,2 @@
+ccross@google.com
+tomcherry@google.com
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 1cebb5d..0a42053 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -185,7 +185,7 @@
// Erase all pids that will be killed when we kill the process groups.
for (auto it = pids.begin(); it != pids.end();) {
- pid_t pgid = getpgid(pid);
+ pid_t pgid = getpgid(*it);
if (pgids.count(pgid) == 1) {
it = pids.erase(it);
} else {
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index 5d174ae..a9832db 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -24,6 +24,8 @@
#endif
void reset_log_context(android_log_context ctx);
int write_to_logger(android_log_context context, log_id_t id);
+void note_log_drop();
+void stats_log_close();
#ifdef __cplusplus
}
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 3d746db..735088a 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -119,6 +119,18 @@
return retValue;
}
+void note_log_drop() {
+ statsdLoggerWrite.noteDrop();
+}
+
+void stats_log_close() {
+ statsd_writer_init_lock();
+ if (statsdLoggerWrite.close) {
+ (*statsdLoggerWrite.close)();
+ }
+ statsd_writer_init_unlock();
+}
+
/* log_init_lock assumed */
static int __write_to_statsd_initialize_locked() {
if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index 9953bba..afe401f 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -38,6 +38,7 @@
#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static atomic_int dropped = 0;
void statsd_writer_init_lock() {
/*
@@ -59,14 +60,16 @@
static int statsdOpen();
static void statsdClose();
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+static void statsdNoteDrop();
struct android_log_transport_write statsdLoggerWrite = {
- .name = "statsd",
- .sock = -EBADF,
- .available = statsdAvailable,
- .open = statsdOpen,
- .close = statsdClose,
- .write = statsdWrite,
+ .name = "statsd",
+ .sock = -EBADF,
+ .available = statsdAvailable,
+ .open = statsdOpen,
+ .close = statsdClose,
+ .write = statsdWrite,
+ .noteDrop = statsdNoteDrop,
};
/* log_init_lock assumed */
@@ -131,6 +134,10 @@
return 1;
}
+static void statsdNoteDrop() {
+ atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+}
+
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
ssize_t ret;
int sock;
@@ -138,7 +145,6 @@
struct iovec newVec[nr + headerLength];
android_log_header_t header;
size_t i, payloadSize;
- static atomic_int dropped;
sock = atomic_load(&statsdLoggerWrite.sock);
if (sock < 0) switch (sock) {
@@ -252,8 +258,6 @@
if (ret > (ssize_t)sizeof(header)) {
ret -= sizeof(header);
- } else if (ret == -EAGAIN) {
- atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
}
return ret;
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 82e14e0..7289441 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -38,6 +38,8 @@
void (*close)(); /* free up resources */
/* write log to transport, returns number of bytes propagated, or -errno */
int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
+ /* note one log drop */
+ void (*noteDrop)();
};
#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsysutils/include/sysutils/FrameworkCommand.h b/libsysutils/include/sysutils/FrameworkCommand.h
index 3e6264b..db17ba7 100644
--- a/libsysutils/include/sysutils/FrameworkCommand.h
+++ b/libsysutils/include/sysutils/FrameworkCommand.h
@@ -16,8 +16,6 @@
#ifndef __FRAMEWORK_CMD_HANDLER_H
#define __FRAMEWORK_CMD_HANDLER_H
-#include "List.h"
-
class SocketClient;
class FrameworkCommand {
@@ -31,8 +29,7 @@
virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
- const char *getCommand() { return mCommand; }
+ const char* getCommand() const { return mCommand; }
};
-typedef android::sysutils::List<FrameworkCommand *> FrameworkCommandCollection;
#endif
diff --git a/libsysutils/include/sysutils/FrameworkListener.h b/libsysutils/include/sysutils/FrameworkListener.h
index d9a561a..93d99c4 100644
--- a/libsysutils/include/sysutils/FrameworkListener.h
+++ b/libsysutils/include/sysutils/FrameworkListener.h
@@ -17,8 +17,10 @@
#define _FRAMEWORKSOCKETLISTENER_H
#include "SocketListener.h"
-#include "FrameworkCommand.h"
+#include <vector>
+
+class FrameworkCommand;
class SocketClient;
class FrameworkListener : public SocketListener {
@@ -31,7 +33,7 @@
private:
int mCommandCount;
bool mWithSeq;
- FrameworkCommandCollection *mCommands;
+ std::vector<FrameworkCommand*> mCommands;
bool mSkipToNextNullByte;
public:
diff --git a/libsysutils/include/sysutils/List.h b/libsysutils/include/sysutils/List.h
deleted file mode 100644
index 31f7b37..0000000
--- a/libsysutils/include/sysutils/List.h
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Templated list class. Normally we'd use STL, but we don't have that.
-// This class mimics STL's interfaces.
-//
-// Objects are copied into the list with the '=' operator or with copy-
-// construction, so if the compiler's auto-generated versions won't work for
-// you, define your own.
-//
-// The only class you want to use from here is "List".
-//
-#ifndef _SYSUTILS_LIST_H
-#define _SYSUTILS_LIST_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-namespace android {
-namespace sysutils {
-
-/*
- * Doubly-linked list. Instantiate with "List<MyClass> myList".
- *
- * Objects added to the list are copied using the assignment operator,
- * so this must be defined.
- */
-template<typename T>
-class List
-{
-protected:
- /*
- * One element in the list.
- */
- class _Node {
- public:
- explicit _Node(const T& val) : mVal(val) {}
- ~_Node() {}
- inline T& getRef() { return mVal; }
- inline const T& getRef() const { return mVal; }
- inline _Node* getPrev() const { return mpPrev; }
- inline _Node* getNext() const { return mpNext; }
- inline void setVal(const T& val) { mVal = val; }
- inline void setPrev(_Node* ptr) { mpPrev = ptr; }
- inline void setNext(_Node* ptr) { mpNext = ptr; }
- private:
- friend class List;
- friend class _ListIterator;
- T mVal;
- _Node* mpPrev;
- _Node* mpNext;
- };
-
- /*
- * Iterator for walking through the list.
- */
-
- template <typename TYPE>
- struct CONST_ITERATOR {
- typedef _Node const * NodePtr;
- typedef const TYPE Type;
- };
-
- template <typename TYPE>
- struct NON_CONST_ITERATOR {
- typedef _Node* NodePtr;
- typedef TYPE Type;
- };
-
- template<
- typename U,
- template <class> class Constness
- >
- class _ListIterator {
- typedef _ListIterator<U, Constness> _Iter;
- typedef typename Constness<U>::NodePtr _NodePtr;
- typedef typename Constness<U>::Type _Type;
-
- explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}
-
- public:
- _ListIterator() {}
- _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}
- ~_ListIterator() {}
-
- // this will handle conversions from iterator to const_iterator
- // (and also all convertible iterators)
- // Here, in this implementation, the iterators can be converted
- // if the nodes can be converted
- template<typename V> explicit
- _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}
-
-
- /*
- * Dereference operator. Used to get at the juicy insides.
- */
- _Type& operator*() const { return mpNode->getRef(); }
- _Type* operator->() const { return &(mpNode->getRef()); }
-
- /*
- * Iterator comparison.
- */
- inline bool operator==(const _Iter& right) const {
- return mpNode == right.mpNode; }
-
- inline bool operator!=(const _Iter& right) const {
- return mpNode != right.mpNode; }
-
- /*
- * handle comparisons between iterator and const_iterator
- */
- template<typename OTHER>
- inline bool operator==(const OTHER& right) const {
- return mpNode == right.mpNode; }
-
- template<typename OTHER>
- inline bool operator!=(const OTHER& right) const {
- return mpNode != right.mpNode; }
-
- /*
- * Incr/decr, used to move through the list.
- */
- inline _Iter& operator++() { // pre-increment
- mpNode = mpNode->getNext();
- return *this;
- }
- const _Iter operator++(int) { // post-increment
- _Iter tmp(*this);
- mpNode = mpNode->getNext();
- return tmp;
- }
- inline _Iter& operator--() { // pre-increment
- mpNode = mpNode->getPrev();
- return *this;
- }
- const _Iter operator--(int) { // post-increment
- _Iter tmp(*this);
- mpNode = mpNode->getPrev();
- return tmp;
- }
-
- inline _NodePtr getNode() const { return mpNode; }
-
- _NodePtr mpNode; /* should be private, but older gcc fails */
- private:
- friend class List;
- };
-
-public:
- List() {
- prep();
- }
- List(const List<T>& src) { // copy-constructor
- prep();
- insert(begin(), src.begin(), src.end());
- }
- virtual ~List() {
- clear();
- delete[] (unsigned char*) mpMiddle;
- }
-
- typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;
- typedef _ListIterator<T, CONST_ITERATOR> const_iterator;
-
- List<T>& operator=(const List<T>& right);
-
- /* returns true if the list is empty */
- inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }
-
- /* return #of elements in list */
- size_t size() const {
- return size_t(distance(begin(), end()));
- }
-
- /*
- * Return the first element or one past the last element. The
- * _Node* we're returning is converted to an "iterator" by a
- * constructor in _ListIterator.
- */
- inline iterator begin() {
- return iterator(mpMiddle->getNext());
- }
- inline const_iterator begin() const {
- return const_iterator(const_cast<_Node const*>(mpMiddle->getNext()));
- }
- inline iterator end() {
- return iterator(mpMiddle);
- }
- inline const_iterator end() const {
- return const_iterator(const_cast<_Node const*>(mpMiddle));
- }
-
- /* add the object to the head or tail of the list */
- void push_front(const T& val) { insert(begin(), val); }
- void push_back(const T& val) { insert(end(), val); }
-
- /* insert before the current node; returns iterator at new node */
- iterator insert(iterator posn, const T& val)
- {
- _Node* newNode = new _Node(val); // alloc & copy-construct
- newNode->setNext(posn.getNode());
- newNode->setPrev(posn.getNode()->getPrev());
- posn.getNode()->getPrev()->setNext(newNode);
- posn.getNode()->setPrev(newNode);
- return iterator(newNode);
- }
-
- /* insert a range of elements before the current node */
- void insert(iterator posn, const_iterator first, const_iterator last) {
- for ( ; first != last; ++first)
- insert(posn, *first);
- }
-
- /* remove one entry; returns iterator at next node */
- iterator erase(iterator posn) {
- _Node* pNext = posn.getNode()->getNext();
- _Node* pPrev = posn.getNode()->getPrev();
- pPrev->setNext(pNext);
- pNext->setPrev(pPrev);
- delete posn.getNode();
- return iterator(pNext);
- }
-
- /* remove a range of elements */
- iterator erase(iterator first, iterator last) {
- while (first != last)
- erase(first++); // don't erase than incr later!
- return iterator(last);
- }
-
- /* remove all contents of the list */
- void clear() {
- _Node* pCurrent = mpMiddle->getNext();
- _Node* pNext;
-
- while (pCurrent != mpMiddle) {
- pNext = pCurrent->getNext();
- delete pCurrent;
- pCurrent = pNext;
- }
- mpMiddle->setPrev(mpMiddle);
- mpMiddle->setNext(mpMiddle);
- }
-
- /*
- * Measure the distance between two iterators. On exist, "first"
- * will be equal to "last". The iterators must refer to the same
- * list.
- *
- * FIXME: This is actually a generic iterator function. It should be a
- * template function at the top-level with specializations for things like
- * vector<>, which can just do pointer math). Here we limit it to
- * _ListIterator of the same type but different constness.
- */
- template<
- typename U,
- template <class> class CL,
- template <class> class CR
- >
- ptrdiff_t distance(
- _ListIterator<U, CL> first, _ListIterator<U, CR> last) const
- {
- ptrdiff_t count = 0;
- while (first != last) {
- ++first;
- ++count;
- }
- return count;
- }
-
-private:
- /*
- * I want a _Node but don't need it to hold valid data. More
- * to the point, I don't want T's constructor to fire, since it
- * might have side-effects or require arguments. So, we do this
- * slightly uncouth storage alloc.
- */
- void prep() {
- mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];
- mpMiddle->setPrev(mpMiddle);
- mpMiddle->setNext(mpMiddle);
- }
-
- /*
- * This node plays the role of "pointer to head" and "pointer to tail".
- * It sits in the middle of a circular list of nodes. The iterator
- * runs around the circle until it encounters this one.
- */
- _Node* mpMiddle;
-};
-
-/*
- * Assignment operator.
- *
- * The simplest way to do this would be to clear out the target list and
- * fill it with the source. However, we can speed things along by
- * re-using existing elements.
- */
-template<class T>
-List<T>& List<T>::operator=(const List<T>& right)
-{
- if (this == &right)
- return *this; // self-assignment
- iterator firstDst = begin();
- iterator lastDst = end();
- const_iterator firstSrc = right.begin();
- const_iterator lastSrc = right.end();
- while (firstSrc != lastSrc && firstDst != lastDst)
- *firstDst++ = *firstSrc++;
- if (firstSrc == lastSrc) // ran out of elements in source?
- erase(firstDst, lastDst); // yes, erase any extras
- else
- insert(lastDst, firstSrc, lastSrc); // copy remaining over
- return *this;
-}
-
-}; // namespace sysutils
-}; // namespace android
-
-#endif // _SYSUTILS_LIST_H
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 835e226..b07853a 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -28,8 +28,6 @@
static const int CMD_BUF_SIZE = 1024;
-#define UNUSED __attribute__((unused))
-
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
SocketListener(socketName, true, withSeq) {
init(socketName, withSeq);
@@ -45,8 +43,7 @@
init(nullptr, false);
}
-void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
- mCommands = new FrameworkCommandCollection();
+void FrameworkListener::init(const char* /*socketName*/, bool withSeq) {
errorRate = 0;
mCommandCount = 0;
mWithSeq = withSeq;
@@ -91,11 +88,10 @@
}
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
- mCommands->push_back(cmd);
+ mCommands.push_back(cmd);
}
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
- FrameworkCommandCollection::iterator i;
int argc = 0;
char *argv[FrameworkListener::CMD_ARGS_MAX];
char tmp[CMD_BUF_SIZE];
@@ -193,9 +189,7 @@
goto out;
}
- for (i = mCommands->begin(); i != mCommands->end(); ++i) {
- FrameworkCommand *c = *i;
-
+ for (auto* c : mCommands) {
if (!strcmp(argv[0], c->getCommand())) {
if (c->runCommand(cli, argc, argv)) {
SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index 0625db7..fe2f3d6 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -27,6 +27,8 @@
#include <sys/types.h>
#include <unistd.h>
+#include <android-base/file.h>
+#include <android-base/macros.h>
#include <log/log.h>
#include <sysutils/SocketClient.h>
@@ -145,7 +147,8 @@
switch (*arg) {
case '\\':
case '"':
- *(current++) = '\\'; // fallthrough
+ *(current++) = '\\';
+ FALLTHROUGH_INTENDED;
default:
*(current++) = *(arg++);
}
diff --git a/libsysutils/src/SocketListener_test.cpp b/libsysutils/src/SocketListener_test.cpp
index fed4546..d6bfd02 100644
--- a/libsysutils/src/SocketListener_test.cpp
+++ b/libsysutils/src/SocketListener_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <sysutils/FrameworkCommand.h>
#include <sysutils/FrameworkListener.h>
#include <poll.h>
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index be2145d..970e05c 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -178,6 +178,7 @@
"tests/JitDebugTest.cpp",
"tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
+ "tests/MapInfoCreateMemoryTest.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
"tests/MapsTest.cpp",
@@ -188,6 +189,7 @@
"tests/MemoryOfflineBufferTest.cpp",
"tests/MemoryOfflineTest.cpp",
"tests/MemoryRangeTest.cpp",
+ "tests/MemoryRangesTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/MemoryTest.cpp",
"tests/RegsInfoTest.cpp",
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index cd9ef61..0fa1638 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -21,6 +21,7 @@
#include <type_traits>
#include <vector>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <unwindstack/DwarfError.h>
@@ -154,13 +155,15 @@
break;
case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
*cur_pc += value;
- // Fall through to log the value.
+ FALLTHROUGH_INTENDED;
+ // Fall through to log the value.
case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
string += " " + std::to_string(value);
break;
case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
*cur_pc = value;
- // Fall through to log the value.
+ FALLTHROUGH_INTENDED;
+ // Fall through to log the value.
case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
if (std::is_same<AddressType, uint32_t>::value) {
string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 6061f61..57a780e 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -461,6 +461,7 @@
if (reg == eval_info->cie->return_address_register) {
eval_info->return_address_undefined = true;
}
+ break;
default:
break;
}
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 39378a3..64005ae 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -102,7 +102,54 @@
if (!(flags & PROT_READ)) {
return nullptr;
}
- return new MemoryRange(process_memory, start, end - start, 0);
+
+ // Need to verify that this elf is valid. It's possible that
+ // only part of the elf file to be mapped into memory is in the executable
+ // map. In this case, there will be another read-only map that includes the
+ // first part of the elf file. This is done if the linker rosegment
+ // option is used.
+ std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
+ bool valid;
+ uint64_t max_size;
+ Elf::GetInfo(memory.get(), &valid, &max_size);
+ if (valid) {
+ // Valid elf, we are done.
+ return memory.release();
+ }
+
+ if (name.empty() || maps_ == nullptr) {
+ return nullptr;
+ }
+
+ // Find the read-only map that has the same name and has an offset closest
+ // to the current offset but less than the offset of the current map.
+ // For shared libraries, there should be a r-x map that has a non-zero
+ // offset and then a r-- map that has a zero offset.
+ // For shared libraries loaded from an apk, there should be a r-x map that
+ // has a non-zero offset and then a r-- map that has a non-zero offset less
+ // than the offset from the r-x map.
+ uint64_t closest_offset = 0;
+ MapInfo* ro_map_info = nullptr;
+ for (auto map_info : *maps_) {
+ if (map_info->flags == PROT_READ && map_info->name == name && map_info->offset < offset &&
+ map_info->offset >= closest_offset) {
+ ro_map_info = map_info;
+ closest_offset = ro_map_info->offset;
+ }
+ }
+
+ if (ro_map_info != nullptr) {
+ // Make sure that relative pc values are corrected properly.
+ elf_offset = offset - closest_offset;
+
+ MemoryRanges* ranges = new MemoryRanges;
+ ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
+ ro_map_info->end - ro_map_info->start, 0));
+ ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
+
+ return ranges;
+ }
+ return nullptr;
}
Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index e676a5a..8729871 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -66,13 +66,13 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+ maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
});
}
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name, uint64_t load_bias) {
- MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
+ MapInfo* map_info = new MapInfo(this, start, end, offset, flags, name);
map_info->load_bias = load_bias;
maps_.push_back(map_info);
}
@@ -97,7 +97,7 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(start, end, pgoff, flags, name));
+ maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
});
}
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index beb2aad..cfa8c6d 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -316,6 +316,18 @@
return memory_->Read(read_addr, dst, read_length);
}
+void MemoryRanges::Insert(MemoryRange* memory) {
+ maps_.emplace(memory->offset() + memory->length(), memory);
+}
+
+size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
+ auto entry = maps_.upper_bound(addr);
+ if (entry != maps_.end()) {
+ return entry->second->Read(addr, dst, size);
+ }
+ return 0;
+}
+
bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
auto memory_file = std::make_shared<MemoryFileAtOffset>();
if (!memory_file->Init(file, offset)) {
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index ac0df41..9755c48 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -29,20 +29,25 @@
namespace unwindstack {
// Forward declarations.
+class Maps;
class Memory;
struct MapInfo {
- MapInfo() = default;
- MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
- MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name)
- : start(start),
+ MapInfo(Maps* maps) : maps_(maps) {}
+ MapInfo(Maps* maps, uint64_t start, uint64_t end) : maps_(maps), start(start), end(end) {}
+ MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const char* name)
+ : maps_(maps),
+ start(start),
end(end),
offset(offset),
flags(flags),
name(name),
load_bias(static_cast<uint64_t>(-1)) {}
- MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
- : start(start),
+ MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const std::string& name)
+ : maps_(maps),
+ start(start),
end(end),
offset(offset),
flags(flags),
@@ -50,6 +55,8 @@
load_bias(static_cast<uint64_t>(-1)) {}
~MapInfo() = default;
+ Maps* maps_ = nullptr;
+
uint64_t start = 0;
uint64_t end = 0;
uint64_t offset = 0;
@@ -69,14 +76,14 @@
uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
+ Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+
private:
MapInfo(const MapInfo&) = delete;
void operator=(const MapInfo&) = delete;
Memory* GetFileMemory();
- Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
-
// Protect the creation of the elf object.
std::mutex mutex_;
};
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index dee5e98..9c425cb 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -22,6 +22,7 @@
#include <unistd.h>
#include <atomic>
+#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -119,6 +120,9 @@
size_t Read(uint64_t addr, void* dst, size_t size) override;
+ uint64_t offset() { return offset_; }
+ uint64_t length() { return length_; }
+
private:
std::shared_ptr<Memory> memory_;
uint64_t begin_;
@@ -126,6 +130,19 @@
uint64_t offset_;
};
+class MemoryRanges : public Memory {
+ public:
+ MemoryRanges() = default;
+ virtual ~MemoryRanges() = default;
+
+ void Insert(MemoryRange* memory);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
+};
+
class MemoryOffline : public Memory {
public:
MemoryOffline() = default;
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 4dd8cb0..40f9f8e 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -120,7 +120,7 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(0, 0x10000, 0, 0x5, tf.path);
+ MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x500, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -134,7 +134,7 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
+ MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x600, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -148,7 +148,7 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x400, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -156,7 +156,7 @@
TEST(DexFileTest, create_using_memory_empty_file) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -164,7 +164,7 @@
TEST(DexFileTest, create_using_memory_file_does_not_exist) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
}
@@ -178,7 +178,7 @@
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+ MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
@@ -200,7 +200,7 @@
TEST(DexFileTest, get_method) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
@@ -227,7 +227,7 @@
TEST(DexFileTest, get_method_empty) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
+ MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
ASSERT_TRUE(dex_file != nullptr);
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 89331ea..1afd4ef 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -79,8 +79,8 @@
uint64_t start = 0x1000;
uint64_t end = 0x20000;
- MapInfo info1(start, end, 0, 0x5, tf.path);
- MapInfo info2(start, end, 0, 0x5, tf.path);
+ MapInfo info1(nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info2(nullptr, start, end, 0, 0x5, tf.path);
Elf* elf1 = info1.GetElf(memory_, true);
ASSERT_TRUE(elf1->valid());
@@ -120,17 +120,17 @@
uint64_t start = 0x1000;
uint64_t end = 0x20000;
// Will have an elf at offset 0 in file.
- MapInfo info0_1(start, end, 0, 0x5, tf.path);
- MapInfo info0_2(start, end, 0, 0x5, tf.path);
+ MapInfo info0_1(nullptr, start, end, 0, 0x5, tf.path);
+ MapInfo info0_2(nullptr, start, end, 0, 0x5, tf.path);
// Will have an elf at offset 0x100 in file.
- MapInfo info100_1(start, end, 0x100, 0x5, tf.path);
- MapInfo info100_2(start, end, 0x100, 0x5, tf.path);
+ MapInfo info100_1(nullptr, start, end, 0x100, 0x5, tf.path);
+ MapInfo info100_2(nullptr, start, end, 0x100, 0x5, tf.path);
// Will have an elf at offset 0x200 in file.
- MapInfo info200_1(start, end, 0x200, 0x5, tf.path);
- MapInfo info200_2(start, end, 0x200, 0x5, tf.path);
+ MapInfo info200_1(nullptr, start, end, 0x200, 0x5, tf.path);
+ MapInfo info200_2(nullptr, start, end, 0x200, 0x5, tf.path);
// Will have an elf at offset 0 in file.
- MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
Elf* elf0_1 = info0_1.GetElf(memory_, true);
ASSERT_TRUE(elf0_1->valid());
@@ -217,10 +217,10 @@
uint64_t start = 0x1000;
uint64_t end = 0x20000;
// Multiple info sections at different offsets will have non-zero elf offsets.
- MapInfo info300_1(start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(start, end, 0x300, 0x5, tf.path);
- MapInfo info400_1(start, end, 0x400, 0x5, tf.path);
- MapInfo info400_2(start, end, 0x400, 0x5, tf.path);
+ MapInfo info300_1(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info300_2(nullptr, start, end, 0x300, 0x5, tf.path);
+ MapInfo info400_1(nullptr, start, end, 0x400, 0x5, tf.path);
+ MapInfo info400_2(nullptr, start, end, 0x400, 0x5, tf.path);
Elf* elf300_1 = info300_1.GetElf(memory_, true);
ASSERT_TRUE(elf300_1->valid());
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 55fe16f..9a117b2 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -297,7 +297,7 @@
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
- MapInfo map_info(0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000);
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 866b5b4..2a73c7e 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -32,8 +32,10 @@
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
+#include "ElfTestUtils.h"
#include "MemoryFake.h"
namespace unwindstack {
@@ -94,7 +96,7 @@
TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
- MapInfo info(0x100, 0x100, 0, 0, elf_.path);
+ MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
@@ -112,7 +114,7 @@
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(0x100, 0x200, 0x100, 0, elf_.path);
+ MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -133,7 +135,7 @@
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(0x100, 0x200, 0x100, 0, elf_at_100_.path);
+ MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_at_100_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -156,7 +158,7 @@
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+ MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -172,7 +174,7 @@
}
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
@@ -192,27 +194,24 @@
// Set up some memory so that a valid local memory object would
// be returned if the file mapping fails, but the device check is incorrect.
std::vector<uint8_t> buffer(1024);
- MapInfo info;
- info.start = reinterpret_cast<uint64_t>(buffer.data());
- info.end = info.start + buffer.size();
- info.offset = 0;
+ uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
+ MapInfo info(nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
- info.flags = 0x8000;
- info.name = "/dev/something";
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
}
TEST_F(MapInfoCreateMemoryTest, process_memory) {
- MapInfo info;
- info.start = 0x2000;
- info.end = 0x3000;
- info.offset = 0;
+ MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ std::vector<uint8_t> buffer(1024);
+ memcpy(buffer.data(), &ehdr, sizeof(ehdr));
// Verify that the the process_memory object is used, so seed it
// with memory.
- std::vector<uint8_t> buffer(1024);
- for (size_t i = 0; i < buffer.size(); i++) {
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
buffer[i] = i % 256;
}
memory_->SetMemory(info.start, buffer.data(), buffer.size());
@@ -222,7 +221,8 @@
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
- for (size_t i = 0; i < buffer.size(); i++) {
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
}
@@ -230,4 +230,87 @@
ASSERT_FALSE(memory->ReadFully(buffer.size(), buffer.data(), 1));
}
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_zero_offset) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2600, 0, PROT_READ, "/only/in/memory.so", 0);
+ maps.Add(0x3000, 0x5000, 0x4000, PROT_READ | PROT_EXEC, "/only/in/memory.so", 0);
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x1600 - sizeof(ehdr), 0xab);
+
+ // Set the memory in the r-x map.
+ memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
+
+ MapInfo* map_info = maps.Find(0x3000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_EQ(0x4000UL, map_info->elf_offset);
+ EXPECT_EQ(0x4000UL, map_info->offset);
+
+ // Verify that reading values from this memory works properly.
+ std::vector<uint8_t> buffer(0x4000);
+ size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1600UL, bytes);
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < bytes; i++) {
+ ASSERT_EQ(0xab, buffer[i]) << "Failed at byte " << i;
+ }
+
+ bytes = mem->Read(0x4000, buffer.data(), buffer.size());
+ ASSERT_EQ(0x2000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x5d, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MapInfoCreateMemoryTest, valid_rosegment_non_zero_offset) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2000, 0, PROT_READ, "/only/in/memory.apk", 0);
+ maps.Add(0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+ maps.Add(0x3000, 0x4000, 0xa000, PROT_READ, "/only/in/memory.apk", 0);
+ maps.Add(0x4000, 0x5000, 0xb000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+
+ // Setup an elf at offset 0x1000 in memory.
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x1000 + sizeof(ehdr), 0x2000 - sizeof(ehdr), 0x12);
+ memory_->SetMemoryBlock(0x2000, 0x1000, 0x23);
+
+ // Setup an elf at offset 0x3000 in memory..
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x4000 - sizeof(ehdr), 0x34);
+ memory_->SetMemoryBlock(0x4000, 0x1000, 0x43);
+
+ MapInfo* map_info = maps.Find(0x4000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_EQ(0x1000UL, map_info->elf_offset);
+ EXPECT_EQ(0xb000UL, map_info->offset);
+
+ // Verify that reading values from this memory works properly.
+ std::vector<uint8_t> buffer(0x4000);
+ size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1000UL, bytes);
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < bytes; i++) {
+ ASSERT_EQ(0x34, buffer[i]) << "Failed at byte " << i;
+ }
+
+ bytes = mem->Read(0x1000, buffer.data(), buffer.size());
+ ASSERT_EQ(0x1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x43, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 861b82f..918c028 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -69,7 +69,7 @@
};
TEST_F(MapInfoGetElfTest, invalid) {
- MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
// The map is empty, but this should still create an invalid elf object.
Elf* elf = info.GetElf(process_memory_, false);
@@ -78,7 +78,7 @@
}
TEST_F(MapInfoGetElfTest, valid32) {
- MapInfo info(0x3000, 0x4000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -92,7 +92,7 @@
}
TEST_F(MapInfoGetElfTest, valid64) {
- MapInfo info(0x8000, 0x9000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
@@ -106,7 +106,7 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
- MapInfo info(0x4000, 0x8000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x4000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -122,7 +122,7 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
- MapInfo info(0x6000, 0x8000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x6000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -138,7 +138,7 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
- MapInfo info(0x2000, 0x3000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -154,7 +154,7 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
- MapInfo info(0x5000, 0x8000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
[&](uint64_t offset, const void* ptr, size_t size) {
@@ -170,7 +170,7 @@
}
TEST_F(MapInfoGetElfTest, end_le_start) {
- MapInfo info(0x1000, 0x1000, 0, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -197,7 +197,7 @@
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x1000);
memset(buffer.data(), 0, buffer.size());
@@ -226,7 +226,7 @@
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -256,7 +256,7 @@
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -284,7 +284,7 @@
}
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -312,7 +312,7 @@
}
TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
- MapInfo info(0x9000, 0xa000, 0x1000, 0, "");
+ MapInfo info(nullptr, 0x9000, 0xa000, 0x1000, 0, "");
// Create valid elf data in process memory only.
Elf64_Ehdr ehdr;
@@ -333,7 +333,8 @@
}
TEST_F(MapInfoGetElfTest, check_device_maps) {
- MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+ "/dev/something");
// Create valid elf data in process memory for this to verify that only
// the name is causing invalid elf data.
@@ -378,7 +379,7 @@
wait = true;
// Create all of the threads and have them do the GetElf at the same time
// to make it likely that a race will occur.
- MapInfo info(0x7000, 0x8000, 0x1000, PROT_READ, "");
+ MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
while (wait)
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 7e64a8a..f5ac6cb 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -50,7 +50,7 @@
process_memory_.reset(memory_);
elf_ = new ElfFake(new MemoryFake);
elf_container_.reset(elf_);
- map_info_.reset(new MapInfo(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+ map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
}
void MultipleThreadTest(uint64_t expected_load_bias);
@@ -63,7 +63,7 @@
};
TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
- MapInfo info(0x1000, 0x2000, 0, PROT_READ, "");
+ MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
}
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 9622ba5..6bdd0b2 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -63,7 +63,7 @@
}
TEST(MapsTest, verify_parse_line) {
- MapInfo info;
+ MapInfo info(nullptr);
VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
EXPECT_EQ(1U, info.start);
@@ -136,7 +136,7 @@
}
TEST(MapsTest, verify_large_values) {
- MapInfo info;
+ MapInfo info(nullptr);
#if defined(__LP64__)
VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/tests/MemoryFake.cpp
index 60936cd..5695dfc 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/tests/MemoryFake.cpp
@@ -23,6 +23,17 @@
namespace unwindstack {
+void MemoryFake::SetMemoryBlock(uint64_t addr, size_t length, uint8_t value) {
+ for (size_t i = 0; i < length; i++, addr++) {
+ auto entry = data_.find(addr);
+ if (entry != data_.end()) {
+ entry->second = value;
+ } else {
+ data_.insert({addr, value});
+ }
+ }
+}
+
void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
for (size_t i = 0; i < length; i++, addr++) {
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index 764a6c3..20610a5 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -36,6 +36,8 @@
void SetMemory(uint64_t addr, const void* memory, size_t length);
+ void SetMemoryBlock(uint64_t addr, size_t length, uint8_t value);
+
void SetData8(uint64_t addr, uint8_t value) {
SetMemory(addr, &value, sizeof(value));
}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index cb1a0c9..2bac95b 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -15,7 +15,6 @@
*/
#include <stdint.h>
-#include <string.h>
#include <memory>
#include <vector>
@@ -28,30 +27,34 @@
namespace unwindstack {
-TEST(MemoryRangeTest, read) {
- std::vector<uint8_t> src(1024);
- memset(src.data(), 0x4c, 1024);
- MemoryFake* memory_fake = new MemoryFake;
- std::shared_ptr<Memory> process_memory(memory_fake);
- memory_fake->SetMemory(9001, src);
+class MemoryRangeTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ process_memory_.reset();
+ memory_fake_ = new MemoryFake;
+ process_memory_.reset(memory_fake_);
+ }
- MemoryRange range(process_memory, 9001, src.size(), 0);
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_fake_ = nullptr;
+};
+
+TEST_F(MemoryRangeTest, read_fully) {
+ memory_fake_->SetMemoryBlock(9000, 2048, 0x4c);
+
+ MemoryRange range(process_memory_, 9001, 1024, 0);
std::vector<uint8_t> dst(1024);
- ASSERT_TRUE(range.ReadFully(0, dst.data(), src.size()));
- for (size_t i = 0; i < 1024; i++) {
+ ASSERT_TRUE(range.ReadFully(0, dst.data(), dst.size()));
+ for (size_t i = 0; i < dst.size(); i++) {
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
}
-TEST(MemoryRangeTest, read_near_limit) {
- std::vector<uint8_t> src(4096);
- memset(src.data(), 0x4c, 4096);
- MemoryFake* memory_fake = new MemoryFake;
- std::shared_ptr<Memory> process_memory(memory_fake);
- memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read_fully_near_limit) {
+ memory_fake_->SetMemoryBlock(0, 8192, 0x4c);
- MemoryRange range(process_memory, 1000, 1024, 0);
+ MemoryRange range(process_memory_, 1000, 1024, 0);
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
@@ -68,7 +71,7 @@
ASSERT_TRUE(range.ReadFully(1020, dst.data(), 4));
}
-TEST(MemoryRangeTest, read_overflow) {
+TEST_F(MemoryRangeTest, read_fully_overflow) {
std::vector<uint8_t> buffer(100);
std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
@@ -76,19 +79,28 @@
ASSERT_FALSE(overflow->ReadFully(UINT64_MAX - 10, buffer.data(), 100));
}
-TEST(MemoryRangeTest, Read) {
- std::vector<uint8_t> src(4096);
- memset(src.data(), 0x4c, 4096);
- MemoryFake* memory_fake = new MemoryFake;
- std::shared_ptr<Memory> process_memory(memory_fake);
- memory_fake->SetMemory(1000, src);
+TEST_F(MemoryRangeTest, read) {
+ memory_fake_->SetMemoryBlock(0, 4096, 0x4c);
- MemoryRange range(process_memory, 1000, 1024, 0);
+ MemoryRange range(process_memory_, 1000, 1024, 0);
+
std::vector<uint8_t> dst(1024);
- ASSERT_EQ(4U, range.Read(1020, dst.data(), 1024));
+ ASSERT_EQ(4U, range.Read(1020, dst.data(), dst.size()));
for (size_t i = 0; i < 4; i++) {
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
}
+TEST_F(MemoryRangeTest, read_non_zero_offset) {
+ memory_fake_->SetMemoryBlock(1000, 1024, 0x12);
+
+ MemoryRange range(process_memory_, 1000, 1024, 400);
+
+ std::vector<uint8_t> dst(1024);
+ ASSERT_EQ(1024U, range.Read(400, dst.data(), dst.size()));
+ for (size_t i = 0; i < dst.size(); i++) {
+ ASSERT_EQ(0x12U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
new file mode 100644
index 0000000..d24fcd2
--- /dev/null
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryRangesTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ MemoryFake* memory = new MemoryFake;
+ process_memory_.reset(memory);
+ memory->SetMemoryBlock(1000, 5000, 0x15);
+ memory->SetMemoryBlock(6000, 12000, 0x26);
+ memory->SetMemoryBlock(14000, 20000, 0x37);
+ memory->SetMemoryBlock(20000, 22000, 0x48);
+
+ ranges_.reset(new MemoryRanges);
+ ranges_->Insert(new MemoryRange(process_memory_, 15000, 100, 4000));
+ ranges_->Insert(new MemoryRange(process_memory_, 10000, 2000, 2000));
+ ranges_->Insert(new MemoryRange(process_memory_, 3000, 1000, 0));
+ ranges_->Insert(new MemoryRange(process_memory_, 19000, 1000, 6000));
+ ranges_->Insert(new MemoryRange(process_memory_, 20000, 1000, 7000));
+ }
+
+ std::shared_ptr<Memory> process_memory_;
+ std::unique_ptr<MemoryRanges> ranges_;
+};
+
+TEST_F(MemoryRangesTest, read) {
+ std::vector<uint8_t> dst(2000);
+ size_t bytes = ranges_->Read(0, dst.data(), dst.size());
+ ASSERT_EQ(1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x15U, dst[i]) << "Failed at byte " << i;
+ }
+
+ bytes = ranges_->Read(2000, dst.data(), dst.size());
+ ASSERT_EQ(2000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x26U, dst[i]) << "Failed at byte " << i;
+ }
+
+ bytes = ranges_->Read(4000, dst.data(), dst.size());
+ ASSERT_EQ(100UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryRangesTest, read_fail) {
+ std::vector<uint8_t> dst(4096);
+ ASSERT_EQ(0UL, ranges_->Read(1000, dst.data(), dst.size()));
+ ASSERT_EQ(0UL, ranges_->Read(5000, dst.data(), dst.size()));
+ ASSERT_EQ(0UL, ranges_->Read(8000, dst.data(), dst.size()));
+}
+
+TEST_F(MemoryRangesTest, read_across_ranges) {
+ // The MemoryRanges object does not support reading across a range,
+ // so this will only read in the first range.
+ std::vector<uint8_t> dst(4096);
+ size_t bytes = ranges_->Read(6000, dst.data(), dst.size());
+ ASSERT_EQ(1000UL, bytes);
+ for (size_t i = 0; i < bytes; i++) {
+ ASSERT_EQ(0x37U, dst[i]) << "Failed at byte " << i;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 90c3fe6..00264c2 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
RegsX86_64 regs_x86_64;
RegsMips regs_mips;
RegsMips64 regs_mips64;
- MapInfo map_info(0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000);
Elf* invalid_elf = new Elf(nullptr);
map_info.elf.reset(invalid_elf);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2428f68..4369030 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -58,51 +58,54 @@
protected:
static void SetUpTestCase() {
maps_.FakeClear();
- MapInfo* info = new MapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
+ MapInfo* info =
+ new MapInfo(&maps_, 0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
ElfFake* elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
+ info = new MapInfo(&maps_, 0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+ info = new MapInfo(&maps_, 0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
"/dev/fake_device");
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so");
+ info = new MapInfo(&maps_, 0x20000, 0x22000, 0, PROT_READ | PROT_WRITE,
+ "/system/fake/libunwind.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
+ info = new MapInfo(&maps_, 0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
+ info = new MapInfo(&maps_, 0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
+ info = new MapInfo(&maps_, 0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
+ info = new MapInfo(&maps_, 0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+ info = new MapInfo(&maps_, 0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/fake/fake.vdex");
info->load_bias = 0;
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ info = new MapInfo(&maps_, 0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/fake/fake_load_bias.so");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
@@ -110,7 +113,7 @@
elf->FakeSetLoadBias(0x5000);
maps_.FakeAddMapInfo(info);
- info = new MapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ info = new MapInfo(&maps_, 0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/fake/fake_offset.oat");
elf = new ElfFake(new MemoryFake);
info->elf.reset(elf);
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 1c1bdf7..600c91c 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -59,6 +59,7 @@
"-Werror",
],
header_libs: [
+ "libbase_headers",
"libutils_headers",
],
export_header_lib_headers: [
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 583c6b9..5feb2aa 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -62,11 +62,17 @@
}
// Move Constructor.
-FileMap::FileMap(FileMap&& other)
- : mFileName(other.mFileName), mBasePtr(other.mBasePtr), mBaseLength(other.mBaseLength),
- mDataOffset(other.mDataOffset), mDataPtr(other.mDataPtr), mDataLength(other.mDataLength)
+FileMap::FileMap(FileMap&& other) noexcept
+ : mFileName(other.mFileName),
+ mBasePtr(other.mBasePtr),
+ mBaseLength(other.mBaseLength),
+ mDataOffset(other.mDataOffset),
+ mDataPtr(other.mDataPtr),
+ mDataLength(other.mDataLength)
#if defined(__MINGW32__)
- , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
+ ,
+ mFileHandle(other.mFileHandle),
+ mFileMapping(other.mFileMapping)
#endif
{
other.mFileName = nullptr;
@@ -79,7 +85,7 @@
}
// Move assign operator.
-FileMap& FileMap::operator=(FileMap&& other) {
+FileMap& FileMap::operator=(FileMap&& other) noexcept {
mFileName = other.mFileName;
mBasePtr = other.mBasePtr;
mBaseLength = other.mBaseLength;
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 3f1e79a..ae10789 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -19,6 +19,8 @@
#include <memory>
+#include <android-base/macros.h>
+
#include <utils/RefBase.h>
#include <utils/CallStack.h>
@@ -479,7 +481,7 @@
case INITIAL_STRONG_VALUE:
refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
- // fall through...
+ FALLTHROUGH_INTENDED;
case 0:
refs->mBase->onFirstRef();
}
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 82f650d..5f0a51f 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -16,8 +16,9 @@
#define LOG_TAG "unicode"
-#include <utils/Unicode.h>
+#include <android-base/macros.h>
#include <limits.h>
+#include <utils/Unicode.h>
#include <log/log.h>
@@ -105,8 +106,11 @@
switch (bytes)
{ /* note: everything falls through. */
case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+ FALLTHROUGH_INTENDED;
case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+ FALLTHROUGH_INTENDED;
case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+ FALLTHROUGH_INTENDED;
case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
}
}
diff --git a/libutils/include/utils/FileMap.h b/libutils/include/utils/FileMap.h
index 8d402a3..f9f8f3c 100644
--- a/libutils/include/utils/FileMap.h
+++ b/libutils/include/utils/FileMap.h
@@ -52,8 +52,8 @@
public:
FileMap(void);
- FileMap(FileMap&& f);
- FileMap& operator=(FileMap&& f);
+ FileMap(FileMap&& f) noexcept;
+ FileMap& operator=(FileMap&& f) noexcept;
/*
* Create a new mapping on an open file.
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 3abce17..1571129 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -56,7 +56,7 @@
sp(T* other); // NOLINT(implicit)
sp(const sp<T>& other);
- sp(sp<T>&& other);
+ sp(sp<T>&& other) noexcept;
template<typename U> sp(U* other); // NOLINT(implicit)
template<typename U> sp(const sp<U>& other); // NOLINT(implicit)
template<typename U> sp(sp<U>&& other); // NOLINT(implicit)
@@ -67,7 +67,7 @@
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
- sp& operator = (sp<T>&& other);
+ sp& operator=(sp<T>&& other) noexcept;
template<typename U> sp& operator = (const sp<U>& other);
template<typename U> sp& operator = (sp<U>&& other);
@@ -125,9 +125,8 @@
m_ptr->incStrong(this);
}
-template<typename T>
-sp<T>::sp(sp<T>&& other)
- : m_ptr(other.m_ptr) {
+template <typename T>
+sp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {
other.m_ptr = nullptr;
}
@@ -169,8 +168,8 @@
return *this;
}
-template<typename T>
-sp<T>& sp<T>::operator =(sp<T>&& other) {
+template <typename T>
+sp<T>& sp<T>::operator=(sp<T>&& other) noexcept {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (oldPtr) oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 2606aa9..fd3f602 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -158,4 +158,5 @@
"libbase",
"libziparchive",
],
+ recovery_available: true,
}
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index c350a27..0e0caf1 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -91,10 +91,10 @@
explicit ZipWriter(FILE* f);
// Move constructor.
- ZipWriter(ZipWriter&& zipWriter);
+ ZipWriter(ZipWriter&& zipWriter) noexcept;
// Move assignment.
- ZipWriter& operator=(ZipWriter&& zipWriter);
+ ZipWriter& operator=(ZipWriter&& zipWriter) noexcept;
/**
* Starts a new zip entry with the given path and flags.
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index add6e14..6a3db6b 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -122,21 +122,36 @@
#endif
}
+static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
+ const ZipStringOffset& zip_string_offset) {
+ const ZipString from_offset = zip_string_offset.GetZipString(start);
+ return from_offset == zip_string;
+}
+
+/**
+ * Returns offset of ZipString#name from the start of the central directory in the memory map.
+ * For valid ZipStrings contained in the zip archive mmap, 0 < offset < 0xffffff.
+ */
+static inline uint32_t GetOffset(const uint8_t* name, const uint8_t* start) {
+ CHECK_GT(name, start);
+ CHECK_LT(name, start + 0xffffff);
+ return static_cast<uint32_t>(name - start);
+}
+
/*
* Convert a ZipEntry to a hash table index, verifying that it's in a
* valid range.
*/
-static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size,
- const ZipString& name) {
+static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
+ const ZipString& name, const uint8_t* start) {
const uint32_t hash = ComputeHash(name);
// NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
uint32_t ent = hash & (hash_table_size - 1);
- while (hash_table[ent].name != NULL) {
- if (hash_table[ent] == name) {
+ while (hash_table[ent].name_offset != 0) {
+ if (isZipStringEqual(start, name, hash_table[ent])) {
return ent;
}
-
ent = (ent + 1) & (hash_table_size - 1);
}
@@ -147,8 +162,8 @@
/*
* Add a new entry to the hash table.
*/
-static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size,
- const ZipString& name) {
+static int32_t AddToHash(ZipStringOffset* hash_table, const uint64_t hash_table_size,
+ const ZipString& name, const uint8_t* start) {
const uint64_t hash = ComputeHash(name);
uint32_t ent = hash & (hash_table_size - 1);
@@ -156,20 +171,26 @@
* We over-allocated the table, so we're guaranteed to find an empty slot.
* Further, we guarantee that the hashtable size is not 0.
*/
- while (hash_table[ent].name != NULL) {
- if (hash_table[ent] == name) {
+ while (hash_table[ent].name_offset != 0) {
+ if (isZipStringEqual(start, name, hash_table[ent])) {
// We've found a duplicate entry. We don't accept it
ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
return kDuplicateEntry;
}
ent = (ent + 1) & (hash_table_size - 1);
}
-
- hash_table[ent].name = name.name;
+ hash_table[ent].name_offset = GetOffset(name.name, start);
hash_table[ent].name_length = name.name_length;
return 0;
}
+#if defined(__BIONIC__)
+uint64_t GetOwnerTag(const ZipArchive* archive) {
+ return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
+ reinterpret_cast<uint64_t>(archive));
+}
+#endif
+
ZipArchive::ZipArchive(const int fd, bool assume_ownership)
: mapped_zip(fd),
close_file(assume_ownership),
@@ -181,7 +202,7 @@
hash_table(nullptr) {
#if defined(__BIONIC__)
if (assume_ownership) {
- android_fdsan_exchange_owner_tag(fd, 0, reinterpret_cast<uint64_t>(this));
+ android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
}
#endif
}
@@ -199,7 +220,7 @@
ZipArchive::~ZipArchive() {
if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
#if defined(__BIONIC__)
- android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), reinterpret_cast<uint64_t>(this));
+ android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), GetOwnerTag(this));
#else
close(mapped_zip.GetFileDescriptor());
#endif
@@ -209,7 +230,8 @@
}
static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
- off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
+ off64_t file_length, off64_t read_amount,
+ uint8_t* scan_buffer) {
const off64_t search_start = file_length - read_amount;
if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
@@ -362,7 +384,7 @@
*/
archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
archive->hash_table =
- reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString)));
+ reinterpret_cast<ZipStringOffset*>(calloc(archive->hash_table_size, sizeof(ZipStringOffset)));
if (archive->hash_table == nullptr) {
ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
archive->hash_table_size, sizeof(ZipString));
@@ -418,7 +440,8 @@
ZipString entry_name;
entry_name.name = file_name;
entry_name.name_length = file_name_length;
- const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name);
+ const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name,
+ archive->central_directory.GetBasePtr());
if (add_result != 0) {
ALOGW("Zip: Error adding entry to hash table %d", add_result);
return add_result;
@@ -538,7 +561,9 @@
// Recover the start of the central directory entry from the filename
// pointer. The filename is the first entry past the fixed-size data,
// so we can just subtract back from that.
- const uint8_t* ptr = archive->hash_table[ent].name;
+ const ZipString from_offset =
+ archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+ const uint8_t* ptr = from_offset.name;
ptr -= sizeof(CentralDirectoryRecord);
// This is the base of our mmapped region, we have to sanity check that
@@ -648,8 +673,9 @@
ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
return kIoError;
}
-
- if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
+ const ZipString from_offset =
+ archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+ if (memcmp(from_offset.name, name_buf.data(), nameLen)) {
return kInconsistentInformation;
}
@@ -747,19 +773,19 @@
return kInvalidEntryName;
}
- const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName);
-
+ const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName,
+ archive->central_directory.GetBasePtr());
if (ent < 0) {
ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
return ent;
}
-
return FindEntry(archive, ent, data);
}
int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
if (handle == NULL) {
+ ALOGW("Zip: Null ZipArchiveHandle");
return kInvalidHandle;
}
@@ -771,19 +797,19 @@
const uint32_t currentOffset = handle->position;
const uint32_t hash_table_length = archive->hash_table_size;
- const ZipString* hash_table = archive->hash_table;
-
+ const ZipStringOffset* hash_table = archive->hash_table;
for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
- if (hash_table[i].name != NULL &&
- (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) &&
- (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) {
+ const ZipString from_offset =
+ hash_table[i].GetZipString(archive->central_directory.GetBasePtr());
+ if (hash_table[i].name_offset != 0 &&
+ (handle->prefix.name_length == 0 || from_offset.StartsWith(handle->prefix)) &&
+ (handle->suffix.name_length == 0 || from_offset.EndsWith(handle->suffix))) {
handle->position = (i + 1);
const int error = FindEntry(archive, i, data);
if (!error) {
- name->name = hash_table[i].name;
+ name->name = from_offset.name;
name->name_length = hash_table[i].name_length;
}
-
return error;
}
}
@@ -877,7 +903,7 @@
return FileWriter(fd, declared_length);
}
- FileWriter(FileWriter&& other)
+ FileWriter(FileWriter&& other) noexcept
: fd_(other.fd_),
declared_length_(other.declared_length_),
total_bytes_written_(other.total_bytes_written_) {
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 0a73300..83cb11f 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -136,6 +136,26 @@
size_t length_;
};
+/**
+ * More space efficient string representation of strings in an mmaped zipped file than
+ * std::string_view or ZipString. Using ZipString as an entry in the ZipArchive hashtable wastes
+ * space. ZipString stores a pointer to a string (on 64 bit, 8 bytes) and the length to read from
+ * that pointer, 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting 6 bytes.
+ * ZipStringOffset stores a 4 byte offset from a fixed location in the memory mapped file instead
+ * of the entire address, consuming 8 bytes with alignment.
+ */
+struct ZipStringOffset {
+ uint32_t name_offset;
+ uint16_t name_length;
+
+ const ZipString GetZipString(const uint8_t* start) const {
+ ZipString zip_string;
+ zip_string.name = start + name_offset;
+ zip_string.name_length = name_length;
+ return zip_string;
+ }
+};
+
struct ZipArchive {
// open Zip archive
mutable MappedZipFile mapped_zip;
@@ -154,7 +174,7 @@
// allocate so the maximum number entries can never be higher than
// ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
uint32_t hash_table_size;
- ZipString* hash_table;
+ ZipStringOffset* hash_table;
ZipArchive(const int fd, bool assume_ownership);
ZipArchive(void* address, size_t length);
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 6ad3366..ed1d135 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -97,7 +97,7 @@
}
}
-ZipWriter::ZipWriter(ZipWriter&& writer)
+ZipWriter::ZipWriter(ZipWriter&& writer) noexcept
: file_(writer.file_),
seekable_(writer.seekable_),
current_offset_(writer.current_offset_),
@@ -109,7 +109,7 @@
writer.state_ = State::kError;
}
-ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) noexcept {
file_ = writer.file_;
seekable_ = writer.seekable_;
current_offset_ = writer.current_offset_;
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 58c2ba8..6840ed0 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -265,7 +265,7 @@
}
content.erase(pos);
uid_t ret;
- if (!android::base::ParseInt(content, &ret, uid_t(0))) {
+ if (!android::base::ParseUint(content, &ret, uid_t(0))) {
return -1;
}
return ret;
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 02534f2..1980dc6 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -31,6 +31,7 @@
#include <sys/socket.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
+#include <time.h>
#include <unistd.h>
#include <cutils/properties.h>
@@ -38,6 +39,7 @@
#include <lmkd.h>
#include <log/log.h>
#include <log/log_event_list.h>
+#include <log/log_time.h>
#ifdef LMKD_LOG_STATS
#include "statslog.h"
@@ -83,6 +85,10 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#define EIGHT_MEGA (1 << 23)
+#define TARGET_UPDATE_MIN_INTERVAL_MS 1000
+
+#define NS_PER_MS (NS_PER_SEC / MS_PER_SEC)
+
/* Defined as ProcessList.SYSTEM_ADJ in ProcessList.java */
#define SYSTEM_ADJ (-900)
@@ -91,6 +97,8 @@
#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define FAIL_REPORT_RLIMIT_MS 1000
+
/* default to old in-kernel interface if no memory pressure events */
static bool use_inkernel_interface = true;
static bool has_inkernel_module;
@@ -494,6 +502,12 @@
return true;
}
+static inline long get_time_diff_ms(struct timespec *from,
+ struct timespec *to) {
+ return (to->tv_sec - from->tv_sec) * (long)MS_PER_SEC +
+ (to->tv_nsec - from->tv_nsec) / (long)NS_PER_MS;
+}
+
static void cmd_procprio(LMKD_CTRL_PACKET packet) {
struct proc *procp;
char path[80];
@@ -604,18 +618,52 @@
static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
int i;
struct lmk_target target;
+ char minfree_str[PROPERTY_VALUE_MAX];
+ char *pstr = minfree_str;
+ char *pend = minfree_str + sizeof(minfree_str);
+ static struct timespec last_req_tm;
+ struct timespec curr_tm;
- if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
+ if (ntargets < 1 || ntargets > (int)ARRAY_SIZE(lowmem_adj))
return;
+ /*
+ * Ratelimit minfree updates to once per TARGET_UPDATE_MIN_INTERVAL_MS
+ * to prevent DoS attacks
+ */
+ if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
+ ALOGE("Failed to get current time");
+ return;
+ }
+
+ if (get_time_diff_ms(&last_req_tm, &curr_tm) <
+ TARGET_UPDATE_MIN_INTERVAL_MS) {
+ ALOGE("Ignoring frequent updated to lmkd limits");
+ return;
+ }
+
+ last_req_tm = curr_tm;
+
for (i = 0; i < ntargets; i++) {
lmkd_pack_get_target(packet, i, &target);
lowmem_minfree[i] = target.minfree;
lowmem_adj[i] = target.oom_adj_score;
+
+ pstr += snprintf(pstr, pend - pstr, "%d:%d,", target.minfree,
+ target.oom_adj_score);
+ if (pstr >= pend) {
+ /* if no more space in the buffer then terminate the loop */
+ pstr = pend;
+ break;
+ }
}
lowmem_targets_size = ntargets;
+ /* Override the last extra comma */
+ pstr[-1] = '\0';
+ property_set("sys.lmk.minfree_levels", minfree_str);
+
if (has_inkernel_module) {
char minfreestr[128];
char killpriostr[128];
@@ -1051,8 +1099,7 @@
}
/* Kill one process specified by procp. Returns the size of the process killed */
-static int kill_one_process(struct proc* procp, int min_score_adj,
- enum vmpressure_level level) {
+static int kill_one_process(struct proc* procp) {
int pid = procp->pid;
uid_t uid = procp->uid;
char *taskname;
@@ -1086,11 +1133,8 @@
/* CAP_KILL required */
r = kill(pid, SIGKILL);
- ALOGI(
- "Killing '%s' (%d), uid %d, adj %d\n"
- " to free %ldkB because system is under %s memory pressure (min_oom_adj=%d)\n",
- taskname, pid, uid, procp->oomadj, tasksize * page_k,
- level_name[level], min_score_adj);
+ ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
+ taskname, pid, uid, procp->oomadj, tasksize * page_k);
pid_remove(pid);
TRACE_KILL_END();
@@ -1117,8 +1161,7 @@
* If pages_to_free is set to 0 only one process will be killed.
* Returns the size of the killed processes.
*/
-static int find_and_kill_processes(enum vmpressure_level level,
- int min_score_adj, int pages_to_free) {
+static int find_and_kill_processes(int min_score_adj, int pages_to_free) {
int i;
int killed_size;
int pages_freed = 0;
@@ -1137,7 +1180,7 @@
if (!procp)
break;
- killed_size = kill_one_process(procp, min_score_adj, level);
+ killed_size = kill_one_process(procp);
if (killed_size >= 0) {
#ifdef LMKD_LOG_STATS
if (enable_stats_log && !lmk_state_change_start) {
@@ -1228,12 +1271,6 @@
level - 1 : level);
}
-static inline unsigned long get_time_diff_ms(struct timeval *from,
- struct timeval *to) {
- return (to->tv_sec - from->tv_sec) * 1000 +
- (to->tv_usec - from->tv_usec) / 1000;
-}
-
static void mp_event_common(int data, uint32_t events __unused) {
int ret;
unsigned long long evcount;
@@ -1242,8 +1279,9 @@
enum vmpressure_level lvl;
union meminfo mi;
union zoneinfo zi;
- static struct timeval last_report_tm;
- static unsigned long skip_count = 0;
+ struct timespec curr_tm;
+ static struct timespec last_kill_tm;
+ static unsigned long kill_skip_count = 0;
enum vmpressure_level level = (enum vmpressure_level)data;
long other_free = 0, other_file = 0;
int min_score_adj;
@@ -1272,19 +1310,22 @@
}
}
+ if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
+ ALOGE("Failed to get current time");
+ return;
+ }
+
if (kill_timeout_ms) {
- struct timeval curr_tm;
- gettimeofday(&curr_tm, NULL);
- if (get_time_diff_ms(&last_report_tm, &curr_tm) < kill_timeout_ms) {
- skip_count++;
+ if (get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) {
+ kill_skip_count++;
return;
}
}
- if (skip_count > 0) {
+ if (kill_skip_count > 0) {
ALOGI("%lu memory pressure events were skipped after a kill!",
- skip_count);
- skip_count = 0;
+ kill_skip_count);
+ kill_skip_count = 0;
}
if (meminfo_parse(&mi) < 0 || zoneinfo_parse(&zi) < 0) {
@@ -1380,7 +1421,7 @@
do_kill:
if (low_ram_device) {
/* For Go devices kill only one task */
- if (find_and_kill_processes(level, level_oomadj[level], 0) == 0) {
+ if (find_and_kill_processes(level_oomadj[level], 0) == 0) {
if (debug_process_killing) {
ALOGI("Nothing to kill");
}
@@ -1389,6 +1430,8 @@
}
} else {
int pages_freed;
+ static struct timespec last_report_tm;
+ static unsigned long report_skip_count = 0;
if (!use_minfree_levels) {
/* If pressure level is less than critical and enough free swap then ignore */
@@ -1416,27 +1459,41 @@
min_score_adj = level_oomadj[level];
}
- pages_freed = find_and_kill_processes(level, min_score_adj, pages_to_free);
+ pages_freed = find_and_kill_processes(min_score_adj, pages_to_free);
+
+ if (pages_freed == 0) {
+ /* Rate limit kill reports when nothing was reclaimed */
+ if (get_time_diff_ms(&last_report_tm, &curr_tm) < FAIL_REPORT_RLIMIT_MS) {
+ report_skip_count++;
+ return;
+ }
+ }
+
+ /* Log meminfo whenever we kill or when report rate limit allows */
+ meminfo_log(&mi);
+ if (pages_freed >= pages_to_free) {
+ /* Reset kill time only if reclaimed enough memory */
+ last_kill_tm = curr_tm;
+ }
if (use_minfree_levels) {
- ALOGI("Killing because cache %ldkB is below "
- "limit %ldkB for oom_adj %d\n"
- " Free memory is %ldkB %s reserved",
- other_file * page_k, minfree * page_k, min_score_adj,
- other_free * page_k, other_free >= 0 ? "above" : "below");
+ ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB, cache(%ldkB) and "
+ "free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
+ pages_to_free * page_k, pages_freed * page_k,
+ other_file * page_k, mi.field.nr_free_pages * page_k,
+ zi.field.totalreserve_pages * page_k,
+ minfree * page_k, min_score_adj);
+ } else {
+ ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB at oom_adj %d",
+ pages_to_free * page_k, pages_freed * page_k, min_score_adj);
}
- if (pages_freed < pages_to_free) {
- ALOGI("Unable to free enough memory (pages to free=%d, pages freed=%d)",
- pages_to_free, pages_freed);
- } else {
- ALOGI("Reclaimed enough memory (pages to free=%d, pages freed=%d)",
- pages_to_free, pages_freed);
- gettimeofday(&last_report_tm, NULL);
+ if (report_skip_count > 0) {
+ ALOGI("Suppressed %lu failed kill reports", report_skip_count);
+ report_skip_count = 0;
}
- if (pages_freed > 0) {
- meminfo_log(&mi);
- }
+
+ last_report_tm = curr_tm;
}
}
diff --git a/lmkd/tests/lmkd_test.cpp b/lmkd/tests/lmkd_test.cpp
index 1996bae..f54b25c 100644
--- a/lmkd/tests/lmkd_test.cpp
+++ b/lmkd/tests/lmkd_test.cpp
@@ -39,7 +39,7 @@
#define LMKDTEST_RESPAWN_FLAG "LMKDTEST_RESPAWN"
#define LMKD_LOGCAT_MARKER "lowmemorykiller"
-#define LMKD_KILL_MARKER_TEMPLATE LMKD_LOGCAT_MARKER ": Killing '%s'"
+#define LMKD_KILL_MARKER_TEMPLATE LMKD_LOGCAT_MARKER ": Kill '%s'"
#define OOM_MARKER "Out of memory"
#define OOM_KILL_MARKER "Killed process"
#define MIN_LOG_SIZE 100
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 0f56337..115b1a3 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -16,6 +16,7 @@
#include "logcat.h"
+#include <android-base/macros.h>
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
@@ -41,6 +42,7 @@
#include <atomic>
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include <android-base/file.h>
@@ -565,23 +567,14 @@
return android_log_setPrintFormat(context->logformat, format);
}
-static const char multipliers[][2] = { { "" }, { "K" }, { "M" }, { "G" } };
-
-static unsigned long value_of_size(unsigned long value) {
- for (unsigned i = 0;
- (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
- value /= 1024, ++i)
- ;
- return value;
-}
-
-static const char* multiplier_of_size(unsigned long value) {
- unsigned i;
+static std::pair<unsigned long, const char*> format_of_size(unsigned long value) {
+ static const char multipliers[][3] = {{""}, {"Ki"}, {"Mi"}, {"Gi"}};
+ size_t i;
for (i = 0;
(i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
value /= 1024, ++i)
;
- return multipliers[i];
+ return std::make_pair(value, multipliers[i]);
}
// String to unsigned int, returns -1 if it fails
@@ -967,7 +960,7 @@
case 't':
got_t = true;
mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
- // FALLTHRU
+ FALLTHROUGH_INTENDED;
case 'T':
if (strspn(optarg, "0123456789") != strlen(optarg)) {
char* cp = parseTime(tail_time, optarg);
@@ -1017,7 +1010,7 @@
getLogSize = true;
break;
}
- // FALLTHRU
+ FALLTHROUGH_INTENDED;
case 'G': {
char* cp;
@@ -1031,15 +1024,15 @@
case 'g':
case 'G':
setLogSize *= 1024;
- // FALLTHRU
+ FALLTHROUGH_INTENDED;
case 'm':
case 'M':
setLogSize *= 1024;
- // FALLTHRU
+ FALLTHROUGH_INTENDED;
case 'k':
case 'K':
setLogSize *= 1024;
- // FALLTHRU
+ FALLTHROUGH_INTENDED;
case '\0':
break;
@@ -1059,7 +1052,7 @@
getPruneList = true;
break;
}
- // FALLTHRU
+ FALLTHROUGH_INTENDED;
case 'P':
setPruneList = optarg;
@@ -1472,12 +1465,14 @@
if ((size < 0) || (readable < 0)) {
reportErrorName(&getSizeFail, dev->device, allSelected);
} else {
+ auto size_format = format_of_size(size);
+ auto readable_format = format_of_size(readable);
std::string str = android::base::StringPrintf(
- "%s: ring buffer is %ld%sb (%ld%sb consumed),"
- " max entry is %db, max payload is %db\n",
+ "%s: ring buffer is %lu %sB (%lu %sB consumed),"
+ " max entry is %d B, max payload is %d B\n",
dev->device,
- value_of_size(size), multiplier_of_size(size),
- value_of_size(readable), multiplier_of_size(readable),
+ size_format.first, size_format.second,
+ readable_format.first, readable_format.second,
(int)LOGGER_ENTRY_MAX_LEN,
(int)LOGGER_ENTRY_MAX_PAYLOAD);
TEMP_FAILURE_RETRY(write(context->output_fd,
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index cc1632a..9483bb2 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -31,6 +31,7 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <log/event_tag_map.h>
@@ -557,47 +558,48 @@
while (fgets(buffer, sizeof(buffer), fp)) {
int size, consumed, max, payload;
- char size_mult[3], consumed_mult[3];
+ char size_mult[4], consumed_mult[4];
long full_size, full_consumed;
size = consumed = max = payload = 0;
// NB: crash log can be very small, not hit a Kb of consumed space
// doubly lucky we are not including it.
- if (6 != sscanf(buffer,
- "%*s ring buffer is %d%2s (%d%2s consumed),"
- " max entry is %db, max payload is %db",
- &size, size_mult, &consumed, consumed_mult, &max,
- &payload)) {
- fprintf(stderr, "WARNING: Parse error: %s", buffer);
- continue;
- }
+ EXPECT_EQ(6, sscanf(buffer,
+ "%*s ring buffer is %d %3s (%d %3s consumed),"
+ " max entry is %d B, max payload is %d B",
+ &size, size_mult, &consumed, consumed_mult, &max, &payload))
+ << "Parse error on: " << buffer;
full_size = size;
switch (size_mult[0]) {
case 'G':
full_size *= 1024;
- /* FALLTHRU */
+ FALLTHROUGH_INTENDED;
case 'M':
full_size *= 1024;
- /* FALLTHRU */
+ FALLTHROUGH_INTENDED;
case 'K':
full_size *= 1024;
- /* FALLTHRU */
- case 'b':
+ FALLTHROUGH_INTENDED;
+ case 'B':
break;
+ default:
+ ADD_FAILURE() << "Parse error on multiplier: " << size_mult;
}
full_consumed = consumed;
switch (consumed_mult[0]) {
case 'G':
full_consumed *= 1024;
- /* FALLTHRU */
+ FALLTHROUGH_INTENDED;
case 'M':
full_consumed *= 1024;
- /* FALLTHRU */
+ FALLTHROUGH_INTENDED;
case 'K':
full_consumed *= 1024;
- /* FALLTHRU */
- case 'b':
+ FALLTHROUGH_INTENDED;
+ case 'B':
break;
+ default:
+ ADD_FAILURE() << "Parse error on multiplier: " << consumed_mult;
}
EXPECT_GT((full_size * 9) / 4, full_consumed);
EXPECT_GT(full_size, max);
@@ -1229,39 +1231,38 @@
}
int size, consumed, max, payload;
- char size_mult[3], consumed_mult[3];
+ char size_mult[4], consumed_mult[4];
size = consumed = max = payload = 0;
if (6 == sscanf(buffer,
- "events: ring buffer is %d%2s (%d%2s consumed),"
- " max entry is %db, max payload is %db",
- &size, size_mult, &consumed, consumed_mult, &max,
- &payload)) {
+ "events: ring buffer is %d %3s (%d %3s consumed),"
+ " max entry is %d B, max payload is %d B",
+ &size, size_mult, &consumed, consumed_mult, &max, &payload)) {
long full_size = size, full_consumed = consumed;
switch (size_mult[0]) {
case 'G':
full_size *= 1024;
- /* FALLTHRU */
+ FALLTHROUGH_INTENDED;
case 'M':
full_size *= 1024;
- /* FALLTHRU */
+ FALLTHROUGH_INTENDED;
case 'K':
full_size *= 1024;
- /* FALLTHRU */
- case 'b':
+ FALLTHROUGH_INTENDED;
+ case 'B':
break;
}
switch (consumed_mult[0]) {
case 'G':
full_consumed *= 1024;
- /* FALLTHRU */
+ FALLTHROUGH_INTENDED;
case 'M':
full_consumed *= 1024;
- /* FALLTHRU */
+ FALLTHROUGH_INTENDED;
case 'K':
full_consumed *= 1024;
- /* FALLTHRU */
- case 'b':
+ FALLTHROUGH_INTENDED;
+ case 'B':
break;
}
EXPECT_GT(full_size, full_consumed);
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index e4393a3..513c0c3 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -475,9 +475,7 @@
static int convertKernelPrioToAndroidPrio(int pri) {
switch (pri & LOG_PRIMASK) {
case LOG_EMERG:
- // FALLTHRU
case LOG_ALERT:
- // FALLTHRU
case LOG_CRIT:
return ANDROID_LOG_FATAL;
@@ -488,9 +486,7 @@
return ANDROID_LOG_WARN;
default:
- // FALLTHRU
case LOG_NOTICE:
- // FALLTHRU
case LOG_INFO:
break;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index d6b8ab3..469f6dc 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -531,7 +531,7 @@
name = std::string_view(alloc->c_str(), alloc->size());
}
- explicit TagNameKey(TagNameKey&& rval)
+ explicit TagNameKey(TagNameKey&& rval) noexcept
: alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
rval.alloc = nullptr;
}
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
index 576a677..c3cf746 100644
--- a/mkbootimg/Android.bp
+++ b/mkbootimg/Android.bp
@@ -31,3 +31,34 @@
header_libs: ["libmkbootimg_abi_headers"],
export_header_lib_headers: ["libmkbootimg_abi_headers"],
}
+
+python_defaults {
+ name: "mkbootimg_defaults",
+
+ version: {
+ py2: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ py3: {
+ enabled: false,
+ embedded_launcher: false,
+ },
+ },
+}
+
+python_binary_host {
+ name: "mkbootimg",
+ defaults: ["mkbootimg_defaults"],
+ srcs: [
+ "mkbootimg.py",
+ ],
+}
+
+python_binary_host {
+ name: "unpack_bootimg",
+ defaults: ["mkbootimg_defaults"],
+ srcs: [
+ "unpack_bootimg.py",
+ ],
+}
diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk
deleted file mode 100644
index 92e1e27..0000000
--- a/mkbootimg/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := mkbootimg
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-
-LOCAL_MODULE := mkbootimg
-
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := unpack_bootimg
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-
-LOCAL_MODULE := unpack_bootimg
-
-include $(BUILD_PREBUILT)
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index bce308b..4432f9e 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -110,25 +110,25 @@
*/
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; /* offset to recovery dtbo in boot image */
+ uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO/ACPIO image */
+ uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
uint32_t header_size;
} __attribute__((packed));
/* When the boot image header has a version of 1, the structure of the boot
* image is as follows:
*
- * +-----------------+
- * | boot header | 1 page
- * +-----------------+
- * | kernel | n pages
- * +-----------------+
- * | ramdisk | m pages
- * +-----------------+
- * | second stage | o pages
- * +-----------------+
- * | recovery dtbo | p pages
- * +-----------------+
+ * +---------------------+
+ * | boot header | 1 page
+ * +---------------------+
+ * | kernel | n pages
+ * +---------------------+
+ * | ramdisk | m pages
+ * +---------------------+
+ * | second stage | o pages
+ * +---------------------+
+ * | recovery dtbo/acpio | p pages
+ * +---------------------+
* n = (kernel_size + page_size - 1) / page_size
* m = (ramdisk_size + page_size - 1) / page_size
* o = (second_size + page_size - 1) / page_size
@@ -136,13 +136,14 @@
*
* 0. all entities are page_size aligned in flash
* 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)
+ * 2. recovery_dtbo/recovery_acpio 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) at
* the specified physical address (kernel_addr, etc)
- * 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.
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery
+ * dtbo/acpio 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.
* 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg.py
old mode 100755
new mode 100644
similarity index 95%
rename from mkbootimg/mkbootimg
rename to mkbootimg/mkbootimg.py
index fda9af0..2eb2bab
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg.py
@@ -161,7 +161,10 @@
required=True)
parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
- parser.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
+ recovery_dtbo_group = parser.add_mutually_exclusive_group()
+ recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
+ recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
+ type=FileType('rb'), metavar='RECOVERY_ACPIO', dest='recovery_dtbo')
parser.add_argument('--cmdline', help='extra arguments to be passed on the '
'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg.py
old mode 100755
new mode 100644
similarity index 100%
rename from mkbootimg/unpack_bootimg
rename to mkbootimg/unpack_bootimg.py
diff --git a/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
deleted file mode 100644
index eed2ab5..0000000
--- a/platform_tools_tool_version.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (C) 2017 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.
-
-# We rewrite ${PLATFORM_SDK_VERSION} with 0 rather than $(PLATFORM_SDK_VERSION)
-# because on the actual platform tools release branches the file contains a
-# literal instead. Using 0 lets us easily distinguish non-canonical builds.
-platform_tools_version := $(shell sed \
- 's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
- development/sdk/plat_tools_source.prop_template \
- )
-tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 07f0797..a4c3955 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -77,7 +77,7 @@
#
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data odm oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
+ sbin dev proc sys system data odm oem acct config storage mnt apex $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
@@ -206,6 +206,7 @@
LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
ld_config_template := $(LOCAL_PATH)/etc/ld.config.txt
+check_backward_compatibility := true
vndk_version := $(PLATFORM_VNDK_VERSION)
include $(LOCAL_PATH)/update_and_install_ld_config.mk
@@ -243,6 +244,7 @@
LOCAL_MODULE_STEM := $$(LOCAL_MODULE)
include $(BUILD_SYSTEM)/base_rules.mk
ld_config_template := $(LOCAL_PATH)/etc/ld.config.txt
+check_backward_compatibility := true
vndk_version := $(1)
lib_list_from_prebuilts := true
include $(LOCAL_PATH)/update_and_install_ld_config.mk
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index f2892f8..d3e80c9 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -8,7 +8,6 @@
dir.system = /system/bin/
dir.system = /system/xbin/
dir.system = /%PRODUCT%/bin/
-dir.system = /%PRODUCT_SERVICES%/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
@@ -78,9 +77,9 @@
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths += /product/${LIB}
+namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.default.asan.search.paths += /product_services/${LIB}
+namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.permitted.paths = /data
namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -345,7 +344,9 @@
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
namespace.system.asan.search.paths += /data/asan/product/${LIB}
-namespace.system.asan.search.paths += /product/${LIB}
+namespace.system.asan.search.paths += /%PRODUCT%/${LIB}
+namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.system.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
###############################################################################
# Namespace config for binaries under /postinstall.
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index db65c14..7e354ac 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,7 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
-dir.system = /product/bin/
+dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
@@ -41,7 +41,8 @@
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
@@ -50,7 +51,9 @@
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths += /product/${LIB}
+namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
###############################################################################
# "sphal" namespace
@@ -209,7 +212,8 @@
namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.search.paths += /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
@@ -230,7 +234,9 @@
namespace.default.asan.search.paths += /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths += /product/${LIB}
+namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
###############################################################################
# Namespace config for binaries under /postinstall.
@@ -243,4 +249,5 @@
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index e20b95d..d8f6095 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,6 +1,7 @@
# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
libandroid.so
libaaudio.so
+libbinder_ndk.so
libc.so
libcamera2ndk.so
libdl.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index ff0813d..20905bf 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -2,6 +2,7 @@
libandroid.so
libandroidthings.so
libaaudio.so
+libbinder_ndk.so
libc.so
libcamera2ndk.so
libdl.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 3c46094..4ece5b5 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,6 +1,7 @@
# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
libandroid.so
libaaudio.so
+libbinder_ndk.so
libc.so
libcamera2ndk.so
libdl.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1c2ef64..6a6a8f9 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -269,6 +269,12 @@
# that they can be chown'd to system:system later on boot
write /sys/class/leds/vibrator/trigger "transient"
+ # Setup APEX mount point and its security context
+ mount tmpfs tmpfs /apex nodev noexec nosuid
+ chmod 0755 /apex
+ chown root root /apex
+ restorecon /apex
+
# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
@@ -306,12 +312,12 @@
# /data, which in turn can only be loaded when system properties are present.
trigger post-fs-data
- # Now we can start zygote for devices with file based encryption
- trigger zygote-start
-
# Load persist properties and override properties (if enabled) from /data.
trigger load_persist_props_action
+ # Now we can start zygote for devices with file based encryption
+ trigger zygote-start
+
# Remove a file to wake up anything waiting for firmware.
trigger firmware_mounts_complete
@@ -400,6 +406,7 @@
# Make sure we have the device encryption key.
start vold
+ exec - system system -- /system/bin/vdc checkpoint prepareDriveForCheckpoint /data
installkey /data
# Start bootcharting as soon as possible after the data partition is
@@ -407,6 +414,9 @@
mkdir /data/bootchart 0755 shell shell
bootchart start
+ # Start apexd as soon as we can
+ start apexd
+
# Avoid predictable entropy pool. Carry over entropy from previous boot.
copy /data/system/entropy.dat /dev/urandom
@@ -520,6 +530,8 @@
mkdir /data/anr 0775 system system
+ mkdir /data/apex 0770 root root
+
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc
mkdir /data/nfc/param 0770 nfc nfc
diff --git a/rootdir/ld_config_backward_compatibility_check.py b/rootdir/ld_config_backward_compatibility_check.py
new file mode 100755
index 0000000..1a27578
--- /dev/null
+++ b/rootdir/ld_config_backward_compatibility_check.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import glob
+import os.path
+import re
+import sys
+
+PREBUILTS_VNDK_DIR = "prebuilts/vndk"
+VENDOR_DIRECTORIES = ('/vendor', '/odm')
+
+def find_latest_vndk_snapshot_version():
+ """Returns latest vndk snapshot version in current source tree.
+ It will skip the test if the snapshot directories are not found.
+
+ Returns:
+ latest_version: string
+ """
+ vndk_dir_list = glob.glob(PREBUILTS_VNDK_DIR + "/v*")
+ if not vndk_dir_list:
+ """Exit without error because we may have source trees that do not include
+ VNDK snapshot directories in it.
+ """
+ sys.exit(0)
+ vndk_ver_list = [re.match(r".*/v(\d+)", vndk_dir).group(1)
+ for vndk_dir in vndk_dir_list]
+ latest_version = max(vndk_ver_list)
+ if latest_version == '27':
+ """Exit without error because VNDK v27 is not using ld.config.txt template
+ """
+ sys.exit(0)
+ return latest_version
+
+def get_vendor_configuration(ld_config_file):
+ """Reads the ld.config.txt file to parse the namespace configurations.
+ It finds the configurations that include vendor directories.
+
+ Args:
+ ld_config_file: string, path (relative to build top) of the ld.config.txt
+ file.
+ Returns:
+ configs: dict{string:[string]}, dictionary of namespace configurations.
+ it has 'section + property' names as keys and the directory list
+ as values.
+ """
+ try:
+ conf_file = open(ld_config_file)
+ except IOError:
+ print("error: could not read %s" % ld_config_file)
+ sys.exit(1)
+
+ configs = dict()
+ current_section = None
+
+ with conf_file:
+ for line in conf_file:
+ # ignore comments
+ found = line.find('#')
+ if found != -1:
+ line = line[:found]
+ line = line.strip()
+ if not line:
+ continue
+
+ if line[0] == '[' and line[-1] == ']':
+ # new section started
+ current_section = line[1:-1]
+ continue
+
+ if current_section == None:
+ continue
+
+ found = line.find('+=')
+ opr_len = 2
+ if found == -1:
+ found = line.find('=')
+ opr_len = 1
+ if found == -1:
+ continue
+
+ namespace = line[:found].strip()
+ if not namespace.endswith(".paths"):
+ # check ".paths" only
+ continue
+ namespace = '[' + current_section + ']' + namespace
+ values = line[found + opr_len:].strip()
+ directories = values.split(':')
+
+ for directory in directories:
+ if any(vendor_dir in directory for vendor_dir in VENDOR_DIRECTORIES):
+ if namespace in configs:
+ configs[namespace].append(directory)
+ else:
+ configs[namespace] = [directory]
+
+ return configs
+
+def get_snapshot_config(version):
+ """Finds the ld.config.{version}.txt file from the VNDK snapshot directory.
+ In the vndk prebuilt directory (prebuilts/vndk/v{version}), it searches
+ {arch}/configs/ld.config.{version}.txt file, where {arch} is one of ('arm64',
+ 'arm', 'x86_64', 'x86').
+
+ Args:
+ version: string, the VNDK snapshot version to search.
+ Returns:
+ ld_config_file: string, relative path to ld.config.{version}.txt
+ """
+ arch_list = ('arm64', 'arm', 'x86_64', 'x86')
+ for arch in arch_list:
+ ld_config_file = (PREBUILTS_VNDK_DIR
+ + "/v{0}/{1}/configs/ld.config.{0}.txt".format(version, arch))
+ if os.path.isfile(ld_config_file):
+ return ld_config_file
+ print("error: cannot find ld.config.{0}.txt file in snapshot v{0}"
+ .format(version))
+ sys.exit(1)
+
+def check_backward_compatibility(ld_config, vndk_snapshot_version):
+ """Checks backward compatibility for current ld.config.txt file with the
+ old ld.config.txt file. If any of the vendor directories in the old namespace
+ configurations are missing, the test will fail. It is allowed to have new
+ vendor directories in current ld.config.txt file.
+
+ Args:
+ ld_config: string, relative path to current ld.config.txt file.
+ vndk_snapshot_version: string, the VNDK snapshot version that has an old
+ ld.config.txt file to compare.
+ Returns:
+ result: bool, True if the current configuration is backward compatible.
+ """
+ current_config = get_vendor_configuration(ld_config)
+ old_config = get_vendor_configuration(
+ get_snapshot_config(vndk_snapshot_version))
+ for namespace in old_config:
+ if namespace not in current_config:
+ print("error: cannot find %s which was provided in ld.config.%s.txt"
+ % (namespace, vndk_snapshot_version))
+ return False
+ for path in old_config[namespace]:
+ if not path in current_config[namespace]:
+ print("error: %s for %s in ld.config.%s.txt are missing in %s"
+ % (path, namespace, vndk_snapshot_version, ld_config))
+ return False
+ return True
+
+def main():
+ if len(sys.argv) != 2:
+ print ("Usage: %s target_ld_config_txt_file_name" % sys.argv[0])
+ sys.exit(1)
+
+ latest_vndk_snapshot_version = find_latest_vndk_snapshot_version()
+ if not check_backward_compatibility(sys.argv[1],
+ latest_vndk_snapshot_version):
+ print("error: %s has backward incompatible changes to old "
+ "vendor partition." % sys.argv[1])
+ sys.exit(1)
+
+ # Current ld.config.txt file is backward compatible
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 4d6df77..56a30b2 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -18,10 +18,13 @@
# Read inputs
ld_config_template := $(strip $(ld_config_template))
+check_backward_compatibility := $(strip $(check_backward_compatibility))
vndk_version := $(strip $(vndk_version))
lib_list_from_prebuilts := $(strip $(lib_list_from_prebuilts))
libz_is_llndk := $(strip $(libz_is_llndk))
+compatibility_check_script := \
+ $(LOCAL_PATH)/ld_config_backward_compatibility_check.py
intermediates_dir := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE))
library_lists_dir := $(intermediates_dir)
ifeq ($(lib_list_from_prebuilts),true)
@@ -82,11 +85,19 @@
$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(sanitizer_runtime_libraries)
$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_SUFFIX := $(vndk_version_suffix)
$(LOCAL_BUILT_MODULE): PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
+$(LOCAL_BUILT_MODULE): PRIVATE_COMP_CHECK_SCRIPT := $(compatibility_check_script)
deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
$(vndkprivate_libraries_file)
+ifeq ($(check_backward_compatibility),true)
+deps += $(compatibility_check_script)
+endif
$(LOCAL_BUILT_MODULE): $(ld_config_template) $(deps)
@echo "Generate: $< -> $@"
+ifeq ($(check_backward_compatibility),true)
+ @echo "Checking backward compatibility..."
+ $(hide) $(PRIVATE_COMP_CHECK_SCRIPT) $<
+endif
@mkdir -p $(dir $@)
$(call private-filter-out-private-libs,$(PRIVATE_LLNDK_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
$(hide) sed -e "s?%LLNDK_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)?g" $< >$@
@@ -108,9 +119,11 @@
$(hide) rm -f $@.bak
ld_config_template :=
+check_backward_compatibility :=
vndk_version :=
lib_list_from_prebuilts :=
libz_is_llndk :=
+compatibility_check_script :=
intermediates_dir :=
library_lists_dir :=
llndk_libraries_file :=
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 3d7521c..2d4a26f 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -21,6 +21,7 @@
"tcpdump",
"toolbox",
"toybox",
+ "unzip",
],
}
@@ -31,6 +32,7 @@
"sh.recovery",
"toolbox.recovery",
"toybox.recovery",
+ "unzip.recovery",
],
}
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 7466728..733b60f 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -62,7 +62,7 @@
"uid_info.cpp",
"storaged.proto",
":storaged_aidl",
- "binder/android/os/storaged/IStoragedPrivate.aidl",
+ ":storaged_aidl_private",
],
static_libs: ["libhealthhalutils"],
@@ -116,4 +116,13 @@
srcs: [
"binder/android/os/IStoraged.aidl",
],
+ path: "binder",
+}
+
+filegroup {
+ name: "storaged_aidl_private",
+ srcs: [
+ "binder/android/os/storaged/IStoragedPrivate.aidl",
+ ],
+ path: "binder",
}
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 1050033..8b5001d 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -129,6 +129,10 @@
bool success = false;
auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
+ if (result == Result::NOT_SUPPORTED) {
+ LOG_TO(SYSTEM, DEBUG) << "getDiskStats is not supported on health HAL.";
+ return;
+ }
if (result != Result::SUCCESS || halStats.size() == 0) {
LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
<< " and size " << halStats.size();
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 5605f66..8c0b3d1 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -370,8 +370,12 @@
void health_storage_info_t::report() {
auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
+ if (result == Result::NOT_SUPPORTED) {
+ LOG_TO(SYSTEM, DEBUG) << "getStorageInfo is not supported on health HAL.";
+ return;
+ }
if (result != Result::SUCCESS || halInfos.size() == 0) {
- LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with result " << toString(result)
+ LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with result " << toString(result)
<< " and size " << halInfos.size();
return;
}
@@ -380,7 +384,7 @@
});
if (!ret.isOk()) {
- LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with " << ret.description();
+ LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with " << ret.description();
}
}
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index 8e3b3b1..98cbcc3 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -21,6 +21,7 @@
#include <cutils/log.h>
#include <keymaster/android_keymaster_messages.h>
#include <trusty_keymaster/TrustyKeymaster3Device.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
using ::keymaster::AbortOperationRequest;
using ::keymaster::AbortOperationResponse;
@@ -111,7 +112,8 @@
}
}
}
- KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} {
+ KmParamSet(KmParamSet&& other) noexcept
+ : keymaster_key_param_set_t{other.params, other.length} {
other.length = 0;
other.params = nullptr;
}
@@ -393,20 +395,32 @@
const hidl_vec<KeyParameter>& inParams,
const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {
UpdateOperationRequest request;
- request.op_handle = operationHandle;
- request.input.Reinitialize(input.data(), input.size());
- request.additional_params.Reinitialize(KmParamSet(inParams));
-
UpdateOperationResponse response;
- impl_->UpdateOperation(request, &response);
-
- uint32_t resultConsumed = 0;
hidl_vec<KeyParameter> resultParams;
hidl_vec<uint8_t> resultBlob;
- if (response.error == KM_ERROR_OK) {
- resultConsumed = response.input_consumed;
- resultParams = kmParamSet2Hidl(response.output_params);
- resultBlob = kmBuffer2hidlVec(response.output);
+ uint32_t resultConsumed = 0;
+
+ request.op_handle = operationHandle;
+ request.additional_params.Reinitialize(KmParamSet(inParams));
+
+ size_t inp_size = input.size();
+ size_t ser_size = request.SerializedSize();
+
+ if (ser_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+ response.error = KM_ERROR_INVALID_INPUT_LENGTH;
+ } else {
+ if (ser_size + inp_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+ inp_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - ser_size;
+ }
+ request.input.Reinitialize(input.data(), inp_size);
+
+ impl_->UpdateOperation(request, &response);
+
+ if (response.error == KM_ERROR_OK) {
+ resultConsumed = response.input_consumed;
+ resultParams = kmParamSet2Hidl(response.output_params);
+ resultBlob = kmBuffer2hidlVec(response.output);
+ }
}
_hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);
return Void();
diff --git a/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc b/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
index e9d3054..503f3de 100644
--- a/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
+++ b/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
@@ -1,4 +1,4 @@
service vendor.keymaster-3-0 /vendor/bin/hw/android.hardware.keymaster@3.0-service.trusty
class early_hal
user nobody
- group system drmrpc
+ group drmrpc
diff --git a/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h b/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
index 5a80795..a483c0d 100644
--- a/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
+++ b/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
@@ -80,6 +80,8 @@
const keymaster_blob_t* input, const keymaster_blob_t* signature,
keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
keymaster_error_t abort(keymaster_operation_handle_t operation_handle);
+ keymaster_error_t delete_key(const keymaster_key_blob_t* key);
+ keymaster_error_t delete_all_keys();
private:
keymaster_error_t Send(uint32_t command, const Serializable& request,
diff --git a/trusty/keymaster/legacy/trusty_keymaster_device.cpp b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
index ea00a92..88c3e7b 100644
--- a/trusty/keymaster/legacy/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
@@ -70,8 +70,8 @@
device_.export_key = export_key;
device_.attest_key = attest_key;
device_.upgrade_key = upgrade_key;
- device_.delete_key = nullptr;
- device_.delete_all_keys = nullptr;
+ device_.delete_key = delete_key;
+ device_.delete_all_keys = delete_all_keys;
device_.begin = begin;
device_.update = update;
device_.finish = finish;
@@ -122,10 +122,10 @@
void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
RequestType* request) {
request->additional_params.Clear();
- if (client_id) {
+ if (client_id && client_id->data_length > 0) {
request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
}
- if (app_data) {
+ if (app_data && app_data->data_length > 0) {
request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
}
}
@@ -606,6 +606,34 @@
return trusty_keymaster_send(KM_ABORT_OPERATION, request, &response);
}
+keymaster_error_t TrustyKeymasterDevice::delete_key(const keymaster_key_blob_t* key) {
+ ALOGD("Device received delete_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ if (!key || !key->key_material)
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+
+ DeleteKeyRequest request(message_version_);
+ request.SetKeyMaterial(*key);
+ DeleteKeyResponse response(message_version_);
+ return trusty_keymaster_send(KM_DELETE_KEY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::delete_all_keys() {
+ ALOGD("Device received delete_all_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ DeleteAllKeysRequest request(message_version_);
+ DeleteAllKeysResponse response(message_version_);
+ return trusty_keymaster_send(KM_DELETE_ALL_KEYS, request, &response);
+}
+
hw_device_t* TrustyKeymasterDevice::hw_device() {
return &device_.common;
}
@@ -719,4 +747,15 @@
return convert_device(dev)->abort(operation_handle);
}
+/* static */
+keymaster_error_t TrustyKeymasterDevice::delete_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key) {
+ return convert_device(dev)->delete_key(key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::delete_all_keys(const keymaster2_device_t* dev) {
+ return convert_device(dev)->delete_all_keys();
+}
+
} // namespace keymaster